Skip to content

Commit

Permalink
[ui] Polish runs feed UI
Browse files Browse the repository at this point in the history
  • Loading branch information
bengotow committed Nov 3, 2024
1 parent d4fa3bc commit 89187bc
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ export const PageHeader = (props: Props) => {
>
{title && (
<Box
padding={{vertical: 8}}
style={{minHeight: 52, alignContent: 'center'}}
flex={{direction: 'row', justifyContent: 'space-between', alignItems: 'center'}}
flex={{direction: 'row', justifyContent: 'space-between', alignItems: 'center', gap: 8}}
>
<Box flex={{direction: 'row', alignItems: 'center', gap: 12, wrap: 'wrap'}}>
{title}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,17 +317,19 @@ export const AssetNodeOverview = ({
</AttributeAndValue>

<AttributeAndValue label="Code location">
<Box flex={{direction: 'column'}}>
<Box flex={{direction: 'column', alignItems: 'flex-start', gap: 8}}>
<AssetDefinedInMultipleReposNotice
assetKey={cachedOrLiveAssetNode.assetKey}
loadedFromRepo={repoAddress!}
/>
<RepositoryLink repoAddress={repoAddress!} />
{location && (
<Caption color={Colors.textLighter()}>
Loaded {dayjs.unix(location.updatedTimestamp).fromNow()}
</Caption>
)}
<Box flex={{direction: 'column'}}>
<RepositoryLink repoAddress={repoAddress!} />
{location && (
<Caption color={Colors.textLighter()}>
Loaded {dayjs.unix(location.updatedTimestamp).fromNow()}
</Caption>
)}
</Box>
</Box>
</AttributeAndValue>
<AttributeAndValue label="Owners">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {Box, Colors, Spinner, useViewport} from '@dagster-io/ui-components';
import {Box, Colors, Mono, Spinner, useViewport} from '@dagster-io/ui-components';
import {useVirtualizer} from '@tanstack/react-virtual';
import React from 'react';
import {Link} from 'react-router-dom';
Expand All @@ -12,6 +12,7 @@ import {
TimelineRowContainer,
} from '../../runs/RunTimeline';
import {TimelineRun} from '../../runs/RunTimelineTypes';
import {titleForRun} from '../../runs/RunUtils';
import {TimeElapsed} from '../../runs/TimeElapsed';
import {RunBatch, batchRunsForTimeline} from '../../runs/batchRunsForTimeline';
import {mergeStatusToBackground} from '../../runs/mergeStatusToBackground';
Expand Down Expand Up @@ -165,7 +166,9 @@ export const ExecutionTimelineRow = ({
>
<Box flex={{alignItems: 'center', gap: 4}}>
<RunStatusDot status={run.status} size={12} />
<Link to={`/runs/${run.id}`}>{run.id.slice(0, 8)}</Link>
<Link to={`/runs/${run.id}`}>
<Mono>{titleForRun(run)}</Mono>
</Link>
</Box>
<TimeElapsed startUnix={run.startTime / 1000} endUnix={run.endTime / 1000} />
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export const AssetKeyTagCollection = React.memo((props: AssetKeyTagCollectionPro
const assetKey = assetKeys[0]!;
return (
<TagActionsPopover
childrenMiddleTruncate
data={{key: '', value: ''}}
actions={[
{
Expand All @@ -105,13 +106,13 @@ export const AssetKeyTagCollection = React.memo((props: AssetKeyTagCollectionPro
>
{useTags ? (
<Tag intent="none" interactive icon="asset">
{displayNameForAssetKey(assetKey)}
<MiddleTruncate text={displayNameForAssetKey(assetKey)} />
</Tag>
) : (
<Link to={assetDetailsPathForKey(assetKey)}>
<Box flex={{direction: 'row', gap: 8, alignItems: 'center'}}>
<Icon color={Colors.accentGray()} name="asset" size={16} />
{displayNameForAssetKey(assetKey)}
<MiddleTruncate text={displayNameForAssetKey(assetKey)} />
</Box>
</Link>
)}
Expand Down Expand Up @@ -176,16 +177,17 @@ export const AssetCheckTagCollection = React.memo((props: AssetCheckTagCollectio
<TagActionsPopover
data={{key: '', value: ''}}
actions={[{label: 'View asset check', to: assetDetailsPathForAssetCheck(check)}]}
childrenMiddleTruncate
>
{useTags ? (
<Tag intent="none" interactive icon="asset_check">
{labelForAssetCheck(check)}
<MiddleTruncate text={labelForAssetCheck(check)} />
</Tag>
) : (
<Link to={assetDetailsPathForAssetCheck(check)}>
<Box flex={{direction: 'row', gap: 8, alignItems: 'center'}}>
<Icon color={Colors.accentGray()} name="asset_check" size={16} />
{labelForAssetCheck(check)}
<MiddleTruncate text={labelForAssetCheck(check)} />
</Box>
</Link>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ export const CreatedByTag = ({repoAddress, tags, onAddTag}: Props) => {
return (
<TagActionsPopover
data={tag}
childrenMiddleTruncate
actions={[
{
label: 'Add to filter',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const RunTargetLink = ({
repoAddress: RepoAddress | null;
}) => {
return isHiddenAssetGroupJob(run.pipelineName) ? (
<Box flex={{gap: 16, alignItems: 'end', wrap: 'wrap'}}>
<Box flex={{gap: 16, alignItems: 'end', wrap: 'wrap'}} style={{minWidth: 0, maxWidth: '100%'}}>
<AssetKeyTagCollection assetKeys={assetKeysForRun(run)} />
<AssetCheckTagCollection assetChecks={run.assetCheckSelection} />
</Box>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ export const RunsFeedRow = ({
flex={{direction: 'row', alignItems: 'center', wrap: 'wrap'}}
style={{gap: '4px 8px', lineHeight: 0}}
>
{entry.__typename === 'PartitionBackfill' ? (
<Tag intent="none">
<span>Backfill</span>
</Tag>
) : undefined}

<RunRowTags
run={{...entry, mode: 'default'}}
isJob={true}
Expand Down Expand Up @@ -187,7 +193,7 @@ export const RunsFeedRow = ({
};

const TEMPLATE_COLUMNS =
'60px minmax(0, 2fr) minmax(0, 2fr) minmax(0, 1fr) 140px 150px 120px 132px';
'60px minmax(0, 2fr) minmax(0, 1.2fr) minmax(0, 1fr) 140px 150px 120px 132px';

export const RunsFeedTableHeader = ({checkbox}: {checkbox: React.ReactNode}) => {
return (
Expand Down
64 changes: 52 additions & 12 deletions js_modules/dagster-ui/packages/ui-core/src/runs/RunsFeedTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
ifPlural,
} from '@dagster-io/ui-components';
import {useVirtualizer} from '@tanstack/react-virtual';
import React, {useMemo, useRef} from 'react';
import React, {useEffect, useMemo, useRef} from 'react';

import {RunBulkActionsMenu} from './RunActionsMenu';
import {RunTableEmptyState} from './RunTableEmptyState';
Expand Down Expand Up @@ -81,6 +81,20 @@ export const RunsFeedTable = ({
);
const backfillsExcluded = selectedEntries.length - selectedRuns.length;

const resetScrollOnLoad = useRef(false);
useEffect(() => {
// When you click "Next page" from the bottom of page 1, we show the indeterminate
// loading state and want to scroll to the top when the new results arrive. It looks
// bad to do it immediately, and the `entries` can also change on their own (and
// sometimes with new rows), so we do this explicitly for pagination cases using a ref.
if (!loading && resetScrollOnLoad.current) {
resetScrollOnLoad.current = false;
if (parentRef.current) {
parentRef.current.scrollTop = 0;
}
}
}, [loading]);

const actionBar = (
<Box flex={{direction: 'column', gap: 8}}>
<Box
Expand All @@ -90,7 +104,23 @@ export const RunsFeedTable = ({
>
{actionBarComponents ?? <span />}
<Box flex={{gap: 12, alignItems: 'center'}} style={{marginRight: 8}}>
<CursorHistoryControls {...paginationProps} style={{marginTop: 0}} />
<CursorHistoryControls
style={{marginTop: 0}}
hasPrevCursor={paginationProps.hasPrevCursor}
hasNextCursor={paginationProps.hasNextCursor}
popCursor={() => {
resetScrollOnLoad.current = true;
paginationProps.popCursor();
}}
advanceCursor={() => {
resetScrollOnLoad.current = true;
paginationProps.advanceCursor();
}}
reset={() => {
resetScrollOnLoad.current = true;
paginationProps.reset();
}}
/>
<RunBulkActionsMenu
clearSelection={() => onToggleAll(false)}
selected={selectedRuns}
Expand Down Expand Up @@ -122,27 +152,37 @@ export const RunsFeedTable = ({
);

function content() {
const header = (
<RunsFeedTableHeader
checkbox={
<CheckAllBox
checkedCount={checkedIds.size}
totalCount={entries.length}
onToggleAll={onToggleAll}
/>
}
/>
);

if (entries.length === 0 && !loading) {
const anyFilter = !!Object.keys(filter || {}).length;
if (emptyState) {
return <>{emptyState()}</>;
}

return <RunTableEmptyState anyFilter={anyFilter} />;
return (
<div style={{overflow: 'hidden'}}>
{header}
<RunTableEmptyState anyFilter={anyFilter} />
</div>
);
}

return (
<div style={{overflow: 'hidden'}}>
<IndeterminateLoadingBar $loading={loading} />
<Container ref={parentRef} style={scroll ? {overflow: 'auto'} : {overflow: 'visible'}}>
<RunsFeedTableHeader
checkbox={
<CheckAllBox
checkedCount={checkedIds.size}
totalCount={entries.length}
onToggleAll={onToggleAll}
/>
}
/>
{header}
{entries.length === 0 && loading && (
<Box flex={{direction: 'row', justifyContent: 'center'}} padding={32}>
<SpinnerWithText label="Loading runs…" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
RunTagValuesQuery,
RunTagValuesQueryVariables,
} from './types/RunsFilterInput.types';
import {RUNS_FEED_CURSOR_KEY} from './useRunsFeedEntries';
import {COMMON_COLLATOR} from '../app/Util';
import {__ASSET_JOB_PREFIX} from '../asset-graph/Utils';
import {RunStatus, RunsFilter} from '../graphql/types';
Expand Down Expand Up @@ -104,7 +105,11 @@ export function useQueryPersistedRunFilters(enabledFilters?: RunFilterTokenType[
return useQueryPersistedState<RunFilterToken[]>(
useMemo(
() => ({
encode: (tokens) => ({q: tokensAsStringArray(tokens), cursor: undefined}),
encode: (tokens) => ({
q: tokensAsStringArray(tokens),
cursor: undefined,
[RUNS_FEED_CURSOR_KEY]: undefined,
}),
decode: ({q = []}) =>
tokenizedValuesFromStringArray(q, RUN_PROVIDERS_EMPTY).filter(
(t) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {BaseTag, Box, SubwayDot} from '@dagster-io/ui-components';
import {BaseTag, Box, MiddleTruncate, SubwayDot} from '@dagster-io/ui-components';

type Props = {
email: string;
Expand All @@ -16,6 +16,12 @@ export function UserDisplay({email, isFilter}: Props) {
{email}
</Box>
) : (
<BaseTag key="user" icon={<div style={{margin: '0 4px 0 -4px'}}>{icon}</div>} label={email} />
<Box flex={{direction: 'row'}} style={{minWidth: 0, maxWidth: '100%'}}>
<BaseTag
key="user"
icon={<div style={{margin: '0 4px 0 -4px'}}>{icon}</div>}
label={<MiddleTruncate text={email} />}
/>
</Box>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {gql, useQuery} from '../apollo-client';
import {PYTHON_ERROR_FRAGMENT} from '../app/PythonErrorFragment';
import {RunsFilter} from '../graphql/types';

const PAGE_SIZE = 25;
const PAGE_SIZE = 29;

const RUNS_FEED_CURSOR_KEY = `runs_before`;
export const RUNS_FEED_CURSOR_KEY = `runs_before`;

export function useRunsFeedEntries(
filter: RunsFilter,
Expand Down

0 comments on commit 89187bc

Please sign in to comment.