Skip to content

Commit

Permalink
(feat.) Sensor UI Filtering (#19559)
Browse files Browse the repository at this point in the history
## Summary & Motivation

We want to allow filtering sensors by type on this sensors page.

Based off this figma 

https://www.figma.com/file/ngfhVNvNQxkZ9OV8QVuHzR/AMP%2FSensors-UI?type=design&node-id=833-2070&mode=design&t=v2l3ORFfobUhSjue-0


## How I Tested These Changes
<img width="688" alt="Screenshot 2024-02-02 at 10 06 20 AM"
src="https://github.com/dagster-io/dagster/assets/2286579/276dd118-c281-4b25-89e0-5c23a6e42913">
<img width="270" alt="Screenshot 2024-02-02 at 9 25 22 AM"
src="https://github.com/dagster-io/dagster/assets/2286579/14a7fc2b-4674-4677-9e71-de9fed5b9d8c">
<img width="1697" alt="Screenshot 2024-02-02 at 9 25 17 AM"
src="https://github.com/dagster-io/dagster/assets/2286579/030eadee-8afc-43a7-85e8-11d4eb94b81b">
  • Loading branch information
salazarm authored Feb 2, 2024
1 parent 3bc3964 commit 143d555
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ import graph_neighbors from '../icon-svgs/graph_neighbors.svg';
import graph_upstream from '../icon-svgs/graph_upstream.svg';
import history from '../icon-svgs/history.svg';
import history_toggle_off from '../icon-svgs/history_toggle_off.svg';
import hourglass from '../icon-svgs/hourglass.svg';
import hourglass_bottom from '../icon-svgs/hourglass_bottom.svg';
import id from '../icon-svgs/id.svg';
import infinity from '../icon-svgs/infinity.svg';
Expand All @@ -96,6 +97,7 @@ import materialization from '../icon-svgs/materialization.svg';
import menu from '../icon-svgs/menu.svg';
import menu_book from '../icon-svgs/menu_book.svg';
import more_horiz from '../icon-svgs/more_horiz.svg';
import multi_asset from '../icon-svgs/multi_asset.svg';
import nightlight from '../icon-svgs/nightlight.svg';
import no_access from '../icon-svgs/no_access.svg';
import observation from '../icon-svgs/observation.svg';
Expand Down Expand Up @@ -176,6 +178,7 @@ export const Icons = {
materialization,
observation,
job,
multi_asset,
op,
op_selector,
op_dynamic: bolt,
Expand Down Expand Up @@ -276,6 +279,7 @@ export const Icons = {
info,
history,
history_toggle_off,
hourglass,
hourglass_bottom,
layers,
line_style,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
TextInput,
Tooltip,
} from '@dagster-io/ui-components';
import {useContext, useMemo} from 'react';
import {useContext, useMemo, useState} from 'react';

import {BASIC_INSTIGATION_STATE_FRAGMENT} from './BasicInstigationStateFragment';
import {OverviewSensorTable} from './OverviewSensorsTable';
Expand All @@ -24,6 +24,7 @@ import {visibleRepoKeys} from './visibleRepoKeys';
import {PYTHON_ERROR_FRAGMENT} from '../app/PythonErrorFragment';
import {FIFTEEN_SECONDS, useQueryRefreshAtInterval} from '../app/QueryRefresh';
import {useTrackPageView} from '../app/analytics';
import {SensorType} from '../graphql/types';
import {useDocumentTitle} from '../hooks/useDocumentTitle';
import {useQueryPersistedState} from '../hooks/useQueryPersistedState';
import {useSelectionReducer} from '../hooks/useSelectionReducer';
Expand All @@ -36,12 +37,33 @@ import {CheckAllBox} from '../ui/CheckAllBox';
import {useFilters} from '../ui/Filters';
import {useCodeLocationFilter} from '../ui/Filters/useCodeLocationFilter';
import {useInstigationStatusFilter} from '../ui/Filters/useInstigationStatusFilter';
import {useStaticSetFilter} from '../ui/Filters/useStaticSetFilter';
import {SearchInputSpinner} from '../ui/SearchInputSpinner';
import {SENSOR_TYPE_META} from '../workspace/VirtualizedSensorRow';
import {WorkspaceContext} from '../workspace/WorkspaceContext';
import {buildRepoAddress} from '../workspace/buildRepoAddress';
import {repoAddressAsHumanString} from '../workspace/repoAddressAsString';
import {RepoAddress} from '../workspace/types';

function toSetFilterValue(type: SensorType) {
const label = SENSOR_TYPE_META[type].name;
return {
label,
value: {type, label},
match: [label],
};
}

const SENSOR_TYPE_TO_FILTER: Partial<Record<SensorType, ReturnType<typeof toSetFilterValue>>> = {
[SensorType.ASSET]: toSetFilterValue(SensorType.ASSET),
[SensorType.AUTOMATION_POLICY]: toSetFilterValue(SensorType.AUTOMATION_POLICY),
[SensorType.FRESHNESS_POLICY]: toSetFilterValue(SensorType.FRESHNESS_POLICY),
[SensorType.MULTI_ASSET]: toSetFilterValue(SensorType.MULTI_ASSET),
[SensorType.RUN_STATUS]: toSetFilterValue(SensorType.RUN_STATUS),
[SensorType.STANDARD]: toSetFilterValue(SensorType.STANDARD),
};
const ALL_SENSOR_TYPE_FILTERS = Object.values(SENSOR_TYPE_TO_FILTER);

export const OverviewSensorsRoot = () => {
useTrackPageView();
useDocumentTitle('Overview | Sensors');
Expand All @@ -56,9 +78,26 @@ export const OverviewSensorsRoot = () => {
const codeLocationFilter = useCodeLocationFilter();
const runningStateFilter = useInstigationStatusFilter();

const [sensorTypes, setSensorTypes] = useState<Set<SensorType>>(() => new Set());

const sensorTypeFilter = useStaticSetFilter({
name: 'Sensor type',
allValues: ALL_SENSOR_TYPE_FILTERS,
icon: 'sensors',
getStringValue: (value) => value.label,
initialState: useMemo(() => {
return new Set(Array.from(sensorTypes).map((type) => SENSOR_TYPE_TO_FILTER[type]!.value));
}, [sensorTypes]),

renderLabel: ({value}) => <span>{value.label}</span>,
onStateChanged: (state) => {
setSensorTypes(new Set(Array.from(state).map((value) => value.type)));
},
});

const filters = useMemo(
() => [codeLocationFilter, runningStateFilter],
[codeLocationFilter, runningStateFilter],
() => [codeLocationFilter, runningStateFilter, sensorTypeFilter],
[codeLocationFilter, runningStateFilter, sensorTypeFilter],
);
const {button: filterButton, activeFiltersJsx} = useFilters({filters});

Expand All @@ -81,16 +120,23 @@ export const OverviewSensorsRoot = () => {
}, [data, visibleRepos]);

const {state: runningState} = runningStateFilter;

const filteredBuckets = useMemo(() => {
return repoBuckets.map(({sensors, ...rest}) => {
return {
...rest,
sensors: runningState.size
? sensors.filter(({sensorState}) => runningState.has(sensorState.status))
: sensors,
sensors: sensors.filter(({sensorState, sensorType}) => {
if (runningState.size && !runningState.has(sensorState.status)) {
return false;
}
if (sensorTypes.size && !sensorTypes.has(sensorType)) {
return false;
}
return true;
}),
};
});
}, [repoBuckets, runningState]);
}, [repoBuckets, runningState, sensorTypes]);

const sanitizedSearch = searchValue.trim().toLocaleLowerCase();
const anySearch = sanitizedSearch.length > 0;
Expand Down Expand Up @@ -306,7 +352,7 @@ export const OverviewSensorsRoot = () => {

type RepoBucket = {
repoAddress: RepoAddress;
sensors: {name: string; sensorState: BasicInstigationStateFragment}[];
sensors: {name: string; sensorType: SensorType; sensorState: BasicInstigationStateFragment}[];
};

const buildBuckets = (data?: OverviewSensorsQuery): RepoBucket[] => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import {gql, useLazyQuery} from '@apollo/client';
import {Box, Caption, Checkbox, Colors, MiddleTruncate, Tooltip} from '@dagster-io/ui-components';
import {
Box,
Caption,
Checkbox,
Colors,
IconName,
MiddleTruncate,
Tag,
Tooltip,
} from '@dagster-io/ui-components';
import * as React from 'react';
import {Link} from 'react-router-dom';
import styled from 'styled-components';
Expand All @@ -9,7 +18,7 @@ import {RepoAddress} from './types';
import {SingleSensorQuery, SingleSensorQueryVariables} from './types/VirtualizedSensorRow.types';
import {workspacePathFromAddress} from './workspacePath';
import {FIFTEEN_SECONDS, useQueryRefreshAtInterval} from '../app/QueryRefresh';
import {InstigationStatus} from '../graphql/types';
import {InstigationStatus, SensorType} from '../graphql/types';
import {LastRunSummary} from '../instance/LastRunSummary';
import {TICK_TAG_FRAGMENT} from '../instigation/InstigationTick';
import {BasicInstigationStateFragment} from '../overview/types/BasicInstigationStateFragment.types';
Expand All @@ -20,8 +29,8 @@ import {SensorTargetList} from '../sensors/SensorTargetList';
import {TickStatusTag} from '../ticks/TickStatusTag';
import {HeaderCell, Row, RowCell} from '../ui/VirtualizedTable';

const TEMPLATE_COLUMNS_WITH_CHECKBOX = '60px 1.5fr 1fr 76px 120px 148px 180px';
const TEMPLATE_COLUMNS = '1.5fr 1fr 76px 120px 148px 180px';
const TEMPLATE_COLUMNS_WITH_CHECKBOX = '60px 1.5fr 120px 1fr 76px 120px 148px 180px';
const TEMPLATE_COLUMNS = '1.5fr 120px 1fr 76px 120px 148px 180px';

interface SensorRowProps {
name: string;
Expand Down Expand Up @@ -94,6 +103,9 @@ export const VirtualizedSensorRow = (props: SensorRowProps) => {

const tick = sensorData?.sensorState.ticks[0];

const sensorType = sensorData?.sensorType;
const sensorInfo = sensorType ? SENSOR_TYPE_META[sensorType] : null;

return (
<Row $height={height} $start={start}>
<RowGrid border="bottom" $showCheckboxColumn={showCheckboxColumn}>
Expand Down Expand Up @@ -133,6 +145,17 @@ export const VirtualizedSensorRow = (props: SensorRowProps) => {
</div>
</Box>
</RowCell>
<RowCell>
{sensorInfo ? (
sensorInfo.description ? (
<Tooltip content={sensorInfo.description}>
<Tag icon={sensorInfo.icon}>{sensorInfo.name}</Tag>
</Tooltip>
) : (
<Tag icon={sensorInfo.icon}>{sensorInfo.name}</Tag>
)
) : null}
</RowCell>
<RowCell>
<Box flex={{direction: 'column', gap: 4}} style={{fontSize: '12px'}}>
<SensorTargetList targets={sensorData?.targets} repoAddress={repoAddress} />
Expand Down Expand Up @@ -201,6 +224,7 @@ export const VirtualizedSensorHeader = (props: {checkbox: React.ReactNode}) => {
</HeaderCell>
) : null}
<HeaderCell>Name</HeaderCell>
<HeaderCell>Type</HeaderCell>
<HeaderCell>Target</HeaderCell>
<HeaderCell>Running</HeaderCell>
<HeaderCell>Frequency</HeaderCell>
Expand All @@ -217,6 +241,49 @@ const RowGrid = styled(Box)<{$showCheckboxColumn: boolean}>`
height: 100%;
`;

export const SENSOR_TYPE_META: Record<
SensorType,
{name: string; icon: IconName; description: string | null}
> = {
[SensorType.ASSET]: {
name: 'Asset',
icon: 'asset',
description: 'Asset sensors instigate runs when a materialization occurs',
},
[SensorType.AUTOMATION_POLICY]: {
name: 'Automation',
icon: 'hourglass',
description: 'Automation policy sensors react to defined automation policy conditions',
},
[SensorType.FRESHNESS_POLICY]: {
name: 'Freshness policy',
icon: 'hourglass',
description:
'Freshness sensors check the freshness of assets on each tick, then perform an action in response to that status',
},
[SensorType.MULTI_ASSET]: {
name: 'Multi-asset',
icon: 'multi_asset',
description:
'Multi asset sensors trigger job executions based on multiple asset materialization event streams',
},
[SensorType.RUN_STATUS]: {
name: 'Run status',
icon: 'alternate_email',
description: 'Run status sensors react to run status',
},
[SensorType.STANDARD]: {
name: 'Standard',
icon: 'sensors',
description: null,
},
[SensorType.UNKNOWN]: {
name: 'Standard',
icon: 'sensors',
description: null,
},
};

const SINGLE_SENSOR_QUERY = gql`
query SingleSensorQuery($selector: SensorSelector!) {
sensorOrError(sensorSelector: $selector) {
Expand Down

2 comments on commit 143d555

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

Built with commit 143d555.
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-64qwnsxuf-elementl.vercel.app

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

Please sign in to comment.