From 1006db5522d051ecc7260fe6725d752893394fe1 Mon Sep 17 00:00:00 2001 From: Tim Jennison Date: Mon, 3 Feb 2025 19:32:48 +0000 Subject: [PATCH] Refactor TreeGrid to have typed items and data * Both items and data can be typed separately, though it mostly only makes sense to customize one or the other. EntityGroup criteria have generic data and typed items to store extra associated info such as the entity group but the study pages just have typed data because it's simpler. * Pass the item to rowCustomization callbacks since it contains the data as well. Type it appropriately. * Split the root children out so there's no need for an awkward root object. --- ui/src/addByCode.tsx | 25 ++--- ui/src/addCohort.tsx | 13 +-- ui/src/addCriteria.tsx | 35 +++--- ui/src/cohortReview/cohortReviewList.tsx | 11 +- ui/src/cohortReview/participantsList.tsx | 16 +-- .../cohortReview/plugins/occurrenceTable.tsx | 29 ++--- ui/src/components/treegrid.test.tsx | 68 ++++++------ ui/src/components/treegrid.tsx | 50 +++++---- ui/src/criteria/classification.tsx | 47 +++----- ui/src/criteria/survey.tsx | 100 ++++++++---------- ui/src/export.tsx | 19 ++-- ui/src/featureSet/addFeatureSet.tsx | 19 +--- ui/src/featureSet/featureSet.tsx | 18 ++-- ui/src/sampleApp/studiesList.tsx | 13 ++- ui/src/sampleApp/studyOverview.tsx | 13 ++- 15 files changed, 211 insertions(+), 265 deletions(-) diff --git a/ui/src/addByCode.tsx b/ui/src/addByCode.tsx index 54ff37858..f58008951 100644 --- a/ui/src/addByCode.tsx +++ b/ui/src/addByCode.tsx @@ -7,6 +7,7 @@ import { TreeGrid, TreeGridColumn, TreeGridId, + TreeGridItem, useArrayAsTreeGridData, } from "components/treegrid"; import ActionBar from "actionBar"; @@ -24,7 +25,7 @@ import { cohortURL, useIsSecondBlock } from "router"; import { insertCohortCriteria, useCohortContext } from "cohortContext"; import { isValid } from "util/valid"; -type LookupEntryItem = { +type LookupEntryData = { config?: JSX.Element; code: DataKey; name?: string; @@ -48,7 +49,7 @@ export function AddByCode() { [underlay.criteriaSelectors] ); - const lookupEntriesState = useSWRMutation( + const lookupEntriesState = useSWRMutation( { component: "AddByCode", query, @@ -110,7 +111,7 @@ export function AddByCode() { const data = useArrayAsTreeGridData(lookupEntriesState?.data ?? [], "code"); const onInsert = useCallback(() => { - const configMap = new Map(); + const configMap = new Map(); lookupEntriesState.data?.forEach((e) => { if (!e.entry || !selected.has(e.code)) { return; @@ -196,19 +197,13 @@ export function AddByCode() { {lookupEntriesState.data?.length ? ( - > columns={columns} data={data} - rowCustomization={(id: TreeGridId) => { - if (!lookupEntriesState.data) { - return undefined; - } - - const item = data.get(id)?.data as LookupEntryItem; - if (!item) { - return undefined; - } - + rowCustomization={( + id: TreeGridId, + { data }: TreeGridItem + ) => { const sel = selected.has(id); return [ { @@ -218,7 +213,7 @@ export function AddByCode() { size="small" fontSize="inherit" checked={sel} - disabled={!item.entry} + disabled={!data.entry} onChange={() => { updateSelected((selected) => { if (sel) { diff --git a/ui/src/addCohort.tsx b/ui/src/addCohort.tsx index 40dc87749..cd7c176ef 100644 --- a/ui/src/addCohort.tsx +++ b/ui/src/addCohort.tsx @@ -51,7 +51,7 @@ export function AddCohort() { const navigate = useNavigate(); const params = useBaseParams(); - const cohortsState = useSWR( + const cohortsState = useSWR( { type: "cohorts", studyId, @@ -112,16 +112,7 @@ export function AddCohort() { { - if (!cohortsState.data) { - return undefined; - } - - const cohortData = data.get(id)?.data as CohortData; - if (!cohortData) { - return undefined; - } - + rowCustomization={(id: TreeGridId, { data: cohortData }) => { return [ { column: columns.length - 2, diff --git a/ui/src/addCriteria.tsx b/ui/src/addCriteria.tsx index 0bb903992..6662206bc 100644 --- a/ui/src/addCriteria.tsx +++ b/ui/src/addCriteria.tsx @@ -23,7 +23,7 @@ import { TreeGrid, TreeGridId, TreeGridItem, - TreeGridRowData, + TreeGridData, } from "components/treegrid"; import { MergedItem } from "data/mergeLists"; import { Criteria, isTemporalSection } from "data/source"; @@ -352,9 +352,7 @@ function AddCriteria(props: AddCriteriaProps) { const search = useCallback(async () => { const children: DataKey[] = []; - const data = new Map([ - ["root", { data: {}, children }], - ]); + const rows = new Map(); if (query) { const res = await searchCriteria( @@ -376,13 +374,16 @@ function AddCriteria(props: AddCriteriaProps) { }, entry: entry, }; - data.set(key, item); + rows.set(key, item); }); } - return data; + return { + rows, + children, + }; }, [underlaySource, query, selectedOptions, optionsMap]); - const searchState = useSWRImmutable>( + const searchState = useSWRImmutable>( { component: "AddCriteria", underlayName: underlay.name, @@ -522,7 +523,7 @@ function AddCriteria(props: AddCriteriaProps) { {!!query ? ( - {!searchState.data?.get("root")?.children?.length ? ( + {!searchState.data?.children?.length ? ( { - if (!searchState.data) { - return undefined; - } - - const item = searchState.data.get(id); - const option = optionsMap.get(item?.entry?.source ?? ""); + data={searchState.data} + rowCustomization={(id, item) => { + const option = optionsMap.get(item.entry?.source ?? ""); if (!option || !item?.entry?.source) { throw new Error( `Item source "${item?.entry?.source}" doesn't match any criteria config ID.` @@ -554,7 +547,9 @@ function AddCriteria(props: AddCriteriaProps) { content: (