@databiosphere/findable-ui 45.0.0 → 46.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +36 -0
  3. package/lib/components/Detail/components/Table/common/utils.js +1 -1
  4. package/lib/components/Detail/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.js +1 -1
  5. package/lib/components/Detail/components/Table/components/TableRows/tableRows.js +1 -1
  6. package/lib/components/Detail/components/Table/table.js +7 -1
  7. package/lib/components/Index/components/EntityView/components/layout/Summary/summary.js +1 -1
  8. package/lib/components/Index/table/hook.js +7 -1
  9. package/lib/components/Layout/components/Outline/outline.js +3 -1
  10. package/lib/components/Layout/components/Outline/types.d.ts +1 -1
  11. package/lib/components/Table/components/TableCell/components/RowSelectionCell/constants.d.ts +2 -0
  12. package/lib/components/Table/components/TableCell/components/RowSelectionCell/constants.js +13 -0
  13. package/lib/components/Table/components/TableCell/components/RowSelectionCell/rowSelectionCell.js +12 -8
  14. package/lib/components/Table/components/TableRow/tableRow.styles.d.ts +2 -0
  15. package/lib/components/Table/components/TableRow/tableRow.styles.js +15 -1
  16. package/lib/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.js +1 -1
  17. package/lib/components/Table/components/TableRows/tableRows.js +1 -1
  18. package/lib/components/Table/features/RowSelectionValidation/constants.d.ts +2 -0
  19. package/lib/components/Table/features/RowSelectionValidation/constants.js +14 -0
  20. package/lib/components/Table/features/RowSelectionValidation/types.d.ts +8 -0
  21. package/lib/components/Table/features/RowSelectionValidation/types.js +1 -0
  22. package/lib/components/Table/features/RowSelectionValidation/utils.d.ts +8 -0
  23. package/lib/components/Table/features/RowSelectionValidation/utils.js +11 -0
  24. package/lib/components/Table/features/entities.d.ts +3 -2
  25. package/lib/components/TableCreator/options/hook.js +0 -3
  26. package/lib/components/common/Button/components/FileDownloadButton/fileDownloadButton.d.ts +5 -0
  27. package/lib/components/common/Button/components/FileDownloadButton/fileDownloadButton.js +14 -0
  28. package/lib/components/common/CustomIcon/components/UncheckedDisabledIcon/uncheckedDisabledIcon.d.ts +2 -0
  29. package/lib/components/common/CustomIcon/components/UncheckedDisabledIcon/uncheckedDisabledIcon.js +8 -0
  30. package/lib/utils/mdx/files/mapMDXSlugByFilePaths.d.ts +8 -0
  31. package/lib/utils/mdx/files/mapMDXSlugByFilePaths.js +39 -0
  32. package/lib/utils/mdx/files/resolveRelativeDirs.d.ts +6 -0
  33. package/lib/utils/mdx/files/resolveRelativeDirs.js +9 -0
  34. package/lib/utils/mdx/frontmatter/getMatter.d.ts +8 -0
  35. package/lib/utils/mdx/frontmatter/getMatter.js +12 -0
  36. package/lib/utils/mdx/frontmatter/types.d.ts +7 -0
  37. package/lib/utils/mdx/frontmatter/types.js +1 -0
  38. package/lib/utils/mdx/frontmatter/validateMatter.d.ts +8 -0
  39. package/lib/utils/mdx/frontmatter/validateMatter.js +13 -0
  40. package/lib/utils/mdx/plugins/rehypeSlug.d.ts +6 -0
  41. package/lib/utils/mdx/plugins/rehypeSlug.js +34 -0
  42. package/lib/utils/mdx/plugins/remarkHeadings.d.ts +29 -0
  43. package/lib/utils/mdx/plugins/remarkHeadings.js +50 -0
  44. package/lib/utils/mdx/plugins/utils.d.ts +13 -0
  45. package/lib/utils/mdx/plugins/utils.js +25 -0
  46. package/lib/utils/mdx/staticGeneration/staticPaths.d.ts +8 -0
  47. package/lib/utils/mdx/staticGeneration/staticPaths.js +12 -0
  48. package/lib/utils/mdx/staticGeneration/staticProps.d.ts +5 -0
  49. package/lib/utils/mdx/staticGeneration/staticProps.js +44 -0
  50. package/lib/utils/mdx/staticGeneration/types.d.ts +12 -0
  51. package/lib/utils/mdx/staticGeneration/types.js +1 -0
  52. package/lib/utils/mdx/staticGeneration/utils.d.ts +15 -0
  53. package/lib/utils/mdx/staticGeneration/utils.js +26 -0
  54. package/package.json +8 -5
  55. package/src/components/Detail/components/Table/common/utils.ts +1 -1
  56. package/src/components/Detail/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.tsx +1 -0
  57. package/src/components/Detail/components/Table/components/TableRows/tableRows.tsx +2 -0
  58. package/src/components/Detail/components/Table/table.tsx +7 -1
  59. package/src/components/Index/components/EntityView/components/layout/Summary/summary.tsx +8 -6
  60. package/src/components/Index/table/hook.ts +7 -1
  61. package/src/components/Layout/components/Outline/outline.tsx +6 -1
  62. package/src/components/Layout/components/Outline/types.ts +1 -1
  63. package/src/components/Table/components/TableCell/components/RowSelectionCell/constants.ts +15 -0
  64. package/src/components/Table/components/TableCell/components/RowSelectionCell/rowSelectionCell.tsx +25 -13
  65. package/src/components/Table/components/TableRow/tableRow.styles.ts +19 -1
  66. package/src/components/Table/components/TableRows/components/CollapsableRows/collapsableRows.tsx +1 -0
  67. package/src/components/Table/components/TableRows/tableRows.tsx +2 -0
  68. package/src/components/Table/features/RowSelectionValidation/constants.ts +24 -0
  69. package/src/components/Table/features/RowSelectionValidation/types.ts +10 -0
  70. package/src/components/Table/features/RowSelectionValidation/utils.ts +15 -0
  71. package/src/components/Table/features/entities.ts +10 -2
  72. package/src/components/TableCreator/options/hook.ts +0 -3
  73. package/src/components/common/Button/components/FileDownloadButton/fileDownloadButton.tsx +27 -0
  74. package/src/components/common/CustomIcon/components/UncheckedDisabledIcon/uncheckedDisabledIcon.tsx +27 -0
  75. package/src/utils/mdx/files/mapMDXSlugByFilePaths.ts +48 -0
  76. package/src/utils/mdx/files/resolveRelativeDirs.ts +10 -0
  77. package/src/utils/mdx/frontmatter/getMatter.ts +13 -0
  78. package/src/utils/mdx/frontmatter/types.ts +7 -0
  79. package/src/utils/mdx/frontmatter/validateMatter.ts +20 -0
  80. package/src/utils/mdx/plugins/rehypeSlug.ts +36 -0
  81. package/src/utils/mdx/plugins/remarkHeadings.ts +67 -0
  82. package/src/utils/mdx/plugins/utils.ts +27 -0
  83. package/src/utils/mdx/staticGeneration/staticPaths.ts +18 -0
  84. package/src/utils/mdx/staticGeneration/staticProps.ts +62 -0
  85. package/src/utils/mdx/staticGeneration/types.ts +16 -0
  86. package/src/utils/mdx/staticGeneration/utils.ts +32 -0
  87. package/tests/chart.test.tsx +4 -2
  88. package/tests/markdownCell.test.tsx +1 -2
  89. package/tests/rowSelectionValidation.test.ts +282 -0
@@ -0,0 +1,50 @@
1
+ import { visit } from "unist-util-visit";
2
+ import { generateUniqueId, slugifyHeading } from "./utils";
3
+ /**
4
+ * Remark plugin to generate an outline from MDX content.
5
+ * The outline is a list of headings with their depth, hash, and value.
6
+ * @param options - Options.
7
+ * @param options.depth - Depth of the headings.
8
+ * @param options.depth.max - Maximum depth of the headings.
9
+ * @param options.depth.min - Minimum depth of the headings.
10
+ * @param options.outline - Outline items.
11
+ * @returns plugin to generate an outline from MDX content.
12
+ */
13
+ export function remarkHeadings({ depth: { max = 3, min = 2 } = {}, outline, }) {
14
+ return (tree) => {
15
+ const setOfIds = new Set();
16
+ visit(tree, "heading", (node) => {
17
+ const heading = node;
18
+ const { children, depth } = heading;
19
+ if (depth < min || depth > max)
20
+ return;
21
+ const value = getHeadingTextValue(children);
22
+ const headingSlug = slugifyHeading(value);
23
+ const hash = generateUniqueId(setOfIds, headingSlug);
24
+ outline.push({
25
+ depth,
26
+ hash,
27
+ value,
28
+ });
29
+ });
30
+ };
31
+ }
32
+ /**
33
+ * Returns the value of the heading.
34
+ * @param children - Phrasing content.
35
+ * @param value - List of heading text values.
36
+ * @returns heading text value.
37
+ */
38
+ export function getHeadingTextValue(children, value = []) {
39
+ for (const child of children) {
40
+ if ("value" in child) {
41
+ value.push(child.value);
42
+ }
43
+ if ("children" in child) {
44
+ // Recurse into nested children, accumulating text into the shared `value` array.
45
+ // The return value is ignored here because accumulation happens via in-place mutation.
46
+ getHeadingTextValue(child.children, value);
47
+ }
48
+ }
49
+ return value.join("");
50
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Returns node ID, ensuring uniqueness.
3
+ * @param setOfIds - Set of IDs.
4
+ * @param slug - Slug.
5
+ * @returns node ID.
6
+ */
7
+ export declare function generateUniqueId(setOfIds: Set<string>, slug: string): string;
8
+ /**
9
+ * Slugify the heading text to generate an ID.
10
+ * @param headingText - Heading text.
11
+ * @returns heading ID.
12
+ */
13
+ export declare function slugifyHeading(headingText: string): string;
@@ -0,0 +1,25 @@
1
+ import slugify from "slugify";
2
+ /**
3
+ * Returns node ID, ensuring uniqueness.
4
+ * @param setOfIds - Set of IDs.
5
+ * @param slug - Slug.
6
+ * @returns node ID.
7
+ */
8
+ export function generateUniqueId(setOfIds, slug) {
9
+ let id = slug;
10
+ let i = 1;
11
+ while (setOfIds.has(id)) {
12
+ id = `${slug}-${i}`;
13
+ i++;
14
+ }
15
+ setOfIds.add(id);
16
+ return id;
17
+ }
18
+ /**
19
+ * Slugify the heading text to generate an ID.
20
+ * @param headingText - Heading text.
21
+ * @returns heading ID.
22
+ */
23
+ export function slugifyHeading(headingText) {
24
+ return slugify(headingText, { lower: true, strict: true });
25
+ }
@@ -0,0 +1,8 @@
1
+ import { GetStaticPathsResult } from "next";
2
+ /**
3
+ * Builds Next.js static paths for MDX page files found under the given relative directories.
4
+ * Each path’s `slug` is composed of the file’s relative directory segments plus the MDX filename (without extension).
5
+ * @param relativeDirs - Relative directories to scan for MDX pages.
6
+ * @returns Array of path objects suitable for `getStaticPaths`.
7
+ */
8
+ export declare function buildStaticPaths(relativeDirs: string[]): GetStaticPathsResult["paths"];
@@ -0,0 +1,12 @@
1
+ import { mapMDXSlugByFilePaths } from "../files/mapMDXSlugByFilePaths";
2
+ import { resolveRelativeDirs } from "../files/resolveRelativeDirs";
3
+ /**
4
+ * Builds Next.js static paths for MDX page files found under the given relative directories.
5
+ * Each path’s `slug` is composed of the file’s relative directory segments plus the MDX filename (without extension).
6
+ * @param relativeDirs - Relative directories to scan for MDX pages.
7
+ * @returns Array of path objects suitable for `getStaticPaths`.
8
+ */
9
+ export function buildStaticPaths(relativeDirs) {
10
+ const slugByFilePaths = mapMDXSlugByFilePaths(resolveRelativeDirs(relativeDirs));
11
+ return [...slugByFilePaths].map(([, slug]) => ({ params: { slug } }));
12
+ }
@@ -0,0 +1,5 @@
1
+ import { SerializeOptions } from "next-mdx-remote/dist/types";
2
+ import { GetStaticPropsResult } from "next/types";
3
+ import { FrontmatterProps } from "../frontmatter/types";
4
+ import { StaticProps } from "./types";
5
+ export declare function buildStaticProps<F extends object, P extends object>(fileName: string | undefined, slug: string[] | undefined, frontmatterFn?: (frontmatter: FrontmatterProps<F> | undefined) => FrontmatterProps<F> | undefined, serializeOptions?: SerializeOptions, otherProps?: P): Promise<GetStaticPropsResult<StaticProps<FrontmatterProps<F>, P>> | undefined>;
@@ -0,0 +1,44 @@
1
+ import { serialize } from "next-mdx-remote/serialize";
2
+ import remarkGfm from "remark-gfm";
3
+ import { getMatter } from "../frontmatter/getMatter";
4
+ import { validateMatter } from "../frontmatter/validateMatter";
5
+ import { rehypeSlug } from "../plugins/rehypeSlug";
6
+ import { remarkHeadings } from "../plugins/remarkHeadings";
7
+ export async function buildStaticProps(fileName, slug, frontmatterFn = (frontmatter) => frontmatter, serializeOptions = {}, otherProps = {}) {
8
+ if (!slug)
9
+ return;
10
+ if (!fileName)
11
+ return;
12
+ // Extract frontmatter and content from the MDX file.
13
+ const { content, data } = getMatter(fileName);
14
+ const frontmatter = frontmatterFn(validateMatter(data));
15
+ // If the frontmatter is hidden, return.
16
+ if (!frontmatter || frontmatter.hidden)
17
+ return;
18
+ // We expect the frontmatter to have a title.
19
+ if (!frontmatter.title)
20
+ return;
21
+ // Serialize the MDX content.
22
+ const outline = [];
23
+ const mdxSource = await serialize(content, {
24
+ ...serializeOptions,
25
+ mdxOptions: {
26
+ development: false,
27
+ rehypePlugins: [rehypeSlug],
28
+ remarkPlugins: [[remarkHeadings, { outline }], remarkGfm],
29
+ ...serializeOptions.mdxOptions,
30
+ },
31
+ scope: { ...serializeOptions.scope, frontmatter },
32
+ });
33
+ const { title: pageTitle } = frontmatter;
34
+ return {
35
+ props: {
36
+ frontmatter,
37
+ mdxSource,
38
+ outline,
39
+ pageTitle,
40
+ slug,
41
+ ...otherProps,
42
+ },
43
+ };
44
+ }
@@ -0,0 +1,12 @@
1
+ import { MDXRemoteSerializeResult } from "next-mdx-remote";
2
+ import { OutlineItem } from "../../../components/Layout/components/Outline/types";
3
+ import { FrontmatterProps } from "../frontmatter/types";
4
+ interface BaseStaticProps<F extends object> {
5
+ frontmatter: FrontmatterProps<F> | null;
6
+ mdxSource: MDXRemoteSerializeResult | null;
7
+ outline: OutlineItem[] | null;
8
+ pageTitle: string;
9
+ slug: string[];
10
+ }
11
+ export type StaticProps<F extends object, P extends object> = BaseStaticProps<F> & P;
12
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { GetStaticPropsContext } from "next/types";
2
+ /**
3
+ * Builds the MDX file path from the given list of doc directories and slug.
4
+ * @param dirs - Doc directories e.g. ["app", "docs"].
5
+ * @param slug - Doc slug e.g. ["learn", "featured-analyses"].
6
+ * @returns MDX file path.
7
+ */
8
+ export declare function buildMDXFilePath(dirs: string[], slug: string[] | undefined): string | undefined;
9
+ /**
10
+ * Returns the MDX page slug for the given static props context and section.
11
+ * @param props - Static props context.
12
+ * @param section - Docs section e.g. "learn".
13
+ * @returns MDX page slug.
14
+ */
15
+ export declare function buildMDXSlug(props: GetStaticPropsContext, section?: string): string[] | undefined;
@@ -0,0 +1,26 @@
1
+ import { resolveRelativeDirs } from "../files/resolveRelativeDirs";
2
+ /**
3
+ * Builds the MDX file path from the given list of doc directories and slug.
4
+ * @param dirs - Doc directories e.g. ["app", "docs"].
5
+ * @param slug - Doc slug e.g. ["learn", "featured-analyses"].
6
+ * @returns MDX file path.
7
+ */
8
+ export function buildMDXFilePath(dirs, slug) {
9
+ if (!slug)
10
+ return;
11
+ return resolveRelativeDirs(dirs.concat(slug)).concat(".mdx");
12
+ }
13
+ /**
14
+ * Returns the MDX page slug for the given static props context and section.
15
+ * @param props - Static props context.
16
+ * @param section - Docs section e.g. "learn".
17
+ * @returns MDX page slug.
18
+ */
19
+ export function buildMDXSlug(props, section) {
20
+ const slug = props.params?.slug;
21
+ if (!slug || typeof slug === "string")
22
+ return;
23
+ if (section)
24
+ return [section, ...slug];
25
+ return slug;
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databiosphere/findable-ui",
3
- "version": "45.0.0",
3
+ "version": "46.0.0",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
@@ -26,7 +26,7 @@
26
26
  "@commitlint/config-conventional": "^17.4.2",
27
27
  "@emotion/jest": "^11.13.0",
28
28
  "@mui/types": "^7.4.0",
29
- "@next/eslint-plugin-next": "^14.2.30",
29
+ "@next/eslint-plugin-next": "^14.2.32",
30
30
  "@storybook/addon-actions": "^8.6.4",
31
31
  "@storybook/addon-essentials": "^8.6.4",
32
32
  "@storybook/addon-interactions": "^8.6.4",
@@ -46,7 +46,7 @@
46
46
  "@types/uuid": "8.3.4",
47
47
  "@typescript-eslint/eslint-plugin": "^7.18.0",
48
48
  "eslint": "^8.33.0",
49
- "eslint-config-next": "^14.2.30",
49
+ "eslint-config-next": "^14.2.32",
50
50
  "eslint-config-prettier": "^8.6.0",
51
51
  "eslint-plugin-eslint-comments": "^3.2.0",
52
52
  "eslint-plugin-jsdoc": "^48.1.0",
@@ -76,10 +76,12 @@
76
76
  "@tanstack/react-table": "^8.19.2",
77
77
  "@tanstack/react-virtual": "^3.13.12",
78
78
  "copy-to-clipboard": "3.3.1",
79
+ "gray-matter": "^4.0.3",
79
80
  "isomorphic-dompurify": "^2.22.0",
80
81
  "ky": "^1.7.2",
81
- "next": "^14.2.30",
82
- "next-auth": "^4.24.7",
82
+ "next": "^14.2.32",
83
+ "next-auth": "4.24.11",
84
+ "next-mdx-remote": "^4.4.1",
83
85
  "react": "^18.3.1",
84
86
  "react-dom": "^18.3.1",
85
87
  "react-dropzone": "^14.2.3",
@@ -92,6 +94,7 @@
92
94
  "remark-gfm": "^3.0.1",
93
95
  "remark-parse": "^10.0.2",
94
96
  "remark-rehype": "^10.1.0",
97
+ "slugify": "^1.6.6",
95
98
  "unified": "^10.1.2",
96
99
  "uuid": "8.3.2",
97
100
  "yup": "^1.6.1"
@@ -13,7 +13,7 @@ export function generateColumnDefinitions<T extends RowData>(
13
13
  ...column,
14
14
  meta: {
15
15
  ...column.meta,
16
- header: column.meta?.header || (column.header as string),
16
+ header: column.meta?.header ?? (column.header as string),
17
17
  },
18
18
  };
19
19
  });
@@ -31,6 +31,7 @@ export const CollapsableRows = <T extends RowData>({
31
31
  key={row.id}
32
32
  id={row.id}
33
33
  isPreview={row.getIsPreview()}
34
+ isSelected={row.getIsSelected()}
34
35
  >
35
36
  <CollapsableCell
36
37
  isDisabled={isCollapsableRowDisabled(tableInstance)}
@@ -37,9 +37,11 @@ export const TableRows = <T extends RowData>({
37
37
  key={row.id}
38
38
  id={row.id}
39
39
  canExpand={row.getCanExpand()}
40
+ canSelect={row.getCanSelect()}
40
41
  isExpanded={row.getIsExpanded()}
41
42
  isGrouped={row.getIsGrouped()}
42
43
  isPreview={row.getIsPreview()}
44
+ isSelected={row.getIsSelected()}
43
45
  onClick={() => handleToggleExpanded(row)}
44
46
  >
45
47
  {row.getVisibleCells().map((cell) => {
@@ -23,6 +23,7 @@ import { TableHead } from "../../../Table/components/TableHead/tableHead";
23
23
  import { TABLE_DOWNLOAD_OPTIONS } from "../../../Table/featureOptions/tableDownload/constants";
24
24
  import { ROW_POSITION } from "../../../Table/features/RowPosition/constants";
25
25
  import { ROW_PREVIEW } from "../../../Table/features/RowPreview/constants";
26
+ import { ROW_SELECTION_VALIDATION } from "../../../Table/features/RowSelectionValidation/constants";
26
27
  import { TABLE_DOWNLOAD } from "../../../Table/features/TableDownload/constants";
27
28
  import { GridTable } from "../../../Table/table.styles";
28
29
  import { generateColumnDefinitions } from "./common/utils";
@@ -60,7 +61,12 @@ export const Table = <T extends RowData>({
60
61
  const { stickyHeader = false } = table || {};
61
62
  const { sx: tableContainerSx } = tableContainer || {};
62
63
  const tableInstance = useReactTable({
63
- _features: [ROW_POSITION, ROW_PREVIEW, TABLE_DOWNLOAD],
64
+ _features: [
65
+ ROW_POSITION,
66
+ ROW_PREVIEW,
67
+ ROW_SELECTION_VALIDATION,
68
+ TABLE_DOWNLOAD,
69
+ ],
64
70
  columns: generateColumnDefinitions([
65
71
  COLUMN_DEF.ROW_POSITION as ColumnDef<T>,
66
72
  ...columns,
@@ -26,12 +26,14 @@ export const Summary = ({
26
26
  <Typography variant={TYPOGRAPHY_PROPS.VARIANT.BODY_SMALL_500}>
27
27
  {count}
28
28
  </Typography>
29
- <Typography
30
- color={TYPOGRAPHY_PROPS.COLOR.INK_LIGHT}
31
- variant={TYPOGRAPHY_PROPS.VARIANT.BODY_SMALL_400}
32
- >
33
- {label}
34
- </Typography>
29
+ {label && (
30
+ <Typography
31
+ color={TYPOGRAPHY_PROPS.COLOR.INK_LIGHT}
32
+ variant={TYPOGRAPHY_PROPS.VARIANT.BODY_SMALL_400}
33
+ >
34
+ {label}
35
+ </Typography>
36
+ )}
35
37
  </Fragment>
36
38
  ))}
37
39
  </StyledGrid>
@@ -38,6 +38,7 @@ import { TABLE_DOWNLOAD_OPTIONS } from "../../Table/featureOptions/tableDownload
38
38
  import { ROW_POSITION } from "../../Table/features/RowPosition/constants";
39
39
  import { ROW_PREVIEW } from "../../Table/features/RowPreview/constants";
40
40
  import { RowPreviewState } from "../../Table/features/RowPreview/entities";
41
+ import { ROW_SELECTION_VALIDATION } from "../../Table/features/RowSelectionValidation/constants";
41
42
  import { TABLE_DOWNLOAD } from "../../Table/features/TableDownload/constants";
42
43
  import { buildBaseColumnDef } from "../../TableCreator/common/utils";
43
44
  import { useTableOptions } from "../../TableCreator/options/hook";
@@ -164,7 +165,12 @@ UseTableProps): UseTable<T> => {
164
165
  * - This will simplify the configuration structure and centralize table state definitions, reducing redundancy and improving clarity.
165
166
  */
166
167
  const table = useReactTable<T>({
167
- _features: [ROW_POSITION, ROW_PREVIEW, TABLE_DOWNLOAD],
168
+ _features: [
169
+ ROW_POSITION,
170
+ ROW_PREVIEW,
171
+ ROW_SELECTION_VALIDATION,
172
+ TABLE_DOWNLOAD,
173
+ ],
168
174
  columns: columnDefs,
169
175
  data: listItems || [],
170
176
  enableColumnFilters: true, // client-side filtering.
@@ -10,7 +10,12 @@ export const Outline = ({
10
10
  outline,
11
11
  ...props /* MuiTabsProps */
12
12
  }: OutlineProps): JSX.Element | null => {
13
- const { indicatorColor, onChange, orientation, value } = useTabs(outline);
13
+ const { indicatorColor, onChange, orientation, value } = useTabs(
14
+ outline ?? []
15
+ );
16
+
17
+ if (!outline?.length) return null;
18
+
14
19
  return (
15
20
  <StyledTabs
16
21
  className={className}
@@ -12,5 +12,5 @@ export interface OutlineItem {
12
12
 
13
13
  export interface OutlineProps extends BaseComponentProps, TabsProps {
14
14
  Contents: ElementType<ContentsTabProps>;
15
- outline: OutlineItem[];
15
+ outline?: OutlineItem[] | null;
16
16
  }
@@ -0,0 +1,15 @@
1
+ import { TooltipProps } from "@mui/material";
2
+
3
+ export const TOOLTIP_PROPS: Partial<TooltipProps> = {
4
+ arrow: true,
5
+ disableInteractive: true,
6
+ slotProps: {
7
+ popper: {
8
+ modifiers: [
9
+ { name: "offset", options: { offset: [0, -4] } },
10
+ { name: "preventOverflow", options: { padding: 8 } },
11
+ ],
12
+ },
13
+ tooltip: { sx: { maxWidth: "220px" } },
14
+ },
15
+ };
@@ -1,24 +1,36 @@
1
- import { Checkbox as MCheckbox } from "@mui/material";
1
+ import { Checkbox as MCheckbox, Tooltip } from "@mui/material";
2
2
  import { CellContext, RowData } from "@tanstack/react-table";
3
3
  import React from "react";
4
4
  import { CheckedIcon } from "../../../../../common/CustomIcon/components/CheckedIcon/checkedIcon";
5
+ import { UncheckedDisabledIcon } from "../../../../../common/CustomIcon/components/UncheckedDisabledIcon/uncheckedDisabledIcon";
5
6
  import { UncheckedIcon } from "../../../../../common/CustomIcon/components/UncheckedIcon/uncheckedIcon";
7
+ import { TOOLTIP_PROPS } from "./constants";
6
8
 
7
9
  export const RowSelectionCell = <T extends RowData, TValue = unknown>({
8
10
  row,
9
11
  }: CellContext<T, TValue>): JSX.Element => {
10
- const { getIsSelected, getToggleSelectedHandler } = row;
12
+ const {
13
+ getCanSelect,
14
+ getIsSelected,
15
+ getSelectionValidation,
16
+ getToggleSelectedHandler,
17
+ } = row;
11
18
  return (
12
- <MCheckbox
13
- checked={getIsSelected()}
14
- checkedIcon={<CheckedIcon />}
15
- icon={<UncheckedIcon />}
16
- /*
17
- * Prevents click events from bubbling up to parent components
18
- * (such as CardActionArea or Accordion) when the checkbox is activated.
19
- */
20
- onClick={(e) => e.stopPropagation()}
21
- onChange={getToggleSelectedHandler()}
22
- />
19
+ <Tooltip {...TOOLTIP_PROPS} title={getSelectionValidation?.()}>
20
+ <span>
21
+ <MCheckbox
22
+ checked={getIsSelected()}
23
+ checkedIcon={<CheckedIcon />}
24
+ disabled={!getCanSelect()}
25
+ icon={getCanSelect() ? <UncheckedIcon /> : <UncheckedDisabledIcon />}
26
+ /*
27
+ * Prevents click events from bubbling up to parent components
28
+ * (such as CardActionArea or Accordion) when the checkbox is activated.
29
+ */
30
+ onClick={(e) => e.stopPropagation()}
31
+ onChange={getToggleSelectedHandler()}
32
+ />
33
+ </span>
34
+ </Tooltip>
23
35
  );
24
36
  };
@@ -6,17 +6,21 @@ import { PALETTE } from "../../../../styles/common/constants/palette";
6
6
 
7
7
  export interface StyledTableRowProps {
8
8
  canExpand?: boolean;
9
+ canSelect?: boolean;
9
10
  isExpanded?: boolean;
10
11
  isGrouped?: boolean;
11
12
  isPreview?: boolean;
13
+ isSelected?: boolean;
12
14
  }
13
15
 
14
16
  export const StyledTableRow = styled(MTableRow, {
15
17
  shouldForwardProp: (prop) =>
16
18
  prop !== "canExpand" &&
19
+ prop !== "canSelect" &&
17
20
  prop !== "isExpanded" &&
21
+ prop !== "isGrouped" &&
18
22
  prop !== "isPreview" &&
19
- prop !== "isGrouped",
23
+ prop !== "isSelected",
20
24
  })<StyledTableRowProps>`
21
25
  && {
22
26
  transition: background-color 300ms ease-in;
@@ -54,5 +58,19 @@ export const StyledTableRow = styled(MTableRow, {
54
58
  css`
55
59
  background-color: ${PALETTE.PRIMARY_LIGHTEST};
56
60
  `}
61
+
62
+ ${({ isSelected }) =>
63
+ isSelected &&
64
+ css`
65
+ background-color: ${PALETTE.PRIMARY_LIGHTEST};
66
+ `}
67
+
68
+ ${({ canSelect }) =>
69
+ !canSelect &&
70
+ css`
71
+ .MuiTableCell-root {
72
+ color: ${PALETTE.INK_LIGHT};
73
+ }
74
+ `}
57
75
  }
58
76
  `;
@@ -33,6 +33,7 @@ export const CollapsableRows = <T extends RowData>({
33
33
  data-index={rowIndex}
34
34
  ref={virtualizer.measureElement}
35
35
  isPreview={row.getIsPreview()}
36
+ isSelected={row.getIsSelected()}
36
37
  >
37
38
  <CollapsableCell
38
39
  isDisabled={isCollapsableRowDisabled(tableInstance)}
@@ -30,10 +30,12 @@ export const TableRows = <T extends RowData>({
30
30
  <StyledTableRow
31
31
  key={row.id}
32
32
  canExpand={getCanExpand()}
33
+ canSelect={row.getCanSelect()}
33
34
  data-index={rowIndex}
34
35
  isExpanded={getIsExpanded()}
35
36
  isGrouped={getIsGrouped()}
36
37
  isPreview={getIsPreview()}
38
+ isSelected={row.getIsSelected()}
37
39
  onClick={() => handleToggleExpanded(row)}
38
40
  ref={virtualizer.measureElement}
39
41
  >
@@ -0,0 +1,24 @@
1
+ import {
2
+ Row,
3
+ RowData,
4
+ Table,
5
+ TableFeature,
6
+ TableOptionsResolved,
7
+ } from "@tanstack/react-table";
8
+ import { getSelectionValidation } from "./utils";
9
+
10
+ export const ROW_SELECTION_VALIDATION: TableFeature = {
11
+ createRow: <T extends RowData>(row: Row<T>, table: Table<T>): void => {
12
+ row.getSelectionValidation = (): string | undefined => {
13
+ return getSelectionValidation(row, table);
14
+ };
15
+ },
16
+ getDefaultOptions: <T extends RowData>(): Partial<
17
+ TableOptionsResolved<T>
18
+ > => {
19
+ return {
20
+ enableRowSelectionValidation: false,
21
+ getRowSelectionValidation: undefined,
22
+ };
23
+ },
24
+ };
@@ -0,0 +1,10 @@
1
+ import { Row, RowData } from "@tanstack/react-table";
2
+
3
+ export interface RowSelectionValidationOptions<T extends RowData> {
4
+ enableRowSelectionValidation?: boolean;
5
+ getRowSelectionValidation?: (row: Row<T>) => string | undefined;
6
+ }
7
+
8
+ export interface RowSelectionValidationRow {
9
+ getSelectionValidation?: () => string | undefined;
10
+ }
@@ -0,0 +1,15 @@
1
+ import { Row, RowData, Table } from "@tanstack/react-table";
2
+
3
+ /**
4
+ * Returns the validation message for a row.
5
+ * @param row - Row.
6
+ * @param table - Table.
7
+ * @returns The validation message for the row.
8
+ */
9
+ export function getSelectionValidation<T extends RowData>(
10
+ row: Row<T>,
11
+ table: Table<T>
12
+ ): string | undefined {
13
+ if (!table.options.enableRowSelectionValidation) return;
14
+ return table.options.getRowSelectionValidation?.(row);
15
+ }
@@ -6,6 +6,10 @@ import {
6
6
  RowPreviewRow,
7
7
  RowPreviewTableState,
8
8
  } from "./RowPreview/entities";
9
+ import {
10
+ RowSelectionValidationOptions,
11
+ RowSelectionValidationRow,
12
+ } from "./RowSelectionValidation/types";
9
13
  import {
10
14
  TableDownloadColumn,
11
15
  TableDownloadInstance,
@@ -23,8 +27,12 @@ export type CustomFeatureInitialTableState = Partial<RowPreviewTableState>;
23
27
  export interface CustomFeatureOptions<T extends RowData>
24
28
  extends TableDownloadOptions<T>,
25
29
  RowPositionOptions,
26
- RowPreviewOptions {}
30
+ RowPreviewOptions,
31
+ RowSelectionValidationOptions<T> {}
27
32
 
28
- export interface CustomFeatureRow extends RowPositionRow, RowPreviewRow {}
33
+ export interface CustomFeatureRow
34
+ extends RowPositionRow,
35
+ RowPreviewRow,
36
+ RowSelectionValidationRow {}
29
37
 
30
38
  export type CustomFeatureTableState = RowPreviewTableState;
@@ -3,7 +3,6 @@ import { useConfig } from "../../../hooks/useConfig";
3
3
  import { useExpandedOptions } from "./expanded/hook";
4
4
  import { useGroupingOptions } from "./grouping/hook";
5
5
  import { useInitialState } from "./initialState/hook";
6
- import { useRowSelectionOptions } from "./rowSelection/hook";
7
6
  import { useSortingOptions } from "./sorting/hook";
8
7
  import { useVisibilityOptions } from "./visibility/hook";
9
8
 
@@ -12,14 +11,12 @@ export function useTableOptions<T extends RowData>(): Partial<TableOptions<T>> {
12
11
  const tableOptions = entityConfig.list.tableOptions;
13
12
  const expandedOptions = useExpandedOptions<T>();
14
13
  const groupingOptions = useGroupingOptions();
15
- const rowSelectionOptions = useRowSelectionOptions<T>();
16
14
  const sortingOptions = useSortingOptions<T>();
17
15
  const visibilityOptions = useVisibilityOptions();
18
16
  const initialState = useInitialState<T>(tableOptions);
19
17
  return {
20
18
  ...expandedOptions,
21
19
  ...groupingOptions,
22
- ...rowSelectionOptions,
23
20
  ...sortingOptions, // TODO(cc) merge of all sorting options.
24
21
  ...visibilityOptions,
25
22
  ...tableOptions,