@databiosphere/findable-ui 12.0.0 → 13.0.1

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 (66) hide show
  1. package/.github/workflows/release-please.yml +48 -0
  2. package/.github/workflows/run-checks.yml +41 -15
  3. package/.prettierignore +1 -0
  4. package/CHANGELOG.md +25 -0
  5. package/jest.config.js +1 -1
  6. package/lib/common/analytics/entities.d.ts +0 -2
  7. package/lib/common/analytics/entities.js +0 -1
  8. package/lib/components/Export/common/tracking.js +0 -1
  9. package/lib/components/Export/components/ExportMethod/exportMethod.d.ts +2 -1
  10. package/lib/components/Export/components/ExportMethod/exportMethod.js +2 -3
  11. package/lib/components/Index/components/Hero/components/ExportButton/exportButton.js +1 -1
  12. package/lib/components/Support/components/SupportRequest/components/SupportRequestForm/supportRequestForm.js +1 -3
  13. package/lib/components/TempError/tempError.d.ts +2 -2
  14. package/lib/components/TempError/tempError.js +4 -4
  15. package/lib/config/entities.d.ts +0 -1
  16. package/lib/entity/api/service.js +16 -18
  17. package/lib/entity/common/client.d.ts +6 -10
  18. package/lib/entity/common/client.js +21 -31
  19. package/lib/entity/common/service.js +6 -8
  20. package/lib/entity/common/utils.d.ts +4 -4
  21. package/lib/entity/common/utils.js +3 -3
  22. package/lib/shared/utils.d.ts +0 -5
  23. package/lib/shared/utils.js +0 -8
  24. package/package.json +4 -3
  25. package/src/common/analytics/entities.ts +0 -2
  26. package/src/common/analytics/readme-analytics.md +17 -7
  27. package/src/components/Export/common/tracking.ts +0 -1
  28. package/src/components/Export/components/ExportMethod/exportMethod.tsx +14 -4
  29. package/src/components/Index/components/Hero/components/ExportButton/exportButton.tsx +5 -1
  30. package/src/components/Support/components/SupportRequest/components/SupportRequestForm/supportRequestForm.tsx +1 -2
  31. package/src/components/TempError/tempError.tsx +10 -9
  32. package/src/config/entities.ts +0 -1
  33. package/src/entity/api/service.ts +24 -27
  34. package/src/entity/common/client.ts +22 -40
  35. package/src/entity/common/service.ts +8 -10
  36. package/src/entity/common/utils.ts +4 -6
  37. package/src/shared/utils.ts +0 -9
  38. package/tests/authentication.test.ts +7 -1
  39. package/tests/azulFileDownload.test.tsx +9 -3
  40. package/tests/fetchApi.test.ts +93 -0
  41. package/tests/tsconfig.json +1 -0
  42. package/tests/useFileLocation.test.ts +6 -2
  43. package/lib/components/Detail/components/Table/components/TableHead/tableHead.d.ts +0 -8
  44. package/lib/components/Detail/components/Table/components/TableHead/tableHead.js +0 -36
  45. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Authentication/components/RequestAuthentication/requestAuthentication.d.ts +0 -6
  46. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Authentication/components/RequestAuthentication/requestAuthentication.js +0 -13
  47. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Authentication/components/RequestAuthentication/requestAuthentication.styles.d.ts +0 -36
  48. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Authentication/components/RequestAuthentication/requestAuthentication.styles.js +0 -9
  49. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Search/components/SearchButton/searchButton.d.ts +0 -5
  50. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Search/components/SearchButton/searchButton.js +0 -10
  51. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Search/components/SearchButton/searchButton.styles.d.ts +0 -36
  52. package/lib/components/Layout/components/Header/components/Content/components/Actions/components/Search/components/SearchButton/searchButton.styles.js +0 -9
  53. package/lib/components/Table/components/EntityViewToggle/entityViewToggle.d.ts +0 -2
  54. package/lib/components/Table/components/EntityViewToggle/entityViewToggle.js +0 -37
  55. package/lib/components/common/IconButton/components/LoadingIconButton/loadingIconButton.d.ts +0 -5
  56. package/lib/components/common/IconButton/components/LoadingIconButton/loadingIconButton.js +0 -10
  57. package/lib/components/common/IconButton/components/LoadingIconButton/loadingIconButton.stories.d.ts +0 -3
  58. package/lib/components/common/IconButton/components/LoadingIconButton/loadingIconButton.stories.js +0 -9
  59. package/lib/hooks/useCategoryConfigs.d.ts +0 -6
  60. package/lib/hooks/useCategoryConfigs.js +0 -17
  61. package/lib/hooks/useEntityListRelatedView.d.ts +0 -15
  62. package/lib/hooks/useEntityListRelatedView.js +0 -62
  63. package/lib/hooks/useMenu.d.ts +0 -10
  64. package/lib/hooks/useMenu.js +0 -17
  65. package/lib/hooks/useMenuWithPosition.d.ts +0 -14
  66. package/lib/hooks/useMenuWithPosition.js +0 -33
@@ -0,0 +1,48 @@
1
+ on:
2
+ push:
3
+ branches:
4
+ - main
5
+
6
+ permissions:
7
+ contents: write
8
+ pull-requests: write
9
+
10
+ name: release-please
11
+
12
+ jobs:
13
+ release-please:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: googleapis/release-please-action@v4
17
+ id: release
18
+ with:
19
+ # this assumes that you have created a personal access token
20
+ # (PAT) and configured it as a GitHub action secret named
21
+ # `MY_RELEASE_PLEASE_TOKEN` (this secret name is not important).
22
+ token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
23
+ # This is a built-in strategy in release-please, see "Action Inputs" for more options
24
+ release-type: node
25
+
26
+ # The logic below handles the npm publication:
27
+ - uses: actions/checkout@v4
28
+
29
+ # These if statements ensure that a publication only occurs when a new release is created:
30
+ - uses: actions/setup-node@v4
31
+ if: ${{ steps.release.outputs.release_created }}
32
+ with:
33
+ node-version: 20
34
+ registry-url: "https://registry.npmjs.org"
35
+
36
+ - name: Install dependencies
37
+ if: ${{ steps.release.outputs.release_created }}
38
+ run: npm ci
39
+
40
+ - name: Compile
41
+ if: ${{ steps.release.outputs.release_created }}
42
+ run: npx tsc
43
+
44
+ - name: Publish to NPM
45
+ if: ${{ steps.release.outputs.release_created }}
46
+ run: npm publish
47
+ env:
48
+ NODE_AUTH_TOKEN: ${{ secrets.DATABIOSPHERE_FINDABLE_UI_NPM_PUBLISH_TOKEN }} # Ensure this token is scoped to only the permissions required for npm publication to limit security risks.
@@ -1,31 +1,57 @@
1
1
  name: Run checks
2
- on: [pull_request]
2
+ on:
3
+ pull_request:
4
+ branches:
5
+ - "**"
3
6
 
4
7
  jobs:
5
8
  build:
6
9
  runs-on: ubuntu-latest
7
10
  steps:
8
- - uses: actions/checkout@v2
9
- - uses: actions/setup-node@v2
11
+ - name: Checkout repository
12
+ uses: actions/checkout@v4
13
+
14
+ - name: Set up Node.js
15
+ uses: actions/setup-node@v4
10
16
  with:
11
17
  node-version: "20.10.0"
18
+ cache: "npm"
19
+
20
+ - name: Install dependencies
21
+ run: npm ci
22
+
23
+ - name: Check code format
24
+ run: npm run check-format
25
+
26
+ - name: Run linter
27
+ run: npm run lint
28
+
29
+ - name: Run tests
30
+ run: npm run test
12
31
 
13
- - run: |
14
- npm ci
15
- npm run check-format
16
- npm run lint
17
- npm run test
18
- npm run test-compile
32
+ - name: Test compilation
33
+ run: npm run test-compile
19
34
 
20
35
  install-without-lock:
21
36
  runs-on: ubuntu-latest
22
37
  steps:
23
- - uses: actions/checkout@v2
24
- - uses: actions/setup-node@v2
38
+ - name: Checkout repository
39
+ uses: actions/checkout@v4
40
+
41
+ - name: Set up Node.js
42
+ uses: actions/setup-node@v4
25
43
  with:
26
44
  node-version: "20.10.0"
45
+ cache: "npm"
46
+
47
+ - name: Remove package lock
48
+ run: rm package-lock.json
49
+
50
+ - name: Install dependencies without lock
51
+ run: npm install
52
+
53
+ - name: Run tests
54
+ run: npm run test
27
55
 
28
- - run: |
29
- rm package-lock.json
30
- npm install
31
- npm run test-compile
56
+ - name: Test compilation
57
+ run: npm run test-compile
package/.prettierignore CHANGED
@@ -1,5 +1,6 @@
1
1
  # general
2
2
  README.md
3
+ CHANGELOG.md
3
4
 
4
5
  # testing
5
6
  coverage
package/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ ## [13.0.1](https://github.com/DataBiosphere/findable-ui/compare/v13.0.0...v13.0.1) (2024-10-18)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * give release-please workflow step an id ([#235](https://github.com/DataBiosphere/findable-ui/issues/235)) ([#236](https://github.com/DataBiosphere/findable-ui/issues/236)) ([420733f](https://github.com/DataBiosphere/findable-ui/commit/420733f3d6dc51f44301b06476550f499b4ffbd3))
9
+
10
+ ## [13.0.0](https://github.com/DataBiosphere/findable-ui/compare/v12.0.0...v13.0.0) (2024-10-18)
11
+
12
+
13
+ ### ⚠ BREAKING CHANGES
14
+
15
+ * use ky with limited retries instead of axios ([#203](https://github.com/DataBiosphere/findable-ui/issues/203)) (#216)
16
+
17
+ ### Features
18
+
19
+ * remove dry run from npm publish ([#231](https://github.com/DataBiosphere/findable-ui/issues/231)) ([#234](https://github.com/DataBiosphere/findable-ui/issues/234)) ([f44d0c3](https://github.com/DataBiosphere/findable-ui/commit/f44d0c3cb2859af6b87d33f5f6e16673db77ec52))
20
+ * use ky with limited retries instead of axios ([#203](https://github.com/DataBiosphere/findable-ui/issues/203)) ([#216](https://github.com/DataBiosphere/findable-ui/issues/216)) ([ec18080](https://github.com/DataBiosphere/findable-ui/commit/ec18080e3191f99a88e9ddec174c9f953d9e3a41))
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * add changelog to gitignore ([#232](https://github.com/DataBiosphere/findable-ui/issues/232)) ([#233](https://github.com/DataBiosphere/findable-ui/issues/233)) ([b99ba34](https://github.com/DataBiosphere/findable-ui/commit/b99ba34088199b561b38493888206ed475a209e4))
package/jest.config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /** @type {import('ts-jest').JestConfigWithTsJest} */
2
2
  module.exports = {
3
- preset: "ts-jest",
3
+ preset: "ts-jest/presets/js-with-ts-esm",
4
4
  testEnvironment: "jest-environment-jsdom",
5
5
  };
@@ -17,7 +17,6 @@ export declare enum EVENT_PARAM {
17
17
  COLUMN_NAME = "column_name",
18
18
  CURRENT_QUERY = "current_query",
19
19
  ENTITY_NAME = "entity_name",
20
- ENTITY_TYPE = "entity_type",
21
20
  FILTER_NAME = "filter_name",
22
21
  FILTER_VALUE = "filter_value",
23
22
  INDEX = "index",
@@ -46,7 +45,6 @@ export type EventParams = {
46
45
  [EVENT_NAME.BULK_DOWNLOAD_REQUESTED]: {
47
46
  [EVENT_PARAM.CATALOG]: string;
48
47
  [EVENT_PARAM.CURRENT_QUERY]: string;
49
- [EVENT_PARAM.ENTITY_TYPE]: string;
50
48
  [EVENT_PARAM.INDEX]: string;
51
49
  [EVENT_PARAM.TOOL_NAME]: string;
52
50
  };
@@ -18,7 +18,6 @@ export var EVENT_PARAM;
18
18
  EVENT_PARAM["COLUMN_NAME"] = "column_name";
19
19
  EVENT_PARAM["CURRENT_QUERY"] = "current_query";
20
20
  EVENT_PARAM["ENTITY_NAME"] = "entity_name";
21
- EVENT_PARAM["ENTITY_TYPE"] = "entity_type";
22
21
  EVENT_PARAM["FILTER_NAME"] = "filter_name";
23
22
  EVENT_PARAM["FILTER_VALUE"] = "filter_value";
24
23
  EVENT_PARAM["INDEX"] = "index";
@@ -17,7 +17,6 @@ export function bulkDownloadTracking(index, toolName, requestParams) {
17
17
  track(EVENT_NAME.BULK_DOWNLOAD_REQUESTED, {
18
18
  [EVENT_PARAM.CATALOG]: catalog,
19
19
  [EVENT_PARAM.CURRENT_QUERY]: currentQuery,
20
- [EVENT_PARAM.ENTITY_TYPE]: "Bulk Download",
21
20
  [EVENT_PARAM.INDEX]: index,
22
21
  [EVENT_PARAM.TOOL_NAME]: toolName,
23
22
  });
@@ -6,5 +6,6 @@ export interface ExportMethodProps {
6
6
  isAccessible?: boolean;
7
7
  route: string;
8
8
  title: string;
9
+ trackingId?: string;
9
10
  }
10
- export declare const ExportMethod: ({ buttonLabel, description, footnote, isAccessible, route, title, }: ExportMethodProps) => JSX.Element;
11
+ export declare const ExportMethod: ({ buttonLabel, description, footnote, isAccessible, route, title, trackingId, }: ExportMethodProps) => JSX.Element;
@@ -6,7 +6,7 @@ import { FluidPaper } from "../../../common/Paper/paper.styles";
6
6
  import { SectionTitle } from "../../../common/Section/components/SectionTitle/sectionTitle";
7
7
  import { Section, SectionActions, SectionContent, } from "../../../common/Section/section.styles";
8
8
  import { ExportButton, SectionFootnote } from "./exportMethod.styles";
9
- export const ExportMethod = ({ buttonLabel, description, footnote, isAccessible = true, route, title, }) => {
9
+ export const ExportMethod = ({ buttonLabel, description, footnote, isAccessible = true, route, title, trackingId, }) => {
10
10
  const { disabled, message } = useDownloadStatus();
11
11
  return (React.createElement(FluidPaper, null,
12
12
  React.createElement(Section, null,
@@ -16,7 +16,6 @@ export const ExportMethod = ({ buttonLabel, description, footnote, isAccessible
16
16
  React.createElement(SectionActions, null,
17
17
  React.createElement(Tooltip, { arrow: true, title: message },
18
18
  React.createElement("span", null,
19
- React.createElement(Link, { href: route, legacyBehavior: true, passHref: true },
20
- React.createElement(ExportButton, { disabled: disabled || !isAccessible }, buttonLabel))))),
19
+ React.createElement(Link, { href: route, legacyBehavior: true, passHref: true }, trackingId ? (React.createElement(ExportButton, { disabled: disabled || !isAccessible, id: trackingId }, buttonLabel)) : (React.createElement(ExportButton, { disabled: disabled || !isAccessible }, buttonLabel)))))),
21
20
  footnote && React.createElement(SectionFootnote, null, footnote))));
22
21
  };
@@ -8,5 +8,5 @@ export const ExportButton = () => {
8
8
  return (React.createElement(Tooltip, { arrow: true, title: message },
9
9
  React.createElement("span", null,
10
10
  React.createElement(Link, { href: "/export", legacyBehavior: true, passHref: true },
11
- React.createElement(Button, { disabled: disabled || isLoading, href: "passHref" }, "Export")))));
11
+ React.createElement(Button, { disabled: disabled || isLoading, href: "passHref", id: "button-cohort-export" }, "Export")))));
12
12
  };
@@ -105,8 +105,6 @@ export const SupportRequestForm = ({ setFormSubmitted, supportRequest, }) => {
105
105
  submitting: false,
106
106
  }));
107
107
  setFormSubmitted(true); // Shows form submitted message.
108
- // Execute GTM tracking.
109
- // TODO(cc) add GTM tracking.
110
108
  }
111
109
  catch (error) {
112
110
  setFormState((formState) => ({
@@ -131,7 +129,7 @@ export const SupportRequestForm = ({ setFormSubmitted, supportRequest, }) => {
131
129
  React.createElement(UploadFile, { isDragActive: isDragActive, label: FORM_CONTROL_LABEL.ATTACHMENT_TOKEN, onAttachmentDeleted: onAttachmentDeleted, open: open, ...formState })),
132
130
  React.createElement(Section, null,
133
131
  React.createElement(SectionActions, null,
134
- React.createElement(ButtonPrimary, { disabled: Boolean(errors) || formState.submitting, fullWidth: true, onClick: onSupportRequestSubmitted }, "Send")))))));
132
+ React.createElement(ButtonPrimary, { disabled: Boolean(errors) || formState.submitting, fullWidth: true, onClick: onSupportRequestSubmitted, id: "button-support-request" }, "Send")))))));
135
133
  };
136
134
  /**
137
135
  * Build support request model from form values.
@@ -1,6 +1,6 @@
1
- import { AxiosError } from "axios";
1
+ import { HTTPError } from "ky";
2
2
  interface TempErrorProps {
3
- error: Error | AxiosError;
3
+ error: Error | HTTPError;
4
4
  }
5
5
  export declare const TempError: ({ error }: TempErrorProps) => JSX.Element;
6
6
  export {};
@@ -1,12 +1,12 @@
1
- import { isAxiosError } from "axios";
1
+ import { HTTPError } from "ky";
2
2
  import React from "react";
3
3
  import { ErrorBox } from "./components/errorBox";
4
4
  export const TempError = ({ error }) => {
5
- const { code, request } = isAxiosError(error)
5
+ const { code, request } = error instanceof HTTPError
6
6
  ? {
7
7
  ...error,
8
- code: error.response?.status,
9
- request: error.request.responseURL,
8
+ code: error.response.status,
9
+ request: error.response.url,
10
10
  }
11
11
  : { ...error, code: null, request: null };
12
12
  return (React.createElement("div", null,
@@ -111,7 +111,6 @@ export interface DataSourceConfig {
111
111
  defaultParams?: {
112
112
  catalog: string;
113
113
  };
114
- entityURL?: string;
115
114
  url: string;
116
115
  }
117
116
  /**
@@ -5,11 +5,11 @@
5
5
  import { APIEndpoints, AZUL_PARAM, } from "../../apis/azul/common/entities";
6
6
  import { transformFilters } from "../../apis/azul/common/filterTransformer";
7
7
  import { getConfig } from "../../config/config";
8
- import { getDefaultDetailParams, getDefaultListParams, getEntityURL, } from "../../shared/utils";
8
+ import { getDefaultDetailParams, getDefaultListParams, } from "../../shared/utils";
9
9
  import { convertUrlParams } from "../../utils/url";
10
- import { api } from "../common/client";
10
+ import { fetchApi } from "../common/client";
11
11
  import { fetchEntitiesFromURL } from "../common/service";
12
- import { getAxiosRequestOptions } from "../common/utils";
12
+ import { getKyRequestOptions } from "../common/utils";
13
13
  /**
14
14
  * Make a GET or POST request for a list of entities
15
15
  * @param apiPath - Path that will be used to compose the API url
@@ -38,7 +38,7 @@ export const fetchAllEntities = async (apiPath, accessToken, catalog, listParams
38
38
  let hits = result.hits;
39
39
  let nextPage = result.pagination.next;
40
40
  while (nextPage) {
41
- const { data: nextPageJson } = await api().get(nextPage);
41
+ const nextPageJson = await (await fetchApi(nextPage)).json();
42
42
  nextPage = nextPageJson.pagination.next;
43
43
  hits = [...hits, ...nextPageJson.hits];
44
44
  }
@@ -49,8 +49,8 @@ export const fetchAllEntities = async (apiPath, accessToken, catalog, listParams
49
49
  * @returns name of the default catalog and all available catalogs.
50
50
  */
51
51
  export const fetchCatalog = async () => {
52
- const res = await api().get(APIEndpoints.CATALOGS);
53
- return res.data;
52
+ const res = await fetchApi(APIEndpoints.CATALOGS);
53
+ return await res.json();
54
54
  };
55
55
  /**
56
56
  * Request to get a single project.
@@ -66,15 +66,13 @@ export const fetchEntityDetail = async (id, apiPath, catalog, accessToken, defau
66
66
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- this response type can't be determined beforehand
67
67
  ) => {
68
68
  const catalogParam = catalog ? { [AZUL_PARAM.CATALOG]: catalog } : undefined;
69
- const options = getAxiosRequestOptions(accessToken);
70
- const baseURL = getEntityURL();
71
- return await api(baseURL)
72
- .get(`${apiPath}/${id}?${convertUrlParams({
69
+ const options = getKyRequestOptions(accessToken);
70
+ return await fetchApi(`${apiPath}/${id}?${convertUrlParams({
73
71
  ...defaultParams,
74
72
  ...catalogParam,
75
73
  })}`, options)
76
74
  .then((res) => {
77
- return res.data;
75
+ return res.json();
78
76
  })
79
77
  .catch((error) => {
80
78
  if (swallow404) {
@@ -109,9 +107,9 @@ export const fetchSummary = async (filterState, catalog, accessToken) => {
109
107
  [AZUL_PARAM.FILTERS]: filtersParam,
110
108
  };
111
109
  }
112
- const options = getAxiosRequestOptions(accessToken);
113
- const res = await api().get(`${apiPath}?${convertUrlParams({ ...summaryParams })}`, options);
114
- return res.data;
110
+ const options = getKyRequestOptions(accessToken);
111
+ const res = await fetchApi(`${apiPath}?${convertUrlParams({ ...summaryParams })}`, options);
112
+ return await res.json();
115
113
  };
116
114
  /**
117
115
  * Fetch summary from given URL.
@@ -120,8 +118,8 @@ export const fetchSummary = async (filterState, catalog, accessToken) => {
120
118
  * @returns summary response.
121
119
  */
122
120
  export const fetchSummaryFromURL = async (path, accessToken) => {
123
- const res = await api().get(path, getAxiosRequestOptions(accessToken));
124
- return res.data;
121
+ const res = await fetchApi(path, getKyRequestOptions(accessToken));
122
+ return await res.json();
125
123
  };
126
124
  /**
127
125
  * Fetch system status from given URL.
@@ -129,6 +127,6 @@ export const fetchSummaryFromURL = async (path, accessToken) => {
129
127
  * @returns system status.
130
128
  */
131
129
  export const fetchSystemStatusFromURL = async (URL) => {
132
- const res = await api().get(URL);
133
- return res.data;
130
+ const res = await fetchApi(URL);
131
+ return await res.json();
134
132
  };
@@ -1,12 +1,8 @@
1
- import { AxiosInstance } from "axios";
1
+ import { Options, ResponsePromise } from "ky";
2
2
  /**
3
- * Adding response interceptors to axios instances.
4
- * @param api - AxiosInstance.
3
+ * Makes an HTTP request with the API URL as a base.
4
+ * @param url - URL to fetch.
5
+ * @param options - Ky options.
6
+ * @returns Ky response.
5
7
  */
6
- export declare const configureInterceptors: (api: AxiosInstance) => void;
7
- /**
8
- * Returns a singleton Axios instance configured for making HTTP requests to a specified base URL.
9
- * @param baseURL - The base URL to use for the AxiosInstance.
10
- * @returns axios instance.
11
- */
12
- export declare const api: (baseURL?: string) => AxiosInstance;
8
+ export declare function fetchApi<T>(url: string, options?: Options): ResponsePromise<T>;
@@ -1,37 +1,27 @@
1
- import axios, { HttpStatusCode, } from "axios";
1
+ import ky from "ky";
2
2
  import { getURL } from "../../shared/utils";
3
- let axiosInstance = null;
3
+ let kyInstance = null;
4
4
  /**
5
- * Adding response interceptors to axios instances.
6
- * @param api - AxiosInstance.
5
+ * Makes an HTTP request with the API URL as a base.
6
+ * @param url - URL to fetch.
7
+ * @param options - Ky options.
8
+ * @returns Ky response.
7
9
  */
8
- export const configureInterceptors = (api) => {
9
- api.interceptors.response.use((response) => response, (error) => {
10
- const { config, response } = error;
11
- if (response?.status === HttpStatusCode.ServiceUnavailable && config) {
12
- const retryAfterValue = response.headers["Retry-After"];
13
- const waitingTime = retryAfterValue ? +retryAfterValue : 0;
14
- return new Promise((resolve) => {
15
- setTimeout(() => resolve(api(config)), waitingTime);
16
- });
17
- }
18
- else {
19
- return Promise.reject(error);
20
- }
21
- });
22
- };
23
- /**
24
- * Returns a singleton Axios instance configured for making HTTP requests to a specified base URL.
25
- * @param baseURL - The base URL to use for the AxiosInstance.
26
- * @returns axios instance.
27
- */
28
- export const api = (baseURL = getURL()) => {
29
- if (!axiosInstance) {
30
- axiosInstance = axios.create({
31
- baseURL,
10
+ export function fetchApi(url, options = {}) {
11
+ if (!kyInstance) {
12
+ kyInstance = ky.create({
13
+ prefixUrl: getURL(),
14
+ retry: {
15
+ delay: (attemptCount) => 1000 * 3 ** (attemptCount - 1),
16
+ limit: 3,
17
+ },
32
18
  timeout: 20 * 1000,
33
19
  });
34
- configureInterceptors(axiosInstance);
35
20
  }
36
- return axiosInstance;
37
- };
21
+ // If a full URL is provided, there shouldn't be a prefix URL. Otherwise, Ky requires that the URL not start with a slash.
22
+ if (/^https?:\/\//.test(url))
23
+ options = { prefixUrl: "", ...options };
24
+ else
25
+ url = url.replace(/^\//, "");
26
+ return kyInstance.get(url, options);
27
+ }
@@ -1,6 +1,5 @@
1
- import { getEntityURL } from "../../shared/utils";
2
- import { api } from "./client";
3
- import { getAxiosRequestOptions } from "./utils";
1
+ import { fetchApi } from "./client";
2
+ import { getKyRequestOptions } from "./utils";
4
3
  /**
5
4
  * Fetch entities from the given URL.
6
5
  * @param URL - URL.
@@ -8,8 +7,8 @@ import { getAxiosRequestOptions } from "./utils";
8
7
  * @returns entities.
9
8
  */
10
9
  export const fetchEntitiesFromURL = async (URL, accessToken) => {
11
- const res = await api().get(URL, getAxiosRequestOptions(accessToken));
12
- return res.data;
10
+ const res = await fetchApi(URL, getKyRequestOptions(accessToken));
11
+ return await res.json();
13
12
  };
14
13
  /**
15
14
  * Fetch entity from the given URL.
@@ -18,7 +17,6 @@ export const fetchEntitiesFromURL = async (URL, accessToken) => {
18
17
  * @returns entity.
19
18
  */
20
19
  export const fetchEntityFromURL = async (URL, accessToken) => {
21
- const baseURL = getEntityURL();
22
- const res = await api(baseURL).get(URL, getAxiosRequestOptions(accessToken));
23
- return res.data;
20
+ const res = await fetchApi(URL, getKyRequestOptions(accessToken));
21
+ return await res.json();
24
22
  };
@@ -1,7 +1,7 @@
1
- import { AxiosRequestConfig } from "axios";
1
+ import { Options } from "ky";
2
2
  /**
3
- * Returns Axios request configuration.
3
+ * Returns Ky request configuration.
4
4
  * @param accessToken - Access token.
5
- * @returns Axios request configuration.
5
+ * @returns Ky request configuration.
6
6
  */
7
- export declare function getAxiosRequestOptions(accessToken: string | undefined): AxiosRequestConfig;
7
+ export declare function getKyRequestOptions(accessToken: string | undefined): Options;
@@ -1,9 +1,9 @@
1
1
  /**
2
- * Returns Axios request configuration.
2
+ * Returns Ky request configuration.
3
3
  * @param accessToken - Access token.
4
- * @returns Axios request configuration.
4
+ * @returns Ky request configuration.
5
5
  */
6
- export function getAxiosRequestOptions(accessToken) {
6
+ export function getKyRequestOptions(accessToken) {
7
7
  return {
8
8
  headers: accessToken ? { Authorization: "Bearer " + accessToken } : {},
9
9
  };
@@ -9,11 +9,6 @@ export declare function getDefaultDetailParams(): DataSourceConfig["defaultDetai
9
9
  * @returns application DEFAULT_LIST_PARAMS.
10
10
  */
11
11
  export declare function getDefaultListParams(): DataSourceConfig["defaultListParams"] | DataSourceConfig["defaultParams"];
12
- /**
13
- * Returns entity source URL.
14
- * @returns entity source URL.
15
- */
16
- export declare function getEntityURL(): string;
17
12
  /**
18
13
  * Returns application URL.
19
14
  * @returns application url.
@@ -17,14 +17,6 @@ export function getDefaultListParams() {
17
17
  const { defaultListParams, defaultParams } = dataSource;
18
18
  return { ...defaultListParams, ...defaultParams };
19
19
  }
20
- /**
21
- * Returns entity source URL.
22
- * @returns entity source URL.
23
- */
24
- export function getEntityURL() {
25
- const dataSource = getConfig().dataSource;
26
- return dataSource.entityURL || dataSource.url;
27
- }
28
20
  /**
29
21
  * Returns application URL.
30
22
  * @returns application url.
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@databiosphere/findable-ui",
3
- "version": "12.0.0",
3
+ "version": "13.0.1",
4
4
  "description": "",
5
5
  "scripts": {
6
- "test": "jest",
6
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
7
7
  "lint": "eslint .",
8
8
  "check-format": "prettier --check .",
9
9
  "storybook": "storybook dev -p 6006",
@@ -57,6 +57,7 @@
57
57
  "husky": "^8.0.3",
58
58
  "jest": "^29.4.1",
59
59
  "jest-environment-jsdom": "^29.4.1",
60
+ "jest-fetch-mock": "^3.0.3",
60
61
  "prettier": "^2.8.3",
61
62
  "prettier-plugin-organize-imports": "^3.2.2",
62
63
  "storybook": "^7.6.17",
@@ -70,9 +71,9 @@
70
71
  "@mui/material": "^6.0.2",
71
72
  "@tanstack/react-table": "^8.19.2",
72
73
  "@tanstack/react-virtual": "^3.0.0-beta.59",
73
- "axios": "^1.6.7",
74
74
  "copy-to-clipboard": "3.3.1",
75
75
  "isomorphic-dompurify": "0.24.0",
76
+ "ky": "^1.7.2",
76
77
  "next": "^14.1.0",
77
78
  "react": "^18.3.1",
78
79
  "react-dom": "^18.3.1",
@@ -20,7 +20,6 @@ export enum EVENT_PARAM {
20
20
  COLUMN_NAME = "column_name",
21
21
  CURRENT_QUERY = "current_query",
22
22
  ENTITY_NAME = "entity_name",
23
- ENTITY_TYPE = "entity_type",
24
23
  FILTER_NAME = "filter_name",
25
24
  FILTER_VALUE = "filter_value",
26
25
  INDEX = "index",
@@ -52,7 +51,6 @@ export type EventParams = {
52
51
  [EVENT_NAME.BULK_DOWNLOAD_REQUESTED]: {
53
52
  [EVENT_PARAM.CATALOG]: string;
54
53
  [EVENT_PARAM.CURRENT_QUERY]: string;
55
- [EVENT_PARAM.ENTITY_TYPE]: string;
56
54
  [EVENT_PARAM.INDEX]: string;
57
55
  [EVENT_PARAM.TOOL_NAME]: string;
58
56
  };
@@ -6,10 +6,20 @@ See below a list of the currently available events.
6
6
 
7
7
  ### GA4 Event Inventory
8
8
 
9
- | Event | Parameters | Description |
10
- | ------------------------- | --------------------------------------------------------------- | --------------------------------------------------------------- |
11
- | `bulk_download_requested` | `catalog`, `current_query`, `entity_type`, `index`, `tool_name` | Runs when the HCA-DCP "Request curl command" button is selected |
12
- | `entity_selected` | `entity_name` | Runs when an entity (tab) is selected |
13
- | `entity_table_paginated` | `entity_name`, `pagination_directed` | Runs when the page forward/backwards buttons are clicked |
14
- | `entity_table_sorted` | `entity_name`, `column_name`, `sort_direction` | Runs each time a column in the entity table is sorted |
15
- | `filter_selected` | `filter_name`, `filter_value` | Runs each time a filter is selected |
9
+ | Event | Parameters | Description |
10
+ | ------------------------- | ------------------------------------------------ | --------------------------------------------------------------- |
11
+ | `bulk_download_requested` | `catalog`, `current_query`, `index`, `tool_name` | Runs when the HCA-DCP "Request curl command" button is selected |
12
+ | `entity_selected` | `entity_name` | Runs when an entity (tab) is selected |
13
+ | `entity_table_paginated` | `entity_name`, `pagination_direction` | Runs when the page forward/backwards buttons are clicked |
14
+ | `entity_table_sorted` | `entity_name`, `column_name`, `sort_direction` | Runs each time a column in the entity table is sorted |
15
+ | `filter_selected` | `filter_name`, `filter_value` | Runs each time a filter is selected |
16
+
17
+ ### Tracking-related ID Inventory
18
+
19
+ Some GA4 Events are tracked using ids instead of events. Below is an inventory of the ids that, when clicked, trigger an event:
20
+
21
+ | Component | Id name | "All Elements" or "Just Links"? | Purpose |
22
+ | -------------------- | -------------------------------- | ------------------------------- | ------------------------------------------- |
23
+ | `SupportRequestForm` | `button-support-request` | "All Elements" | To track submitted support requests |
24
+ | `ExportMethod` | The value passed as `trackingId` | "All Elements" | To separately track ExportMethod events |
25
+ | `ExportButton` | `button-cohort-export` | "Just Links" | To track clicks to the Cohort Export button |
@@ -23,7 +23,6 @@ export function bulkDownloadTracking(
23
23
  track(EVENT_NAME.BULK_DOWNLOAD_REQUESTED, {
24
24
  [EVENT_PARAM.CATALOG]: catalog,
25
25
  [EVENT_PARAM.CURRENT_QUERY]: currentQuery,
26
- [EVENT_PARAM.ENTITY_TYPE]: "Bulk Download",
27
26
  [EVENT_PARAM.INDEX]: index,
28
27
  [EVENT_PARAM.TOOL_NAME]: toolName,
29
28
  });