Skip to content

Commit

Permalink
Add exclusion support to FilterableGroup UI
Browse files Browse the repository at this point in the history
  • Loading branch information
Timothy Jennison authored and tjennison-work committed Oct 22, 2024
1 parent 4fa2340 commit c31f7b7
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 44 deletions.
3 changes: 3 additions & 0 deletions ui/src/components/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export type SearchProps = {
searchKey?: string;
delayMs?: number;

disabled?: boolean;
showSearchButton?: boolean;
};

Expand Down Expand Up @@ -70,6 +71,7 @@ export function Search(props: SearchProps) {
<form onSubmit={handleSubmit}>
<Stack direction="row" justifyContent="center" alignItems="center">
<TextField
disabled={props.disabled}
autoFocus
fullWidth
name="query"
Expand All @@ -87,6 +89,7 @@ export function Search(props: SearchProps) {
endAdornment: values.query?.length ? (
<InputAdornment position="end">
<IconButton
disabled={props.disabled}
onClick={() => {
form.reset();
onSearch("");
Expand Down
181 changes: 137 additions & 44 deletions ui/src/criteria/filterableGroup.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import AddIcon from "@mui/icons-material/Add";
import ClearIcon from "@mui/icons-material/Clear";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import FilterListIcon from "@mui/icons-material/FilterList";
import Button from "@mui/material/Button";
import IconButton from "@mui/material/IconButton";
Expand All @@ -11,6 +13,7 @@ import Typography from "@mui/material/Typography";
import { CriteriaPlugin, generateId, registerCriteriaPlugin } from "cohort";
import Checkbox from "components/checkbox";
import Empty from "components/empty";
import { containedIconButtonSx } from "components/iconButton";
import Loading from "components/loading";
import { Search } from "components/search";
import { useSimpleDialog } from "components/simpleDialog";
Expand Down Expand Up @@ -188,6 +191,9 @@ function FilterableGroupEdit(props: FilterableGroupEditProps) {
const [rowsPerPage, setRowsPerPage] = useState(25);

const [filters, setFilters] = useState<ValueData[]>([]);
const [selectedExclusion, setSelectedExclusion] = useState<
string | undefined
>();

const [unconfirmedChangesDialog, showUnconfirmedChangesDialog] =
useSimpleDialog();
Expand Down Expand Up @@ -330,6 +336,7 @@ function FilterableGroupEdit(props: FilterableGroupEditProps) {
>
<Search
placeholder="Search variants"
disabled={!!selectedExclusion}
onSearch={(query: string) => {
updateSearchState((data: SearchState) => {
data.query = query;
Expand All @@ -349,48 +356,62 @@ function FilterableGroupEdit(props: FilterableGroupEditProps) {
{instancesState.data?.length && instancesState.data?.[0]?.total ? (
<GridLayout rows fillRow={1}>
<GridLayout cols spacing={2} sx={{ px: 5 }}>
<FilterButton
hintData={instancesState.data?.[0]?.hintData}
entity={entityGroup.selectionEntity.name}
config={props.config}
filters={filters}
setFilters={setFilters}
/>
<Tooltip
title={
!selectAllEnabled
? "Number of results must be between 25 and 10,000 to Select all. For results less than 25, selections must be made individually."
: ""
}
>
<span>
<Button
variant="outlined"
disabled={!selectAllEnabled}
endIcon={<AddIcon />}
onClick={() =>
updateLocalCriteria((data) => {
data.selected.push({
id: generateId(),
all: {
query: searchState?.query ?? "",
values: filters,
exclusions: [],
},
});
})
}
>
Select all
</Button>
</span>
</Tooltip>
{!selectedExclusion ? (
<FilterButton
hintData={instancesState.data?.[0]?.hintData}
entity={entityGroup.selectionEntity.name}
config={props.config}
filters={filters}
setFilters={setFilters}
/>
) : null}
{!selectedExclusion ? (
<Tooltip
title={
!selectAllEnabled
? "Number of results must be between 25 and 10,000 to Select all. For results less than 25, selections must be made individually."
: ""
}
>
<span>
<Button
variant="outlined"
disabled={!selectAllEnabled}
endIcon={<AddIcon />}
onClick={() =>
updateLocalCriteria((data) => {
data.selected.push({
id: generateId(),
all: {
query: searchState?.query ?? "",
values: filters,
exclusions: [],
},
});
})
}
>
Select all
</Button>
</span>
</Tooltip>
) : null}
{selectedExclusion ? (
<Button
variant="contained"
onClick={() => setSelectedExclusion(undefined)}
>
Confirm exclusions
</Button>
) : null}
</GridLayout>
{instancesState.data[currentPage] ? (
<ResultsPage
columns={columns}
nodes={instancesState.data[currentPage].nodes}
selectedSet={selectedSet}
selectedExclusion={selectedExclusion}
localCriteria={localCriteria}
updateLocalCriteria={updateLocalCriteria}
/>
) : null}
Expand Down Expand Up @@ -459,6 +480,7 @@ function FilterableGroupEdit(props: FilterableGroupEditProps) {
{selectionTitle(s)}
</Typography>
<IconButton
disabled={!!selectedExclusion}
onClick={() =>
updateLocalCriteria((data) => {
data.selected.splice(i, 1);
Expand All @@ -472,6 +494,21 @@ function FilterableGroupEdit(props: FilterableGroupEditProps) {
<SelectAllStats
config={props.config}
selectAll={s.all}
selected={s.id === selectedExclusion}
setSelected={(selected: boolean) => {
const all = s.all;

setSelectedExclusion(
selected ? s.id : undefined
);
if (selected && all) {
updateSearchState((data: SearchState) => {
data.query = all.query;
});

setFilters(all.values);
}
}}
/>
) : null}
</GridLayout>
Expand Down Expand Up @@ -508,6 +545,8 @@ type ResultsPageProps = {
columns: TreeGridColumn[];
nodes: EntityNode[];
selectedSet: Set<DataKey>;
selectedExclusion?: string;
localCriteria: Data;
updateLocalCriteria: (fn: (data: Data) => void) => void;
};

Expand All @@ -517,17 +556,52 @@ function ResultsPage(props: ResultsPageProps) {
"key"
);

const selectAll = useMemo(() => {
return props.localCriteria.selected.find(
(s) => s.id === props.selectedExclusion
)?.all;
}, [props.localCriteria, props.selectedExclusion]);

return (
<TreeGrid
columns={props.columns}
data={data}
rowCustomization={(key: TreeGridId, rowData: TreeGridRowData) => {
const found = !!props.selectedSet.has(key);
const found = selectAll
? !!selectAll.exclusions.find((e) => e.key === key)
: props.selectedSet.has(key);
const newSelection = {
key,
name: String(rowData[props.columns[0].key] ?? "Unknown"),
};

return [
{
column: 0,
prefixElements: (
prefixElements: selectAll ? (
<IconButton
color={!found ? "error" : undefined}
sx={found ? containedIconButtonSx("error") : undefined}
onClick={() => {
props.updateLocalCriteria((data) => {
const all = data.selected.find(
(s) => s.id === props.selectedExclusion
)?.all;
if (all) {
if (found) {
all.exclusions = all.exclusions.filter(
(e) => e.key !== key
);
} else {
all.exclusions.push(newSelection);
}
}
});
}}
>
<ClearIcon fontSize="small" />
</IconButton>
) : (
<Checkbox
size="small"
fontSize="inherit"
Expand All @@ -541,12 +615,7 @@ function ResultsPage(props: ResultsPageProps) {
} else {
data.selected.push({
id: generateId(),
single: {
key,
name: String(
rowData[props.columns[0].key] ?? "Unknown"
),
},
single: newSelection,
});
}
});
Expand Down Expand Up @@ -629,6 +698,8 @@ function FilterButton(props: FilterButtonProps) {
type SelectAllStatsProps = {
config: configProto.FilterableGroup;
selectAll: SelectAll;
selected?: boolean;
setSelected?: (selected: boolean) => void;
};

function SelectAllStats(props: SelectAllStatsProps) {
Expand Down Expand Up @@ -656,6 +727,28 @@ function SelectAllStats(props: SelectAllStatsProps) {
</Typography>
);
})}
<GridLayout cols rowAlign="middle">
{props.setSelected ? (
<IconButton
sx={props.selected ? containedIconButtonSx("primary") : undefined}
onClick={() => {
if (props.setSelected) {
props.setSelected(!props.selected);
}
}}
>
<EditIcon fontSize="small" />
</IconButton>
) : null}
<Typography variant="body2em">
{"Exclusions: "}
<Typography variant="body2" component="span">
{props.selectAll.exclusions.length
? props.selectAll.exclusions.map((e) => e.name).join(", ")
: "None"}
</Typography>
</Typography>
</GridLayout>
</GridLayout>
);
}
Expand Down

0 comments on commit c31f7b7

Please sign in to comment.