Skip to content

Commit

Permalink
[UI] Refactoring of partition wipe dialog (#23196)
Browse files Browse the repository at this point in the history
## Summary & Motivation

Preparations + improvements to the structure of the partition wipe
modal, and a new injection site allowing it to be modified.

Misc:

- I updated the TagSelector padding slightly so that it doesn't change
height when you put in the first tag (prev 32 => 36px).
- I fixed a GraphQL query error when you wiped asset events and the
asset catalog table tried to reload with no variables passed to it's
GraphQL query.

## How I Tested These Changes

Verified that OSS functionality is unchanged.

Co-authored-by: bengotow <[email protected]>
  • Loading branch information
bengotow and bengotow authored Jul 30, 2024
1 parent 20e8596 commit 1ba71a3
Show file tree
Hide file tree
Showing 24 changed files with 1,275 additions and 755 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {UserPreferences} from '@dagster-io/ui-core/app/UserSettingsDialog/UserPr
import {useAssetGraphExplorerFilters} from '@dagster-io/ui-core/asset-graph/useAssetGraphExplorerFilters.oss';
import {AssetCatalogTableBottomActionBar} from '@dagster-io/ui-core/assets/AssetCatalogTableBottomActionBar.oss';
import {AssetPageHeader} from '@dagster-io/ui-core/assets/AssetPageHeader.oss';
import {AssetWipeDialog} from '@dagster-io/ui-core/assets/AssetWipeDialog.oss';
import {AssetsGraphHeader} from '@dagster-io/ui-core/assets/AssetsGraphHeader.oss';
import AssetsOverviewRoot from '@dagster-io/ui-core/assets/AssetsOverviewRoot.oss';
import {useAssetCatalogFiltering} from '@dagster-io/ui-core/assets/useAssetCatalogFiltering.oss';
Expand All @@ -21,6 +22,7 @@ export const InjectedComponents = ({children}: {children: React.ReactNode}) => {
AssetsOverview: AssetsOverviewRoot,
FallthroughRoot,
AssetsGraphHeader,
AssetWipeDialog,
OverviewPageAlerts: null,
RunMetricsDialog: null,
AssetCatalogTableBottomActionBar,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const ButtonGroup = <T extends string | number>(props: Props<T>) => {
const buttonElement = (
<BaseButton
key={id}
aria-selected={isActive}
fillColor={isActive ? Colors.backgroundLighterHover() : fillColor}
fillColorHover={isActive ? Colors.backgroundLighterHover() : fillColorHover}
textColor={isActive ? Colors.textDefault() : Colors.textLight()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ export const TagSelectorContainer = styled.div`
align-items: center;
${TextInputStyles}
min-height: 32px;
padding: 4px 8px;
`;

const Placeholder = styled.div`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {assertUnreachable} from './Util';
import {useAssetGraphExplorerFilters} from '../asset-graph/useAssetGraphExplorerFilters.oss';
import {AssetCatalogTableBottomActionBar} from '../assets/AssetCatalogTableBottomActionBar.oss';
import {AssetPageHeader} from '../assets/AssetPageHeader.oss';
import {AssetWipeDialog} from '../assets/AssetWipeDialog.oss';
import {AssetsGraphHeader} from '../assets/AssetsGraphHeader.oss';
import AssetsOverviewRoot from '../assets/AssetsOverviewRoot.oss';
import {useAssetCatalogFiltering} from '../assets/useAssetCatalogFiltering.oss';
Expand All @@ -29,6 +30,7 @@ type InjectedComponentContextType = {
AssetsOverview: AComponentFromComponent<typeof AssetsOverviewRoot> | null;
FallthroughRoot: AComponentFromComponent<typeof FallthroughRoot> | null;
AssetsGraphHeader: AComponentFromComponent<typeof AssetsGraphHeader> | null;
AssetWipeDialog: AComponentFromComponent<typeof AssetWipeDialog> | null;

RunMetricsDialog: AComponentWithProps<{
runId: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ interface Props {
belowActionBarComponents: React.ReactNode;
prefixPath: string[];
displayPathForAsset: (asset: Asset) => string[];
requery?: RefetchQueriesFunction;
searchPath: string;
isFiltered: boolean;
computeKindFilter?: StaticSetFilter<string>;
Expand All @@ -52,7 +51,6 @@ export const AssetTable = ({
refreshState,
prefixPath,
displayPathForAsset,
requery,
searchPath,
isFiltered,
view,
Expand Down Expand Up @@ -194,7 +192,7 @@ export const AssetTable = ({
assetKeys={toWipe || []}
isOpen={!!toWipe}
onClose={() => setToWipe(undefined)}
requery={requery}
onComplete={() => refreshState.refetch()}
/>
</>
);
Expand Down
16 changes: 15 additions & 1 deletion js_modules/dagster-ui/packages/ui-core/src/assets/AssetView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
} from './types/AssetView.types';
import {healthRefreshHintFromLiveData} from './usePartitionHealthData';
import {useReportEventsModal} from './useReportEventsModal';
import {useWipeModal} from './useWipeModal';
import {currentPageAtom} from '../app/analytics';
import {Timestamp} from '../app/time/Timestamp';
import {AssetLiveDataRefreshButton, useAssetLiveData} from '../asset-data/AssetLiveDataProvider';
Expand Down Expand Up @@ -276,6 +277,15 @@ export const AssetView = ({
setCurrentPage(({specificPath}) => ({specificPath, path: `${path}?view=${selectedTab}`}));
}, [path, selectedTab, setCurrentPage]);

const wipe = useWipeModal(
definition
? {
assetKey: definition.assetKey,
repository: definition.repository,
}
: null,
refresh,
);
const reportEvents = useReportEventsModal(
definition
? {
Expand Down Expand Up @@ -327,10 +337,14 @@ export const AssetView = ({
<LaunchAssetExecutionButton
scope={{all: [definition]}}
showChangedAndMissingOption={false}
additionalDropdownOptions={reportEvents.dropdownOptions}
additionalDropdownOptions={[
...reportEvents.dropdownOptions,
...wipe.dropdownOptions,
]}
/>
) : undefined}
{reportEvents.element}
{wipe.element}
</Box>
}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import {RefetchQueriesFunction} from '@apollo/client';
// eslint-disable-next-line no-restricted-imports
import {ProgressBar} from '@blueprintjs/core';
import {
Body1,
Box,
Button,
Dialog,
DialogBody,
DialogFooter,
Group,
ifPlural,
} from '@dagster-io/ui-components';
import {memo, useMemo} from 'react';

import {VirtualizedSimpleAssetKeyList} from './VirtualizedSimpleAssetKeyList';
import {asAssetPartitionRangeInput} from './asInput';
import {useWipeAssets} from './useWipeAssets';
import {AssetKeyInput} from '../graphql/types';
import {NavigationBlock} from '../runs/NavigationBlock';
import {numberFormatter} from '../ui/formatters';

export const AssetWipeDialog = memo(
(props: {
assetKeys: AssetKeyInput[];
isOpen: boolean;
onClose: () => void;
onComplete?: () => void;
requery?: RefetchQueriesFunction;
}) => {
return (
<Dialog
isOpen={props.isOpen}
title="Wipe materializations"
onClose={props.onClose}
style={{width: '80vw', maxWidth: '1200px', minWidth: '600px'}}
>
<AssetWipeDialogInner {...props} />
</Dialog>
);
},
);

export const AssetWipeDialogInner = memo(
({
assetKeys,
onClose,
onComplete,
requery,
}: {
assetKeys: AssetKeyInput[];
onClose: () => void;
onComplete?: () => void;
requery?: RefetchQueriesFunction;
}) => {
const {wipeAssets, isWiping, isDone, wipedCount, failedCount} = useWipeAssets({
refetchQueries: requery,
onClose,
onComplete,
});

const content = useMemo(() => {
if (isDone) {
return (
<Box flex={{direction: 'column'}}>
{wipedCount ? <Body1>{numberFormatter.format(wipedCount)} Wiped</Body1> : null}
{failedCount ? <Body1>{numberFormatter.format(failedCount)} Failed</Body1> : null}
</Box>
);
} else if (!isWiping) {
return (
<Group direction="column" spacing={16}>
<div>
Are you sure you want to wipe materializations for{' '}
{numberFormatter.format(assetKeys.length)}{' '}
{ifPlural(assetKeys.length, 'asset', 'assets')}?
</div>
<VirtualizedSimpleAssetKeyList assetKeys={assetKeys} style={{maxHeight: '50vh'}} />
<div>
Assets defined only by their historical materializations will disappear from the Asset
Catalog. Software-defined assets will remain unless their definition is also deleted.
</div>
<strong>This action cannot be undone.</strong>
</Group>
);
}
const value = assetKeys.length > 0 ? (wipedCount + failedCount) / assetKeys.length : 1;
return (
<Box flex={{gap: 8, direction: 'column'}}>
<div>Wiping...</div>
<ProgressBar intent="primary" value={Math.max(0.1, value)} animate={value < 1} />
<NavigationBlock message="Wiping in progress, please do not navigate away yet." />
</Box>
);
}, [isDone, isWiping, assetKeys, wipedCount, failedCount]);

return (
<>
<DialogBody>{content}</DialogBody>
<DialogFooter topBorder>
<Button intent={isDone ? 'primary' : 'none'} onClick={onClose}>
{isDone ? 'Done' : 'Cancel'}
</Button>
{isDone ? null : (
<Button
intent="danger"
onClick={() => wipeAssets(assetKeys.map((key) => asAssetPartitionRangeInput(key)))}
disabled={isWiping}
loading={isWiping}
>
Wipe
</Button>
)}
</DialogFooter>
</>
);
},
);
Loading

2 comments on commit 1ba71a3

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for dagit-storybook ready!

✅ Preview
https://dagit-storybook-ic3wd3lun-elementl.vercel.app

Built with commit 1ba71a3.
This pull request is being automatically deployed with vercel-action

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy preview for dagit-core-storybook ready!

✅ Preview
https://dagit-core-storybook-9aat25uzf-elementl.vercel.app

Built with commit 1ba71a3.
This pull request is being automatically deployed with vercel-action

Please sign in to comment.