@databiosphere/findable-ui 21.0.0 → 21.1.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.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "21.0.0"
2
+ ".": "21.1.0"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [21.1.0](https://github.com/DataBiosphere/findable-ui/compare/v21.0.0...v21.1.0) (2025-01-30)
4
+
5
+
6
+ ### Features
7
+
8
+ * support unifying export views [#4102](https://github.com/DataBiosphere/findable-ui/issues/4102) ([#312](https://github.com/DataBiosphere/findable-ui/issues/312)) ([cbeb6e5](https://github.com/DataBiosphere/findable-ui/commit/cbeb6e5c6c264049767c3e7a5bebe4c72e25e6b1))
9
+
3
10
  ## [21.0.0](https://github.com/DataBiosphere/findable-ui/compare/v20.0.0...v21.0.0) (2025-01-06)
4
11
 
5
12
 
@@ -6,7 +6,9 @@ export declare const COLLATOR_CASE_INSENSITIVE: Intl.Collator;
6
6
  * Values to determine the index for each param.
7
7
  * https://host/explore/[slug]/[param-uuid]/[param-tab]
8
8
  * - ExploreView 0 returns the current UUID
9
- * - ExploreView 1 returns the current tab
9
+ * - ExploreView 1 returns the current tab (or choose export)
10
+ * - ExportView 2 returns the export method
10
11
  */
11
12
  export declare const PARAMS_INDEX_UUID = 0;
12
13
  export declare const PARAMS_INDEX_TAB = 1;
14
+ export declare const PARAMS_INDEX_EXPORT_METHOD = 2;
@@ -9,7 +9,9 @@ export const COLLATOR_CASE_INSENSITIVE = new Intl.Collator("en", {
9
9
  * Values to determine the index for each param.
10
10
  * https://host/explore/[slug]/[param-uuid]/[param-tab]
11
11
  * - ExploreView 0 returns the current UUID
12
- * - ExploreView 1 returns the current tab
12
+ * - ExploreView 1 returns the current tab (or choose export)
13
+ * - ExportView 2 returns the export method
13
14
  */
14
15
  export const PARAMS_INDEX_UUID = 0;
15
- export const PARAMS_INDEX_TAB = 1;
16
+ export const PARAMS_INDEX_TAB = 1; // Note "export" (i.e. not a tab) can possibly be at this index too
17
+ export const PARAMS_INDEX_EXPORT_METHOD = 2;
@@ -133,8 +133,10 @@ export interface EntityConfig<T = any, I = any> extends TabConfig {
133
133
  entityMapper?: EntityMapper<T, I>;
134
134
  exploreMode: ExploreMode;
135
135
  explorerTitle?: SiteConfig["explorerTitle"];
136
+ export?: ExportConfig;
136
137
  getId?: GetIdFunction<T>;
137
138
  getTitle?: GetTitleFunction<T>;
139
+ hideTabs?: boolean;
138
140
  list: ListConfig<T>;
139
141
  listView?: ListViewConfig;
140
142
  options?: Options;
@@ -0,0 +1,6 @@
1
+ import { ExportConfig } from "../config/entities";
2
+ /**
3
+ * Returns the export configuration for the given entity.
4
+ * @returns export configuration.
5
+ */
6
+ export declare const useEntityExportConfig: () => ExportConfig;
@@ -0,0 +1,12 @@
1
+ import { useConfig } from "./useConfig";
2
+ /**
3
+ * Returns the export configuration for the given entity.
4
+ * @returns export configuration.
5
+ */
6
+ export const useEntityExportConfig = () => {
7
+ const { entityConfig } = useConfig();
8
+ if (!entityConfig.export) {
9
+ throw new Error("This entity config does not have an export field set");
10
+ }
11
+ return entityConfig.export;
12
+ };
@@ -30,11 +30,11 @@ export const EntityDetailView = (props) => {
30
30
  const { push, query } = useRouter();
31
31
  const { entityConfig } = useConfig();
32
32
  const { mainColumn, sideColumn } = currentTab;
33
- const { detail, route: entityRoute } = entityConfig;
33
+ const { detail, hideTabs, route: entityRoute } = entityConfig;
34
34
  const { detailOverviews, top } = detail;
35
35
  const uuid = query.params?.[PARAMS_INDEX_UUID];
36
36
  const isDetailOverview = detailOverviews?.includes(currentTab.label);
37
- const tabs = getTabs(entityConfig);
37
+ const tabs = hideTabs ? [] : getTabs(entityConfig);
38
38
  const title = useEntityHeadTitle(response);
39
39
  if (!response) {
40
40
  return React.createElement("span", null); //TODO: return the loading UI component
@@ -50,5 +50,5 @@ export const EntityDetailView = (props) => {
50
50
  return (React.createElement(Fragment, null,
51
51
  title && (React.createElement(Head, null,
52
52
  React.createElement("title", null, title))),
53
- React.createElement(DetailView, { isDetailOverview: isDetailOverview, mainColumn: React.createElement(ComponentCreator, { components: mainColumn, response: response }), sideColumn: sideColumn ? (React.createElement(ComponentCreator, { components: sideColumn, response: response })) : undefined, Tabs: React.createElement(Tabs, { onTabChange: onTabChange, tabs: tabs, value: tabRoute }), top: React.createElement(ComponentCreator, { components: top, response: response }) })));
53
+ React.createElement(DetailView, { isDetailOverview: isDetailOverview, mainColumn: React.createElement(ComponentCreator, { components: mainColumn, response: response }), sideColumn: sideColumn ? (React.createElement(ComponentCreator, { components: sideColumn, response: response })) : undefined, Tabs: hideTabs ? (React.createElement(React.Fragment, null)) : (React.createElement(Tabs, { onTabChange: onTabChange, tabs: tabs, value: tabRoute })), top: React.createElement(ComponentCreator, { components: top, response: response }) })));
54
54
  };
@@ -0,0 +1,2 @@
1
+ import { EntityDetailViewProps } from "views/EntityDetailView/entityDetailView";
2
+ export declare const EntityExportMethodView: (props: EntityDetailViewProps) => JSX.Element;
@@ -0,0 +1,41 @@
1
+ import { useRouter } from "next/router";
2
+ import React from "react";
3
+ import { PARAMS_INDEX_EXPORT_METHOD } from "../../common/constants";
4
+ import { ComponentCreator } from "../../components/ComponentCreator/ComponentCreator";
5
+ import { BackPageView } from "../../components/Layout/components/BackPage/backPageView";
6
+ import { useEntityExportConfig } from "../../hooks/useEntityExportConfig";
7
+ import { useFetchEntity } from "../../hooks/useFetchEntity";
8
+ import { useUpdateURLCatalogParams } from "../../hooks/useUpdateURLCatalogParam";
9
+ export const EntityExportMethodView = (props) => {
10
+ // Update the catalog param if necessary.
11
+ useUpdateURLCatalogParams();
12
+ // Grab the entity to be exported.
13
+ const { response } = useFetchEntity(props);
14
+ // Get the column definitions for the entity export.
15
+ const { query } = useRouter();
16
+ const { exportMethods, tabs } = useEntityExportConfig();
17
+ const { sideColumn } = tabs[0];
18
+ const { mainColumn, top } = getExportMethodConfig(exportMethods, query) || {};
19
+ // Wait for the entity to be fetched.
20
+ if (!response) {
21
+ return React.createElement("span", null);
22
+ }
23
+ return (React.createElement(BackPageView, { mainColumn: React.createElement(ComponentCreator, { components: mainColumn || [], response: response }), sideColumn: sideColumn ? (React.createElement(ComponentCreator, { components: sideColumn, response: response })) : undefined, top: React.createElement(ComponentCreator, { components: top || [], response: response }) }));
24
+ };
25
+ /**
26
+ * Returns the export method configuration for the given pathname.
27
+ * @param exportMethods - Export methods config.
28
+ * @param query - Router query object.
29
+ * @returns export method configuration.
30
+ */
31
+ function getExportMethodConfig(exportMethods, query) {
32
+ // Determine the selected export method from the URL.
33
+ const exportMethodRoute = query.params?.[PARAMS_INDEX_EXPORT_METHOD];
34
+ if (!exportMethodRoute) {
35
+ return;
36
+ }
37
+ // Find the config for the selected export method.
38
+ return exportMethods.find(({ route }) => {
39
+ return route.includes(exportMethodRoute);
40
+ });
41
+ }
@@ -0,0 +1,2 @@
1
+ import { EntityDetailViewProps } from "views/EntityDetailView/entityDetailView";
2
+ export declare const EntityExportView: (props: EntityDetailViewProps) => JSX.Element;
@@ -0,0 +1,21 @@
1
+ import React from "react";
2
+ import { ComponentCreator } from "../../components/ComponentCreator/ComponentCreator";
3
+ import { BackPageView } from "../../components/Layout/components/BackPage/backPageView";
4
+ import { useEntityExportConfig } from "../../hooks/useEntityExportConfig";
5
+ import { useFetchEntity } from "../../hooks/useFetchEntity";
6
+ import { useUpdateURLCatalogParams } from "../../hooks/useUpdateURLCatalogParam";
7
+ export const EntityExportView = (props) => {
8
+ // Update the catalog param if necessary.
9
+ useUpdateURLCatalogParams();
10
+ // Grab the entity to be exported.
11
+ const { response } = useFetchEntity(props);
12
+ // Get the column definitions for the entity export.
13
+ const { tabs, top } = useEntityExportConfig();
14
+ const currentTab = tabs[0];
15
+ const { mainColumn, sideColumn } = currentTab;
16
+ // Wait for the entity to be fetched.
17
+ if (!response) {
18
+ return React.createElement("span", null);
19
+ }
20
+ return (React.createElement(BackPageView, { mainColumn: React.createElement(ComponentCreator, { components: mainColumn, response: response }), sideColumn: sideColumn ? (React.createElement(ComponentCreator, { components: sideColumn, response: response })) : undefined, top: React.createElement(ComponentCreator, { components: top, response: response }) }));
21
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databiosphere/findable-ui",
3
- "version": "21.0.0",
3
+ "version": "21.1.0",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
@@ -10,7 +10,9 @@ export const COLLATOR_CASE_INSENSITIVE = new Intl.Collator("en", {
10
10
  * Values to determine the index for each param.
11
11
  * https://host/explore/[slug]/[param-uuid]/[param-tab]
12
12
  * - ExploreView 0 returns the current UUID
13
- * - ExploreView 1 returns the current tab
13
+ * - ExploreView 1 returns the current tab (or choose export)
14
+ * - ExportView 2 returns the export method
14
15
  */
15
16
  export const PARAMS_INDEX_UUID = 0;
16
- export const PARAMS_INDEX_TAB = 1;
17
+ export const PARAMS_INDEX_TAB = 1; // Note "export" (i.e. not a tab) can possibly be at this index too
18
+ export const PARAMS_INDEX_EXPORT_METHOD = 2;
@@ -176,8 +176,10 @@ export interface EntityConfig<T = any, I = any> extends TabConfig {
176
176
  entityMapper?: EntityMapper<T, I>;
177
177
  exploreMode: ExploreMode;
178
178
  explorerTitle?: SiteConfig["explorerTitle"];
179
+ export?: ExportConfig;
179
180
  getId?: GetIdFunction<T>;
180
181
  getTitle?: GetTitleFunction<T>;
182
+ hideTabs?: boolean;
181
183
  list: ListConfig<T>;
182
184
  listView?: ListViewConfig;
183
185
  options?: Options;
@@ -0,0 +1,16 @@
1
+ import { ExportConfig } from "../config/entities";
2
+ import { useConfig } from "./useConfig";
3
+
4
+ /**
5
+ * Returns the export configuration for the given entity.
6
+ * @returns export configuration.
7
+ */
8
+ export const useEntityExportConfig = (): ExportConfig => {
9
+ const { entityConfig } = useConfig();
10
+
11
+ if (!entityConfig.export) {
12
+ throw new Error("This entity config does not have an export field set");
13
+ }
14
+
15
+ return entityConfig.export;
16
+ };
@@ -41,11 +41,11 @@ export const EntityDetailView = (props: EntityDetailViewProps): JSX.Element => {
41
41
  const { push, query } = useRouter();
42
42
  const { entityConfig } = useConfig();
43
43
  const { mainColumn, sideColumn } = currentTab;
44
- const { detail, route: entityRoute } = entityConfig;
44
+ const { detail, hideTabs, route: entityRoute } = entityConfig;
45
45
  const { detailOverviews, top } = detail;
46
46
  const uuid = query.params?.[PARAMS_INDEX_UUID];
47
47
  const isDetailOverview = detailOverviews?.includes(currentTab.label);
48
- const tabs = getTabs(entityConfig);
48
+ const tabs = hideTabs ? [] : getTabs(entityConfig);
49
49
  const title = useEntityHeadTitle(response);
50
50
 
51
51
  if (!response) {
@@ -78,7 +78,13 @@ export const EntityDetailView = (props: EntityDetailViewProps): JSX.Element => {
78
78
  <ComponentCreator components={sideColumn} response={response} />
79
79
  ) : undefined
80
80
  }
81
- Tabs={<Tabs onTabChange={onTabChange} tabs={tabs} value={tabRoute} />}
81
+ Tabs={
82
+ hideTabs ? (
83
+ <></>
84
+ ) : (
85
+ <Tabs onTabChange={onTabChange} tabs={tabs} value={tabRoute} />
86
+ )
87
+ }
82
88
  top={<ComponentCreator components={top} response={response} />}
83
89
  />
84
90
  </Fragment>
@@ -0,0 +1,67 @@
1
+ import { useRouter } from "next/router";
2
+ import type { ParsedUrlQuery } from "querystring";
3
+ import React from "react";
4
+ import { EntityDetailViewProps } from "views/EntityDetailView/entityDetailView";
5
+ import { PARAMS_INDEX_EXPORT_METHOD } from "../../common/constants";
6
+ import { ComponentCreator } from "../../components/ComponentCreator/ComponentCreator";
7
+ import { BackPageView } from "../../components/Layout/components/BackPage/backPageView";
8
+ import { ExportMethodConfig } from "../../config/entities";
9
+ import { useEntityExportConfig } from "../../hooks/useEntityExportConfig";
10
+ import { useFetchEntity } from "../../hooks/useFetchEntity";
11
+ import { useUpdateURLCatalogParams } from "../../hooks/useUpdateURLCatalogParam";
12
+
13
+ export const EntityExportMethodView = (
14
+ props: EntityDetailViewProps
15
+ ): JSX.Element => {
16
+ // Update the catalog param if necessary.
17
+ useUpdateURLCatalogParams();
18
+
19
+ // Grab the entity to be exported.
20
+ const { response } = useFetchEntity(props);
21
+
22
+ // Get the column definitions for the entity export.
23
+ const { query } = useRouter();
24
+ const { exportMethods, tabs } = useEntityExportConfig();
25
+ const { sideColumn } = tabs[0];
26
+ const { mainColumn, top } = getExportMethodConfig(exportMethods, query) || {};
27
+
28
+ // Wait for the entity to be fetched.
29
+ if (!response) {
30
+ return <span></span>;
31
+ }
32
+
33
+ return (
34
+ <BackPageView
35
+ mainColumn={
36
+ <ComponentCreator components={mainColumn || []} response={response} />
37
+ }
38
+ sideColumn={
39
+ sideColumn ? (
40
+ <ComponentCreator components={sideColumn} response={response} />
41
+ ) : undefined
42
+ }
43
+ top={<ComponentCreator components={top || []} response={response} />}
44
+ />
45
+ );
46
+ };
47
+
48
+ /**
49
+ * Returns the export method configuration for the given pathname.
50
+ * @param exportMethods - Export methods config.
51
+ * @param query - Router query object.
52
+ * @returns export method configuration.
53
+ */
54
+ function getExportMethodConfig(
55
+ exportMethods: ExportMethodConfig[],
56
+ query: ParsedUrlQuery
57
+ ): ExportMethodConfig | undefined {
58
+ // Determine the selected export method from the URL.
59
+ const exportMethodRoute = query.params?.[PARAMS_INDEX_EXPORT_METHOD];
60
+ if (!exportMethodRoute) {
61
+ return;
62
+ }
63
+ // Find the config for the selected export method.
64
+ return exportMethods.find(({ route }) => {
65
+ return route.includes(exportMethodRoute);
66
+ });
67
+ }
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import { EntityDetailViewProps } from "views/EntityDetailView/entityDetailView";
3
+ import { ComponentCreator } from "../../components/ComponentCreator/ComponentCreator";
4
+ import { BackPageView } from "../../components/Layout/components/BackPage/backPageView";
5
+ import { useEntityExportConfig } from "../../hooks/useEntityExportConfig";
6
+ import { useFetchEntity } from "../../hooks/useFetchEntity";
7
+ import { useUpdateURLCatalogParams } from "../../hooks/useUpdateURLCatalogParam";
8
+
9
+ export const EntityExportView = (props: EntityDetailViewProps): JSX.Element => {
10
+ // Update the catalog param if necessary.
11
+ useUpdateURLCatalogParams();
12
+
13
+ // Grab the entity to be exported.
14
+ const { response } = useFetchEntity(props);
15
+
16
+ // Get the column definitions for the entity export.
17
+ const { tabs, top } = useEntityExportConfig();
18
+ const currentTab = tabs[0];
19
+ const { mainColumn, sideColumn } = currentTab;
20
+
21
+ // Wait for the entity to be fetched.
22
+ if (!response) {
23
+ return <span></span>;
24
+ }
25
+
26
+ return (
27
+ <BackPageView
28
+ mainColumn={
29
+ <ComponentCreator components={mainColumn} response={response} />
30
+ }
31
+ sideColumn={
32
+ sideColumn ? (
33
+ <ComponentCreator components={sideColumn} response={response} />
34
+ ) : undefined
35
+ }
36
+ top={<ComponentCreator components={top} response={response} />}
37
+ />
38
+ );
39
+ };