@databiosphere/findable-ui 36.0.0 → 37.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.
Files changed (37) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +19 -0
  3. package/lib/components/DataDictionary/components/Filters/components/ColumnFilters/columnFilters.d.ts +1 -1
  4. package/lib/components/DataDictionary/components/Filters/components/ColumnFilters/columnFilters.js +3 -0
  5. package/lib/components/DataDictionary/components/Filters/stories/filters.stories.js +1 -0
  6. package/lib/components/Export/components/DownloadCurlCommand/downloadCurlCommand.d.ts +2 -1
  7. package/lib/components/Export/components/DownloadCurlCommand/downloadCurlCommand.js +3 -1
  8. package/lib/components/Export/components/ExportToTerra/exportToTerra.d.ts +2 -1
  9. package/lib/components/Export/components/ExportToTerra/exportToTerra.js +3 -1
  10. package/lib/components/Export/components/ManifestDownload/manifestDownload.d.ts +2 -1
  11. package/lib/components/Export/components/ManifestDownload/manifestDownload.js +3 -1
  12. package/lib/components/Table/components/TableCell/components/CodeCell/codeCell.d.ts +2 -1
  13. package/lib/components/Table/components/TableFeatures/GlobalFilter/globalFilter.d.ts +1 -1
  14. package/lib/components/Table/components/TableFeatures/GlobalFilter/globalFilter.js +4 -1
  15. package/lib/hooks/useFileManifest/useFileManifestFileCount.d.ts +10 -0
  16. package/lib/hooks/useFileManifest/useFileManifestFileCount.js +44 -0
  17. package/lib/hooks/useRequestManifest/utils.d.ts +21 -14
  18. package/lib/hooks/useRequestManifest/utils.js +33 -21
  19. package/lib/mocks/useRequestFileManifest.mocks.js +2 -0
  20. package/lib/providers/fileManifestState/constants.js +1 -0
  21. package/lib/providers/fileManifestState.d.ts +16 -1
  22. package/lib/providers/fileManifestState.js +6 -0
  23. package/package.json +1 -1
  24. package/src/components/DataDictionary/components/Filters/components/ColumnFilters/columnFilters.tsx +5 -1
  25. package/src/components/DataDictionary/components/Filters/stories/filters.stories.tsx +1 -0
  26. package/src/components/Export/components/DownloadCurlCommand/downloadCurlCommand.tsx +4 -0
  27. package/src/components/Export/components/ExportToTerra/exportToTerra.tsx +4 -0
  28. package/src/components/Export/components/ManifestDownload/manifestDownload.tsx +4 -0
  29. package/src/components/Table/components/TableCell/components/CodeCell/codeCell.tsx +5 -2
  30. package/src/components/Table/components/TableFeatures/GlobalFilter/globalFilter.tsx +6 -2
  31. package/src/hooks/useFileManifest/useFileManifestFileCount.ts +65 -0
  32. package/src/hooks/useRequestManifest/utils.ts +37 -24
  33. package/src/mocks/useRequestFileManifest.mocks.ts +2 -0
  34. package/src/providers/fileManifestState/constants.ts +1 -0
  35. package/src/providers/fileManifestState.tsx +23 -0
  36. package/tests/buildRequestFilters.test.ts +5 -13
  37. package/tests/useRequestManifest.test.ts +10 -0
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "36.0.0"
2
+ ".": "37.1.0"
3
3
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # Changelog
2
2
 
3
+ ## [37.1.0](https://github.com/DataBiosphere/findable-ui/compare/v37.0.0...v37.1.0) (2025-06-19)
4
+
5
+
6
+ ### Features
7
+
8
+ * hide global filtering search bar when global filters are disabled ([#547](https://github.com/DataBiosphere/findable-ui/issues/547)) ([#548](https://github.com/DataBiosphere/findable-ui/issues/548)) ([1a392ba](https://github.com/DataBiosphere/findable-ui/commit/1a392bac3707451404365989dc3187f59c181998))
9
+
10
+ ## [37.0.0](https://github.com/DataBiosphere/findable-ui/compare/v36.0.0...v37.0.0) (2025-06-19)
11
+
12
+
13
+ ### ⚠ BREAKING CHANGES
14
+
15
+ * file format filter dropped from curl manifest request ([#543](https://github.com/DataBiosphere/findable-ui/issues/543)) (#544)
16
+
17
+ ### Bug Fixes
18
+
19
+ * file format filter dropped from curl manifest request ([#543](https://github.com/DataBiosphere/findable-ui/issues/543)) ([#544](https://github.com/DataBiosphere/findable-ui/issues/544)) ([3169f5c](https://github.com/DataBiosphere/findable-ui/commit/3169f5ca961d1ca518b9820dc8878ad3d224a2f4))
20
+ * refactor CodeCell to use node prop instead of value string ([#540](https://github.com/DataBiosphere/findable-ui/issues/540)) ([#542](https://github.com/DataBiosphere/findable-ui/issues/542)) ([28b7fb8](https://github.com/DataBiosphere/findable-ui/commit/28b7fb8e8646ede29098f6a612b66d3ccef34bf3))
21
+
3
22
  ## [36.0.0](https://github.com/DataBiosphere/findable-ui/compare/v35.2.0...v36.0.0) (2025-06-18)
4
23
 
5
24
 
@@ -1,4 +1,4 @@
1
1
  import { RowData } from "@tanstack/react-table";
2
2
  import { Attribute } from "../../../../../../common/entities";
3
3
  import { ColumnFiltersProps } from "./types";
4
- export declare const ColumnFilters: <T extends RowData = Attribute>({ table, }: ColumnFiltersProps<T>) => JSX.Element;
4
+ export declare const ColumnFilters: <T extends RowData = Attribute>({ table, }: ColumnFiltersProps<T>) => JSX.Element | null;
@@ -5,5 +5,8 @@ import { ColumnFilter } from "../../../../../Table/components/TableFeatures/Colu
5
5
  export const ColumnFilters = ({ table, }) => {
6
6
  const columns = table.getAllColumns();
7
7
  const columnFilters = columns.filter((column) => column.getCanFilter());
8
+ const enableColumnFilters = table.options.enableColumnFilters;
9
+ if (!enableColumnFilters)
10
+ return null;
8
11
  return (React.createElement(ButtonGroup, { ...BUTTON_GROUP_PROPS.SECONDARY_OUTLINED }, columnFilters.map((column) => (React.createElement(ColumnFilter, { key: column.id, column: column })))));
9
12
  };
@@ -30,6 +30,7 @@ const DefaultStory = () => {
30
30
  const table = {
31
31
  getAllColumns: () => [DESCRIPTION, REQUIRED, BIONETWORK, EXAMPLE].map(makeColumn),
32
32
  getState: () => ({ globalFilter }),
33
+ options: { enableColumnFilters: true, enableGlobalFilter: true },
33
34
  setGlobalFilter,
34
35
  };
35
36
  return React.createElement(Filters, { table: table });
@@ -13,6 +13,7 @@ interface DownloadCurlCommandProps {
13
13
  filters: Filters;
14
14
  formFacet: FormFacet;
15
15
  manifestDownloadFormat?: ManifestDownloadFormat;
16
+ speciesFacetName: string;
16
17
  }
17
- export declare const DownloadCurlCommand: ({ DownloadCurlForm, DownloadCurlStart, DownloadCurlSuccess, fileManifestState, fileSummaryFacetName, filters, formFacet, manifestDownloadFormat, }: DownloadCurlCommandProps) => JSX.Element;
18
+ export declare const DownloadCurlCommand: ({ DownloadCurlForm, DownloadCurlStart, DownloadCurlSuccess, fileManifestState, fileSummaryFacetName, filters, formFacet, manifestDownloadFormat, speciesFacetName, }: DownloadCurlCommandProps) => JSX.Element;
18
19
  export {};
@@ -2,14 +2,16 @@ import React, { useState } from "react";
2
2
  import { MANIFEST_DOWNLOAD_FORMAT } from "../../../../apis/azul/common/entities";
3
3
  import { useExploreState } from "../../../../hooks/useExploreState";
4
4
  import { useFileManifest } from "../../../../hooks/useFileManifest/useFileManifest";
5
+ import { useFileManifestFileCount } from "../../../../hooks/useFileManifest/useFileManifestFileCount";
5
6
  import { useRequestFileLocation, } from "../../../../hooks/useRequestFileLocation";
6
7
  import { useRequestManifest } from "../../../../hooks/useRequestManifest/useRequestManifest";
7
8
  import { BULK_DOWNLOAD_EXECUTION_ENVIRONMENT, } from "../../common/entities";
8
9
  import { trackBulkDownloadRequested } from "../../common/tracking";
9
10
  import { DownloadCurlCommandNotStarted } from "./components/DownloadCurlCommandNotStarted/downloadCurlCommandNotStarted";
10
11
  import { DownloadCurlCommandReady } from "./components/DownloadCurlCommandReady/downloadCurlCommandReady";
11
- export const DownloadCurlCommand = ({ DownloadCurlForm, DownloadCurlStart, DownloadCurlSuccess, fileManifestState, fileSummaryFacetName, filters, formFacet, manifestDownloadFormat = MANIFEST_DOWNLOAD_FORMAT.CURL, }) => {
12
+ export const DownloadCurlCommand = ({ DownloadCurlForm, DownloadCurlStart, DownloadCurlSuccess, fileManifestState, fileSummaryFacetName, filters, formFacet, manifestDownloadFormat = MANIFEST_DOWNLOAD_FORMAT.CURL, speciesFacetName, }) => {
12
13
  useFileManifest(filters, fileSummaryFacetName);
14
+ useFileManifestFileCount(filters, speciesFacetName, fileSummaryFacetName);
13
15
  const [executionEnvironment, setExecutionEnvironment] = useState(BULK_DOWNLOAD_EXECUTION_ENVIRONMENT.BASH);
14
16
  const { exploreState: { tabValue: entityList }, } = useExploreState();
15
17
  const { requestMethod, requestUrl } = useRequestManifest(manifestDownloadFormat, formFacet);
@@ -14,5 +14,6 @@ export interface ExportToTerraProps {
14
14
  formFacet: FormFacet;
15
15
  manifestDownloadFormat?: ManifestDownloadFormat;
16
16
  manifestDownloadFormats: ManifestDownloadFormat[];
17
+ speciesFacetName: string;
17
18
  }
18
- export declare const ExportToTerra: ({ ExportForm, ExportToTerraStart, ExportToTerraSuccess, fileManifestState, fileSummaryFacetName, filters, formFacet, manifestDownloadFormat, manifestDownloadFormats, }: ExportToTerraProps) => JSX.Element;
19
+ export declare const ExportToTerra: ({ ExportForm, ExportToTerraStart, ExportToTerraSuccess, fileManifestState, fileSummaryFacetName, filters, formFacet, manifestDownloadFormat, manifestDownloadFormats, speciesFacetName, }: ExportToTerraProps) => JSX.Element;
@@ -2,15 +2,17 @@ import React from "react";
2
2
  import { useExploreState } from "../../../../hooks/useExploreState";
3
3
  import { useExportToTerraResponseURL } from "../../../../hooks/useExportToTerraResponseURL";
4
4
  import { useFileManifest } from "../../../../hooks/useFileManifest/useFileManifest";
5
+ import { useFileManifestFileCount } from "../../../../hooks/useFileManifest/useFileManifestFileCount";
5
6
  import { useFileManifestFormat } from "../../../../hooks/useFileManifest/useFileManifestFormat";
6
7
  import { useRequestFileLocation } from "../../../../hooks/useRequestFileLocation";
7
8
  import { useRequestManifest } from "../../../../hooks/useRequestManifest/useRequestManifest";
8
9
  import { trackExportToTerraRequested } from "../../common/tracking";
9
10
  import { ExportToTerraNotStarted } from "./components/ExportToTerraNotStarted/exportToTerraNotStarted";
10
11
  import { ExportToTerraReady } from "./components/ExportToTerraReady/exportToTerraReady";
11
- export const ExportToTerra = ({ ExportForm, ExportToTerraStart, ExportToTerraSuccess, fileManifestState, fileSummaryFacetName, filters, formFacet, manifestDownloadFormat, manifestDownloadFormats, }) => {
12
+ export const ExportToTerra = ({ ExportForm, ExportToTerraStart, ExportToTerraSuccess, fileManifestState, fileSummaryFacetName, filters, formFacet, manifestDownloadFormat, manifestDownloadFormats, speciesFacetName, }) => {
12
13
  const { exploreState: { tabValue: entityList }, } = useExploreState();
13
14
  useFileManifest(filters, fileSummaryFacetName);
15
+ useFileManifestFileCount(filters, speciesFacetName, fileSummaryFacetName);
14
16
  const fileManifestFormatState = useFileManifestFormat(manifestDownloadFormat);
15
17
  const { requestMethod, requestParams, requestUrl } = useRequestManifest(fileManifestFormatState.fileManifestFormat, formFacet);
16
18
  const { data, isLoading, run } = useRequestFileLocation(requestUrl, requestMethod);
@@ -13,5 +13,6 @@ export interface ManifestDownloadProps {
13
13
  manifestDownloadFormat?: ManifestDownloadFormat;
14
14
  ManifestDownloadStart: ElementType;
15
15
  ManifestDownloadSuccess: ElementType;
16
+ speciesFacetName: string;
16
17
  }
17
- export declare const ManifestDownload: ({ fileManifestState, fileSummaryFacetName, filters, formFacet, ManifestDownloadForm, manifestDownloadFormat, ManifestDownloadStart, ManifestDownloadSuccess, }: ManifestDownloadProps) => JSX.Element;
18
+ export declare const ManifestDownload: ({ fileManifestState, fileSummaryFacetName, filters, formFacet, ManifestDownloadForm, manifestDownloadFormat, ManifestDownloadStart, ManifestDownloadSuccess, speciesFacetName, }: ManifestDownloadProps) => JSX.Element;
@@ -2,13 +2,15 @@ import React from "react";
2
2
  import { MANIFEST_DOWNLOAD_FORMAT } from "../../../../apis/azul/common/entities";
3
3
  import { useExploreState } from "../../../../hooks/useExploreState";
4
4
  import { useFileManifest } from "../../../../hooks/useFileManifest/useFileManifest";
5
+ import { useFileManifestFileCount } from "../../../../hooks/useFileManifest/useFileManifestFileCount";
5
6
  import { useRequestFileLocation, } from "../../../../hooks/useRequestFileLocation";
6
7
  import { useRequestManifest } from "../../../../hooks/useRequestManifest/useRequestManifest";
7
8
  import { trackFileManifestRequested } from "../../common/tracking";
8
9
  import { ManifestDownloadNotStarted } from "./components/ManifestDownloadNotStarted/manifestDownloadNotStarted";
9
10
  import { ManifestDownloadReady } from "./components/ManifestDownloadReady/manifestDownloadReady";
10
- export const ManifestDownload = ({ fileManifestState, fileSummaryFacetName, filters, formFacet, ManifestDownloadForm, manifestDownloadFormat = MANIFEST_DOWNLOAD_FORMAT.COMPACT, ManifestDownloadStart, ManifestDownloadSuccess, }) => {
11
+ export const ManifestDownload = ({ fileManifestState, fileSummaryFacetName, filters, formFacet, ManifestDownloadForm, manifestDownloadFormat = MANIFEST_DOWNLOAD_FORMAT.COMPACT, ManifestDownloadStart, ManifestDownloadSuccess, speciesFacetName, }) => {
11
12
  useFileManifest(filters, fileSummaryFacetName);
13
+ useFileManifestFileCount(filters, speciesFacetName, fileSummaryFacetName);
12
14
  const { exploreState: { tabValue: entityList }, } = useExploreState();
13
15
  const { requestMethod, requestUrl } = useRequestManifest(manifestDownloadFormat, formFacet);
14
16
  const { data, isLoading, run } = useRequestFileLocation(requestUrl, requestMethod);
@@ -1,3 +1,4 @@
1
1
  import { CellContext, RowData } from "@tanstack/react-table";
2
+ import { ReactNode } from "react";
2
3
  import { BaseComponentProps } from "../../../../../types";
3
- export declare const CodeCell: <T extends RowData, TValue extends string = string>({ className, getValue, }: BaseComponentProps & CellContext<T, TValue>) => JSX.Element | null;
4
+ export declare const CodeCell: <T extends RowData, TValue extends ReactNode = ReactNode>({ className, getValue, }: BaseComponentProps & CellContext<T, TValue>) => JSX.Element | null;
@@ -1,3 +1,3 @@
1
1
  import { RowData } from "@tanstack/react-table";
2
2
  import { GlobalFilterProps } from "./types";
3
- export declare const GlobalFilter: <T extends RowData>({ className, table, ...props }: GlobalFilterProps<T>) => JSX.Element;
3
+ export declare const GlobalFilter: <T extends RowData>({ className, table, ...props }: GlobalFilterProps<T>) => JSX.Element | null;
@@ -4,7 +4,10 @@ import { SearchInputAdornment } from "../../../../common/OutlinedInput/component
4
4
  import { StyledOutlinedInput } from "../../../../common/OutlinedInput/outlinedInput.styles";
5
5
  import { OUTLINED_INPUT_PROPS } from "./constants";
6
6
  export const GlobalFilter = ({ className, table, ...props /* MuiOutlinedInputProps */ }) => {
7
- const { getState, setGlobalFilter } = table;
7
+ const { getState, options, setGlobalFilter } = table;
8
8
  const { globalFilter } = getState();
9
+ const { enableGlobalFilter } = options;
10
+ if (!enableGlobalFilter)
11
+ return null;
9
12
  return (React.createElement(StyledOutlinedInput, { ...OUTLINED_INPUT_PROPS, className: className, endAdornment: React.createElement(ClearInputAdornment, { in: !!globalFilter, onClick: () => setGlobalFilter(undefined) }), hasValue: !!globalFilter, onChange: (e) => setGlobalFilter(e.target.value), startAdornment: React.createElement(SearchInputAdornment, null), value: globalFilter ?? "", ...props }));
10
13
  };
@@ -0,0 +1,10 @@
1
+ import { Filters } from "../../common/entities";
2
+ /**
3
+ * Fetches the latest file count from the summary endpoint, excluding filters
4
+ * that match the provided species and file facet names, and updates the file manifest state.
5
+ *
6
+ * @param initialFilters - The initial set of filters to apply.
7
+ * @param speciesFacetName - The facet name representing species to exclude from the summary request.
8
+ * @param fileFacetName - The facet name representing file type to exclude from the summary request.
9
+ */
10
+ export declare const useFileManifestFileCount: (initialFilters: Filters | undefined, speciesFacetName: string, fileFacetName: string) => void;
@@ -0,0 +1,44 @@
1
+ import { useEffect, useState } from "react";
2
+ import { FileManifestActionKind } from "../../providers/fileManifestState";
3
+ import { useCatalog } from "../useCatalog";
4
+ import { useFileManifestState } from "../useFileManifestState";
5
+ import { useFetchSummary } from "./useFetchSummary";
6
+ /**
7
+ * Fetches the latest file count from the summary endpoint, excluding filters
8
+ * that match the provided species and file facet names, and updates the file manifest state.
9
+ *
10
+ * @param initialFilters - The initial set of filters to apply.
11
+ * @param speciesFacetName - The facet name representing species to exclude from the summary request.
12
+ * @param fileFacetName - The facet name representing file type to exclude from the summary request.
13
+ */
14
+ export const useFileManifestFileCount = (initialFilters, speciesFacetName, fileFacetName) => {
15
+ // Initial file manifest filter.
16
+ const [initFilters] = useState(() => initialFilters);
17
+ // File manifest dispatch.
18
+ const { fileManifestDispatch } = useFileManifestState();
19
+ // Determine catalog.
20
+ const catalog = useCatalog(); // catalog should be defined.
21
+ // Get filters required for fetching the summary.
22
+ const filters = excludeFacetsFromFilters(initFilters, [
23
+ speciesFacetName,
24
+ fileFacetName,
25
+ ]);
26
+ // Fetch file count from summary.
27
+ const { summary: { fileCount } = {} } = useFetchSummary(filters, catalog, true);
28
+ useEffect(() => {
29
+ fileManifestDispatch({
30
+ payload: { fileCount },
31
+ type: FileManifestActionKind.UpdateFileCount,
32
+ });
33
+ }, [fileCount, fileManifestDispatch]);
34
+ };
35
+ /**
36
+ * Returns a new filters array with the specified facet names excluded.
37
+ *
38
+ * @param filters - The list of filters to process.
39
+ * @param facetNames - The facet names to exclude from the filters.
40
+ * @returns The filters array excluding any filters matching the provided facet names.
41
+ */
42
+ function excludeFacetsFromFilters(filters, facetNames) {
43
+ return (filters || []).filter(({ categoryKey }) => !facetNames.includes(categoryKey));
44
+ }
@@ -4,18 +4,21 @@ import { FormFacet } from "../../components/Export/common/entities";
4
4
  import { FileManifestState } from "../../providers/fileManifestState";
5
5
  import { UseRequestManifest } from "./types";
6
6
  /**
7
- * Returns true if all form facets have all their terms selected.
8
- * @param formFacet - Form related file facets.
9
- * @returns true if all form facets have all their terms selected.
7
+ * Determines whether all files are selected by comparing the file count to the summary file count.
8
+ *
9
+ * @param state - The file manifest state object.
10
+ * @returns True if all files are selected; otherwise, false.
10
11
  */
11
- export declare function areAllFormFilterTermsSelected(formFacet: FormFacet): boolean;
12
+ export declare function areAllFilesSelected(state: FileManifestState): boolean;
12
13
  /**
13
- * Generates the filters for a request URL based on the file manifest state.
14
- * - **all form facets have all their terms selected** - returns filters from state without form filters.
15
- * - **at least one form facet has an unselected term** - returns filters from state.
16
- * @param state - File manifest state.
17
- * @param formFacet - Form related file facets.
18
- * @returns filters for the request URL.
14
+ * Builds the filters object for a request URL based on the file manifest state and form facets.
15
+ *
16
+ * - If all files are selected, returns filters from state excluding fully selected form filters.
17
+ * - If only some files are selected, returns the current filters from state.
18
+ *
19
+ * @param state - The file manifest state object.
20
+ * @param formFacet - The form-related file facets.
21
+ * @returns The filters to use for the request URL.
19
22
  */
20
23
  export declare function buildRequestFilters(state: FileManifestState, formFacet: FormFacet): Filters;
21
24
  /**
@@ -42,11 +45,15 @@ export declare function excludeFullySelectedFormFilters(state: FileManifestState
42
45
  */
43
46
  export declare function isCatalogReady(catalog: string | undefined): boolean;
44
47
  /**
45
- * Returns true if the file manifest state is ready for a request.
46
- * A file manifest state is considered ready if it is both enabled (`isEnabled` is `true`)
47
- * and not currently loading (`isLoading` is `false`).
48
+ * Determines if the file manifest state is ready to be used in a request.
49
+ *
50
+ * The state is considered ready when:
51
+ * - isEnabled is true
52
+ * - fileCount is defined
53
+ * - isLoading is false
54
+ *
48
55
  * @param fileManifestState - File manifest state.
49
- * @returns true if the file manifest state is ready for a request.
56
+ * @returns true if the file manifest state is ready.
50
57
  */
51
58
  export declare function isFileManifestStateReady(fileManifestState: FileManifestState): boolean;
52
59
  /**
@@ -2,29 +2,35 @@ import { AZUL_PARAM, MANIFEST_DOWNLOAD_FORMAT, } from "../../apis/azul/common/en
2
2
  import { transformFilters } from "../../apis/azul/common/filterTransformer";
3
3
  import { REQUEST_MANIFEST } from "./constants";
4
4
  /**
5
- * Returns true if all form facets have all their terms selected.
6
- * @param formFacet - Form related file facets.
7
- * @returns true if all form facets have all their terms selected.
5
+ * Determines whether all files are selected by comparing the file count to the summary file count.
6
+ *
7
+ * @param state - The file manifest state object.
8
+ * @returns True if all files are selected; otherwise, false.
8
9
  */
9
- export function areAllFormFilterTermsSelected(formFacet) {
10
- return Object.values(formFacet)
11
- .filter(Boolean)
12
- .every(({ selectedTermCount, termCount }) => selectedTermCount === termCount);
10
+ export function areAllFilesSelected(state) {
11
+ const { fileCount, summary } = state;
12
+ // Return false if file count or summary file count is undefined.
13
+ if (fileCount === undefined || summary?.fileCount === undefined)
14
+ return false;
15
+ // Return true if file count equals summary file count.
16
+ return fileCount === summary.fileCount;
13
17
  }
14
18
  /**
15
- * Generates the filters for a request URL based on the file manifest state.
16
- * - **all form facets have all their terms selected** - returns filters from state without form filters.
17
- * - **at least one form facet has an unselected term** - returns filters from state.
18
- * @param state - File manifest state.
19
- * @param formFacet - Form related file facets.
20
- * @returns filters for the request URL.
19
+ * Builds the filters object for a request URL based on the file manifest state and form facets.
20
+ *
21
+ * - If all files are selected, returns filters from state excluding fully selected form filters.
22
+ * - If only some files are selected, returns the current filters from state.
23
+ *
24
+ * @param state - The file manifest state object.
25
+ * @param formFacet - The form-related file facets.
26
+ * @returns The filters to use for the request URL.
21
27
  */
22
28
  export function buildRequestFilters(state, formFacet) {
23
- // Form terms are fully selected; return filters excluding form filters.
24
- if (areAllFormFilterTermsSelected(formFacet)) {
29
+ // Return filters from state excluding form filters if all files are selected.
30
+ if (areAllFilesSelected(state)) {
25
31
  return excludeFullySelectedFormFilters(state, formFacet);
26
32
  }
27
- // Form terms are partially selected; return filters.
33
+ // Return current filters from state.
28
34
  return state.filters;
29
35
  }
30
36
  /**
@@ -84,14 +90,20 @@ export function isCatalogReady(catalog) {
84
90
  return typeof catalog === "string";
85
91
  }
86
92
  /**
87
- * Returns true if the file manifest state is ready for a request.
88
- * A file manifest state is considered ready if it is both enabled (`isEnabled` is `true`)
89
- * and not currently loading (`isLoading` is `false`).
93
+ * Determines if the file manifest state is ready to be used in a request.
94
+ *
95
+ * The state is considered ready when:
96
+ * - isEnabled is true
97
+ * - fileCount is defined
98
+ * - isLoading is false
99
+ *
90
100
  * @param fileManifestState - File manifest state.
91
- * @returns true if the file manifest state is ready for a request.
101
+ * @returns true if the file manifest state is ready.
92
102
  */
93
103
  export function isFileManifestStateReady(fileManifestState) {
94
- return fileManifestState.isEnabled && !fileManifestState.isLoading;
104
+ return (fileManifestState.isEnabled &&
105
+ fileManifestState.fileCount !== undefined &&
106
+ !fileManifestState.isLoading);
95
107
  }
96
108
  /**
97
109
  * Checks if the file manifest format is ready for use.
@@ -59,9 +59,11 @@ export const FILTERS = {
59
59
  FORM_SUBSET: [FILTER_FILES_SUBSET, FILTER_OTHER, FILTER_SPECIES],
60
60
  };
61
61
  export const FILE_MANIFEST_STATE = {
62
+ fileCount: 10,
62
63
  filters: FILTERS.FORM_INITIAL_SET,
63
64
  isEnabled: true,
64
65
  isLoading: false,
66
+ summary: { fileCount: 10 },
65
67
  };
66
68
  export const FORM_FACET = {
67
69
  COMPLETE_SET: {
@@ -1,4 +1,5 @@
1
1
  export const FILE_MANIFEST_STATE = {
2
+ fileCount: undefined,
2
3
  fileSummary: undefined,
3
4
  fileSummaryFacetName: undefined,
4
5
  fileSummaryFilters: [],
@@ -6,6 +6,7 @@ import { FileFacet } from "../hooks/useFileManifest/common/entities";
6
6
  * File manifest state.
7
7
  */
8
8
  export type FileManifestState = {
9
+ fileCount: number | undefined;
9
10
  filesFacets: FileFacet[];
10
11
  fileSummary?: AzulSummaryResponse;
11
12
  fileSummaryFacetName?: string;
@@ -37,6 +38,7 @@ export declare function FileManifestStateProvider({ children, }: FileManifestSta
37
38
  export declare enum FileManifestActionKind {
38
39
  ClearFileManifest = "CLEAR_FILE_MANIFEST",
39
40
  FetchFileManifest = "FETCH_FILE_MANIFEST",
41
+ UpdateFileCount = "UPDATE_FILE_COUNT",
40
42
  UpdateFileManifest = "UPDATE_FILE_MANIFEST",
41
43
  UpdateFilter = "UPDATE_FILTER",
42
44
  UpdateFiltersCategory = "UPDATE_FILTERS_CATEGORY"
@@ -44,7 +46,7 @@ export declare enum FileManifestActionKind {
44
46
  /**
45
47
  * File manifest action.
46
48
  */
47
- export type FileManifestAction = ClearFileManifestAction | FetchFileManifestAction | UpdateFileManifestAction | UpdateFilterAction | UpdateFiltersCategoryAction;
49
+ export type FileManifestAction = ClearFileManifestAction | FetchFileManifestAction | UpdateFileCountAction | UpdateFileManifestAction | UpdateFilterAction | UpdateFiltersCategoryAction;
48
50
  /**
49
51
  * Resets file manifest state.
50
52
  */
@@ -66,6 +68,13 @@ type UpdateFileManifestAction = {
66
68
  payload: UpdateFileManifestPayload;
67
69
  type: FileManifestActionKind.UpdateFileManifest;
68
70
  };
71
+ /**
72
+ * Update file count action.
73
+ */
74
+ type UpdateFileCountAction = {
75
+ payload: UpdateFileCountPayload;
76
+ type: FileManifestActionKind.UpdateFileCount;
77
+ };
69
78
  /**
70
79
  * Update filter action.
71
80
  */
@@ -87,6 +96,12 @@ type FetchFileManifestPayload = {
87
96
  fileSummaryFacetName?: string;
88
97
  filters: Filters;
89
98
  };
99
+ /**
100
+ * Update file count payload.
101
+ */
102
+ export type UpdateFileCountPayload = {
103
+ fileCount: number | undefined;
104
+ };
90
105
  /**
91
106
  * Update file manifest payload.
92
107
  */
@@ -58,6 +58,7 @@ export var FileManifestActionKind;
58
58
  (function (FileManifestActionKind) {
59
59
  FileManifestActionKind["ClearFileManifest"] = "CLEAR_FILE_MANIFEST";
60
60
  FileManifestActionKind["FetchFileManifest"] = "FETCH_FILE_MANIFEST";
61
+ FileManifestActionKind["UpdateFileCount"] = "UPDATE_FILE_COUNT";
61
62
  FileManifestActionKind["UpdateFileManifest"] = "UPDATE_FILE_MANIFEST";
62
63
  FileManifestActionKind["UpdateFilter"] = "UPDATE_FILTER";
63
64
  FileManifestActionKind["UpdateFiltersCategory"] = "UPDATE_FILTERS_CATEGORY";
@@ -75,6 +76,7 @@ function fileManifestReducer(state, action) {
75
76
  case FileManifestActionKind.ClearFileManifest: {
76
77
  return {
77
78
  ...state,
79
+ fileCount: undefined,
78
80
  isEnabled: false,
79
81
  };
80
82
  }
@@ -89,6 +91,10 @@ function fileManifestReducer(state, action) {
89
91
  isEnabled: true,
90
92
  };
91
93
  }
94
+ // Updates file count.
95
+ case FileManifestActionKind.UpdateFileCount: {
96
+ return { ...state, ...payload };
97
+ }
92
98
  // Updates file manifest.
93
99
  case FileManifestActionKind.UpdateFileManifest: {
94
100
  return { ...state, ...payload };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@databiosphere/findable-ui",
3
- "version": "36.0.0",
3
+ "version": "37.1.0",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
@@ -8,9 +8,13 @@ import { ColumnFiltersProps } from "./types";
8
8
 
9
9
  export const ColumnFilters = <T extends RowData = Attribute>({
10
10
  table,
11
- }: ColumnFiltersProps<T>): JSX.Element => {
11
+ }: ColumnFiltersProps<T>): JSX.Element | null => {
12
12
  const columns = table.getAllColumns();
13
13
  const columnFilters = columns.filter((column) => column.getCanFilter());
14
+ const enableColumnFilters = table.options.enableColumnFilters;
15
+
16
+ if (!enableColumnFilters) return null;
17
+
14
18
  return (
15
19
  <ButtonGroup {...BUTTON_GROUP_PROPS.SECONDARY_OUTLINED}>
16
20
  {columnFilters.map((column) => (
@@ -43,6 +43,7 @@ const DefaultStory = (): JSX.Element => {
43
43
  getAllColumns: () =>
44
44
  [DESCRIPTION, REQUIRED, BIONETWORK, EXAMPLE].map(makeColumn),
45
45
  getState: () => ({ globalFilter }),
46
+ options: { enableColumnFilters: true, enableGlobalFilter: true },
46
47
  setGlobalFilter,
47
48
  } as Table<unknown>;
48
49
 
@@ -4,6 +4,7 @@ import { Filters } from "../../../../common/entities";
4
4
  import { useExploreState } from "../../../../hooks/useExploreState";
5
5
  import { FileManifestType } from "../../../../hooks/useFileManifest/common/entities";
6
6
  import { useFileManifest } from "../../../../hooks/useFileManifest/useFileManifest";
7
+ import { useFileManifestFileCount } from "../../../../hooks/useFileManifest/useFileManifestFileCount";
7
8
  import {
8
9
  FileLocation,
9
10
  useRequestFileLocation,
@@ -30,6 +31,7 @@ interface DownloadCurlCommandProps {
30
31
  filters: Filters; // Initializes bulk download filters.
31
32
  formFacet: FormFacet;
32
33
  manifestDownloadFormat?: ManifestDownloadFormat;
34
+ speciesFacetName: string;
33
35
  }
34
36
 
35
37
  export const DownloadCurlCommand = ({
@@ -41,8 +43,10 @@ export const DownloadCurlCommand = ({
41
43
  filters,
42
44
  formFacet,
43
45
  manifestDownloadFormat = MANIFEST_DOWNLOAD_FORMAT.CURL,
46
+ speciesFacetName,
44
47
  }: DownloadCurlCommandProps): JSX.Element => {
45
48
  useFileManifest(filters, fileSummaryFacetName);
49
+ useFileManifestFileCount(filters, speciesFacetName, fileSummaryFacetName);
46
50
  const [executionEnvironment, setExecutionEnvironment] =
47
51
  useState<ExecutionEnvironment>(BULK_DOWNLOAD_EXECUTION_ENVIRONMENT.BASH);
48
52
  const {
@@ -4,6 +4,7 @@ import { useExploreState } from "../../../../hooks/useExploreState";
4
4
  import { useExportToTerraResponseURL } from "../../../../hooks/useExportToTerraResponseURL";
5
5
  import { FileManifestType } from "../../../../hooks/useFileManifest/common/entities";
6
6
  import { useFileManifest } from "../../../../hooks/useFileManifest/useFileManifest";
7
+ import { useFileManifestFileCount } from "../../../../hooks/useFileManifest/useFileManifestFileCount";
7
8
  import { useFileManifestFormat } from "../../../../hooks/useFileManifest/useFileManifestFormat";
8
9
  import { useRequestFileLocation } from "../../../../hooks/useRequestFileLocation";
9
10
  import { useRequestManifest } from "../../../../hooks/useRequestManifest/useRequestManifest";
@@ -24,6 +25,7 @@ export interface ExportToTerraProps {
24
25
  formFacet: FormFacet;
25
26
  manifestDownloadFormat?: ManifestDownloadFormat;
26
27
  manifestDownloadFormats: ManifestDownloadFormat[];
28
+ speciesFacetName: string;
27
29
  }
28
30
 
29
31
  export const ExportToTerra = ({
@@ -36,11 +38,13 @@ export const ExportToTerra = ({
36
38
  formFacet,
37
39
  manifestDownloadFormat,
38
40
  manifestDownloadFormats,
41
+ speciesFacetName,
39
42
  }: ExportToTerraProps): JSX.Element => {
40
43
  const {
41
44
  exploreState: { tabValue: entityList },
42
45
  } = useExploreState();
43
46
  useFileManifest(filters, fileSummaryFacetName);
47
+ useFileManifestFileCount(filters, speciesFacetName, fileSummaryFacetName);
44
48
  const fileManifestFormatState = useFileManifestFormat(manifestDownloadFormat);
45
49
  const { requestMethod, requestParams, requestUrl } = useRequestManifest(
46
50
  fileManifestFormatState.fileManifestFormat,
@@ -4,6 +4,7 @@ import { Filters } from "../../../../common/entities";
4
4
  import { useExploreState } from "../../../../hooks/useExploreState";
5
5
  import { FileManifestType } from "../../../../hooks/useFileManifest/common/entities";
6
6
  import { useFileManifest } from "../../../../hooks/useFileManifest/useFileManifest";
7
+ import { useFileManifestFileCount } from "../../../../hooks/useFileManifest/useFileManifestFileCount";
7
8
  import {
8
9
  FileLocation,
9
10
  useRequestFileLocation,
@@ -25,6 +26,7 @@ export interface ManifestDownloadProps {
25
26
  manifestDownloadFormat?: ManifestDownloadFormat;
26
27
  ManifestDownloadStart: ElementType;
27
28
  ManifestDownloadSuccess: ElementType;
29
+ speciesFacetName: string;
28
30
  }
29
31
 
30
32
  export const ManifestDownload = ({
@@ -36,8 +38,10 @@ export const ManifestDownload = ({
36
38
  manifestDownloadFormat = MANIFEST_DOWNLOAD_FORMAT.COMPACT,
37
39
  ManifestDownloadStart,
38
40
  ManifestDownloadSuccess,
41
+ speciesFacetName,
39
42
  }: ManifestDownloadProps): JSX.Element => {
40
43
  useFileManifest(filters, fileSummaryFacetName);
44
+ useFileManifestFileCount(filters, speciesFacetName, fileSummaryFacetName);
41
45
  const {
42
46
  exploreState: { tabValue: entityList },
43
47
  } = useExploreState();
@@ -1,10 +1,13 @@
1
1
  import { CellContext, RowData } from "@tanstack/react-table";
2
- import React from "react";
2
+ import React, { ReactNode } from "react";
3
3
  import { CHIP_PROPS } from "../../../../../../styles/common/mui/chip";
4
4
  import { BaseComponentProps } from "../../../../../types";
5
5
  import { StyledChip } from "./codeCell.styles";
6
6
 
7
- export const CodeCell = <T extends RowData, TValue extends string = string>({
7
+ export const CodeCell = <
8
+ T extends RowData,
9
+ TValue extends ReactNode = ReactNode
10
+ >({
8
11
  className,
9
12
  getValue,
10
13
  }: BaseComponentProps & CellContext<T, TValue>): JSX.Element | null => {
@@ -10,9 +10,13 @@ export const GlobalFilter = <T extends RowData>({
10
10
  className,
11
11
  table,
12
12
  ...props /* MuiOutlinedInputProps */
13
- }: GlobalFilterProps<T>): JSX.Element => {
14
- const { getState, setGlobalFilter } = table;
13
+ }: GlobalFilterProps<T>): JSX.Element | null => {
14
+ const { getState, options, setGlobalFilter } = table;
15
15
  const { globalFilter } = getState();
16
+ const { enableGlobalFilter } = options;
17
+
18
+ if (!enableGlobalFilter) return null;
19
+
16
20
  return (
17
21
  <StyledOutlinedInput
18
22
  {...OUTLINED_INPUT_PROPS}
@@ -0,0 +1,65 @@
1
+ import { useEffect, useState } from "react";
2
+ import { Filters } from "../../common/entities";
3
+ import { FileManifestActionKind } from "../../providers/fileManifestState";
4
+ import { useCatalog } from "../useCatalog";
5
+ import { useFileManifestState } from "../useFileManifestState";
6
+ import { useFetchSummary } from "./useFetchSummary";
7
+
8
+ /**
9
+ * Fetches the latest file count from the summary endpoint, excluding filters
10
+ * that match the provided species and file facet names, and updates the file manifest state.
11
+ *
12
+ * @param initialFilters - The initial set of filters to apply.
13
+ * @param speciesFacetName - The facet name representing species to exclude from the summary request.
14
+ * @param fileFacetName - The facet name representing file type to exclude from the summary request.
15
+ */
16
+ export const useFileManifestFileCount = (
17
+ initialFilters: Filters | undefined,
18
+ speciesFacetName: string,
19
+ fileFacetName: string
20
+ ): void => {
21
+ // Initial file manifest filter.
22
+ const [initFilters] = useState(() => initialFilters);
23
+
24
+ // File manifest dispatch.
25
+ const { fileManifestDispatch } = useFileManifestState();
26
+
27
+ // Determine catalog.
28
+ const catalog = useCatalog() as string; // catalog should be defined.
29
+
30
+ // Get filters required for fetching the summary.
31
+ const filters = excludeFacetsFromFilters(initFilters, [
32
+ speciesFacetName,
33
+ fileFacetName,
34
+ ]);
35
+
36
+ // Fetch file count from summary.
37
+ const { summary: { fileCount } = {} } = useFetchSummary(
38
+ filters,
39
+ catalog,
40
+ true
41
+ );
42
+
43
+ useEffect(() => {
44
+ fileManifestDispatch({
45
+ payload: { fileCount },
46
+ type: FileManifestActionKind.UpdateFileCount,
47
+ });
48
+ }, [fileCount, fileManifestDispatch]);
49
+ };
50
+
51
+ /**
52
+ * Returns a new filters array with the specified facet names excluded.
53
+ *
54
+ * @param filters - The list of filters to process.
55
+ * @param facetNames - The facet names to exclude from the filters.
56
+ * @returns The filters array excluding any filters matching the provided facet names.
57
+ */
58
+ function excludeFacetsFromFilters(
59
+ filters: Filters | undefined,
60
+ facetNames: string[]
61
+ ): Filters {
62
+ return (filters || []).filter(
63
+ ({ categoryKey }) => !facetNames.includes(categoryKey)
64
+ );
65
+ }
@@ -12,36 +12,41 @@ import { REQUEST_MANIFEST } from "./constants";
12
12
  import { UseRequestManifest } from "./types";
13
13
 
14
14
  /**
15
- * Returns true if all form facets have all their terms selected.
16
- * @param formFacet - Form related file facets.
17
- * @returns true if all form facets have all their terms selected.
15
+ * Determines whether all files are selected by comparing the file count to the summary file count.
16
+ *
17
+ * @param state - The file manifest state object.
18
+ * @returns True if all files are selected; otherwise, false.
18
19
  */
19
- export function areAllFormFilterTermsSelected(formFacet: FormFacet): boolean {
20
- return Object.values(formFacet)
21
- .filter(Boolean)
22
- .every(
23
- ({ selectedTermCount, termCount }: FileFacet) =>
24
- selectedTermCount === termCount
25
- );
20
+ export function areAllFilesSelected(state: FileManifestState): boolean {
21
+ const { fileCount, summary } = state;
22
+
23
+ // Return false if file count or summary file count is undefined.
24
+ if (fileCount === undefined || summary?.fileCount === undefined) return false;
25
+
26
+ // Return true if file count equals summary file count.
27
+ return fileCount === summary.fileCount;
26
28
  }
27
29
 
28
30
  /**
29
- * Generates the filters for a request URL based on the file manifest state.
30
- * - **all form facets have all their terms selected** - returns filters from state without form filters.
31
- * - **at least one form facet has an unselected term** - returns filters from state.
32
- * @param state - File manifest state.
33
- * @param formFacet - Form related file facets.
34
- * @returns filters for the request URL.
31
+ * Builds the filters object for a request URL based on the file manifest state and form facets.
32
+ *
33
+ * - If all files are selected, returns filters from state excluding fully selected form filters.
34
+ * - If only some files are selected, returns the current filters from state.
35
+ *
36
+ * @param state - The file manifest state object.
37
+ * @param formFacet - The form-related file facets.
38
+ * @returns The filters to use for the request URL.
35
39
  */
36
40
  export function buildRequestFilters(
37
41
  state: FileManifestState,
38
42
  formFacet: FormFacet
39
43
  ): Filters {
40
- // Form terms are fully selected; return filters excluding form filters.
41
- if (areAllFormFilterTermsSelected(formFacet)) {
44
+ // Return filters from state excluding form filters if all files are selected.
45
+ if (areAllFilesSelected(state)) {
42
46
  return excludeFullySelectedFormFilters(state, formFacet);
43
47
  }
44
- // Form terms are partially selected; return filters.
48
+
49
+ // Return current filters from state.
45
50
  return state.filters;
46
51
  }
47
52
 
@@ -115,16 +120,24 @@ export function isCatalogReady(catalog: string | undefined): boolean {
115
120
  }
116
121
 
117
122
  /**
118
- * Returns true if the file manifest state is ready for a request.
119
- * A file manifest state is considered ready if it is both enabled (`isEnabled` is `true`)
120
- * and not currently loading (`isLoading` is `false`).
123
+ * Determines if the file manifest state is ready to be used in a request.
124
+ *
125
+ * The state is considered ready when:
126
+ * - isEnabled is true
127
+ * - fileCount is defined
128
+ * - isLoading is false
129
+ *
121
130
  * @param fileManifestState - File manifest state.
122
- * @returns true if the file manifest state is ready for a request.
131
+ * @returns true if the file manifest state is ready.
123
132
  */
124
133
  export function isFileManifestStateReady(
125
134
  fileManifestState: FileManifestState
126
135
  ): boolean {
127
- return fileManifestState.isEnabled && !fileManifestState.isLoading;
136
+ return (
137
+ fileManifestState.isEnabled &&
138
+ fileManifestState.fileCount !== undefined &&
139
+ !fileManifestState.isLoading
140
+ );
128
141
  }
129
142
 
130
143
  /**
@@ -79,9 +79,11 @@ export const FILTERS: Record<string, Filters> = {
79
79
  };
80
80
 
81
81
  export const FILE_MANIFEST_STATE = {
82
+ fileCount: 10,
82
83
  filters: FILTERS.FORM_INITIAL_SET,
83
84
  isEnabled: true,
84
85
  isLoading: false,
86
+ summary: { fileCount: 10 },
85
87
  } as FileManifestState;
86
88
 
87
89
  export const FORM_FACET: Record<string, FormFacet> = {
@@ -1,6 +1,7 @@
1
1
  import { FileManifestState } from "../fileManifestState";
2
2
 
3
3
  export const FILE_MANIFEST_STATE: FileManifestState = {
4
+ fileCount: undefined,
4
5
  fileSummary: undefined,
5
6
  fileSummaryFacetName: undefined,
6
7
  fileSummaryFilters: [],
@@ -24,6 +24,7 @@ import { FILE_MANIFEST_STATE } from "./fileManifestState/constants";
24
24
  * File manifest state.
25
25
  */
26
26
  export type FileManifestState = {
27
+ fileCount: number | undefined;
27
28
  filesFacets: FileFacet[];
28
29
  fileSummary?: AzulSummaryResponse;
29
30
  fileSummaryFacetName?: string;
@@ -133,6 +134,7 @@ export function FileManifestStateProvider({
133
134
  export enum FileManifestActionKind {
134
135
  ClearFileManifest = "CLEAR_FILE_MANIFEST",
135
136
  FetchFileManifest = "FETCH_FILE_MANIFEST",
137
+ UpdateFileCount = "UPDATE_FILE_COUNT",
136
138
  UpdateFileManifest = "UPDATE_FILE_MANIFEST",
137
139
  UpdateFilter = "UPDATE_FILTER",
138
140
  UpdateFiltersCategory = "UPDATE_FILTERS_CATEGORY",
@@ -144,6 +146,7 @@ export enum FileManifestActionKind {
144
146
  export type FileManifestAction =
145
147
  | ClearFileManifestAction
146
148
  | FetchFileManifestAction
149
+ | UpdateFileCountAction
147
150
  | UpdateFileManifestAction
148
151
  | UpdateFilterAction
149
152
  | UpdateFiltersCategoryAction;
@@ -172,6 +175,14 @@ type UpdateFileManifestAction = {
172
175
  type: FileManifestActionKind.UpdateFileManifest;
173
176
  };
174
177
 
178
+ /**
179
+ * Update file count action.
180
+ */
181
+ type UpdateFileCountAction = {
182
+ payload: UpdateFileCountPayload;
183
+ type: FileManifestActionKind.UpdateFileCount;
184
+ };
185
+
175
186
  /**
176
187
  * Update filter action.
177
188
  */
@@ -196,6 +207,13 @@ type FetchFileManifestPayload = {
196
207
  filters: Filters;
197
208
  };
198
209
 
210
+ /**
211
+ * Update file count payload.
212
+ */
213
+ export type UpdateFileCountPayload = {
214
+ fileCount: number | undefined;
215
+ };
216
+
199
217
  /**
200
218
  * Update file manifest payload.
201
219
  */
@@ -235,6 +253,7 @@ function fileManifestReducer(
235
253
  case FileManifestActionKind.ClearFileManifest: {
236
254
  return {
237
255
  ...state,
256
+ fileCount: undefined,
238
257
  isEnabled: false,
239
258
  };
240
259
  }
@@ -252,6 +271,10 @@ function fileManifestReducer(
252
271
  isEnabled: true,
253
272
  };
254
273
  }
274
+ // Updates file count.
275
+ case FileManifestActionKind.UpdateFileCount: {
276
+ return { ...state, ...payload };
277
+ }
255
278
  // Updates file manifest.
256
279
  case FileManifestActionKind.UpdateFileManifest: {
257
280
  return { ...state, ...payload };
@@ -24,26 +24,18 @@ describe("buildRequestFilters", () => {
24
24
  expect(result).toEqual(fileManifestState.filters);
25
25
  });
26
26
 
27
- test("when at least one form facet has no terms selected", () => {
28
- const fileManifestState = getFileManifestState(
29
- FILTERS.FORM_INCOMPLETE_SET
30
- );
27
+ test("when summary file count is not equal to initial file count", () => {
28
+ const fileManifestState = getFileManifestState(FILTERS.FORM_COMPLETE_SET);
31
29
  const result = buildRequestFilters(
32
- fileManifestState,
33
- FORM_FACET.INCOMPLETE_SET
30
+ { ...fileManifestState, summary: { fileCount: 9 } },
31
+ FORM_FACET.COMPLETE_SET
34
32
  );
35
33
  expect(result).toEqual(fileManifestState.filters);
36
34
  });
37
-
38
- test("when at least one form facet has an unselected term", () => {
39
- const fileManifestState = getFileManifestState(FILTERS.FORM_SUBSET);
40
- const result = buildRequestFilters(fileManifestState, FORM_FACET.SUBSET);
41
- expect(result).toEqual(fileManifestState.filters);
42
- });
43
35
  });
44
36
 
45
37
  describe("should return filters excluding form related filters", () => {
46
- test("when all form facets have all their terms selected", () => {
38
+ test("when summary file count is equal to initial file count", () => {
47
39
  const fileManifestState = getFileManifestState(FILTERS.FORM_COMPLETE_SET);
48
40
  const result = buildRequestFilters(
49
41
  fileManifestState,
@@ -109,6 +109,16 @@ describe("useRequestManifest", () => {
109
109
  });
110
110
  });
111
111
 
112
+ test("when fileManifestState fileCount is undefined", () => {
113
+ MOCK_USE_FILE_MANIFEST_STATE.mockReturnValue({
114
+ fileManifestDispatch: jest.fn(),
115
+ fileManifestState: { ...FILE_MANIFEST_STATE, fileCount: undefined },
116
+ });
117
+ testRequestManifest({
118
+ fileManifestFormat: MANIFEST_DOWNLOAD_FORMAT.VERBATIM_PFB,
119
+ });
120
+ });
121
+
112
122
  describe("form selection is not ready", () => {
113
123
  test("when a form facet is undefined", () => {
114
124
  testRequestManifest({