@databiosphere/findable-ui 48.1.0 → 49.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 (49) hide show
  1. package/.github/workflows/release-please.yml +0 -22
  2. package/.release-please-manifest.json +1 -1
  3. package/CHANGELOG.md +27 -0
  4. package/backend/main.py +0 -1
  5. package/lib/common/categories/config/types.d.ts +2 -1
  6. package/lib/common/chart/sort/constants.d.ts +5 -0
  7. package/lib/common/chart/sort/constants.js +9 -0
  8. package/lib/common/chart/sort/types.d.ts +16 -0
  9. package/lib/common/chart/sort/types.js +8 -0
  10. package/lib/common/chart/sort/utils.d.ts +7 -0
  11. package/lib/common/chart/sort/utils.js +19 -0
  12. package/lib/common/chart/types.d.ts +5 -0
  13. package/lib/common/chart/types.js +1 -0
  14. package/lib/common/entities.d.ts +2 -1
  15. package/lib/components/Export/components/DownloadCurlCommand/downloadCurlCommand.d.ts +0 -2
  16. package/lib/components/Export/components/ExportToTerra/exportToTerra.d.ts +0 -2
  17. package/lib/components/Export/components/ManifestDownload/components/ManifestDownloadEntity/manifestDownloadEntity.d.ts +0 -2
  18. package/lib/components/Export/components/ManifestDownload/manifestDownload.d.ts +0 -2
  19. package/lib/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/utils.js +1 -1
  20. package/lib/components/Index/components/EntityView/components/views/ChartView/chartView.js +2 -1
  21. package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UsePlotOptions/hook.js +2 -7
  22. package/lib/components/Index/components/EntityView/components/views/ChartView/components/Chart/stories/args.js +12 -12
  23. package/lib/components/Index/components/EntityView/components/views/ChartView/stories/args.js +3 -3
  24. package/lib/components/Index/components/EntityView/components/views/ChartView/utils.js +1 -1
  25. package/lib/hooks/useCategoryFilter.js +1 -1
  26. package/lib/hooks/useFileManifest/common/entities.d.ts +0 -9
  27. package/lib/hooks/useFileManifest/common/entities.js +1 -9
  28. package/lib/views/EntityDetailView/entityDetailView.js +2 -2
  29. package/package.json +1 -1
  30. package/src/common/categories/config/types.ts +2 -1
  31. package/src/common/chart/sort/constants.ts +13 -0
  32. package/src/common/chart/sort/types.ts +22 -0
  33. package/src/common/chart/sort/utils.ts +22 -0
  34. package/src/common/chart/types.ts +6 -0
  35. package/src/common/entities.ts +2 -1
  36. package/src/components/Export/components/DownloadCurlCommand/downloadCurlCommand.tsx +0 -2
  37. package/src/components/Export/components/ExportToTerra/exportToTerra.tsx +0 -2
  38. package/src/components/Export/components/ManifestDownload/components/ManifestDownloadEntity/manifestDownloadEntity.tsx +0 -2
  39. package/src/components/Export/components/ManifestDownload/manifestDownload.tsx +0 -2
  40. package/src/components/Filter/components/adapters/tanstack/ColumnFiltersAdapter/utils.ts +1 -1
  41. package/src/components/Index/components/EntityView/components/views/ChartView/chartView.tsx +8 -2
  42. package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/hooks/UsePlotOptions/hook.ts +2 -7
  43. package/src/components/Index/components/EntityView/components/views/ChartView/components/Chart/stories/args.ts +12 -12
  44. package/src/components/Index/components/EntityView/components/views/ChartView/stories/args.ts +3 -3
  45. package/src/components/Index/components/EntityView/components/views/ChartView/utils.ts +1 -1
  46. package/src/hooks/useCategoryFilter.ts +1 -1
  47. package/src/hooks/useFileManifest/common/entities.ts +0 -11
  48. package/src/views/EntityDetailView/entityDetailView.tsx +1 -3
  49. package/tests/chartSortUtils.test.ts +119 -0
@@ -44,28 +44,6 @@ jobs:
44
44
  if: ${{ steps.release.outputs.release_created }}
45
45
  run: npx tsc
46
46
 
47
- - name: Debug OIDC Token
48
- if: ${{ steps.release.outputs.release_created }}
49
- run: |
50
- echo "=== OIDC Debug Info ==="
51
- echo "Repository: $GITHUB_REPOSITORY"
52
- echo "Workflow: $GITHUB_WORKFLOW"
53
- echo "Job: $GITHUB_JOB"
54
- echo "Run ID: $GITHUB_RUN_ID"
55
- echo "Ref: $GITHUB_REF"
56
- echo "Token URL available: ${{ env.ACTIONS_ID_TOKEN_REQUEST_URL != '' }}"
57
- echo "=== Requesting OIDC token for npm ==="
58
- if [ -n "$ACTIONS_ID_TOKEN_REQUEST_URL" ]; then
59
- if OIDC_RESPONSE=$(curl -sS -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
60
- "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=npm" 2>/dev/null); then
61
- echo "✓ OIDC token request succeeded"
62
- else
63
- echo "✗ Failed to get OIDC token"
64
- fi
65
- else
66
- echo "✗ ACTIONS_ID_TOKEN_REQUEST_URL is not set - id-token permission may be missing"
67
- fi
68
-
69
47
  - name: Publish to NPM
70
48
  if: ${{ steps.release.outputs.release_created }}
71
49
  run: npm publish --provenance --access public
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "48.1.0"
2
+ ".": "49.0.0"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## [49.0.0](https://github.com/DataBiosphere/findable-ui/compare/v48.1.0...v49.0.0) (2026-02-17)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * chartview: add configurable bar sort order ([#772](https://github.com/DataBiosphere/findable-ui/issues/772)) (#773)
9
+ * remove unused filemanifesttype prop and type definitions from export components ([#767](https://github.com/DataBiosphere/findable-ui/issues/767)) (#768)
10
+
11
+ ### Features
12
+
13
+ * chartview: add configurable bar sort order ([#772](https://github.com/DataBiosphere/findable-ui/issues/772)) ([#773](https://github.com/DataBiosphere/findable-ui/issues/773)) ([bea776a](https://github.com/DataBiosphere/findable-ui/commit/bea776a987a612d30a0010f29af128946f4aad48))
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * remove unused filemanifesttype prop and type definitions from export components ([#767](https://github.com/DataBiosphere/findable-ui/issues/767)) ([#768](https://github.com/DataBiosphere/findable-ui/issues/768)) ([4da929f](https://github.com/DataBiosphere/findable-ui/commit/4da929fcfd3acaccdb60f269bbffa8be033fdec3))
19
+
20
+
21
+ ### Chores
22
+
23
+ * remove debug code ([#762](https://github.com/DataBiosphere/findable-ui/issues/762)) ([#765](https://github.com/DataBiosphere/findable-ui/issues/765)) ([6a6b8f5](https://github.com/DataBiosphere/findable-ui/commit/6a6b8f5087bf972397fa4104f946b41eae03a0da))
24
+
25
+
26
+ ### Code Refactoring
27
+
28
+ * render null instead of empty fragment when hideTabs is true ([#770](https://github.com/DataBiosphere/findable-ui/issues/770)) ([#771](https://github.com/DataBiosphere/findable-ui/issues/771)) ([62733b7](https://github.com/DataBiosphere/findable-ui/commit/62733b7fb271fa6be4a29b7c7d545272a1c3e9ef))
29
+
3
30
  ## [48.1.0](https://github.com/DataBiosphere/findable-ui/compare/v48.0.0...v48.1.0) (2026-01-11)
4
31
 
5
32
 
package/backend/main.py CHANGED
@@ -2,7 +2,6 @@ from fastapi import FastAPI
2
2
 
3
3
  from backend.controllers.facets_controller import router as facets_router
4
4
 
5
-
6
5
  app = FastAPI(title="Findable API")
7
6
 
8
7
  # Register controller(s)
@@ -1,3 +1,4 @@
1
+ import { Chart } from "../../chart/types";
1
2
  import { CategoryKey, DataDictionaryAnnotation, SelectCategoryValueView } from "../../entities";
2
3
  import { RangeViewKind } from "../views/range/types";
3
4
  import { SelectViewKind } from "../views/select/types";
@@ -23,6 +24,6 @@ export interface RangeCategoryConfig extends CommonCategoryConfig, RangeViewKind
23
24
  * Select category config.
24
25
  */
25
26
  export interface SelectCategoryConfig extends CommonCategoryConfig, SelectViewKind {
26
- enableChartView?: boolean;
27
+ chart?: Chart;
27
28
  mapSelectCategoryValue?: (selectCategoryValue: SelectCategoryValueView) => SelectCategoryValueView;
28
29
  }
@@ -0,0 +1,5 @@
1
+ import { CHART_SORT, ChartSortFn } from "./types";
2
+ /**
3
+ * Map of chart sort options to their corresponding sort functions.
4
+ */
5
+ export declare const CHART_SORT_FN: Record<CHART_SORT, ChartSortFn>;
@@ -0,0 +1,9 @@
1
+ import { sortCategoryValueViewsAlpha, sortCategoryValueViewsCount, } from "../../filters/sort/models/utils";
2
+ import { CHART_SORT } from "./types";
3
+ /**
4
+ * Map of chart sort options to their corresponding sort functions.
5
+ */
6
+ export const CHART_SORT_FN = {
7
+ [CHART_SORT.ALPHA]: sortCategoryValueViewsAlpha,
8
+ [CHART_SORT.COUNT]: sortCategoryValueViewsCount,
9
+ };
@@ -0,0 +1,16 @@
1
+ import { SelectCategoryValueView } from "../../entities";
2
+ /**
3
+ * Chart sorting options for select categories.
4
+ */
5
+ export declare enum CHART_SORT {
6
+ ALPHA = "ALPHA",
7
+ COUNT = "COUNT"
8
+ }
9
+ /**
10
+ * Sorting function type for select category values.
11
+ */
12
+ export type ChartSortFn = (a: SelectCategoryValueView, b: SelectCategoryValueView) => number;
13
+ /**
14
+ * Chart sort options - either a preset (ALPHA, COUNT) or a custom sort function.
15
+ */
16
+ export type ChartSortOptions = CHART_SORT | ChartSortFn;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Chart sorting options for select categories.
3
+ */
4
+ export var CHART_SORT;
5
+ (function (CHART_SORT) {
6
+ CHART_SORT["ALPHA"] = "ALPHA";
7
+ CHART_SORT["COUNT"] = "COUNT";
8
+ })(CHART_SORT || (CHART_SORT = {}));
@@ -0,0 +1,7 @@
1
+ import { ChartSortOptions, ChartSortFn } from "./types";
2
+ /**
3
+ * Resolves chart sort options to a sorting function.
4
+ * @param chartSortOptions - Chart sort option (ALPHA, COUNT, or custom function).
5
+ * @returns Sorting function to use for chart data.
6
+ */
7
+ export declare function getChartSortFn(chartSortOptions?: ChartSortOptions): ChartSortFn;
@@ -0,0 +1,19 @@
1
+ import { CHART_SORT_FN } from "./constants";
2
+ import { CHART_SORT } from "./types";
3
+ /**
4
+ * Resolves chart sort options to a sorting function.
5
+ * @param chartSortOptions - Chart sort option (ALPHA, COUNT, or custom function).
6
+ * @returns Sorting function to use for chart data.
7
+ */
8
+ export function getChartSortFn(chartSortOptions) {
9
+ // Default to COUNT sort if not specified.
10
+ if (!chartSortOptions) {
11
+ return CHART_SORT_FN[CHART_SORT.COUNT];
12
+ }
13
+ // If it's a custom function, return it directly.
14
+ if (typeof chartSortOptions === "function") {
15
+ return chartSortOptions;
16
+ }
17
+ // Otherwise, look up the function from the map.
18
+ return CHART_SORT_FN[chartSortOptions];
19
+ }
@@ -0,0 +1,5 @@
1
+ import { ChartSortOptions } from "./sort/types";
2
+ export interface Chart {
3
+ enable: boolean;
4
+ sortBy?: ChartSortOptions;
5
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
1
  import { RowData, TableOptions } from "@tanstack/react-table";
2
+ import { Chart } from "./chart/types";
2
3
  /**
3
4
  * Model of a value of a metadata class.
4
5
  */
@@ -133,7 +134,7 @@ export interface SelectCategoryValueView {
133
134
  */
134
135
  export interface SelectCategoryView {
135
136
  annotation?: DataDictionaryAnnotation;
136
- enableChartView?: boolean;
137
+ chart?: Chart;
137
138
  isDisabled?: boolean;
138
139
  key: CategoryKey;
139
140
  label: string;
@@ -1,6 +1,5 @@
1
1
  import { JSX, ElementType } from "react";
2
2
  import { Filters } from "../../../../common/entities";
3
- import { FileManifestType } from "../../../../hooks/useFileManifest/common/entities";
4
3
  import { FileManifestState } from "../../../../providers/fileManifestState";
5
4
  import { FormFacet, ManifestDownloadFormat } from "../../common/entities";
6
5
  interface DownloadCurlCommandProps {
@@ -8,7 +7,6 @@ interface DownloadCurlCommandProps {
8
7
  DownloadCurlStart: ElementType;
9
8
  DownloadCurlSuccess: ElementType;
10
9
  fileManifestState: FileManifestState;
11
- fileManifestType: FileManifestType;
12
10
  fileSummaryFacetName: string;
13
11
  filters: Filters;
14
12
  formFacet: FormFacet;
@@ -1,6 +1,5 @@
1
1
  import { JSX, ElementType } from "react";
2
2
  import { Filters } from "../../../../common/entities";
3
- import { FileManifestType } from "../../../../hooks/useFileManifest/common/entities";
4
3
  import { FileManifestState } from "../../../../providers/fileManifestState";
5
4
  import { FormFacet, ManifestDownloadFormat } from "../../common/entities";
6
5
  export interface ExportToTerraProps {
@@ -8,7 +7,6 @@ export interface ExportToTerraProps {
8
7
  ExportToTerraStart: ElementType;
9
8
  ExportToTerraSuccess: ElementType;
10
9
  fileManifestState: FileManifestState;
11
- fileManifestType: FileManifestType;
12
10
  fileSummaryFacetName: string;
13
11
  filters: Filters;
14
12
  formFacet: FormFacet;
@@ -1,9 +1,7 @@
1
1
  import { JSX } from "react";
2
2
  import { Filters } from "../../../../../../common/entities";
3
- import { FileManifestType } from "../../../../../../hooks/useFileManifest/common/entities";
4
3
  import { ManifestDownloadFormat } from "../../../../common/entities";
5
4
  export interface ManifestDownloadEntityProps {
6
- fileManifestType: FileManifestType;
7
5
  filters: Filters;
8
6
  manifestDownloadFormat?: ManifestDownloadFormat;
9
7
  metadataFilters: Filters;
@@ -1,11 +1,9 @@
1
1
  import { JSX, ElementType } from "react";
2
2
  import { Filters } from "../../../../common/entities";
3
- import { FileManifestType } from "../../../../hooks/useFileManifest/common/entities";
4
3
  import { FileManifestState } from "../../../../providers/fileManifestState";
5
4
  import { FormFacet, ManifestDownloadFormat } from "../../common/entities";
6
5
  export interface ManifestDownloadProps {
7
6
  fileManifestState: FileManifestState;
8
- fileManifestType: FileManifestType;
9
7
  fileSummaryFacetName: string;
10
8
  filters: Filters;
11
9
  formFacet: FormFacet;
@@ -145,7 +145,7 @@ function mapColumnToSelectCategoryView(column, filterSort, categoryConfig) {
145
145
  const values = mapColumnToSelectCategoryValueView(column, filterSort).map(categoryConfig?.mapSelectCategoryValue || getSelectCategoryValue);
146
146
  return {
147
147
  annotation: undefined,
148
- enableChartView: false,
148
+ chart: undefined,
149
149
  isDisabled,
150
150
  key: column.id,
151
151
  label: getColumnHeader(column),
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Typography } from "@mui/material";
3
3
  import { Fragment } from "react";
4
+ import { getChartSortFn } from "../../../../../../../common/chart/sort/utils";
4
5
  import { TYPOGRAPHY_PROPS } from "../../../../../../../styles/common/mui/typography";
5
6
  import { Loading, LOADING_PANEL_STYLE, } from "../../../../../../Loading/loading";
6
7
  import { StyledToolbar } from "../../../../../../Table/components/TableToolbar/tableToolbar.styles";
@@ -12,5 +13,5 @@ export const ChartView = ({ categoryFilters, entityName, loading, testId, }) =>
12
13
  const { chartViewRef, selectCategoryViews, width } = useChartView(categoryFilters);
13
14
  if (selectCategoryViews.length === 0)
14
15
  return null;
15
- return (_jsxs(Fragment, { children: [_jsx(StyledToolbar, { children: _jsx(ViewToggle, {}) }), _jsx(Loading, { appear: false, autoPosition: false, loading: loading, panelStyle: LOADING_PANEL_STYLE.INHERIT }), _jsx(StyledGrid, { "data-testid": testId, ref: chartViewRef, children: selectCategoryViews.map(({ key, label, values }) => (_jsxs(StyledGridPaperSection, { children: [_jsxs(Typography, { variant: TYPOGRAPHY_PROPS.VARIANT.HEADING_SMALL, children: [entityName, " by ", label] }), _jsx(Chart, { selectCategoryValueViews: values, width: width })] }, key))) })] }));
16
+ return (_jsxs(Fragment, { children: [_jsx(StyledToolbar, { children: _jsx(ViewToggle, {}) }), _jsx(Loading, { appear: false, autoPosition: false, loading: loading, panelStyle: LOADING_PANEL_STYLE.INHERIT }), _jsx(StyledGrid, { "data-testid": testId, ref: chartViewRef, children: selectCategoryViews.map(({ chart, key, label, values }) => (_jsxs(StyledGridPaperSection, { children: [_jsxs(Typography, { variant: TYPOGRAPHY_PROPS.VARIANT.HEADING_SMALL, children: [entityName, " by ", label] }), _jsx(Chart, { selectCategoryValueViews: [...values].sort(getChartSortFn(chart?.sortBy)), width: width })] }, key))) })] }));
16
17
  };
@@ -1,14 +1,9 @@
1
1
  import { useMemo } from "react";
2
- import { sortCategoryValueViewsCount } from "../../../../../../../../../../../common/filters/sort/models/utils";
3
2
  import { getPlotOptions } from "../../barX/plot";
4
3
  import { getCategoryTotalCount } from "../../barX/utils";
5
4
  export const usePlotOptions = (selectCategoryValueViews, width, barCount) => {
6
- // Organise the select category value views (sort and slice) for chart display.
7
- const data = selectCategoryValueViews
8
- // Sort the category values by count and label.
9
- .sort(sortCategoryValueViewsCount)
10
- // Slice the category values to the number of bars to display.
11
- .slice(0, barCount);
5
+ // Slice the category values to the number of bars to display.
6
+ const data = selectCategoryValueViews.slice(0, barCount);
12
7
  // Build the plot options.
13
8
  const options = useMemo(() => getPlotOptions(data, getCategoryTotalCount(selectCategoryValueViews), width), [data, selectCategoryValueViews, width]);
14
9
  return { options };
@@ -12,12 +12,6 @@ export const CHART_ARGS = {
12
12
  label: "male",
13
13
  selected: false,
14
14
  },
15
- {
16
- count: 240,
17
- key: "mixed",
18
- label: "mixed",
19
- selected: false,
20
- },
21
15
  {
22
16
  count: 78715,
23
17
  key: "unknown",
@@ -30,6 +24,12 @@ export const CHART_ARGS = {
30
24
  label: "Unspecified",
31
25
  selected: false,
32
26
  },
27
+ {
28
+ count: 240,
29
+ key: "mixed",
30
+ label: "mixed",
31
+ selected: false,
32
+ },
33
33
  ],
34
34
  width: 800,
35
35
  };
@@ -47,12 +47,6 @@ export const SELECT_CHART_ARGS = {
47
47
  label: "male",
48
48
  selected: true,
49
49
  },
50
- {
51
- count: 240,
52
- key: "mixed",
53
- label: "mixed",
54
- selected: false,
55
- },
56
50
  {
57
51
  count: 78715,
58
52
  key: "unknown",
@@ -65,6 +59,12 @@ export const SELECT_CHART_ARGS = {
65
59
  label: "Unspecified",
66
60
  selected: true,
67
61
  },
62
+ {
63
+ count: 240,
64
+ key: "mixed",
65
+ label: "mixed",
66
+ selected: false,
67
+ },
68
68
  ],
69
69
  width: 800,
70
70
  };
@@ -3,7 +3,7 @@ export const CHART_VIEW_ARGS = {
3
3
  {
4
4
  categoryViews: [
5
5
  {
6
- enableChartView: true,
6
+ chart: { enable: true },
7
7
  key: "biological-sex",
8
8
  label: "Biological Sex",
9
9
  values: [
@@ -40,7 +40,7 @@ export const CHART_VIEW_ARGS = {
40
40
  ],
41
41
  },
42
42
  {
43
- enableChartView: true,
43
+ chart: { enable: true },
44
44
  key: "genusSpecies",
45
45
  label: "Genus Species",
46
46
  values: [
@@ -71,7 +71,7 @@ export const CHART_VIEW_ARGS = {
71
71
  ],
72
72
  },
73
73
  {
74
- enableChartView: false,
74
+ chart: { enable: false },
75
75
  key: "pairedEnd",
76
76
  label: "Paired End",
77
77
  values: [
@@ -8,7 +8,7 @@ export function getSelectCategoryViews(categoryFilters) {
8
8
  return categoryFilters
9
9
  .flatMap(({ categoryViews }) => categoryViews)
10
10
  .filter(isSelectCategoryView)
11
- .filter(({ enableChartView = true }) => enableChartView)
11
+ .filter(({ chart }) => chart?.enable !== false)
12
12
  .filter(({ values }) => values.length > 0);
13
13
  }
14
14
  /**
@@ -35,7 +35,7 @@ function buildCategoryView(selectCategory, selectCategoryValueViews, categoryCon
35
35
  const mapSelectCategoryValue = selectCategoryConfig?.mapSelectCategoryValue || getSelectCategoryValue;
36
36
  return {
37
37
  annotation: selectCategoryConfig?.annotation,
38
- enableChartView: selectCategoryConfig?.enableChartView,
38
+ chart: selectCategoryConfig?.chart,
39
39
  isDisabled: false,
40
40
  key: selectCategory.key,
41
41
  label: getCategoryLabel(selectCategory.key, selectCategoryConfig),
@@ -29,15 +29,6 @@ export interface FileFacet {
29
29
  termsByName: TermsByName;
30
30
  total: number;
31
31
  }
32
- export declare enum FILE_MANIFEST_TYPE {
33
- BULK_DOWNLOAD = "BULK_DOWNLOAD",
34
- DOWNLOAD_MANIFEST = "DOWNLOAD_MANIFEST",
35
- ENTITY_BULK_DOWNLOAD = "ENTITY_BULK_DOWNLOAD",
36
- ENTITY_DOWNLOAD_MANIFEST = "ENTITY_DOWNLOAD_MANIFEST",
37
- ENTITY_EXPORT_TO_TERRA = "ENTITY_EXPORT_TO_TERRA",
38
- EXPORT_TO_TERRA = "EXPORT_TO_TERRA"
39
- }
40
- export type FileManifestType = FILE_MANIFEST_TYPE;
41
32
  export type SelectedSearchTermsBySearchKey = Map<CategoryKey, Set<CategoryValueKey>>;
42
33
  /**
43
34
  * Model of an individual facet value. For example, the term "Homo Sapiens" contained in the facet "Species".
@@ -1,9 +1 @@
1
- export var FILE_MANIFEST_TYPE;
2
- (function (FILE_MANIFEST_TYPE) {
3
- FILE_MANIFEST_TYPE["BULK_DOWNLOAD"] = "BULK_DOWNLOAD";
4
- FILE_MANIFEST_TYPE["DOWNLOAD_MANIFEST"] = "DOWNLOAD_MANIFEST";
5
- FILE_MANIFEST_TYPE["ENTITY_BULK_DOWNLOAD"] = "ENTITY_BULK_DOWNLOAD";
6
- FILE_MANIFEST_TYPE["ENTITY_DOWNLOAD_MANIFEST"] = "ENTITY_DOWNLOAD_MANIFEST";
7
- FILE_MANIFEST_TYPE["ENTITY_EXPORT_TO_TERRA"] = "ENTITY_EXPORT_TO_TERRA";
8
- FILE_MANIFEST_TYPE["EXPORT_TO_TERRA"] = "EXPORT_TO_TERRA";
9
- })(FILE_MANIFEST_TYPE || (FILE_MANIFEST_TYPE = {}));
1
+ export {};
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import Head from "next/head";
3
3
  import { useRouter } from "next/router";
4
4
  import { Fragment } from "react";
@@ -48,5 +48,5 @@ export const EntityDetailView = (props) => {
48
48
  const onTabChange = (tabValue) => {
49
49
  push(`/${entityRoute}/${uuid}/${tabValue}`);
50
50
  };
51
- return (_jsxs(Fragment, { children: [title && (_jsx(Head, { children: _jsx("title", { children: title }) })), _jsx(DetailView, { isDetailOverview: isDetailOverview, mainColumn: _jsx(ComponentCreator, { components: mainColumn, response: response }), sideColumn: sideColumn ? (_jsx(ComponentCreator, { components: sideColumn, response: response })) : undefined, Tabs: hideTabs ? (_jsx(_Fragment, {})) : (_jsx(Tabs, { onTabChange: onTabChange, tabs: tabs, value: tabRoute })), top: _jsx(ComponentCreator, { components: top, response: response }) })] }));
51
+ return (_jsxs(Fragment, { children: [title && (_jsx(Head, { children: _jsx("title", { children: title }) })), _jsx(DetailView, { isDetailOverview: isDetailOverview, mainColumn: _jsx(ComponentCreator, { components: mainColumn, response: response }), sideColumn: sideColumn ? (_jsx(ComponentCreator, { components: sideColumn, response: response })) : undefined, Tabs: hideTabs ? null : (_jsx(Tabs, { onTabChange: onTabChange, tabs: tabs, value: tabRoute })), top: _jsx(ComponentCreator, { components: top, response: response }) })] }));
52
52
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databiosphere/findable-ui",
3
- "version": "48.1.0",
3
+ "version": "49.0.0",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
@@ -1,3 +1,4 @@
1
+ import { Chart } from "../../chart/types";
1
2
  import {
2
3
  CategoryKey,
3
4
  DataDictionaryAnnotation,
@@ -33,7 +34,7 @@ export interface RangeCategoryConfig
33
34
  */
34
35
  export interface SelectCategoryConfig
35
36
  extends CommonCategoryConfig, SelectViewKind {
36
- enableChartView?: boolean;
37
+ chart?: Chart;
37
38
  mapSelectCategoryValue?: (
38
39
  selectCategoryValue: SelectCategoryValueView,
39
40
  ) => SelectCategoryValueView;
@@ -0,0 +1,13 @@
1
+ import {
2
+ sortCategoryValueViewsAlpha,
3
+ sortCategoryValueViewsCount,
4
+ } from "../../filters/sort/models/utils";
5
+ import { CHART_SORT, ChartSortFn } from "./types";
6
+
7
+ /**
8
+ * Map of chart sort options to their corresponding sort functions.
9
+ */
10
+ export const CHART_SORT_FN: Record<CHART_SORT, ChartSortFn> = {
11
+ [CHART_SORT.ALPHA]: sortCategoryValueViewsAlpha,
12
+ [CHART_SORT.COUNT]: sortCategoryValueViewsCount,
13
+ };
@@ -0,0 +1,22 @@
1
+ import { SelectCategoryValueView } from "../../entities";
2
+
3
+ /**
4
+ * Chart sorting options for select categories.
5
+ */
6
+ export enum CHART_SORT {
7
+ ALPHA = "ALPHA",
8
+ COUNT = "COUNT",
9
+ }
10
+
11
+ /**
12
+ * Sorting function type for select category values.
13
+ */
14
+ export type ChartSortFn = (
15
+ a: SelectCategoryValueView,
16
+ b: SelectCategoryValueView,
17
+ ) => number;
18
+
19
+ /**
20
+ * Chart sort options - either a preset (ALPHA, COUNT) or a custom sort function.
21
+ */
22
+ export type ChartSortOptions = CHART_SORT | ChartSortFn;
@@ -0,0 +1,22 @@
1
+ import { CHART_SORT_FN } from "./constants";
2
+ import { CHART_SORT, ChartSortOptions, ChartSortFn } from "./types";
3
+
4
+ /**
5
+ * Resolves chart sort options to a sorting function.
6
+ * @param chartSortOptions - Chart sort option (ALPHA, COUNT, or custom function).
7
+ * @returns Sorting function to use for chart data.
8
+ */
9
+ export function getChartSortFn(
10
+ chartSortOptions?: ChartSortOptions,
11
+ ): ChartSortFn {
12
+ // Default to COUNT sort if not specified.
13
+ if (!chartSortOptions) {
14
+ return CHART_SORT_FN[CHART_SORT.COUNT];
15
+ }
16
+ // If it's a custom function, return it directly.
17
+ if (typeof chartSortOptions === "function") {
18
+ return chartSortOptions;
19
+ }
20
+ // Otherwise, look up the function from the map.
21
+ return CHART_SORT_FN[chartSortOptions];
22
+ }
@@ -0,0 +1,6 @@
1
+ import { ChartSortOptions } from "./sort/types";
2
+
3
+ export interface Chart {
4
+ enable: boolean;
5
+ sortBy?: ChartSortOptions;
6
+ }
@@ -1,4 +1,5 @@
1
1
  import { RowData, TableOptions } from "@tanstack/react-table";
2
+ import { Chart } from "./chart/types";
2
3
 
3
4
  /**
4
5
  * Model of a value of a metadata class.
@@ -152,7 +153,7 @@ export interface SelectCategoryValueView {
152
153
  */
153
154
  export interface SelectCategoryView {
154
155
  annotation?: DataDictionaryAnnotation;
155
- enableChartView?: boolean;
156
+ chart?: Chart;
156
157
  isDisabled?: boolean;
157
158
  key: CategoryKey;
158
159
  label: string;
@@ -2,7 +2,6 @@ import { JSX, ElementType, useState } from "react";
2
2
  import { MANIFEST_DOWNLOAD_FORMAT } from "../../../../apis/azul/common/entities";
3
3
  import { Filters } from "../../../../common/entities";
4
4
  import { useExploreState } from "../../../../hooks/useExploreState";
5
- import { FileManifestType } from "../../../../hooks/useFileManifest/common/entities";
6
5
  import { useFileManifest } from "../../../../hooks/useFileManifest/useFileManifest";
7
6
  import { useFileManifestFileCount } from "../../../../hooks/useFileManifest/useFileManifestFileCount";
8
7
  import {
@@ -26,7 +25,6 @@ interface DownloadCurlCommandProps {
26
25
  DownloadCurlStart: ElementType;
27
26
  DownloadCurlSuccess: ElementType;
28
27
  fileManifestState: FileManifestState;
29
- fileManifestType: FileManifestType;
30
28
  fileSummaryFacetName: string;
31
29
  filters: Filters; // Initializes bulk download filters.
32
30
  formFacet: FormFacet;
@@ -2,7 +2,6 @@ import { JSX, ElementType } from "react";
2
2
  import { Filters } from "../../../../common/entities";
3
3
  import { useExploreState } from "../../../../hooks/useExploreState";
4
4
  import { useExportToTerraResponseURL } from "../../../../hooks/useExportToTerraResponseURL";
5
- import { FileManifestType } from "../../../../hooks/useFileManifest/common/entities";
6
5
  import { useFileManifest } from "../../../../hooks/useFileManifest/useFileManifest";
7
6
  import { useFileManifestFileCount } from "../../../../hooks/useFileManifest/useFileManifestFileCount";
8
7
  import { useFileManifestFormat } from "../../../../hooks/useFileManifest/useFileManifestFormat";
@@ -19,7 +18,6 @@ export interface ExportToTerraProps {
19
18
  ExportToTerraStart: ElementType;
20
19
  ExportToTerraSuccess: ElementType;
21
20
  fileManifestState: FileManifestState;
22
- fileManifestType: FileManifestType;
23
21
  fileSummaryFacetName: string;
24
22
  filters: Filters; // Initializes export to terra filters.
25
23
  formFacet: FormFacet;
@@ -1,13 +1,11 @@
1
1
  import { JSX } from "react";
2
2
  import { Filters } from "../../../../../../common/entities";
3
- import { FileManifestType } from "../../../../../../hooks/useFileManifest/common/entities";
4
3
  import { useFileManifest } from "../../../../../../hooks/useFileManifest/useFileManifest";
5
4
  import { ManifestDownloadFormat } from "../../../../common/entities";
6
5
  import { FileManifestDownload } from "./components/FileManifestDownload/fileManifestDownload";
7
6
  import { FileManifestSpreadsheet } from "./components/FileManifestSpreadsheet/fileManifestSpreadsheet";
8
7
 
9
8
  export interface ManifestDownloadEntityProps {
10
- fileManifestType: FileManifestType;
11
9
  filters: Filters; // Initializes manifest download filters.
12
10
  manifestDownloadFormat?: ManifestDownloadFormat;
13
11
  metadataFilters: Filters; // Metadata filters filters.
@@ -2,7 +2,6 @@ import { JSX, ElementType } from "react";
2
2
  import { MANIFEST_DOWNLOAD_FORMAT } from "../../../../apis/azul/common/entities";
3
3
  import { Filters } from "../../../../common/entities";
4
4
  import { useExploreState } from "../../../../hooks/useExploreState";
5
- import { FileManifestType } from "../../../../hooks/useFileManifest/common/entities";
6
5
  import { useFileManifest } from "../../../../hooks/useFileManifest/useFileManifest";
7
6
  import { useFileManifestFileCount } from "../../../../hooks/useFileManifest/useFileManifestFileCount";
8
7
  import {
@@ -18,7 +17,6 @@ import { ManifestDownloadReady } from "./components/ManifestDownloadReady/manife
18
17
 
19
18
  export interface ManifestDownloadProps {
20
19
  fileManifestState: FileManifestState;
21
- fileManifestType: FileManifestType;
22
20
  fileSummaryFacetName: string;
23
21
  filters: Filters; // Initializes manifest download filters.
24
22
  formFacet: FormFacet;
@@ -221,7 +221,7 @@ function mapColumnToSelectCategoryView<T extends RowData>(
221
221
  );
222
222
  return {
223
223
  annotation: undefined,
224
- enableChartView: false,
224
+ chart: undefined,
225
225
  isDisabled,
226
226
  key: column.id,
227
227
  label: getColumnHeader(column),
@@ -1,5 +1,6 @@
1
1
  import { Typography } from "@mui/material";
2
2
  import { Fragment, JSX } from "react";
3
+ import { getChartSortFn } from "../../../../../../../common/chart/sort/utils";
3
4
  import { TYPOGRAPHY_PROPS } from "../../../../../../../styles/common/mui/typography";
4
5
  import {
5
6
  Loading,
@@ -33,12 +34,17 @@ export const ChartView = ({
33
34
  panelStyle={LOADING_PANEL_STYLE.INHERIT}
34
35
  />
35
36
  <StyledGrid data-testid={testId} ref={chartViewRef}>
36
- {selectCategoryViews.map(({ key, label, values }) => (
37
+ {selectCategoryViews.map(({ chart, key, label, values }) => (
37
38
  <StyledGridPaperSection key={key}>
38
39
  <Typography variant={TYPOGRAPHY_PROPS.VARIANT.HEADING_SMALL}>
39
40
  {entityName} by {label}
40
41
  </Typography>
41
- <Chart selectCategoryValueViews={values} width={width} />
42
+ <Chart
43
+ selectCategoryValueViews={[...values].sort(
44
+ getChartSortFn(chart?.sortBy),
45
+ )}
46
+ width={width}
47
+ />
42
48
  </StyledGridPaperSection>
43
49
  ))}
44
50
  </StyledGrid>
@@ -1,6 +1,5 @@
1
1
  import { useMemo } from "react";
2
2
  import { SelectCategoryValueView } from "../../../../../../../../../../../common/entities";
3
- import { sortCategoryValueViewsCount } from "../../../../../../../../../../../common/filters/sort/models/utils";
4
3
  import { getPlotOptions } from "../../barX/plot";
5
4
  import { getCategoryTotalCount } from "../../barX/utils";
6
5
  import { UsePlotOptions } from "./types";
@@ -10,12 +9,8 @@ export const usePlotOptions = (
10
9
  width: number,
11
10
  barCount: number | undefined,
12
11
  ): UsePlotOptions => {
13
- // Organise the select category value views (sort and slice) for chart display.
14
- const data = selectCategoryValueViews
15
- // Sort the category values by count and label.
16
- .sort(sortCategoryValueViewsCount)
17
- // Slice the category values to the number of bars to display.
18
- .slice(0, barCount);
12
+ // Slice the category values to the number of bars to display.
13
+ const data = selectCategoryValueViews.slice(0, barCount);
19
14
 
20
15
  // Build the plot options.
21
16
  const options = useMemo(
@@ -15,12 +15,6 @@ export const CHART_ARGS: ComponentProps<typeof Chart> = {
15
15
  label: "male",
16
16
  selected: false,
17
17
  },
18
- {
19
- count: 240,
20
- key: "mixed",
21
- label: "mixed",
22
- selected: false,
23
- },
24
18
  {
25
19
  count: 78715,
26
20
  key: "unknown",
@@ -33,6 +27,12 @@ export const CHART_ARGS: ComponentProps<typeof Chart> = {
33
27
  label: "Unspecified",
34
28
  selected: false,
35
29
  },
30
+ {
31
+ count: 240,
32
+ key: "mixed",
33
+ label: "mixed",
34
+ selected: false,
35
+ },
36
36
  ],
37
37
  width: 800,
38
38
  };
@@ -51,12 +51,6 @@ export const SELECT_CHART_ARGS: ComponentProps<typeof Chart> = {
51
51
  label: "male",
52
52
  selected: true,
53
53
  },
54
- {
55
- count: 240,
56
- key: "mixed",
57
- label: "mixed",
58
- selected: false,
59
- },
60
54
  {
61
55
  count: 78715,
62
56
  key: "unknown",
@@ -69,6 +63,12 @@ export const SELECT_CHART_ARGS: ComponentProps<typeof Chart> = {
69
63
  label: "Unspecified",
70
64
  selected: true,
71
65
  },
66
+ {
67
+ count: 240,
68
+ key: "mixed",
69
+ label: "mixed",
70
+ selected: false,
71
+ },
72
72
  ],
73
73
  width: 800,
74
74
  };
@@ -6,7 +6,7 @@ export const CHART_VIEW_ARGS: ComponentProps<typeof ChartView> = {
6
6
  {
7
7
  categoryViews: [
8
8
  {
9
- enableChartView: true,
9
+ chart: { enable: true },
10
10
  key: "biological-sex",
11
11
  label: "Biological Sex",
12
12
  values: [
@@ -43,7 +43,7 @@ export const CHART_VIEW_ARGS: ComponentProps<typeof ChartView> = {
43
43
  ],
44
44
  },
45
45
  {
46
- enableChartView: true,
46
+ chart: { enable: true },
47
47
  key: "genusSpecies",
48
48
  label: "Genus Species",
49
49
  values: [
@@ -74,7 +74,7 @@ export const CHART_VIEW_ARGS: ComponentProps<typeof ChartView> = {
74
74
  ],
75
75
  },
76
76
  {
77
- enableChartView: false,
77
+ chart: { enable: false },
78
78
  key: "pairedEnd",
79
79
  label: "Paired End",
80
80
  values: [
@@ -13,7 +13,7 @@ export function getSelectCategoryViews(
13
13
  return categoryFilters
14
14
  .flatMap(({ categoryViews }) => categoryViews)
15
15
  .filter(isSelectCategoryView)
16
- .filter(({ enableChartView = true }) => enableChartView)
16
+ .filter(({ chart }) => chart?.enable !== false)
17
17
  .filter(({ values }) => values.length > 0);
18
18
  }
19
19
 
@@ -83,7 +83,7 @@ function buildCategoryView(
83
83
  selectCategoryConfig?.mapSelectCategoryValue || getSelectCategoryValue;
84
84
  return {
85
85
  annotation: selectCategoryConfig?.annotation,
86
- enableChartView: selectCategoryConfig?.enableChartView,
86
+ chart: selectCategoryConfig?.chart,
87
87
  isDisabled: false,
88
88
  key: selectCategory.key,
89
89
  label: getCategoryLabel(selectCategory.key, selectCategoryConfig),
@@ -34,17 +34,6 @@ export interface FileFacet {
34
34
  total: number;
35
35
  }
36
36
 
37
- export enum FILE_MANIFEST_TYPE {
38
- BULK_DOWNLOAD = "BULK_DOWNLOAD",
39
- DOWNLOAD_MANIFEST = "DOWNLOAD_MANIFEST",
40
- ENTITY_BULK_DOWNLOAD = "ENTITY_BULK_DOWNLOAD",
41
- ENTITY_DOWNLOAD_MANIFEST = "ENTITY_DOWNLOAD_MANIFEST",
42
- ENTITY_EXPORT_TO_TERRA = "ENTITY_EXPORT_TO_TERRA",
43
- EXPORT_TO_TERRA = "EXPORT_TO_TERRA",
44
- }
45
-
46
- export type FileManifestType = FILE_MANIFEST_TYPE;
47
-
48
37
  export type SelectedSearchTermsBySearchKey = Map<
49
38
  CategoryKey,
50
39
  Set<CategoryValueKey>
@@ -79,9 +79,7 @@ export const EntityDetailView = (props: EntityDetailViewProps): JSX.Element => {
79
79
  ) : undefined
80
80
  }
81
81
  Tabs={
82
- hideTabs ? (
83
- <></>
84
- ) : (
82
+ hideTabs ? null : (
85
83
  <Tabs onTabChange={onTabChange} tabs={tabs} value={tabRoute} />
86
84
  )
87
85
  }
@@ -0,0 +1,119 @@
1
+ import { CHART_SORT_FN } from "../src/common/chart/sort/constants";
2
+ import { CHART_SORT, ChartSortFn } from "../src/common/chart/sort/types";
3
+ import { getChartSortFn } from "../src/common/chart/sort/utils";
4
+ import { SelectCategoryValueView } from "../src/common/entities";
5
+ import {
6
+ sortCategoryValueViewsAlpha,
7
+ sortCategoryValueViewsCount,
8
+ } from "../src/common/filters/sort/models/utils";
9
+
10
+ /**
11
+ * Creates a mock category value view for testing.
12
+ * @param key - The key for the category value.
13
+ * @param count - The count for the category value.
14
+ * @returns A mock SelectCategoryValueView.
15
+ */
16
+ const createMockCategoryValueView = (
17
+ key: unknown,
18
+ count: number,
19
+ ): SelectCategoryValueView => ({
20
+ count,
21
+ key,
22
+ label: key ? String(key) : "",
23
+ selected: false,
24
+ });
25
+
26
+ describe("Chart Sort Utils", () => {
27
+ describe("CHART_SORT_FN", () => {
28
+ it("maps CHART_SORT.ALPHA to sortCategoryValueViewsAlpha", () => {
29
+ expect(CHART_SORT_FN[CHART_SORT.ALPHA]).toBe(sortCategoryValueViewsAlpha);
30
+ });
31
+
32
+ it("maps CHART_SORT.COUNT to sortCategoryValueViewsCount", () => {
33
+ expect(CHART_SORT_FN[CHART_SORT.COUNT]).toBe(sortCategoryValueViewsCount);
34
+ });
35
+ });
36
+
37
+ describe("getChartSortFn", () => {
38
+ it("returns COUNT sort function when undefined", () => {
39
+ const sortFn = getChartSortFn(undefined);
40
+ expect(sortFn).toBe(sortCategoryValueViewsCount);
41
+ });
42
+
43
+ it("returns ALPHA sort function for CHART_SORT.ALPHA", () => {
44
+ const sortFn = getChartSortFn(CHART_SORT.ALPHA);
45
+ expect(sortFn).toBe(sortCategoryValueViewsAlpha);
46
+ });
47
+
48
+ it("returns COUNT sort function for CHART_SORT.COUNT", () => {
49
+ const sortFn = getChartSortFn(CHART_SORT.COUNT);
50
+ expect(sortFn).toBe(sortCategoryValueViewsCount);
51
+ });
52
+
53
+ it("returns custom function when provided", () => {
54
+ const customSortFn: ChartSortFn = (a, b) =>
55
+ a.label.localeCompare(b.label);
56
+ const sortFn = getChartSortFn(customSortFn);
57
+ expect(sortFn).toBe(customSortFn);
58
+ });
59
+
60
+ it("custom sort function works correctly", () => {
61
+ // Custom sort: by label length descending
62
+ const customSortFn: ChartSortFn = (a, b) =>
63
+ b.label.length - a.label.length;
64
+ const sortFn = getChartSortFn(customSortFn);
65
+
66
+ const data = [
67
+ createMockCategoryValueView("a", 10),
68
+ createMockCategoryValueView("abc", 5),
69
+ createMockCategoryValueView("ab", 3),
70
+ ];
71
+
72
+ const sorted = [...data].sort(sortFn);
73
+
74
+ expect(sorted.map((cv) => cv.label)).toEqual(["abc", "ab", "a"]);
75
+ });
76
+ });
77
+
78
+ describe("sorting integration", () => {
79
+ const testData = [
80
+ createMockCategoryValueView("Zebra", 5),
81
+ createMockCategoryValueView("Apple", 10),
82
+ createMockCategoryValueView("Banana", 3),
83
+ ];
84
+
85
+ it("sorts alphabetically with CHART_SORT.ALPHA", () => {
86
+ const sortFn = getChartSortFn(CHART_SORT.ALPHA);
87
+ const sorted = [...testData].sort(sortFn);
88
+
89
+ expect(sorted.map((cv) => cv.label)).toEqual([
90
+ "Apple",
91
+ "Banana",
92
+ "Zebra",
93
+ ]);
94
+ });
95
+
96
+ it("sorts by count descending with CHART_SORT.COUNT", () => {
97
+ const sortFn = getChartSortFn(CHART_SORT.COUNT);
98
+ const sorted = [...testData].sort(sortFn);
99
+
100
+ expect(sorted.map((cv) => cv.label)).toEqual([
101
+ "Apple",
102
+ "Zebra",
103
+ "Banana",
104
+ ]);
105
+ expect(sorted.map((cv) => cv.count)).toEqual([10, 5, 3]);
106
+ });
107
+
108
+ it("defaults to count sort when no option provided", () => {
109
+ const sortFn = getChartSortFn();
110
+ const sorted = [...testData].sort(sortFn);
111
+
112
+ expect(sorted.map((cv) => cv.label)).toEqual([
113
+ "Apple",
114
+ "Zebra",
115
+ "Banana",
116
+ ]);
117
+ });
118
+ });
119
+ });