@databiosphere/findable-ui 38.1.1 → 38.3.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 (45) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +19 -0
  3. package/lib/components/DataDictionary/components/Description/description.d.ts +2 -0
  4. package/lib/components/DataDictionary/components/Description/description.js +8 -0
  5. package/lib/components/DataDictionary/components/Description/description.styles.d.ts +10 -0
  6. package/lib/components/DataDictionary/components/Description/description.styles.js +51 -0
  7. package/lib/components/DataDictionary/components/Description/types.d.ts +3 -0
  8. package/lib/components/DataDictionary/components/Description/types.js +1 -0
  9. package/lib/components/DataDictionary/components/Layout/components/EntitiesLayout/entitiesLayout.styles.js +2 -0
  10. package/lib/components/DataDictionary/components/Table/options/expanded/constants.d.ts +1 -1
  11. package/lib/components/DataDictionary/components/Table/options/expanded/constants.js +1 -0
  12. package/lib/components/DataDictionary/dataDictionary.js +3 -1
  13. package/lib/components/DataDictionary/hooks/UseDataDictionaryConfig/hook.js +1 -0
  14. package/lib/components/DataDictionary/hooks/UseDataDictionaryConfig/types.d.ts +1 -0
  15. package/lib/components/Detail/components/Table/components/TableRows/tableRows.js +2 -1
  16. package/lib/components/Filter/components/FilterRange/filterRange.styles.js +1 -0
  17. package/lib/components/Links/components/Link/components/ExploreViewLink/exploreViewLink.js +2 -2
  18. package/lib/components/Links/components/Link/link.d.ts +0 -1
  19. package/lib/components/MarkdownRenderer/components/Anchor/anchor.js +6 -1
  20. package/lib/components/Table/components/TableCell/components/LinkCell/linkCell.js +6 -1
  21. package/lib/components/Table/components/TableCell/components/RowSelectionCell/rowSelectionCell.js +6 -1
  22. package/lib/components/Table/components/TableFeatures/RowExpanding/utils.d.ts +10 -0
  23. package/lib/components/Table/components/TableFeatures/RowExpanding/utils.js +22 -0
  24. package/lib/components/Table/components/TableRow/tableRow.styles.d.ts +2 -0
  25. package/lib/components/Table/components/TableRow/tableRow.styles.js +23 -5
  26. package/lib/components/Table/components/TableRows/tableRows.js +3 -2
  27. package/package.json +1 -1
  28. package/src/components/DataDictionary/components/Description/description.styles.ts +56 -0
  29. package/src/components/DataDictionary/components/Description/description.tsx +17 -0
  30. package/src/components/DataDictionary/components/Description/types.ts +3 -0
  31. package/src/components/DataDictionary/components/Layout/components/EntitiesLayout/entitiesLayout.styles.ts +2 -0
  32. package/src/components/DataDictionary/components/Table/options/expanded/constants.ts +2 -1
  33. package/src/components/DataDictionary/dataDictionary.tsx +3 -1
  34. package/src/components/DataDictionary/hooks/UseDataDictionaryConfig/hook.ts +1 -0
  35. package/src/components/DataDictionary/hooks/UseDataDictionaryConfig/types.ts +1 -0
  36. package/src/components/Detail/components/Table/components/TableRows/tableRows.tsx +4 -0
  37. package/src/components/Filter/components/FilterRange/filterRange.styles.ts +1 -0
  38. package/src/components/Links/components/Link/components/ExploreViewLink/exploreViewLink.tsx +2 -2
  39. package/src/components/Links/components/Link/link.tsx +0 -1
  40. package/src/components/MarkdownRenderer/components/Anchor/anchor.tsx +5 -0
  41. package/src/components/Table/components/TableCell/components/LinkCell/linkCell.tsx +5 -0
  42. package/src/components/Table/components/TableCell/components/RowSelectionCell/rowSelectionCell.tsx +5 -0
  43. package/src/components/Table/components/TableFeatures/RowExpanding/utils.ts +25 -0
  44. package/src/components/Table/components/TableRow/tableRow.styles.ts +28 -9
  45. package/src/components/Table/components/TableRows/tableRows.tsx +5 -1
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "38.1.1"
2
+ ".": "38.3.0"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [38.3.0](https://github.com/DataBiosphere/findable-ui/compare/v38.2.0...v38.3.0) (2025-07-22)
4
+
5
+
6
+ ### Features
7
+
8
+ * add table row expansion to table components ([#569](https://github.com/DataBiosphere/findable-ui/issues/569)) ([#570](https://github.com/DataBiosphere/findable-ui/issues/570)) ([d23c5b8](https://github.com/DataBiosphere/findable-ui/commit/d23c5b8c091a899295a3005ff80d7cd297949033))
9
+
10
+ ## [38.2.0](https://github.com/DataBiosphere/findable-ui/compare/v38.1.1...v38.2.0) (2025-07-11)
11
+
12
+
13
+ ### Features
14
+
15
+ * add 'introduction' section to data dictionary ([#566](https://github.com/DataBiosphere/findable-ui/issues/566)) ([#567](https://github.com/DataBiosphere/findable-ui/issues/567)) ([5aeb758](https://github.com/DataBiosphere/findable-ui/commit/5aeb758792532a78fa4ec71dc0f7b3ad8c80c376))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * align range filter toggle button styles with design ([#563](https://github.com/DataBiosphere/findable-ui/issues/563)) ([#564](https://github.com/DataBiosphere/findable-ui/issues/564)) ([1cc1ffd](https://github.com/DataBiosphere/findable-ui/commit/1cc1ffd88ae3b2f436717b494e425614412cface))
21
+
3
22
  ## [38.1.1](https://github.com/DataBiosphere/findable-ui/compare/v38.1.0...v38.1.1) (2025-07-09)
4
23
 
5
24
 
@@ -0,0 +1,2 @@
1
+ import { DescriptionProps } from "./types";
2
+ export declare const Description: ({ description, }: DescriptionProps) => JSX.Element | null;
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import { StyledMarkdownRenderer, StyledRoundedPaper, } from "./description.styles";
3
+ export const Description = ({ description, }) => {
4
+ if (!description)
5
+ return null;
6
+ return (React.createElement(StyledRoundedPaper, { elevation: 0 },
7
+ React.createElement(StyledMarkdownRenderer, { value: description })));
8
+ };
@@ -0,0 +1,10 @@
1
+ export declare const StyledRoundedPaper: import("@emotion/styled").StyledComponent<import("../../../types").BaseComponentProps & import("@mui/material").PaperOwnProps & import("@mui/material/OverridableComponent").CommonProps & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
2
+ ref?: ((instance: HTMLDivElement | null) => void | import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof import("react").DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | import("react").RefObject<HTMLDivElement> | null | undefined;
3
+ }, "style" | "className" | "classes" | "children" | "sx" | "elevation" | "square" | "variant"> & {
4
+ component?: React.ElementType;
5
+ } & {
6
+ theme?: import("@emotion/react").Theme;
7
+ }, {}, {}>;
8
+ export declare const StyledMarkdownRenderer: import("@emotion/styled").StyledComponent<import("../../../MarkdownRenderer/types").MarkdownRendererProps & {
9
+ theme?: import("@emotion/react").Theme;
10
+ }, {}, {}>;
@@ -0,0 +1,51 @@
1
+ import styled from "@emotion/styled";
2
+ import { PALETTE } from "../../../../styles/common/constants/palette";
3
+ import { mediaTabletDown, mediaTabletUp, } from "../../../../styles/common/mixins/breakpoints";
4
+ import { textHeadingSmall } from "../../../../styles/common/mixins/fonts";
5
+ import { RoundedPaper } from "../../../common/Paper/components/RoundedPaper/roundedPaper";
6
+ import { MarkdownRenderer } from "../../../MarkdownRenderer/markdownRenderer";
7
+ export const StyledRoundedPaper = styled(RoundedPaper) `
8
+ padding: 20px;
9
+
10
+ ${mediaTabletDown} {
11
+ padding: 20px 16px;
12
+ }
13
+ `;
14
+ export const StyledMarkdownRenderer = styled(MarkdownRenderer) `
15
+ align-self: flex-start;
16
+
17
+ code {
18
+ all: unset;
19
+ font: inherit;
20
+ font-family: Roboto Mono, monospace;
21
+ }
22
+
23
+ h2,
24
+ h3,
25
+ h4,
26
+ h5,
27
+ h6 {
28
+ margin: 8px 0;
29
+ }
30
+
31
+ h2 {
32
+ ${textHeadingSmall};
33
+ font-size: 18px;
34
+ line-height: 26px;
35
+
36
+ ${mediaTabletUp} {
37
+ font-size: 18px;
38
+ line-height: 26px;
39
+ }
40
+ }
41
+
42
+ hr {
43
+ border: none;
44
+ border-bottom: 1px solid ${PALETTE.SMOKE_MAIN};
45
+ margin: 16px 0;
46
+ }
47
+
48
+ p {
49
+ font: inherit;
50
+ }
51
+ `;
@@ -0,0 +1,3 @@
1
+ export interface DescriptionProps {
2
+ description?: string;
3
+ }
@@ -4,6 +4,8 @@ import { LAYOUT_SPACING } from "../../constants";
4
4
  const PB = LAYOUT_SPACING.CONTENT_PADDING_BOTTOM; /* bottom padding */
5
5
  const PT = LAYOUT_SPACING.CONTENT_PADDING_TOP; /* top padding */
6
6
  export const Layout = styled("div") `
7
+ display: grid;
8
+ gap: 16px;
7
9
  grid-column: 2;
8
10
  grid-row: 1;
9
11
  padding-bottom: ${PB}px;
@@ -1,2 +1,2 @@
1
1
  import { ExpandedOptions, RowData } from "@tanstack/react-table";
2
- export declare const EXPANDED_OPTIONS: Pick<ExpandedOptions<RowData>, "enableExpanding" | "getExpandedRowModel">;
2
+ export declare const EXPANDED_OPTIONS: Pick<ExpandedOptions<RowData>, "enableExpanding" | "getExpandedRowModel" | "getRowCanExpand">;
@@ -2,4 +2,5 @@ import { getExpandedRowModel, } from "@tanstack/react-table";
2
2
  export const EXPANDED_OPTIONS = {
3
3
  enableExpanding: true,
4
4
  getExpandedRowModel: getExpandedRowModel(),
5
+ getRowCanExpand: () => true,
5
6
  };
@@ -1,6 +1,7 @@
1
1
  import { Fade } from "@mui/material";
2
2
  import React from "react";
3
3
  import { useLayoutSpacing } from "../../hooks/UseLayoutSpacing/hook";
4
+ import { Description } from "./components/Description/description";
4
5
  import { Entities } from "./components/Entities/entities";
5
6
  import { ColumnFilterTags } from "./components/Filters/components/ColumnFilterTags/columnFilterTags";
6
7
  import { Filters } from "./components/Filters/filters";
@@ -17,7 +18,7 @@ import { useDataDictionaryConfig } from "./hooks/UseDataDictionaryConfig/hook";
17
18
  import { useMeasureFilters } from "./hooks/UseMeasureFilters/hook";
18
19
  export const DataDictionary = ({ className, dictionary, EntitiesLayout = DefaultEntitiesLayout, FiltersLayout = DefaultFiltersLayout, Outline = DefaultOutline, OutlineLayout = DefaultOutlineLayout, Title = DefaultTitle, TitleLayout = DefaultTitleLayout, }) => {
19
20
  // Get dictionary configuration.
20
- const { classes, tableOptions, title } = useDataDictionaryConfig(dictionary);
21
+ const { classes, description, tableOptions, title } = useDataDictionaryConfig(dictionary);
21
22
  // Layout measurements.
22
23
  // Get header and footer dimensions.
23
24
  const { spacing } = useLayoutSpacing();
@@ -40,5 +41,6 @@ export const DataDictionary = ({ className, dictionary, EntitiesLayout = Default
40
41
  React.createElement(ColumnFilterTags, { table: table })),
41
42
  React.createElement(Fade, { in: dimensions.height > 0 },
42
43
  React.createElement(EntitiesLayout, { spacing: entitiesSpacing },
44
+ React.createElement(Description, { description: description }),
43
45
  React.createElement(Entities, { spacing: entitiesSpacing, table: table }))))));
44
46
  };
@@ -20,6 +20,7 @@ export const useDataDictionaryConfig = (dictionary) => {
20
20
  // exists above and would have thrown an error if undefined.
21
21
  return {
22
22
  classes: dataDictionaryConfig.dataDictionary.classes,
23
+ description: dataDictionaryConfig.dataDictionary.description,
23
24
  tableOptions: dataDictionaryConfig.tableOptions,
24
25
  title: dataDictionaryConfig.dataDictionary.title,
25
26
  };
@@ -2,6 +2,7 @@ import { RowData, TableOptions } from "@tanstack/react-table";
2
2
  import { Attribute, Class } from "../../../../common/entities";
3
3
  export interface UseDataDictionaryConfig<T extends RowData = Attribute> {
4
4
  classes: Class<T>[];
5
+ description?: string;
5
6
  tableOptions: Omit<TableOptions<T>, "data" | "getCoreRowModel">;
6
7
  title: string;
7
8
  }
@@ -2,6 +2,7 @@ import { TableCell } from "@mui/material";
2
2
  import { flexRender } from "@tanstack/react-table";
3
3
  import React, { Fragment } from "react";
4
4
  import { getTableCellAlign, getTableCellPadding, } from "../../../../../Table/components/TableCell/common/utils";
5
+ import { handleToggleExpanded } from "../../../../../Table/components/TableFeatures/RowExpanding/utils";
5
6
  import { StyledTableRow } from "../../../../../Table/components/TableRow/tableRow.styles";
6
7
  export const TableRows = ({ rows: leafOrSubRows, tableInstance, tableView, }) => {
7
8
  const { getRowModel } = tableInstance;
@@ -9,7 +10,7 @@ export const TableRows = ({ rows: leafOrSubRows, tableInstance, tableView, }) =>
9
10
  const { tableCell } = tableView || {};
10
11
  const { size: tableCellSize = "medium" } = tableCell || {};
11
12
  return (React.createElement(Fragment, null, (leafOrSubRows || rows).map((row) => {
12
- return (React.createElement(StyledTableRow, { key: row.id, isGrouped: row.getIsGrouped(), isPreview: row.getIsPreview() }, row.getVisibleCells().map((cell) => {
13
+ return (React.createElement(StyledTableRow, { key: row.id, canExpand: row.getCanExpand(), isExpanded: row.getIsExpanded(), isGrouped: row.getIsGrouped(), isPreview: row.getIsPreview(), onClick: () => handleToggleExpanded(row) }, row.getVisibleCells().map((cell) => {
13
14
  if (cell.getIsAggregated())
14
15
  return null; // Display of aggregated cells is currently not supported.
15
16
  if (cell.getIsPlaceholder())
@@ -12,6 +12,7 @@ export const StyledForm = styled("form") `
12
12
 
13
13
  .MuiToggleButton-root {
14
14
  color: ${PALETTE.INK_LIGHT};
15
+ padding: 8px 12px;
15
16
  text-transform: capitalize;
16
17
 
17
18
  &.Mui-selected {
@@ -15,12 +15,12 @@ export const ExploreViewLink = ({ className, label, onClick, target = ANCHOR_TAR
15
15
  const filters = getSelectedFilters(url.query);
16
16
  const grouping = getGrouping(url.query);
17
17
  const sorting = getSorting(url.query);
18
- return (React.createElement(Link, { className: className, href: url.href, onClick: () => {
18
+ return (React.createElement(Link, { className: className, href: url.href, onClick: (e) => {
19
19
  exploreDispatch({
20
20
  payload: { entityListType, filters, grouping, sorting },
21
21
  type: ExploreActionKind.UpdateEntityFilters,
22
22
  });
23
- onClick?.();
23
+ onClick?.(e);
24
24
  }, rel: REL_ATTRIBUTE.NO_OPENER, target: target }, label));
25
25
  };
26
26
  /**
@@ -6,7 +6,6 @@ import { ANCHOR_TARGET, Url } from "../../common/entities";
6
6
  export interface LinkProps extends BaseComponentProps, Omit<MLinkProps, "children" | "component"> {
7
7
  copyable?: boolean;
8
8
  label: ReactNode;
9
- onClick?: () => void;
10
9
  target?: ANCHOR_TARGET;
11
10
  TypographyProps?: TypographyProps;
12
11
  url: Url;
@@ -17,5 +17,10 @@ import { Link } from "../../../Links/components/Link/link";
17
17
  * Note: This component currently does not support these excluded attributes.
18
18
  */
19
19
  export const Anchor = (props) => {
20
- return (React.createElement(Link, { className: props.className, label: props.children, url: props.href || "" }));
20
+ return (React.createElement(Link, { className: props.className, label: props.children,
21
+ /*
22
+ * Prevents click events from bubbling up to parent components
23
+ * (such as CardActionArea or Accordion) when the link is activated.
24
+ */
25
+ onClick: (e) => e.stopPropagation(), url: props.href || "" }));
21
26
  };
@@ -17,5 +17,10 @@ export const LinkCell = ({ className, getValue, }) => {
17
17
  if (!isValid)
18
18
  return (React.createElement(Typography, { className: className, color: TYPOGRAPHY_PROPS.COLOR.INHERIT, component: "span", variant: TYPOGRAPHY_PROPS.VARIANT.INHERIT, ...linkProps }, children));
19
19
  // If the href is valid, return a Link component.
20
- return (React.createElement(MLink, { className: className, color: color, component: getComponent(href, isClientSide), href: href, rel: getRelAttribute(rel, isClientSide), target: getTargetAttribute(target, isClientSide), underline: underline, ...linkProps }, children));
20
+ return (React.createElement(MLink, { className: className, color: color, component: getComponent(href, isClientSide), href: href,
21
+ /*
22
+ * Prevents click events from bubbling up to parent components
23
+ * (such as CardActionArea or Accordion) when the link is activated.
24
+ */
25
+ onClick: (e) => e.stopPropagation(), rel: getRelAttribute(rel, isClientSide), target: getTargetAttribute(target, isClientSide), underline: underline, ...linkProps }, children));
21
26
  };
@@ -4,5 +4,10 @@ import { CheckedIcon } from "../../../../../common/CustomIcon/components/Checked
4
4
  import { UncheckedIcon } from "../../../../../common/CustomIcon/components/UncheckedIcon/uncheckedIcon";
5
5
  export const RowSelectionCell = ({ row, }) => {
6
6
  const { getIsSelected, getToggleSelectedHandler } = row;
7
- return (React.createElement(MCheckbox, { checked: getIsSelected(), checkedIcon: React.createElement(CheckedIcon, null), icon: React.createElement(UncheckedIcon, null), onChange: getToggleSelectedHandler() }));
7
+ return (React.createElement(MCheckbox, { checked: getIsSelected(), checkedIcon: React.createElement(CheckedIcon, null), icon: React.createElement(UncheckedIcon, null),
8
+ /*
9
+ * Prevents click events from bubbling up to parent components
10
+ * (such as CardActionArea or Accordion) when the checkbox is activated.
11
+ */
12
+ onClick: (e) => e.stopPropagation(), onChange: getToggleSelectedHandler() }));
8
13
  };
@@ -0,0 +1,10 @@
1
+ import { Row, RowData } from "@tanstack/react-table";
2
+ /**
3
+ * Handles toggling the expanded state of a row.
4
+ * Rows can not be expanded if:
5
+ * - the row can not be expanded.
6
+ * - the row is grouped (expanded rows are not supported on grouped rows).
7
+ * - the user is selecting text.
8
+ * @param row - Row.
9
+ */
10
+ export declare function handleToggleExpanded<T extends RowData>(row: Row<T>): void;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Handles toggling the expanded state of a row.
3
+ * Rows can not be expanded if:
4
+ * - the row can not be expanded.
5
+ * - the row is grouped (expanded rows are not supported on grouped rows).
6
+ * - the user is selecting text.
7
+ * @param row - Row.
8
+ */
9
+ export function handleToggleExpanded(row) {
10
+ const { getCanExpand, getIsGrouped, toggleExpanded } = row;
11
+ // Row can not be expanded.
12
+ if (!getCanExpand())
13
+ return;
14
+ // Row is grouped - row expansion not supported on a grouped row.
15
+ if (getIsGrouped())
16
+ return;
17
+ // User is selecting text - do not toggle row expanded state.
18
+ if (window.getSelection()?.toString())
19
+ return;
20
+ // Toggle row expanded state.
21
+ toggleExpanded();
22
+ }
@@ -1,4 +1,6 @@
1
1
  export interface StyledTableRowProps {
2
+ canExpand?: boolean;
3
+ isExpanded?: boolean;
2
4
  isGrouped?: boolean;
3
5
  isPreview?: boolean;
4
6
  }
@@ -1,17 +1,20 @@
1
1
  import { css } from "@emotion/react";
2
2
  import styled from "@emotion/styled";
3
3
  import { TableRow as MTableRow } from "@mui/material";
4
- import { primaryLightest, smokeLightest, } from "../../../../styles/common/mixins/colors";
4
+ import { PALETTE } from "../../../../styles/common/constants/palette";
5
5
  import { textBodySmall500 } from "../../../../styles/common/mixins/fonts";
6
6
  export const StyledTableRow = styled(MTableRow, {
7
- shouldForwardProp: (prop) => prop !== "isPreview" && prop !== "isGrouped",
7
+ shouldForwardProp: (prop) => prop !== "canExpand" &&
8
+ prop !== "isExpanded" &&
9
+ prop !== "isPreview" &&
10
+ prop !== "isGrouped",
8
11
  }) `
9
12
  && {
10
13
  transition: background-color 300ms ease-in;
11
14
 
12
15
  ${(props) => props.isGrouped &&
13
16
  css `
14
- background-color: ${smokeLightest(props)};
17
+ background-color: ${PALETTE.SMOKE_LIGHTEST};
15
18
 
16
19
  td {
17
20
  ${textBodySmall500(props)};
@@ -20,9 +23,24 @@ export const StyledTableRow = styled(MTableRow, {
20
23
  }
21
24
  `}
22
25
 
23
- ${(props) => props.isPreview &&
26
+ ${({ canExpand, isExpanded, isGrouped }) => !isGrouped &&
27
+ canExpand &&
24
28
  css `
25
- background-color: ${primaryLightest(props)};
29
+ cursor: pointer;
30
+
31
+ &:hover {
32
+ background-color: #f8fbfd;
33
+ }
34
+
35
+ ${isExpanded &&
36
+ css `
37
+ background-color: #f8fbfd;
38
+ `}
39
+ `}
40
+
41
+ ${({ isPreview }) => isPreview &&
42
+ css `
43
+ background-color: ${PALETTE.PRIMARY_LIGHTEST};
26
44
  `}
27
45
  }
28
46
  `;
@@ -3,14 +3,15 @@ import { flexRender } from "@tanstack/react-table";
3
3
  import React, { Fragment } from "react";
4
4
  import { TEST_IDS } from "../../../../tests/testIds";
5
5
  import { getTableCellAlign, getTableCellPadding, } from "../TableCell/common/utils";
6
+ import { handleToggleExpanded } from "../TableFeatures/RowExpanding/utils";
6
7
  import { StyledTableRow } from "../TableRow/tableRow.styles";
7
8
  export const TableRows = ({ rows, virtualizer, }) => {
8
9
  const virtualItems = virtualizer.getVirtualItems();
9
10
  return (React.createElement(Fragment, null, virtualItems.map((virtualRow) => {
10
11
  const rowIndex = virtualRow.index;
11
12
  const row = rows[rowIndex];
12
- const { getIsGrouped, getIsPreview } = row;
13
- return (React.createElement(StyledTableRow, { key: row.id, "data-index": rowIndex, isGrouped: getIsGrouped(), isPreview: getIsPreview(), ref: virtualizer.measureElement }, row.getVisibleCells().map((cell, i) => {
13
+ const { getCanExpand, getIsExpanded, getIsGrouped, getIsPreview } = row;
14
+ return (React.createElement(StyledTableRow, { key: row.id, canExpand: getCanExpand(), "data-index": rowIndex, isExpanded: getIsExpanded(), isGrouped: getIsGrouped(), isPreview: getIsPreview(), onClick: () => handleToggleExpanded(row), ref: virtualizer.measureElement }, row.getVisibleCells().map((cell, i) => {
14
15
  if (cell.getIsAggregated())
15
16
  return null; // Display of aggregated cells is currently not supported.
16
17
  if (cell.getIsPlaceholder())
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databiosphere/findable-ui",
3
- "version": "38.1.1",
3
+ "version": "38.3.0",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
@@ -0,0 +1,56 @@
1
+ import styled from "@emotion/styled";
2
+ import { PALETTE } from "../../../../styles/common/constants/palette";
3
+ import {
4
+ mediaTabletDown,
5
+ mediaTabletUp,
6
+ } from "../../../../styles/common/mixins/breakpoints";
7
+ import { textHeadingSmall } from "../../../../styles/common/mixins/fonts";
8
+ import { RoundedPaper } from "../../../common/Paper/components/RoundedPaper/roundedPaper";
9
+ import { MarkdownRenderer } from "../../../MarkdownRenderer/markdownRenderer";
10
+
11
+ export const StyledRoundedPaper = styled(RoundedPaper)`
12
+ padding: 20px;
13
+
14
+ ${mediaTabletDown} {
15
+ padding: 20px 16px;
16
+ }
17
+ `;
18
+
19
+ export const StyledMarkdownRenderer = styled(MarkdownRenderer)`
20
+ align-self: flex-start;
21
+
22
+ code {
23
+ all: unset;
24
+ font: inherit;
25
+ font-family: Roboto Mono, monospace;
26
+ }
27
+
28
+ h2,
29
+ h3,
30
+ h4,
31
+ h5,
32
+ h6 {
33
+ margin: 8px 0;
34
+ }
35
+
36
+ h2 {
37
+ ${textHeadingSmall};
38
+ font-size: 18px;
39
+ line-height: 26px;
40
+
41
+ ${mediaTabletUp} {
42
+ font-size: 18px;
43
+ line-height: 26px;
44
+ }
45
+ }
46
+
47
+ hr {
48
+ border: none;
49
+ border-bottom: 1px solid ${PALETTE.SMOKE_MAIN};
50
+ margin: 16px 0;
51
+ }
52
+
53
+ p {
54
+ font: inherit;
55
+ }
56
+ `;
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ import {
3
+ StyledMarkdownRenderer,
4
+ StyledRoundedPaper,
5
+ } from "./description.styles";
6
+ import { DescriptionProps } from "./types";
7
+
8
+ export const Description = ({
9
+ description,
10
+ }: DescriptionProps): JSX.Element | null => {
11
+ if (!description) return null;
12
+ return (
13
+ <StyledRoundedPaper elevation={0}>
14
+ <StyledMarkdownRenderer value={description} />
15
+ </StyledRoundedPaper>
16
+ );
17
+ };
@@ -0,0 +1,3 @@
1
+ export interface DescriptionProps {
2
+ description?: string;
3
+ }
@@ -7,6 +7,8 @@ const PB = LAYOUT_SPACING.CONTENT_PADDING_BOTTOM; /* bottom padding */
7
7
  const PT = LAYOUT_SPACING.CONTENT_PADDING_TOP; /* top padding */
8
8
 
9
9
  export const Layout = styled("div")<LayoutSpacing>`
10
+ display: grid;
11
+ gap: 16px;
10
12
  grid-column: 2;
11
13
  grid-row: 1;
12
14
  padding-bottom: ${PB}px;
@@ -6,8 +6,9 @@ import {
6
6
 
7
7
  export const EXPANDED_OPTIONS: Pick<
8
8
  ExpandedOptions<RowData>,
9
- "enableExpanding" | "getExpandedRowModel"
9
+ "enableExpanding" | "getExpandedRowModel" | "getRowCanExpand"
10
10
  > = {
11
11
  enableExpanding: true,
12
12
  getExpandedRowModel: getExpandedRowModel(),
13
+ getRowCanExpand: () => true,
13
14
  };
@@ -3,6 +3,7 @@ import { RowData } from "@tanstack/react-table";
3
3
  import React from "react";
4
4
  import { Attribute } from "../../common/entities";
5
5
  import { useLayoutSpacing } from "../../hooks/UseLayoutSpacing/hook";
6
+ import { Description } from "./components/Description/description";
6
7
  import { Entities } from "./components/Entities/entities";
7
8
  import { ColumnFilterTags } from "./components/Filters/components/ColumnFilterTags/columnFilterTags";
8
9
  import { Filters } from "./components/Filters/filters";
@@ -30,7 +31,7 @@ export const DataDictionary = <T extends RowData = Attribute>({
30
31
  TitleLayout = DefaultTitleLayout,
31
32
  }: DataDictionaryProps): JSX.Element => {
32
33
  // Get dictionary configuration.
33
- const { classes, tableOptions, title } =
34
+ const { classes, description, tableOptions, title } =
34
35
  useDataDictionaryConfig<T>(dictionary);
35
36
 
36
37
  // Layout measurements.
@@ -64,6 +65,7 @@ export const DataDictionary = <T extends RowData = Attribute>({
64
65
  <Fade in={dimensions.height > 0}>
65
66
  {/* Fade in entities when filters are measured. */}
66
67
  <EntitiesLayout spacing={entitiesSpacing}>
68
+ <Description description={description} />
67
69
  <Entities spacing={entitiesSpacing} table={table} />
68
70
  </EntitiesLayout>
69
71
  </Fade>
@@ -31,6 +31,7 @@ export const useDataDictionaryConfig = <T extends RowData = Attribute>(
31
31
  // exists above and would have thrown an error if undefined.
32
32
  return {
33
33
  classes: dataDictionaryConfig!.dataDictionary.classes,
34
+ description: dataDictionaryConfig!.dataDictionary.description,
34
35
  tableOptions: dataDictionaryConfig!.tableOptions,
35
36
  title: dataDictionaryConfig!.dataDictionary.title,
36
37
  };
@@ -3,6 +3,7 @@ import { Attribute, Class } from "../../../../common/entities";
3
3
 
4
4
  export interface UseDataDictionaryConfig<T extends RowData = Attribute> {
5
5
  classes: Class<T>[];
6
+ description?: string;
6
7
  tableOptions: Omit<TableOptions<T>, "data" | "getCoreRowModel">;
7
8
  title: string;
8
9
  }
@@ -5,6 +5,7 @@ import {
5
5
  getTableCellAlign,
6
6
  getTableCellPadding,
7
7
  } from "../../../../../Table/components/TableCell/common/utils";
8
+ import { handleToggleExpanded } from "../../../../../Table/components/TableFeatures/RowExpanding/utils";
8
9
  import { StyledTableRow } from "../../../../../Table/components/TableRow/tableRow.styles";
9
10
  import { TableView } from "../../table";
10
11
 
@@ -34,8 +35,11 @@ export const TableRows = <T extends RowData>({
34
35
  return (
35
36
  <StyledTableRow
36
37
  key={row.id}
38
+ canExpand={row.getCanExpand()}
39
+ isExpanded={row.getIsExpanded()}
37
40
  isGrouped={row.getIsGrouped()}
38
41
  isPreview={row.getIsPreview()}
42
+ onClick={() => handleToggleExpanded(row)}
39
43
  >
40
44
  {row.getVisibleCells().map((cell) => {
41
45
  if (cell.getIsAggregated()) return null; // Display of aggregated cells is currently not supported.
@@ -13,6 +13,7 @@ export const StyledForm = styled("form")`
13
13
 
14
14
  .MuiToggleButton-root {
15
15
  color: ${PALETTE.INK_LIGHT};
16
+ padding: 8px 12px;
16
17
  text-transform: capitalize;
17
18
 
18
19
  &.Mui-selected {
@@ -46,12 +46,12 @@ export const ExploreViewLink = ({
46
46
  <Link
47
47
  className={className}
48
48
  href={url.href}
49
- onClick={(): void => {
49
+ onClick={(e): void => {
50
50
  exploreDispatch({
51
51
  payload: { entityListType, filters, grouping, sorting },
52
52
  type: ExploreActionKind.UpdateEntityFilters,
53
53
  });
54
- onClick?.();
54
+ onClick?.(e);
55
55
  }}
56
56
  rel={REL_ATTRIBUTE.NO_OPENER}
57
57
  target={target}
@@ -22,7 +22,6 @@ export interface LinkProps
22
22
  Omit<MLinkProps, "children" | "component"> {
23
23
  copyable?: boolean;
24
24
  label: ReactNode /* link label may be an element */;
25
- onClick?: () => void;
26
25
  target?: ANCHOR_TARGET;
27
26
  TypographyProps?: TypographyProps;
28
27
  url: Url /* url specified as UrlObject with href and query defined, and is currently only used for internal links */;
@@ -28,6 +28,11 @@ export const Anchor = (
28
28
  <Link
29
29
  className={props.className}
30
30
  label={props.children}
31
+ /*
32
+ * Prevents click events from bubbling up to parent components
33
+ * (such as CardActionArea or Accordion) when the link is activated.
34
+ */
35
+ onClick={(e) => e.stopPropagation()}
31
36
  url={props.href || ""}
32
37
  />
33
38
  );
@@ -53,6 +53,11 @@ export const LinkCell = <
53
53
  color={color}
54
54
  component={getComponent(href, isClientSide)}
55
55
  href={href}
56
+ /*
57
+ * Prevents click events from bubbling up to parent components
58
+ * (such as CardActionArea or Accordion) when the link is activated.
59
+ */
60
+ onClick={(e) => e.stopPropagation()}
56
61
  rel={getRelAttribute(rel, isClientSide)}
57
62
  target={getTargetAttribute(target, isClientSide)}
58
63
  underline={underline}
@@ -13,6 +13,11 @@ export const RowSelectionCell = <T extends RowData, TValue = unknown>({
13
13
  checked={getIsSelected()}
14
14
  checkedIcon={<CheckedIcon />}
15
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()}
16
21
  onChange={getToggleSelectedHandler()}
17
22
  />
18
23
  );
@@ -0,0 +1,25 @@
1
+ import { Row, RowData } from "@tanstack/react-table";
2
+
3
+ /**
4
+ * Handles toggling the expanded state of a row.
5
+ * Rows can not be expanded if:
6
+ * - the row can not be expanded.
7
+ * - the row is grouped (expanded rows are not supported on grouped rows).
8
+ * - the user is selecting text.
9
+ * @param row - Row.
10
+ */
11
+ export function handleToggleExpanded<T extends RowData>(row: Row<T>): void {
12
+ const { getCanExpand, getIsGrouped, toggleExpanded } = row;
13
+
14
+ // Row can not be expanded.
15
+ if (!getCanExpand()) return;
16
+
17
+ // Row is grouped - row expansion not supported on a grouped row.
18
+ if (getIsGrouped()) return;
19
+
20
+ // User is selecting text - do not toggle row expanded state.
21
+ if (window.getSelection()?.toString()) return;
22
+
23
+ // Toggle row expanded state.
24
+ toggleExpanded();
25
+ }
@@ -1,19 +1,22 @@
1
1
  import { css } from "@emotion/react";
2
2
  import styled from "@emotion/styled";
3
3
  import { TableRow as MTableRow } from "@mui/material";
4
- import {
5
- primaryLightest,
6
- smokeLightest,
7
- } from "../../../../styles/common/mixins/colors";
4
+ import { PALETTE } from "../../../../styles/common/constants/palette";
8
5
  import { textBodySmall500 } from "../../../../styles/common/mixins/fonts";
9
6
 
10
7
  export interface StyledTableRowProps {
8
+ canExpand?: boolean;
9
+ isExpanded?: boolean;
11
10
  isGrouped?: boolean;
12
11
  isPreview?: boolean;
13
12
  }
14
13
 
15
14
  export const StyledTableRow = styled(MTableRow, {
16
- shouldForwardProp: (prop) => prop !== "isPreview" && prop !== "isGrouped",
15
+ shouldForwardProp: (prop) =>
16
+ prop !== "canExpand" &&
17
+ prop !== "isExpanded" &&
18
+ prop !== "isPreview" &&
19
+ prop !== "isGrouped",
17
20
  })<StyledTableRowProps>`
18
21
  && {
19
22
  transition: background-color 300ms ease-in;
@@ -21,7 +24,7 @@ export const StyledTableRow = styled(MTableRow, {
21
24
  ${(props) =>
22
25
  props.isGrouped &&
23
26
  css`
24
- background-color: ${smokeLightest(props)};
27
+ background-color: ${PALETTE.SMOKE_LIGHTEST};
25
28
 
26
29
  td {
27
30
  ${textBodySmall500(props)};
@@ -30,10 +33,26 @@ export const StyledTableRow = styled(MTableRow, {
30
33
  }
31
34
  `}
32
35
 
33
- ${(props) =>
34
- props.isPreview &&
36
+ ${({ canExpand, isExpanded, isGrouped }) =>
37
+ !isGrouped &&
38
+ canExpand &&
39
+ css`
40
+ cursor: pointer;
41
+
42
+ &:hover {
43
+ background-color: #f8fbfd;
44
+ }
45
+
46
+ ${isExpanded &&
47
+ css`
48
+ background-color: #f8fbfd;
49
+ `}
50
+ `}
51
+
52
+ ${({ isPreview }) =>
53
+ isPreview &&
35
54
  css`
36
- background-color: ${primaryLightest(props)};
55
+ background-color: ${PALETTE.PRIMARY_LIGHTEST};
37
56
  `}
38
57
  }
39
58
  `;
@@ -7,6 +7,7 @@ import {
7
7
  getTableCellAlign,
8
8
  getTableCellPadding,
9
9
  } from "../TableCell/common/utils";
10
+ import { handleToggleExpanded } from "../TableFeatures/RowExpanding/utils";
10
11
  import { StyledTableRow } from "../TableRow/tableRow.styles";
11
12
 
12
13
  export interface TableRowsProps<T extends RowData> {
@@ -24,13 +25,16 @@ export const TableRows = <T extends RowData>({
24
25
  {virtualItems.map((virtualRow) => {
25
26
  const rowIndex = virtualRow.index;
26
27
  const row = rows[rowIndex] as Row<T>;
27
- const { getIsGrouped, getIsPreview } = row;
28
+ const { getCanExpand, getIsExpanded, getIsGrouped, getIsPreview } = row;
28
29
  return (
29
30
  <StyledTableRow
30
31
  key={row.id}
32
+ canExpand={getCanExpand()}
31
33
  data-index={rowIndex}
34
+ isExpanded={getIsExpanded()}
32
35
  isGrouped={getIsGrouped()}
33
36
  isPreview={getIsPreview()}
37
+ onClick={() => handleToggleExpanded(row)}
34
38
  ref={virtualizer.measureElement}
35
39
  >
36
40
  {row.getVisibleCells().map((cell, i) => {