Skip to content

Commit

Permalink
feat: Add Select All checkbox to workflow dag filter options
Browse files Browse the repository at this point in the history
Fixes argoproj#11129

- [x] also fixed bug when click some labels of checkboxes, they would change state of other same-named checkboxes. This was due to non-unique IDs on the checkboxes.

Signed-off-by: Ben Compton <3343482+bencompton@users.noreply.github.com>
Co-Authored-by: jmeridth <jmeridth@gmail.com>
Co-Authored-by: Steven Johnson <steven.johnson@procore.com>
  • Loading branch information
jmeridth and stevenbjohnson committed May 25, 2023
1 parent 04d527c commit 3265851
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 29 deletions.
4 changes: 2 additions & 2 deletions ui/src/app/shared/components/filter-drop-down.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export const FilterDropDown = (props: FilterDropDownProps) => {
.map(([label, checked]) => (
<li key={label} className={classNames('top-bar__filter-item')}>
<React.Fragment>
<Checkbox id={`filter__${label}`} checked={checked} onChange={v => item.onChange(label, v)} />
<label htmlFor={`filter__${label}`}>{label}</label>
<Checkbox id={`filter__${i}_${label}`} checked={checked} onChange={v => item.onChange(label, v)} />
<label htmlFor={`filter__${i}_${label}`}>{label}</label>
</React.Fragment>
</li>
))}
Expand Down
98 changes: 71 additions & 27 deletions ui/src/app/shared/components/graph/graph-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,21 @@ require('./graph-panel.scss');

type IconShape = 'rect' | 'circle';

interface NodeGenres {
interface INodeSelectionMap {
[type: string]: boolean;
}

interface NodeClassNames {
[type: string]: boolean;
}

interface NodeTags {
[key: string]: boolean;
}

interface Props {
graph: Graph;
storageScope: string; // the scope of storage, similar graphs should use the same vaulue
options?: React.ReactNode; // add to the option panel
classNames?: string;
nodeGenresTitle: string;
nodeGenres: NodeGenres;
nodeGenres: INodeSelectionMap;
nodeClassNamesTitle?: string;
nodeClassNames?: NodeClassNames;
nodeClassNames?: INodeSelectionMap;
nodeTagsTitle?: string;
nodeTags?: NodeTags;
nodeTags?: INodeSelectionMap;
nodeSize?: number; // default "64"
horizontal?: boolean; // default "false"
hideNodeTypes?: boolean; // default "false"
Expand All @@ -53,9 +45,10 @@ export const GraphPanel = (props: Props) => {
const [nodeSize, setNodeSize] = React.useState<number>(storage.getItem('nodeSize', props.nodeSize));
const [horizontal, setHorizontal] = React.useState<boolean>(storage.getItem('horizontal', !!props.horizontal));
const [fast, setFast] = React.useState<boolean>(storage.getItem('fast', false));
const [nodeGenres, setNodeGenres] = React.useState<NodeGenres>(storage.getItem('nodeGenres', props.nodeGenres));
const [nodeClassNames, setNodeClassNames] = React.useState<NodeClassNames>(storage.getItem('nodeClassNames', props.nodeClassNames));
const [nodeTags, setNodeTags] = React.useState<NodeTags>(props.nodeTags);
const [nodeGenres, setNodeGenres] = React.useState<INodeSelectionMap>(storage.getItem('nodeGenres', props.nodeGenres));
const [nodeClassNames, setNodeClassNames] = React.useState<INodeSelectionMap>(storage.getItem('nodeClassNames', props.nodeClassNames));
const [nodeTags, setNodeTags] = React.useState<INodeSelectionMap>(props.nodeTags);
const [checkAll, setCheckAll] = React.useState<boolean>(true);
const [nodeSearchKeyword, setNodeSearchKeyword] = React.useState<string>('');

useEffect(() => storage.setItem('nodeSize', nodeSize, props.nodeSize), [nodeSize]);
Expand All @@ -70,6 +63,11 @@ export const GraphPanel = (props: Props) => {
useEffect(() => setNodeClassNames(merge(nodeClassNames, props.nodeClassNames)), [props.nodeClassNames]);
useEffect(() => setNodeTags(merge(nodeTags, props.nodeTags)), [props.nodeTags]);

useEffect(() => {
const allSelected = getIsAllSelected(nodeClassNames, nodeTags, nodeGenres);
setCheckAll(allSelected);
}, [nodeGenres, nodeClassNames, nodeTags]);

const visible = (id: Node) => {
const label = props.graph.nodes.get(id);
// If the node matches the search string, return without considering filters
Expand All @@ -91,6 +89,54 @@ export const GraphPanel = (props: Props) => {
return true;
};

const checkBoxHandler = (callback: React.Dispatch<React.SetStateAction<INodeSelectionMap>>, label: string, checked: boolean) => {
callback(v => {
return {
...v,
[label]: checked
};
});
};

const getNodeMapWithAllNodesSetToValue = (nodeSelectionMap: INodeSelectionMap, value: boolean) => {
return Object.keys(nodeSelectionMap).reduce((accumulatedMap, nextKey) => {
return {
...accumulatedMap,
[nextKey]: value
};
}, {});
};

const getIsAllSelected = (nodeClassNames: INodeSelectionMap, nodeTags: INodeSelectionMap, nodeGenres: INodeSelectionMap) => {
const nodeSelections: INodeSelectionMap = {...nodeClassNames, ...nodeTags, ...nodeGenres};
const totalNodeCount = Object.keys(nodeSelections).length;

const selectedNodeCount = Object.keys(nodeSelections).reduce((accumulatedTotal, key) => {
if (nodeSelections[key]) {
return accumulatedTotal + 1;
} else {
return accumulatedTotal;
}
}, 0);

return selectedNodeCount === totalNodeCount;
};

const onSelectAll = (
nodeClassNames: INodeSelectionMap,
nodeTags: INodeSelectionMap,
nodeGenres: INodeSelectionMap,
setNodeClassNames: React.Dispatch<React.SetStateAction<INodeSelectionMap>>,
setNodeTags: React.Dispatch<React.SetStateAction<INodeSelectionMap>>,
setNodeGenres: React.Dispatch<React.SetStateAction<INodeSelectionMap>>
) => {
const isAllSelected = getIsAllSelected(nodeClassNames, nodeTags, nodeGenres);

setNodeClassNames(getNodeMapWithAllNodesSetToValue(nodeClassNames, !isAllSelected));
setNodeTags(getNodeMapWithAllNodesSetToValue(nodeTags, !isAllSelected));
setNodeGenres(getNodeMapWithAllNodesSetToValue(nodeGenres, !isAllSelected));
};

layout(props.graph, nodeSize, horizontal, id => !visible(id), fast);
const width = props.graph.width;
const height = props.graph.height;
Expand All @@ -101,34 +147,32 @@ export const GraphPanel = (props: Props) => {
<div className='graph-options-panel'>
<FilterDropDown
sections={[
{
title: '',
values: {'Check All': checkAll},
onChange: (_, checked) => {
onSelectAll(nodeClassNames, nodeTags, nodeGenres, setNodeClassNames, setNodeTags, setNodeGenres);
}
},
{
title: props.nodeGenresTitle,
values: nodeGenres,
onChange: (label, checked) => {
setNodeGenres(v => {
v[label] = checked;
return Object.assign({}, v);
});
checkBoxHandler(setNodeGenres, label, checked);
}
},
{
title: props.nodeClassNamesTitle,
values: nodeClassNames,
onChange: (label, checked) => {
setNodeClassNames(v => {
v[label] = checked;
return Object.assign({}, v);
});
checkBoxHandler(setNodeClassNames, label, checked);
}
},
{
title: props.nodeTagsTitle,
values: nodeTags,
onChange: (label, checked) => {
setNodeTags(v => {
v[label] = checked;
return Object.assign({}, v);
});
checkBoxHandler(setNodeTags, label, checked);
}
}
]}
Expand Down

0 comments on commit 3265851

Please sign in to comment.