Skip to content

Commit

Permalink
Add details drawer to Grid View (#22123)
Browse files Browse the repository at this point in the history
* make UI and tree work with mapped tasks

basic slide drawer

reformat grid background colors

improve rendering and add selected dag run

fix hover and extra prop

switch from drawer to details section

add tooltip info to details

use API

make side panel collapsible, useTasks,

dag run actions

dag run actions w/ react-query

task instance links

task actions

remove modals

adjust panel width and use status color

minor details styling

add duration to tooltips

add last scheduling decision and fix tests

* move ref and selection to providers

* fix test with mock providers

* update TI and DR buttons

* download logs and external logs

* add extra links to TI details

* download log bug fixes

* fix extra links, hide local TZ if UTC,

* confirm mark task failed/success

* Update confirm modals for runs and tasks

- async/await on mutations instead of useeffect
- add confirmation for run actions

* Fix dialog scrolling

* Code cleanup and fix task clear

* Fix task/run label, dialog focus, dag details overflow, panel open/close

* Add timezone provider

* Fix TimezoneEvent import

* Improve button UX

- Remove details panel title
- Add button to reset root
- Make "More Details" buttons more specific
- Specify timezone as DAG timezone

* autorefresh dag run details

* auto-refresh task instance details

* revert useTreeData changes

None of these changes were relevant to this PR. Better to be done separately.

* Address PR feedback

- useState vs useDisclosure
- Remove extraneous elements
- Copy changes
- Wire up params for runTask
- Breadcrumb padding

* Handle task/run action sideeffects by separating autorefresh and treeData hooks

* Clean up views.py endpoints

- Pass 'Accept' headers for json returns
- Consolidate more endpoints to return json or redirect

* pass request as arg

* remove request as arg

* Anticipate when the 'Accept' header is not present

* Fix argument count errors

* Replace hard coded urls

* Replace hard coded urls in react components

* Update filter upstream link

* Split TaskInstance details component

* Fix undefined variables in tests

* init_api_connexion in tests

- add readme
-  rename context providers to avoid confusion with Airflow Providers

* Fix url params, hide last item breadcrumb links

* Update task run failed copy

* Fix taskinstance/list buttons

Co-authored-by: Tzu-ping Chung <tp@astronomer.io>
GitOrigin-RevId: 2bb26a38070a4b949bfb210ef1d5644e016e373a
  • Loading branch information
2 people authored and Cloud Composer Team committed Sep 12, 2024
1 parent c8145fb commit 25788e7
Show file tree
Hide file tree
Showing 64 changed files with 3,419 additions and 578 deletions.
3 changes: 2 additions & 1 deletion airflow/www/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"@emotion/cache": "^11.4.0",
"@emotion/react": "^11.4.1",
"@emotion/styled": "^11",
"axios": "^0.26.0",
"bootstrap-3-typeahead": "^4.0.2",
"camelcase-keys": "^7.0.0",
"codemirror": "^5.59.1",
Expand All @@ -92,7 +93,7 @@
"nvd3": "^1.8.6",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-icons": "^4.2.0",
"react-icons": "^4.3.1",
"react-query": "^3.34.16",
"redoc": "^2.0.0-rc.63",
"url-search-params-polyfill": "^8.1.0"
Expand Down
20 changes: 2 additions & 18 deletions airflow/www/static/js/dag.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@ function updateModalUrls() {
_flt_3_dag_id: dagId,
_flt_3_task_id: taskId,
_flt_3_map_index: mapIndex,
_oc_TaskInstanceModelView: executionDate,
_oc_TaskInstanceModelView: 'dag_run.execution_date',
});

updateButtonUrl(buttons.mapped, {
_flt_3_dag_id: dagId,
_flt_3_task_id: taskId,
_flt_3_run_id: dagRunId,
_oc_TaskInstanceModelView: executionDate,
_oc_TaskInstanceModelView: 'map_index',
});

updateButtonUrl(buttons.log, {
Expand Down Expand Up @@ -313,22 +313,6 @@ $(document).on('click', '#btn_back', () => {
});
});

export function callModalDag(dag) {
$('#dagModal').modal({});
$('#dagModal').css('margin-top', '0');
$('#run_id').text(dag.run_id);
executionDate = dag.execution_date;
dagRunId = dag.run_id;
updateButtonUrl(buttons.dag_graph_view, {
dag_id: dag && dag.dag_id,
execution_date: dag && dag.execution_date,
});
updateButtonUrl(buttons.dagrun_details, {
dag_id: dag && dag.dag_id,
run_id: dag && dag.run_id,
});
}

// Task Instance Modal actions
$('form[data-action]').on('submit', function submit(e) {
e.preventDefault();
Expand Down
2 changes: 2 additions & 0 deletions airflow/www/static/js/datetime_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export const defaultFormatWithTZ = 'YYYY-MM-DD, HH:mm:ss z';
export const defaultTZFormat = 'z (Z)';
export const dateTimeAttrFormat = 'YYYY-MM-DDThh:mm:ssTZD';

export const TimezoneEvent = 'timezone';

export function formatTimezone(what) {
if (what instanceof moment) {
return what.isUTC() ? 'UTC' : what.format(defaultTZFormat);
Expand Down
10 changes: 9 additions & 1 deletion airflow/www/static/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
* specific language governing permissions and limitations
* under the License.
*/
/* global $, moment, Airflow, window, localStorage, document, hostName, csrfToken */
/* global $, moment, Airflow, window, localStorage, document, hostName, csrfToken, Event */

import {
dateTimeAttrFormat,
formatTimezone,
isoDateToTimeEl,
setDisplayedTimezone,
TimezoneEvent,
} from './datetime_utils';

window.isoDateToTimeEl = isoDateToTimeEl;
Expand All @@ -43,6 +44,13 @@ function displayTime() {

function changeDisplayedTimezone(tz) {
localStorage.setItem('selected-timezone', tz);

// dispatch an event that React can listen for
const event = new Event(TimezoneEvent);
event.value = tz;
event.key = 'selected-timezone';
document.dispatchEvent(event);

setDisplayedTimezone(tz);
displayTime();
$('body').trigger({
Expand Down
78 changes: 9 additions & 69 deletions airflow/www/static/js/tree/InstanceTooltip.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,17 @@
* under the License.
*/

/* global moment */

import React from 'react';
import { Box, Text } from '@chakra-ui/react';

import { formatDateTime, getDuration, formatDuration } from '../datetime_utils';
import { finalStatesMap } from '../utils';
import { formatDuration, getDuration } from '../datetime_utils';
import Time from './Time';

const InstanceTooltip = ({
group,
instance: {
duration, operator, startDate, endDate, state, taskId, runId, mappedStates,
startDate, endDate, duration, state, runId, mappedStates,
},
}) => {
const isGroup = !!group.children;
Expand Down Expand Up @@ -78,87 +77,28 @@ const InstanceTooltip = ({
});
}

const taskIdTitle = isGroup ? 'Task Group Id: ' : 'Task Id: ';

return (
<Box fontSize="12px" py="4px">
<Box fontSize="12px" py="2px">
{group.tooltip && (
<Text>{group.tooltip}</Text>
)}
<Text>
<Text as="strong">Status:</Text>
{isGroup ? 'Overall ' : ''}
Status:
{' '}
{state || 'no status'}
</Text>
{isGroup && (
<>
<br />
<Text as="strong">Group Summary</Text>
{groupSummary}
</>
)}
{group.isMapped && (
<>
<br />
<Text as="strong">
{mappedStates.length}
{' '}
{mappedStates.length === 1 ? 'Task ' : 'Tasks '}
Mapped
</Text>
{mapSummary}
</>
)}
<br />
{isGroup && groupSummary}
<Text>
{taskIdTitle}
{taskId}
</Text>
<Text whiteSpace="nowrap">
Run Id:
{' '}
{runId}
</Text>
{operator && (
<Text>
Operator:
Started:
{' '}
{operator}
<Time dateTime={startDate} />
</Text>
)}
<Text>
Duration:
{' '}
{formatDuration(duration || getDuration(startDate, endDate))}
</Text>
<br />
<Text as="strong">UTC</Text>
<Text>
Started:
{' '}
{startDate && formatDateTime(moment.utc(startDate))}
</Text>
<Text>
Ended:
{' '}
{endDate && formatDateTime(moment.utc(endDate))}
</Text>
<br />
<Text as="strong">
Local:
{' '}
{moment().format('Z')}
</Text>
<Text>
Started:
{' '}
{startDate && formatDateTime(startDate)}
</Text>
<Text>
Ended:
{' '}
{endDate && formatDateTime(endDate)}
</Text>
</Box>
);
};
Expand Down
46 changes: 46 additions & 0 deletions airflow/www/static/js/tree/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->

# Grid View

In 2.3.0 the Tree view was completely rebuilt using React and renamed to Grid. Here is a primer on the new technologies used:

## [React](https://reactjs.org/)

The most popular javascript framework for building user interfaces with reusable components.
Written as javascript and html together in `.jsx` files.
In-component state can be managed via `useState()`, application state that spans many components can be managed via a context provider (see `/context` for examples), API state can be managed by React Query (see below)


## [Chakra UI](https://chakra-ui.com/)

A good component and helper function library. Tooltips, modals, toasts, switches, etc are all out of the box
Styles are applied via global theme when initializing the app or inline with individual components like `<Box padding="5px" />`


## [React Query](https://react-query.tanstack.com/)

A powerful async data handler that makes it easy to manage loading/error states as well as caching, refetching, background updates, etc.
This is our state management for any data that comes from an API.
Each API request is its own hook. Ie `useTasks` will get all the tasks for a DAG


## [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/)

Easily write tests for react components and hooks
45 changes: 45 additions & 0 deletions airflow/www/static/js/tree/ResetRoot.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*!
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import React from 'react';
import { Button, Link } from '@chakra-ui/react';

import { getMetaValue } from '../utils';

const root = getMetaValue('root');
const url = getMetaValue('grid_url_no_root');

const ResetRoot = () => (
root
? (
<Button
as={Link}
variant="outline"
href={url}
colorScheme="blue"
mr={2}
title="Reset root to show the whole DAG"
>
Reset Root
</Button>
)
: null
);

export default ResetRoot;
Loading

0 comments on commit 25788e7

Please sign in to comment.