Skip to content

Commit 5087d0c

Browse files
frano-mFran McDade
andauthored
feat!: implement required filter on data dictionary (#497) (#503)
* feat!: implement required filter on data dictionary (#497) * refactor: tables into a singular table, grouped by class name (#497) * refactor: data dictionary table options (#497) * feat: added filter state to data dictionary (#497) * feat: update outline (#497) * feat: linting (#497) * feat: change filter menu position (#497) * fix: layout constants (#497) * fix: grouped row array index to row.id (#497) * feat: added filter story (#497) * fix: linting (#497) * feat: update story args (#497) * feat: basic column filters test added (#497) * feat: added column filter tests (#497) * feat: added clear all disabled test (#497) --------- Co-authored-by: Fran McDade <franmcdade@Frans-MacBook-Pro.local>
1 parent da5dc6c commit 5087d0c

62 files changed

Lines changed: 1073 additions & 120 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

jest.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
/** @type {import('ts-jest').JestConfigWithTsJest} */
22
module.exports = {
3+
moduleNameMapper: {
4+
"^@storybook/addon-actions$":
5+
"<rootDir>/src/mocks/@storybook/addon-actions.ts",
6+
},
37
preset: "ts-jest/presets/js-with-ts-esm",
48
setupFiles: ["<rootDir>/tests/setup.ts"],
59
testEnvironment: "jest-environment-jsdom",

src/common/entities.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { ColumnDef, RowData } from "@tanstack/react-table";
1+
import { ColumnDef, RowData, TableOptions } from "@tanstack/react-table";
22

33
/**
44
* Model of a value of a metadata class.
55
*/
66
export interface Attribute {
77
// Prefix to fragment mapping, e.g. cxg: "batch_condition", or, general tags e.g. tier: "Tier 1" and bionetwork: ["gut"]
88
annotations?: Record<string, string | string[] | undefined>; // 'undefined' allows for mix of keys across attributes e.g. tier, or tier and cxg, or cxg
9+
classKey?: string; // Programmatic class name or key (e.g. cell, sample) the attribute belongs to.
910
description: string;
1011
example?: string; // Free text example of attribute
1112
multivalued: boolean; // True if attribute can have multiple values
@@ -73,6 +74,7 @@ export interface DataDictionary<T extends RowData = Attribute> {
7374
export interface DataDictionaryConfig<T extends RowData = Attribute> {
7475
columnDefs: ColumnDef<T, T[keyof T]>[];
7576
dataDictionary: DataDictionary<T>;
77+
tableOptions?: Omit<TableOptions<T>, "columns" | "data" | "getCoreRowModel">;
7678
}
7779

7880
/**

src/components/DataDictionary/components/Entities/entities.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,15 @@ import { GRID_PROPS } from "./constants";
77
import { ClassesProps } from "./types";
88

99
export const Entities = <T extends RowData = Attribute>({
10-
classes,
11-
columnDefs,
1210
spacing,
11+
table,
1312
}: ClassesProps<T>): JSX.Element => {
13+
const { getGroupedRowModel } = table;
1414
return (
1515
<Grid {...GRID_PROPS}>
16-
{classes.map((cls) => (
17-
<Entity
18-
key={cls.name}
19-
class={cls}
20-
columnDefs={columnDefs}
21-
spacing={spacing}
22-
/>
16+
{/* Render grouped rows where each "group" is a class e.g. "donor" */}
17+
{getGroupedRowModel().rows.map((row) => (
18+
<Entity key={row.id} row={row} spacing={spacing} table={table} />
2319
))}
2420
</Grid>
2521
);
Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
import { ColumnDef, RowData } from "@tanstack/react-table";
2-
import { Attribute, Class } from "../../../../common/entities";
1+
import { RowData, Table } from "@tanstack/react-table";
2+
import { Attribute } from "../../../../common/entities";
33
import { LayoutSpacing } from "../../hooks/UseLayoutSpacing/types";
44

55
export interface ClassesProps<T extends RowData = Attribute> {
6-
classes: Class<T>[];
7-
columnDefs: ColumnDef<T, T[keyof T]>[];
86
spacing?: LayoutSpacing;
7+
table: Table<T>;
98
}

src/components/DataDictionary/components/Entity/entity.styles.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,17 @@ import styled from "@emotion/styled";
22
import { Typography } from "@mui/material";
33
import { LayoutSpacing } from "../../hooks/UseLayoutSpacing/types";
44
import { ENTITIES_ROW_GAP } from "../Entities/constants";
5+
import { LAYOUT_SPACING } from "../Layout/constants";
6+
7+
const TOP =
8+
ENTITIES_ROW_GAP +
9+
LAYOUT_SPACING.TITLE_HEIGHT +
10+
LAYOUT_SPACING.FILTERS_HEIGHT +
11+
LAYOUT_SPACING.FILTERS_PADDING_TOP +
12+
LAYOUT_SPACING.CONTENT_PADDING_TOP;
513

614
export const StyledTypography = styled(Typography)<Partial<LayoutSpacing>>`
7-
scroll-margin-top: ${({ top = 0 }) => top + ENTITIES_ROW_GAP}px;
15+
scroll-margin-top: ${({ top = 0 }) => top + TOP}px;
816
917
&:hover a {
1018
opacity: 1;

src/components/DataDictionary/components/Entity/entity.tsx

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,37 @@ import React from "react";
44
import { Attribute } from "../../../../common/entities";
55
import { TYPOGRAPHY_PROPS } from "../../../../styles/common/mui/typography";
66
import { AnchorLink } from "../../../common/AnchorLink/anchorLink";
7-
import { useTable } from "../Table/hook";
87
import { Table } from "../Table/table";
98
import { GRID_PROPS } from "./constants";
109
import { StyledTypography } from "./entity.styles";
1110
import { EntityProps } from "./types";
11+
import { getClassMeta } from "./utils";
1212

1313
export const Entity = <T extends RowData = Attribute>({
14-
class: cls,
15-
columnDefs,
14+
row,
1615
spacing,
17-
}: EntityProps<T>): JSX.Element => {
18-
const table = useTable<T>(cls.attributes, columnDefs);
16+
table,
17+
}: EntityProps<T>): JSX.Element | null => {
18+
// Get class key from row.
19+
const classKey = row.getValue<string>("classKey");
20+
21+
// Get class metadata from table options.
22+
const cls = getClassMeta<T>(classKey, table);
23+
24+
// Class not found in table meta - return null.
25+
if (!cls) return null;
26+
1927
return (
2028
<Grid {...GRID_PROPS} rowGap={4}>
29+
{/* Class title and description */}
2130
<Grid {...GRID_PROPS} rowGap={1}>
2231
<StyledTypography
2332
component="h3"
24-
id={cls.name}
33+
id={classKey}
2534
variant={TYPOGRAPHY_PROPS.VARIANT.TEXT_HEADING_SMALL}
2635
{...spacing}
2736
>
28-
{cls.title} <AnchorLink anchorLink={cls.name} />
37+
{cls.title} <AnchorLink anchorLink={classKey} />
2938
</StyledTypography>
3039
<Typography
3140
color={TYPOGRAPHY_PROPS.COLOR.INK_LIGHT}
@@ -35,7 +44,8 @@ export const Entity = <T extends RowData = Attribute>({
3544
{cls.description}
3645
</Typography>
3746
</Grid>
38-
<Table table={table} />
47+
{/* Class attributes table */}
48+
<Table row={row} table={table} />
3949
</Grid>
4050
);
4151
};
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { ColumnDef, RowData } from "@tanstack/react-table";
2-
import { Attribute, Class } from "../../../../common/entities";
1+
import { Row, RowData, Table } from "@tanstack/react-table";
2+
import { Attribute } from "../../../../common/entities";
33
import { LayoutSpacing } from "../../hooks/UseLayoutSpacing/types";
44

55
export interface EntityProps<T extends RowData = Attribute> {
6-
class: Class<T>;
7-
columnDefs: ColumnDef<T, T[keyof T]>[];
6+
row: Row<T>;
87
spacing?: LayoutSpacing;
8+
table: Table<T>;
99
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { RowData, Table } from "@tanstack/react-table";
2+
import { Attribute, Class } from "../../../../common/entities";
3+
import { ClassMeta } from "../Table/types";
4+
5+
/**
6+
* Retrieves class metadata from the table options meta.
7+
* @param classKey - Class key.
8+
* @param table - Table instance.
9+
* @returns Class metadata or undefined.
10+
*/
11+
export function getClassMeta<T extends RowData = Attribute>(
12+
classKey: string | undefined,
13+
table: Table<T>
14+
): Pick<Class<T>, "description" | "title"> | undefined {
15+
if (!classKey) return;
16+
17+
// Grab the table meta and return if not defined.
18+
const meta = table.options.meta;
19+
if (!meta) return;
20+
21+
// Return class metadata if defined.
22+
if ("classMeta" in meta) {
23+
return (meta.classMeta as ClassMeta)?.[classKey];
24+
}
25+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { ButtonGroup } from "@mui/material";
2+
import { RowData } from "@tanstack/react-table";
3+
import React from "react";
4+
import { Attribute } from "../../../../../../common/entities";
5+
import { BUTTON_GROUP_PROPS } from "../../../../../common/ButtonGroup/constants";
6+
import { ColumnFilter } from "../../../../../Table/components/TableFeatures/ColumnFilter/columnFilter";
7+
import { ColumnFiltersProps } from "./types";
8+
9+
export const ColumnFilters = <T extends RowData = Attribute>({
10+
table,
11+
}: ColumnFiltersProps<T>): JSX.Element => {
12+
const columns = table.getAllColumns();
13+
const columnFilters = columns.filter((column) => column.getCanFilter());
14+
return (
15+
<ButtonGroup {...BUTTON_GROUP_PROPS.SECONDARY_OUTLINED}>
16+
{columnFilters.map((column) => (
17+
<ColumnFilter key={column.id} column={column} />
18+
))}
19+
</ButtonGroup>
20+
);
21+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { RowData, Table } from "@tanstack/react-table";
2+
import { Attribute } from "../../../../../../common/entities";
3+
4+
export interface ColumnFiltersProps<T extends RowData = Attribute> {
5+
table: Table<T>;
6+
}

0 commit comments

Comments
 (0)