Skip to content

Commit

Permalink
[dagit] Introduce sticky table headers (#7568)
Browse files Browse the repository at this point in the history
## Summary

Resolves #4182.

A long-standing ticket in our "Later" backlog, I figured I'd go ahead and crank it out.

Make the table headers in Runs and Asset catalog sticky when scrolling. I made a `StickyTableContainer` that we should ideally be able to use elsewhere in Dagit.

## Test Plan

View Runs, Job runs, Asset catalog. Scroll down, verify header stickiness. Verify that the select-all checkbox behaves as expected.

Tested in Chrome, Firefox, Safari.
  • Loading branch information
hellendag committed Apr 26, 2022
1 parent 4d44a79 commit 65f15cd
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 58 deletions.
43 changes: 23 additions & 20 deletions js_modules/dagit/packages/core/src/assets/AssetsCatalogTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {useDocumentTitle} from '../hooks/useDocumentTitle';
import {useQueryPersistedState} from '../hooks/useQueryPersistedState';
import {RepoFilterButton} from '../instance/RepoFilterButton';
import {Loading} from '../ui/Loading';
import {StickyTableContainer} from '../ui/StickyTableContainer';
import {DagsterRepoOption, WorkspaceContext} from '../workspace/WorkspaceContext';
import {buildRepoPath} from '../workspace/buildRepoAddress';

Expand Down Expand Up @@ -116,26 +117,28 @@ export const AssetsCatalogTable: React.FC<{prefixPath?: string[]}> = ({prefixPat

return (
<>
<AssetTable
assets={displayed}
actionBarComponents={
<>
<AssetViewModeSwitch view={view} setView={setView} />
<RepoFilterButton />
<TextInput
value={search || ''}
style={{width: '30vw', minWidth: 150, maxWidth: 400}}
placeholder="Search all asset_keys..."
onChange={(e: React.ChangeEvent<any>) => setSearch(e.target.value)}
/>
<QueryRefreshCountdown refreshState={refreshState} />
</>
}
prefixPath={prefixPath || []}
displayPathForAsset={displayPathForAsset}
maxDisplayCount={PAGE_SIZE}
requery={(_) => [{query: ASSET_CATALOG_TABLE_QUERY}]}
/>
<StickyTableContainer $top={0}>
<AssetTable
assets={displayed}
actionBarComponents={
<>
<AssetViewModeSwitch view={view} setView={setView} />
<RepoFilterButton />
<TextInput
value={search || ''}
style={{width: '30vw', minWidth: 150, maxWidth: 400}}
placeholder="Search all asset_keys..."
onChange={(e: React.ChangeEvent<any>) => setSearch(e.target.value)}
/>
<QueryRefreshCountdown refreshState={refreshState} />
</>
}
prefixPath={prefixPath || []}
displayPathForAsset={displayPathForAsset}
maxDisplayCount={PAGE_SIZE}
requery={(_) => [{query: ASSET_CATALOG_TABLE_QUERY}]}
/>
</StickyTableContainer>
<Box margin={{vertical: 20}}>
<CursorPaginationControls {...paginationProps} />
</Box>
Expand Down
27 changes: 15 additions & 12 deletions js_modules/dagit/packages/core/src/pipelines/PipelineRunsRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from '../runs/RunsFilterInput';
import {useCursorPaginatedQuery} from '../runs/useCursorPaginatedQuery';
import {Loading} from '../ui/Loading';
import {StickyTableContainer} from '../ui/StickyTableContainer';
import {isThisThingAJob, useRepository} from '../workspace/WorkspaceContext';
import {RepoAddress} from '../workspace/types';

Expand Down Expand Up @@ -118,18 +119,20 @@ export const PipelineRunsRoot: React.FC<Props> = (props) => {
</Box>
<QueryRefreshCountdown refreshState={refreshState} />
</Box>
<RunTable
runs={displayed}
onSetFilter={setFilterTokens}
actionBarComponents={
<RunsFilterInput
enabledFilters={ENABLED_FILTERS}
tokens={filterTokens}
onChange={setFilterTokens}
loading={queryResult.loading}
/>
}
/>
<StickyTableContainer $top={0}>
<RunTable
runs={displayed}
onSetFilter={setFilterTokens}
actionBarComponents={
<RunsFilterInput
enabledFilters={ENABLED_FILTERS}
tokens={filterTokens}
onChange={setFilterTokens}
loading={queryResult.loading}
/>
}
/>
</StickyTableContainer>
{hasNextCursor || hasPrevCursor ? (
<div style={{marginTop: '20px'}}>
<CursorHistoryControls {...paginationProps} />
Expand Down
55 changes: 29 additions & 26 deletions js_modules/dagit/packages/core/src/runs/RunsRoot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {useDocumentTitle} from '../hooks/useDocumentTitle';
import {useCanSeeConfig} from '../instance/useCanSeeConfig';
import {RunStatus} from '../types/globalTypes';
import {Loading} from '../ui/Loading';
import {StickyTableContainer} from '../ui/StickyTableContainer';

import {AllScheduledTicks} from './AllScheduledTicks';
import {doneStatuses, inProgressStatuses, queuedStatuses} from './RunStatuses';
Expand Down Expand Up @@ -241,32 +242,34 @@ export const RunsRoot = () => {

return (
<>
<RunTable
runs={pipelineRunsOrError.results.slice(0, PAGE_SIZE)}
onSetFilter={setFilterTokensWithStatus}
filter={filter}
actionBarComponents={
showScheduled ? null : (
<Box flex={{direction: 'column', gap: 8}}>
{selectedTab !== 'all' ? (
<Box flex={{direction: 'row', gap: 8}}>
{filterTokens
.filter((token) => token.token === 'status')
.map(({token, value}) => (
<Tag key={token}>{`${token}:${value}`}</Tag>
))}
</Box>
) : null}
<RunsFilterInput
tokens={mutableTokens}
onChange={setFilterTokensWithStatus}
loading={queryResult.loading}
enabledFilters={enabledFilters}
/>
</Box>
)
}
/>
<StickyTableContainer $top={0}>
<RunTable
runs={pipelineRunsOrError.results.slice(0, PAGE_SIZE)}
onSetFilter={setFilterTokensWithStatus}
filter={filter}
actionBarComponents={
showScheduled ? null : (
<Box flex={{direction: 'column', gap: 8}}>
{selectedTab !== 'all' ? (
<Box flex={{direction: 'row', gap: 8}}>
{filterTokens
.filter((token) => token.token === 'status')
.map(({token, value}) => (
<Tag key={token}>{`${token}:${value}`}</Tag>
))}
</Box>
) : null}
<RunsFilterInput
tokens={mutableTokens}
onChange={setFilterTokensWithStatus}
loading={queryResult.loading}
enabledFilters={enabledFilters}
/>
</Box>
)
}
/>
</StickyTableContainer>
{pipelineRunsOrError.results.length > 0 ? (
<div style={{marginTop: '16px'}}>
<CursorHistoryControls {...paginationProps} />
Expand Down
45 changes: 45 additions & 0 deletions js_modules/dagit/packages/core/src/ui/StickyTableContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {Colors} from '@dagster-io/ui';
import styled from 'styled-components/macro';

interface Props {
$top: number;
}

/**
* Wrap a `Table` component with this to cause its `thead` to be sticky while scrolling.
*
* `$top` is the pixel value of the point in the scrolling container that the `thead`
* should stick to. Probably `0`.
*/
export const StickyTableContainer = styled.div<Props>`
thead tr {
position: sticky;
top: ${({$top}) => $top}px;
background-color: ${Colors.White};
z-index: 1;
}
/**
* Safari won't render a box-shadow on the \`tr\` and we don't want an inset
* box-shadow on \`th\` elements because it will create a double-border on the
* bottom of the \`thead\` in the non-stuck state.
*
* We therefore render an absoulutely-positioned keyline on the bottom of the
* \`th\` elements. This will appear as a border in the stuck state, and will
* overlap the top box-shadow of the first row in the non-stuck state.
*/
thead tr th {
position: relative;
}
thead tr th::after {
content: '';
display: block;
height: 1px;
background-color: ${Colors.KeylineGray};
position: absolute;
bottom: -1px;
left: 0;
right: 0;
}
`;

0 comments on commit 65f15cd

Please sign in to comment.