Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: display execution tags and filter #836

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/console/src/components/Executions/ExecutionFilters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import { SearchInputForm } from 'components/common/SearchInputForm';
import { SingleSelectForm } from 'components/common/SingleSelectForm';
import { FilterPopoverButton } from 'components/Tables/filters/FilterPopoverButton';
import { TagsInputForm } from 'components/common/TagsInputForm';
import {
FilterState,
MultiFilterState,
SearchFilterState,
SingleFilterState,
BooleanFilterState,
TagsFilterState,
} from './filters/types';

const useStyles = makeStyles((theme: Theme) => ({
Expand Down Expand Up @@ -49,6 +51,7 @@

const RenderFilter: React.FC<{ filter: FilterState }> = ({ filter }) => {
const searchFilterState = filter as SearchFilterState;
const tagsFilterState = filter as TagsFilterState;
switch (filter.type) {
case 'single':
return <SingleSelectForm {...(filter as SingleFilterState<any>)} />;
Expand All @@ -61,6 +64,13 @@
defaultValue={searchFilterState.value}
/>
);
case 'tags':
return (

Check warning on line 68 in packages/console/src/components/Executions/ExecutionFilters.tsx

View check run for this annotation

Codecov / codecov/patch

packages/console/src/components/Executions/ExecutionFilters.tsx#L67-L68

Added lines #L67 - L68 were not covered by tests
<TagsInputForm
{...tagsFilterState}
defaultValue={tagsFilterState.tags}
/>
);
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
IconButton,
Button,
CircularProgress,
Chip,
} from '@material-ui/core';
import ArchiveOutlined from '@material-ui/icons/ArchiveOutlined';
import UnarchiveOutline from '@material-ui/icons/UnarchiveOutlined';
Expand All @@ -21,6 +22,7 @@
import { ExecutionState, WorkflowExecutionPhase } from 'models/Execution/enums';
import classnames from 'classnames';
import { LaunchPlanLink } from 'components/LaunchPlan/LaunchPlanLink';
import { getColorFromString } from 'components/utils';
import { WorkflowExecutionsTableState } from '../types';
import { WorkflowExecutionLink } from '../WorkflowExecutionLink';
import { getWorkflowExecutionTimingMS, isExecutionArchived } from '../../utils';
Expand Down Expand Up @@ -100,6 +102,31 @@
);
}

export function getExecutionTagsCell(
execution: Execution,
className: string,
): React.ReactNode {
const isArchived = isExecutionArchived(execution);
const tags = execution.spec.tags ?? [];
return (
<div className={className}>
{tags.map(tag => {
return (

Check warning on line 114 in packages/console/src/components/Executions/Tables/WorkflowExecutionTable/cells.tsx

View check run for this annotation

Codecov / codecov/patch

packages/console/src/components/Executions/Tables/WorkflowExecutionTable/cells.tsx#L113-L114

Added lines #L113 - L114 were not covered by tests
<Chip
key={tag}
label={tag}
size="small"
color="default"
style={{
backgroundColor: isArchived ? undefined : getColorFromString(tag),

Check warning on line 121 in packages/console/src/components/Executions/Tables/WorkflowExecutionTable/cells.tsx

View check run for this annotation

Codecov / codecov/patch

packages/console/src/components/Executions/Tables/WorkflowExecutionTable/cells.tsx#L121

Added line #L121 was not covered by tests
}}
/>
);
})}
</div>
);
}

export function getLaunchPlanCell(
execution: Execution,
className: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Protobuf } from '@flyteorg/flyteidl-types';

const str = {
tableLabel_name: 'execution id',
tableLabel_tags: 'tags',
tableLabel_launchPlan: 'launch plan',
tableLabel_phase: 'status',
tableLabel_startedAt: 'start time',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ export const useStyles = makeStyles((theme: Theme) => ({
flexBasis: workflowExecutionsTableColumnWidths.name,
whiteSpace: 'normal',
},
columnExecutionTags: {
flexGrow: 1,
flexBasis: workflowExecutionsTableColumnWidths.tags,
whiteSpace: 'normal',
},
columnLaunchPlan: {
flexGrow: 1,
flexBasis: workflowExecutionsTableColumnWidths.launchPlan,
Expand Down Expand Up @@ -56,4 +61,9 @@ export const useStyles = makeStyles((theme: Theme) => ({
width: '100px', // same as confirmationButton size
textAlign: 'center',
},
executionTagsStack: {
display: 'flex',
gap: theme.spacing(0.5),
flexWrap: 'wrap',
},
}));
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
getStartTimeCell,
getStatusCell,
getLaunchPlanCell,
getExecutionTagsCell,
} from './cells';
import { useStyles } from './styles';
import t, { patternKey } from './strings';
Expand Down Expand Up @@ -45,6 +46,13 @@ export function useWorkflowExecutionsTableColumns(
key: 'name',
label: t(patternKey('tableLabel', 'name')),
},
{
cellRenderer: ({ execution }) =>
getExecutionTagsCell(execution, styles.executionTagsStack),
className: styles.columnExecutionTags,
key: 'tags',
label: t(patternKey('tableLabel', 'tags')),
},
{
cellRenderer: ({ execution }) =>
getLaunchPlanCell(execution, commonStyles.textWrapped),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ export const workflowExecutionsTableColumnWidths = {
duration: 100,
actions: 130,
lastRun: 130,
name: 240,
name: 200,
launchPlan: 120,
phase: 120,
startedAt: 200,
tags: 200,
};

export const nodeExecutionsTableColumnWidths = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export const filterLabels = {
startTime: 'Start Time',
status: 'Status',
version: 'Version',
tags: 'Tags',
};
14 changes: 13 additions & 1 deletion packages/console/src/components/Executions/filters/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ export interface FilterButtonState {
onClick: () => void;
}

export type FilterStateType = 'single' | 'multi' | 'search' | 'boolean';
export type FilterStateType =
| 'single'
| 'multi'
| 'search'
| 'boolean'
| 'tags';

export interface FilterState {
active: boolean;
Expand Down Expand Up @@ -60,3 +65,10 @@ export interface BooleanFilterState extends FilterState {
setActive: (active: boolean) => void;
type: 'boolean';
}

export interface TagsFilterState extends FilterState {
onChange: (newTags: string[]) => void;
placeholder: string;
type: 'tags';
tags: string[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { FilterState } from './types';
import { useMultiFilterState } from './useMultiFilterState';
import { useSearchFilterState } from './useSearchFilterState';
import { useSingleFilterState } from './useSingleFilterState';
import { useTagsFilterState } from './useTagsFilterState';

export interface ExecutionFiltersState {
appliedFilters: FilterOperation[];
Expand Down Expand Up @@ -45,6 +46,12 @@ export function useWorkflowExecutionFiltersState() {
listHeader: 'Filter By',
queryStateKey: 'status',
}),
useTagsFilterState({
filterKey: 'admin_tag.name',
label: filterLabels.tags,
placeholder: 'Enter Tags String',
queryStateKey: 'tags',
}),
useSearchFilterState({
filterKey: 'workflow.version',
label: filterLabels.version,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { useQueryState } from 'components/hooks/useQueryState';
import { FilterOperationName } from 'models/AdminEntity/types';
import { useEffect, useState } from 'react';
import { TagsFilterState } from './types';
import { useFilterButtonState } from './useFilterButtonState';

function serializeForQueryState(values: any[]) {
return values.join(';');

Check warning on line 8 in packages/console/src/components/Executions/filters/useTagsFilterState.ts

View check run for this annotation

Codecov / codecov/patch

packages/console/src/components/Executions/filters/useTagsFilterState.ts#L7-L8

Added lines #L7 - L8 were not covered by tests
}
function deserializeFromQueryState(stateValue = '') {
return stateValue.split(';');

Check warning on line 11 in packages/console/src/components/Executions/filters/useTagsFilterState.ts

View check run for this annotation

Codecov / codecov/patch

packages/console/src/components/Executions/filters/useTagsFilterState.ts#L10-L11

Added lines #L10 - L11 were not covered by tests
}

interface TagsFilterStateStateArgs {
defaultValue?: string[];
filterKey: string;
filterOperation?: FilterOperationName;
label: string;
placeholder: string;
queryStateKey: string;
}

/** Maintains the state for a `TagsInputForm` filter.
* The generated `FilterOperation` will use the provided `key` and `operation`
* (defaults to `VALUE_IN`)
* The current search value will be synced to the query string using the
* provided `queryStateKey` value.
*/
export function useTagsFilterState({
defaultValue = [],
filterKey,
filterOperation = FilterOperationName.VALUE_IN,
label,
placeholder,
queryStateKey,
}: TagsFilterStateStateArgs): TagsFilterState {
const { params, setQueryStateValue } =
useQueryState<Record<string, string>>();
const queryStateValue = params[queryStateKey];

const [tags, setTags] = useState(defaultValue);
const active = tags.length !== 0;

const button = useFilterButtonState();
const onChange = (newValue: string[]) => {
setTags(newValue);

Check warning on line 46 in packages/console/src/components/Executions/filters/useTagsFilterState.ts

View check run for this annotation

Codecov / codecov/patch

packages/console/src/components/Executions/filters/useTagsFilterState.ts#L46

Added line #L46 was not covered by tests
};

const onReset = () => {
setTags(defaultValue);
button.setOpen(false);

Check warning on line 51 in packages/console/src/components/Executions/filters/useTagsFilterState.ts

View check run for this annotation

Codecov / codecov/patch

packages/console/src/components/Executions/filters/useTagsFilterState.ts#L50-L51

Added lines #L50 - L51 were not covered by tests
};

useEffect(() => {
const queryValue = tags.length ? serializeForQueryState(tags) : undefined;
setQueryStateValue(queryStateKey, queryValue);
}, [tags.join(), queryStateKey]);

useEffect(() => {
if (queryStateValue) {
setTags(deserializeFromQueryState(queryStateValue));

Check warning on line 61 in packages/console/src/components/Executions/filters/useTagsFilterState.ts

View check run for this annotation

Codecov / codecov/patch

packages/console/src/components/Executions/filters/useTagsFilterState.ts#L61

Added line #L61 was not covered by tests
}
}, [queryStateValue]);

const getFilter = () =>
tags.length
? [

Check warning on line 67 in packages/console/src/components/Executions/filters/useTagsFilterState.ts

View check run for this annotation

Codecov / codecov/patch

packages/console/src/components/Executions/filters/useTagsFilterState.ts#L67

Added line #L67 was not covered by tests
{
value: tags,
key: filterKey,
operation: filterOperation,
},
]
: [];

return {
active,
button,
getFilter,
onChange,
onReset,
label,
placeholder,
tags,
type: 'tags',
};
}
Loading
Loading