From 796a8af23b9bdce1ec08bd5b9d7edb860106a6b6 Mon Sep 17 00:00:00 2001 From: bengotow Date: Tue, 25 Jun 2024 00:29:58 -0500 Subject: [PATCH] [ui] Asset health polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - The “All keys” dropdown looks like our other dropdowns (it’s a typeahead that is also a dropdown) - The experimental banner is gone! - Check tag names truncate correctly - The checks that have failed appear in a popover - The tabs have been revamped to match the new designs - The dropdown (Actually all Asset dropdowns) now allow for Report Materialization and View Checks. - The loading state now says “Evaluating” instead of “Scanning” - The empty state now has a spinning daggy on all three tabs except executing, which uses a standard non-ideal state to show the CTA to the catalog page. - Code location and group are now two separate columns, which allows the table to be 56 instead of 64px per row. Remaining: - Product tour - Ability to limit failures to a time range. This is possible for check failures / execution failures until they are partitioned, and then it’s pretty much impossible on top of our current GraphQL schema :( --- .../ui-components/src/components/Icon.tsx | 2 + .../ui-components/src/icon-svgs/executing.svg | 6 ++ .../ui-core/src/assets/AssetActionMenu.tsx | 43 +++++++++++- .../asset-checks/AssetCheckStatusTag.tsx | 66 ++++++++++++++----- .../asset-checks/AssetChecksStatusSummary.tsx | 12 ++-- .../src/assets/useReportEventsModal.tsx | 6 +- .../packages/ui-core/src/ui/TagActions.tsx | 3 + 7 files changed, 110 insertions(+), 28 deletions(-) create mode 100644 js_modules/dagster-ui/packages/ui-components/src/icon-svgs/executing.svg diff --git a/js_modules/dagster-ui/packages/ui-components/src/components/Icon.tsx b/js_modules/dagster-ui/packages/ui-components/src/components/Icon.tsx index 2b87f99bcec23..0ba0c42b279cb 100644 --- a/js_modules/dagster-ui/packages/ui-components/src/components/Icon.tsx +++ b/js_modules/dagster-ui/packages/ui-components/src/components/Icon.tsx @@ -69,6 +69,7 @@ import email from '../icon-svgs/email.svg'; import error from '../icon-svgs/error.svg'; import error_outline from '../icon-svgs/error_outline.svg'; import execute from '../icon-svgs/execute.svg'; +import executing from '../icon-svgs/executing.svg'; import expand from '../icon-svgs/expand.svg'; import expand_arrows from '../icon-svgs/expand_arrows.svg'; import expand_less from '../icon-svgs/expand_less.svg'; @@ -205,6 +206,7 @@ export const Icons = { datatype_number, expectation, execute, + executing, materialization, observation, job, diff --git a/js_modules/dagster-ui/packages/ui-components/src/icon-svgs/executing.svg b/js_modules/dagster-ui/packages/ui-components/src/icon-svgs/executing.svg new file mode 100644 index 0000000000000..76aa006e68031 --- /dev/null +++ b/js_modules/dagster-ui/packages/ui-components/src/icon-svgs/executing.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetActionMenu.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetActionMenu.tsx index 1dddf94655ab6..c26b766d67ad3 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/AssetActionMenu.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/AssetActionMenu.tsx @@ -1,4 +1,13 @@ -import {Button, Icon, Menu, MenuItem, Popover, Spinner, Tooltip} from '@dagster-io/ui-components'; +import { + Button, + Icon, + Menu, + MenuDivider, + MenuItem, + Popover, + Spinner, + Tooltip, +} from '@dagster-io/ui-components'; import {useContext} from 'react'; import { @@ -8,6 +17,7 @@ import { import {useObserveAction} from './LaunchAssetObservationButton'; import {assetDetailsPathForKey} from './assetDetailsPathForKey'; import {AssetTableDefinitionFragment} from './types/AssetTableFragment.types'; +import {useReportEventsModal} from './useReportEventsModal'; import {CloudOSSContext} from '../app/CloudOSSContext'; import {showSharedToaster} from '../app/DomUtils'; import {usePermissionsForLocation} from '../app/Permissions'; @@ -30,14 +40,24 @@ export const AssetActionMenu = (props: Props) => { } = usePermissionsForLocation(repoAddress?.location); const { - featureContext: {canSeeWipeMaterializationAction}, + featureContext: {canSeeWipeMaterializationAction, canSeeMaterializeAction}, } = useContext(CloudOSSContext); const {executeItem, launchpadElement} = useExecuteAssetMenuItem(path, definition); + const reportEvents = useReportEventsModal( + repoAddress + ? { + assetKey: {path}, + isPartitioned: !!definition?.partitionDefinition, + repository: {name: repoAddress.name, location: {name: repoAddress.location}}, + } + : null, + ); return ( <> {launchpadElement} + {reportEvents.element} { icon="asset_group" /> + { disabled={!definition} icon="graph_downstream" /> + {canSeeMaterializeAction && definition?.hasMaterializePermission + ? reportEvents.dropdownOptions.map((option) => ( + + )) + : undefined} + {canSeeWipeMaterializationAction ? : undefined} {canSeeWipeMaterializationAction ? ( ( { const {executionForLatestMaterialization: execution} = assetCheck; @@ -228,29 +231,56 @@ export const AssetCheckStatusTag = ({ export const AssetCheckErrorsTag = ({ checks, severity, + assetKey, }: { checks: AssetCheckLiveFragment[]; severity: AssetCheckSeverity; + assetKey: AssetKeyInput; }) => { - const actions: TagAction[] = []; - const execution = checks[0]?.executionForLatestMaterialization; - if (execution) { + const tagIcon = severity === AssetCheckSeverity.ERROR ? 'cancel' : 'warning_outline'; + const tagIntent = severity === AssetCheckSeverity.ERROR ? 'danger' : 'warning'; + + if (checks.length === 1) { + const actions: TagAction[] = []; + const execution = checks[0]!.executionForLatestMaterialization; + actions.push({ - label: 'View in run logs', - to: linkToRunEvent( - {id: execution.runId}, - {stepKey: execution.stepKey, timestamp: execution.timestamp}, - ), + label: 'View details', + to: assetDetailsPathForAssetCheck({assetKey, name: checks[0]!.name}), }); + if (execution) { + actions.push({ + label: 'View in run logs', + to: linkToRunEvent( + {id: execution.runId}, + {stepKey: execution.stepKey, timestamp: execution.timestamp}, + ), + }); + } + + return ( + + + + + + ); } + return ( - - - {checks.length === 1 ? checks[0]!.name : `${checks.length} failed`} + } + position="top-left" + interactionKind="hover" + className="chunk-popover-target" + > + + {`${checks.length} failed`} - + ); }; diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksStatusSummary.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksStatusSummary.tsx index 548079734545c..875103983ce76 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksStatusSummary.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/asset-checks/AssetChecksStatusSummary.tsx @@ -15,7 +15,11 @@ import {CheckStatusRow} from './AssetCheckStatusTag'; import {assertUnreachable} from '../../app/Util'; import {AssetCheckLiveFragment} from '../../asset-data/types/AssetBaseDataProvider.types'; import {LiveDataForNode} from '../../asset-graph/Utils'; -import {AssetCheckExecutionResolvedStatus, AssetCheckSeverity, AssetKey} from '../../graphql/types'; +import { + AssetCheckExecutionResolvedStatus, + AssetCheckSeverity, + AssetKeyInput, +} from '../../graphql/types'; type AssetCheckIconType = | Exclude< @@ -82,13 +86,13 @@ const titlePerCheckType = (type: AssetCheckIconType) => { } }; -const ChecksSummaryPopover = ({ +export const ChecksSummaryPopover = ({ type, assetKey, assetChecks, }: { type: AssetCheckIconType; - assetKey: AssetKey; + assetKey: AssetKeyInput; assetChecks: AssetCheckLiveFragment[]; }) => { return ( @@ -123,7 +127,7 @@ export const AssetChecksStatusSummary = ({ }: { liveData: LiveDataForNode; rendering: 'dag' | 'tags'; - assetKey: AssetKey; + assetKey: AssetKeyInput; }) => { const byIconType = countBy(liveData.assetChecks, iconTypeFromCheck); diff --git a/js_modules/dagster-ui/packages/ui-core/src/assets/useReportEventsModal.tsx b/js_modules/dagster-ui/packages/ui-core/src/assets/useReportEventsModal.tsx index 2649a97a25410..1abf41555eb78 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/assets/useReportEventsModal.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/assets/useReportEventsModal.tsx @@ -42,7 +42,7 @@ type Asset = { repository: {name: string; location: {name: string}}; }; -export function useReportEventsModal(asset: Asset | null, onEventReported: () => void) { +export function useReportEventsModal(asset: Asset | null, onEventReported?: () => void) { const [isOpen, setIsOpen] = useState(false); const dropdownOptions = useMemo( @@ -85,7 +85,7 @@ const ReportEventDialogBody = ({ repoAddress: RepoAddress; isOpen: boolean; setIsOpen: (open: boolean) => void; - onEventReported: () => void; + onEventReported?: () => void; }) => { const [description, setDescription] = useState(''); const { @@ -159,7 +159,7 @@ const ReportEventDialogBody = ({ icon: 'materialization', intent: 'success', }); - onEventReported(); + onEventReported?.(); setIsOpen(false); } }; diff --git a/js_modules/dagster-ui/packages/ui-core/src/ui/TagActions.tsx b/js_modules/dagster-ui/packages/ui-core/src/ui/TagActions.tsx index 22e031af837fd..6a4e2a726d69a 100644 --- a/js_modules/dagster-ui/packages/ui-core/src/ui/TagActions.tsx +++ b/js_modules/dagster-ui/packages/ui-core/src/ui/TagActions.tsx @@ -37,16 +37,19 @@ export const TagActionsPopover = ({ data, actions, children, + childrenMiddleTruncate, }: { data: TagType; actions: TagAction[]; children: React.ReactNode; + childrenMiddleTruncate?: boolean; }) => { return ( } hoverOpenDelay={100} hoverCloseDelay={100} + targetProps={childrenMiddleTruncate ? {style: {minWidth: 0, maxWidth: '100%'}} : {}} placement="top" interactionKind="hover" >