Skip to content

Commit

Permalink
[10/n] [RFC] Implement updated designs: Schedules [4/N] (#26179)
Browse files Browse the repository at this point in the history
## Summary & Motivation
Linear:
https://linear.app/dagster-labs/issue/FE-708/implement-updated-designs-schedule-[4n]

Video:


https://github.com/user-attachments/assets/0f8cea65-aef7-44f7-833e-fa111b82b703



## How I Tested These Changes
jest, yarn lint, ts, tested locally for all cases: Multiple run
requests, skipped, exception

## Changelog
[ui] improved ui for manual sensor/schedule evaluation
  • Loading branch information
dliu27 authored Dec 9, 2024
1 parent 564161b commit b076a3a
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,6 @@ export const VirtualizedAutomationSensorRow = forwardRef(
return data.sensorOrError;
}, [data]);

const cursor =
sensorData &&
sensorData.sensorState.typeSpecificData &&
sensorData.sensorState.typeSpecificData.__typename === 'SensorData' &&
sensorData.sensorState.typeSpecificData.lastCursor;

const onChange = (e: React.FormEvent<HTMLInputElement>) => {
if (onToggleChecked && e.target instanceof HTMLInputElement) {
const {checked} = e.target;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
MenuItem,
Mono,
NonIdealState,
NonIdealStateWrapper,
Popover,
Spinner,
Subheading,
Expand Down Expand Up @@ -65,8 +66,8 @@ export const EvaluateScheduleDialog = (props: Props) => {
style={{width: '70vw', display: 'flex'}}
title={
<Box flex={{direction: 'row', gap: 8, alignItems: 'center'}}>
<Icon name="schedule" />
<span>{props.name}</span>
<Icon name="preview_tick" />
<span>Preview tick result for {props.name}</span>
</Box>
}
>
Expand Down Expand Up @@ -266,8 +267,8 @@ const EvaluateSchedule = ({repoAddress, name, onClose, jobName}: Props) => {
}));
selectedTimestampRef.current = selectedTimestamp || timestamps[0] || null;
return (
<div>
<ScheduleDescriptor>Select a mock evaluation time</ScheduleDescriptor>
<Box flex={{direction: 'column', gap: 8}}>
<ScheduleDescriptor>Select an evaluation time to simulate</ScheduleDescriptor>
<Popover
isOpen={isTickSelectionOpen}
position="bottom-left"
Expand Down Expand Up @@ -298,7 +299,17 @@ const EvaluateSchedule = ({repoAddress, name, onClose, jobName}: Props) => {
</Button>
</div>
</Popover>
</div>
<div>
Each evaluation of a schedule is called a tick, which is an opportunity for one or more
runs to be launched. Ticks kick off runs, which either materialize a selection of assets
or execute a <a href="https://docs.dagster.io/concepts/ops-jobs-graphs/jobs">job</a>.
You can preview the result for a given tick in the next step.
</div>
<div>
<a href="https://docs.dagster.io/concepts/automation/schedules">Learn more</a> about
schedules
</div>
</Box>
);
}
}, [
Expand All @@ -317,59 +328,88 @@ const EvaluateSchedule = ({repoAddress, name, onClose, jobName}: Props) => {
userTimezone,
]);

const buttons = useMemo(() => {
const leftButtons = useMemo(() => {
if (launching) {
return <Box flex={{direction: 'row', gap: 8}}></Box>;
return null;
}

if (scheduleExecutionData || scheduleExecutionError) {
return (
<Box flex={{direction: 'row', gap: 8}}>
<Tooltip
canShow={!canLaunchAll || launching}
content="Preparing to launch runs"
placement="top-end"
>
<Button disabled={!canLaunchAll || launching} onClick={onLaunchAll}>
<div>Launch all</div>
</Button>
</Tooltip>

<Button
data-testid={testId('test-again')}
onClick={() => {
setScheduleExecutionData(null);
setScheduleExecutionError(null);
}}
>
Test again
</Button>
<Button intent="primary" onClick={onClose}>
Close
</Button>
</Box>
<Button
icon={<Icon name="settings_backup_restore" />}
data-testid={testId('try-again')}
onClick={() => {
setScheduleExecutionData(null);
setScheduleExecutionError(null);
}}
>
Try again
</Button>
);
} else {
return null;
}
}, [launching, scheduleExecutionData, scheduleExecutionError]);

const rightButtons = useMemo(() => {
if (launching) {
return <Box flex={{direction: 'row', gap: 8}}></Box>;
}

if (scheduleExecutionData || scheduleExecutionError) {
const runRequests = scheduleExecutionData?.evaluationResult?.runRequests;
const numRunRequests = runRequests?.length || 0;
const didSkip = !scheduleExecutionError && numRunRequests === 0;

if (scheduleExecutionError || didSkip) {
return (
<Box flex={{direction: 'row', gap: 8}}>
<Button onClick={onClose}>Close</Button>
</Box>
);
} else {
return (
<Box flex={{direction: 'row', gap: 8}}>
<Button onClick={onClose}>Close</Button>
<Tooltip
canShow={!canLaunchAll || launching}
content="Launches all runs and commits tick result"
placement="top-end"
>
<Button
icon={<Icon name="check_filled" />}
intent="primary"
disabled={!canLaunchAll || launching}
onClick={onLaunchAll}
data-testid={testId('launch-all')}
>
<div>Launch all & commit tick result</div>
</Button>
</Tooltip>
</Box>
);
}
}

if (scheduleDryRunMutationLoading) {
return (
<Box flex={{direction: 'row', gap: 8}}>
<Button onClick={onClose}>Cancel</Button>
<Button onClick={onClose}>Close</Button>
</Box>
);
} else {
return (
<>
<Button onClick={onClose}>Cancel</Button>
<Button onClick={onClose}>Close</Button>
<Button
data-testid={testId('evaluate')}
data-testid={testId('continue')}
intent="primary"
disabled={!canSubmitTest}
onClick={() => {
submitTest();
}}
>
Evaluate
Continue
</Button>
</>
);
Expand All @@ -391,7 +431,9 @@ const EvaluateSchedule = ({repoAddress, name, onClose, jobName}: Props) => {
<DialogBody>
<div style={{minHeight: '300px'}}>{content}</div>
</DialogBody>
{buttons ? <DialogFooter topBorder>{buttons}</DialogFooter> : null}
<DialogFooter topBorder left={leftButtons}>
{rightButtons}
</DialogFooter>
</>
);
};
Expand Down Expand Up @@ -449,6 +491,7 @@ const EvaluateScheduleResult = ({
}

const data = scheduleExecutionData;

if (!data || !evaluationResult) {
return (
<NonIdealState
Expand All @@ -470,20 +513,45 @@ const EvaluateScheduleResult = ({
}
if (!evaluationResult.runRequests?.length) {
return (
<div>
<Subheading>Skip Reason</Subheading>
<div>{evaluationResult?.skipReason || 'No skip reason was output'}</div>
</div>
<Box flex={{direction: 'column', gap: 8}}>
<Subheading style={{marginBottom: 8}}>Requested runs (0)</Subheading>
<div>
<SkipReasonNonIdealStateWrapper>
<NonIdealState
icon="missing"
title="No runs requested"
description={
<>
<span>
The schedule function was successfully evaluated but didn&apos;t return any
run requests.
</span>
<span>
<br />
Skip reason:{' '}
{evaluationResult?.skipReason
? `"${evaluationResult.skipReason}"`
: 'No skip reason was output'}
</span>
</>
}
/>
</SkipReasonNonIdealStateWrapper>
</div>
</Box>
);
} else {
return (
<RunRequestTable
runRequests={evaluationResult.runRequests}
repoAddress={repoAddress}
isJob={true}
jobName={jobName}
name={name}
/>
<Box flex={{direction: 'column', gap: 8}}>
<Subheading>Requested runs ({numRunRequests})</Subheading>
<RunRequestTable
runRequests={evaluationResult.runRequests}
repoAddress={repoAddress}
isJob={true}
jobName={jobName}
name={name}
/>
</Box>
);
}
};
Expand Down Expand Up @@ -577,3 +645,11 @@ const Grid = styled.div`
const ScheduleDescriptor = styled.div`
padding-bottom: 2px;
`;

const SkipReasonNonIdealStateWrapper = styled.div`
${NonIdealStateWrapper} {
margin: auto !important;
width: unset !important;
max-width: unset !important;
}
`;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Box, Button} from '@dagster-io/ui-components';
import {Box, Button, Icon} from '@dagster-io/ui-components';
import {useState} from 'react';

import {EvaluateScheduleDialog} from './EvaluateScheduleDialog';
Expand All @@ -20,6 +20,7 @@ export const EvaluateTickButtonSchedule = ({
return (
<Box flex={{direction: 'row', alignItems: 'center', gap: 8}}>
<Button
icon={<Icon name="preview_tick" />}
onClick={() => {
setShowTestTickDialog(true);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('EvaluateScheduleTest', () => {
expect(screen.getByTestId('tick-5')).toBeVisible();
});
await userEvent.click(screen.getByTestId('tick-5'));
await userEvent.click(screen.getByTestId('evaluate'));
await userEvent.click(screen.getByTestId('continue'));
await waitFor(() => {
expect(screen.getByText(/1\s+run request/i)).toBeVisible();
});
Expand All @@ -62,9 +62,9 @@ describe('EvaluateScheduleTest', () => {
});
await userEvent.click(screen.getByTestId('tick-5'));
await waitFor(() => {
expect(screen.getByTestId('evaluate')).not.toBeDisabled();
expect(screen.getByTestId('continue')).not.toBeDisabled();
});
await userEvent.click(screen.getByTestId('evaluate'));
await userEvent.click(screen.getByTestId('continue'));
await waitFor(() => {
expect(screen.getByText('Failed')).toBeVisible();
expect(screen.queryByText('Skipped')).toBe(null);
Expand All @@ -79,7 +79,7 @@ describe('EvaluateScheduleTest', () => {
expect(screen.getByTestId('tick-5')).toBeVisible();
});
await userEvent.click(screen.getByTestId('tick-5'));
await userEvent.click(screen.getByTestId('evaluate'));
await userEvent.click(screen.getByTestId('continue'));
await waitFor(() => {
expect(screen.getByText('Skipped')).toBeVisible();
});
Expand Down

1 comment on commit b076a3a

@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-q1dvia3o0-elementl.vercel.app

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

Please sign in to comment.