@trackunit/filters-filter-bar 1.3.198 → 1.3.201

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 (46) hide show
  1. package/index.cjs.js +146 -80
  2. package/index.esm.js +145 -82
  3. package/package.json +7 -7
  4. package/src/lib/FilterBar.d.ts +5 -1
  5. package/src/lib/components/FilterButtonTooltipLabel.d.ts +7 -0
  6. package/src/lib/components/FiltersMenu.d.ts +15 -3
  7. package/src/lib/components/FiltersMenuContent.d.ts +17 -0
  8. package/src/lib/components/index.d.ts +2 -0
  9. package/src/lib/hooks/useFiltersMenu.d.ts +25 -0
  10. package/src/lib/index.d.ts +1 -0
  11. package/src/lib/types/FilterTypes.d.ts +1 -1
  12. package/src/translation.d.ts +2 -2
  13. package/translation.cjs.js +1 -0
  14. package/translation.cjs10.js +1 -0
  15. package/translation.cjs11.js +1 -0
  16. package/translation.cjs12.js +1 -0
  17. package/translation.cjs13.js +1 -0
  18. package/translation.cjs14.js +1 -0
  19. package/translation.cjs15.js +1 -0
  20. package/translation.cjs16.js +1 -0
  21. package/translation.cjs17.js +1 -0
  22. package/translation.cjs2.js +1 -0
  23. package/translation.cjs3.js +1 -0
  24. package/translation.cjs4.js +1 -0
  25. package/translation.cjs5.js +1 -0
  26. package/translation.cjs6.js +1 -0
  27. package/translation.cjs7.js +1 -0
  28. package/translation.cjs8.js +1 -0
  29. package/translation.cjs9.js +1 -0
  30. package/translation.esm.js +1 -0
  31. package/translation.esm10.js +1 -0
  32. package/translation.esm11.js +1 -0
  33. package/translation.esm12.js +1 -0
  34. package/translation.esm13.js +1 -0
  35. package/translation.esm14.js +1 -0
  36. package/translation.esm15.js +1 -0
  37. package/translation.esm16.js +1 -0
  38. package/translation.esm17.js +1 -0
  39. package/translation.esm2.js +1 -0
  40. package/translation.esm3.js +1 -0
  41. package/translation.esm4.js +1 -0
  42. package/translation.esm5.js +1 -0
  43. package/translation.esm6.js +1 -0
  44. package/translation.esm7.js +1 -0
  45. package/translation.esm8.js +1 -0
  46. package/translation.esm9.js +1 -0
package/index.cjs.js CHANGED
@@ -132,6 +132,7 @@ var defaultTranslations = {
132
132
  "filtersBar.groups.ASSET": "Asset",
133
133
  "filtersBar.groups.CUSTOM_FIELDS": "Custom Fields",
134
134
  "filtersBar.groups.CUSTOMERS": "Customers",
135
+ "filtersBar.groups.FLEET_HEALTH": "Fleet Health",
135
136
  "filtersBar.groups.INTEGRATION": "Integration",
136
137
  "filtersBar.groups.METADATA": "Metadata",
137
138
  "filtersBar.groups.MY_NETWORK": "My Network",
@@ -644,6 +645,25 @@ const DefaultRadioFilter = ({ filterDefinition, filterBarActions, options, loadi
644
645
  }, value: selectedRadioId?.key || "", children: jsxRuntime.jsx(DynamicFilterList, { checked: index => filterBarActions.objectIncludesValue(filterDefinition.filterKey, res[index]?.key || ""), className: "m-1 mt-0", count: index => res[index]?.count, keyMapper: index => res[index]?.key || "", labelMapper: index => res[index]?.label || "", rowCount: res.length, showRequestMoreUseSearch: showRequestMoreUseSearch, type: "Radio" }) })) })] }));
645
646
  };
646
647
 
648
+ /**
649
+ * Tooltip label for the filter button
650
+ */
651
+ const FilterButtonTooltipLabel = ({ filterBarConfig, }) => {
652
+ const [t] = useTranslation();
653
+ switch (filterBarConfig.appliedFilterKeys().length) {
654
+ case 0:
655
+ return t("filtersBar.appliedFiltersTooltip.none");
656
+ case 1:
657
+ return filterBarConfig.appliedFilterKeys()[0]
658
+ ? t("filtersBar.appliedFiltersTooltip.singular", {
659
+ filterName: filterBarConfig.getFilterTitle(filterBarConfig.appliedFilterKeys()[0]),
660
+ })
661
+ : null; // should never happen though
662
+ default:
663
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [t("filtersBar.appliedFiltersTooltip.plural", { count: filterBarConfig.appliedFilterKeys().length }), jsxRuntime.jsx("ul", { className: "list-inside", children: filterBarConfig.appliedFilterKeys().map((appliedFilterKey, index) => (jsxRuntime.jsx("li", { className: "list-disc", children: filterBarConfig.getFilterTitle(appliedFilterKey) }, index))) })] }));
664
+ }
665
+ };
666
+
647
667
  /**
648
668
  * Returns the two first values, appends counter if more.
649
669
  *
@@ -759,6 +779,64 @@ const useGroupFilters = (filterDefinitions, hiddenFilters) => {
759
779
  };
760
780
  const uniqueKeysFromGroups = (filters) => [...new Set(filters.map(filter => filter.group))];
761
781
 
782
+ /**
783
+ * This hook is used to manage the filters menu.
784
+ * It returns the filters that should be shown in the menu, the filters that should be shown directly in the filter bar,
785
+ * and the filters that should be shown in the search results.
786
+ */
787
+ const useFiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], allowShowFiltersDirectly = true, }) => {
788
+ const hideInMenu = react.useMemo(() => {
789
+ return sharedUtils.objectValues(filterBarDefinition)
790
+ .map(filter => {
791
+ const showInFilterBar = filter.showInFilterBar ? filter.showInFilterBar() : true;
792
+ const showInStarredMenu = filter.showInStarredMenu ? filter.showInStarredMenu() : true;
793
+ const showMenuAnywayBecauseFilterHasChanged = filterBarConfig.appliedFilterKeys().includes(filter.filterKey);
794
+ return (!showInFilterBar || !showInStarredMenu) && !showMenuAnywayBecauseFilterHasChanged
795
+ ? filter.filterKey
796
+ : null;
797
+ })
798
+ .filter(sharedUtils.truthy);
799
+ }, [filterBarConfig, filterBarDefinition]);
800
+ const removeCustomFieldsGroup = react.useCallback((groupOfFilters) => groupOfFilters.filter(group => group.key !== "CUSTOM_FIELDS"), []);
801
+ const { filtersGrouped } = useGroupFilters(sharedUtils.objectValues(filterBarDefinition), [...hideInMenu, ...hiddenFilters]);
802
+ const { appliedFilters, filtersToShow, showDirectlyFilters, hasCustomFields } = react.useMemo(() => {
803
+ const allFilters = filtersGrouped.map(group => group.filters).flat();
804
+ return {
805
+ appliedFilters: allFilters.filter(filter => filterBarConfig.appliedFilterKeys().includes(filter.filterKey)),
806
+ filtersToShow: allFilters.filter(filter => !filter.showDirectly),
807
+ showDirectlyFilters: allFilters.filter(filter => filter.showDirectly && allowShowFiltersDirectly),
808
+ hasCustomFields: allFilters.some(filter => filter.group === "CUSTOM_FIELDS"),
809
+ };
810
+ }, [filterBarConfig, filtersGrouped, allowShowFiltersDirectly]);
811
+ const [searchResults, searchText, setSearchText] = reactCoreHooks.useTextSearch(filtersToShow, item => [item.title]);
812
+ const { filtersGrouped: searchResultsGrouped } = useGroupFilters(searchResults, []);
813
+ const { filtersGrouped: filtersToShowGrouped } = useGroupFilters(filtersToShow, []);
814
+ const appliedCustomFields = react.useMemo(() => appliedFilters.filter(filter => filter.group === "CUSTOM_FIELDS"), [appliedFilters]);
815
+ return react.useMemo(() => {
816
+ return {
817
+ appliedFilters,
818
+ hasCustomFields,
819
+ showDirectlyFilters,
820
+ appliedCustomFields,
821
+ searchText,
822
+ setSearchText,
823
+ filtersToShowGrouped,
824
+ searchResultsGrouped,
825
+ removeCustomFieldsGroup,
826
+ };
827
+ }, [
828
+ appliedFilters,
829
+ hasCustomFields,
830
+ showDirectlyFilters,
831
+ appliedCustomFields,
832
+ searchText,
833
+ setSearchText,
834
+ filtersToShowGrouped,
835
+ searchResultsGrouped,
836
+ removeCustomFieldsGroup,
837
+ ]);
838
+ };
839
+
762
840
  /**
763
841
  * FiltersRenderer renders an array of Filter components from filter definitions
764
842
  * It ignores hidden filters.
@@ -779,6 +857,60 @@ const FiltersRenderer = ({ filters, filterBarConfig, visualStyle, }) => {
779
857
  .map(filter => (jsxRuntime.jsx(FilterComponent, { filter: filter, filterBarActions: filterBarConfig, filterState: { values: filterBarConfig.values, setters: filterBarConfig.setters }, visualStyle: visualStyle }, `filter-${filter.filterKey}`)));
780
858
  };
781
859
 
860
+ /**
861
+ * FiltersList is a React component that displays a list of filters within a filter bar.
862
+ *
863
+ * @returns {ReactElement} - Returns the FiltersList component.
864
+ */
865
+ const GroupedFiltersList = ({ filterBarConfig, filtersGrouped, className, dataTestId = "grouped-filters-list", }) => {
866
+ return (jsxRuntime.jsx("div", { className: className, "data-testid": dataTestId, role: "menu", children: filtersGrouped.map((group, idx) => {
867
+ const isLastGroup = idx === filtersGrouped.length - 1;
868
+ return (jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(reactComponents.Text, { className: "text-secondary-400 h-7 px-3 py-2", dataTestId: `${group.key}-group-title`, size: "small", uppercase: true, weight: "bold", children: group.title }), jsxRuntime.jsx("ul", { "aria-labelledby": `${group.key}-group-title`, className: "grid", "data-testid": `${group.key}-group-list`, children: jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: group.filters, visualStyle: "list-item" }) })] }), isLastGroup ? null : jsxRuntime.jsx("div", { className: "bg-secondary-200 h-[1px] w-full", role: "separator" })] }, group.key));
869
+ }) }));
870
+ };
871
+
872
+ /**
873
+ * ResetFiltersButton is a React component that provides a button for resetting filters.
874
+ *
875
+ * @returns {ReactElement | null} The rendered ResetFiltersButton component, or null if no filters have been applied.
876
+ */
877
+ const ResetFiltersButton = ({ resetFiltersToInitialState, dataTestId, className, }) => {
878
+ const [t] = useTranslation();
879
+ return (jsxRuntime.jsxs(reactComponents.Button, { className: className, dataTestId: dataTestId ?? "reset-filters-button", onClick: () => {
880
+ resetFiltersToInitialState();
881
+ }, size: "small", variant: "ghost", children: [t("filtersBar.resetFilters"), jsxRuntime.jsx("span", { className: "sr-only", children: "Resets all applied filters" })] }));
882
+ };
883
+
884
+ /**
885
+ *
886
+ */
887
+ const FiltersMenuContent = ({ filterBarConfig, setShowCustomFilters, setSearchText, searchText, searchResultsGrouped, filtersToShowGrouped, removeCustomFieldsGroup, hasCustomFields, appliedCustomFields, showCustomFilters, }) => {
888
+ const [t] = useTranslation();
889
+ return (jsxRuntime.jsxs(reactComponents.Card, { className: "max-h-[min(600px,_calc(100dvh-32px))] w-[300px] overflow-y-hidden", dataTestId: "starred-filters-menu-popover", children: [jsxRuntime.jsxs("div", { className: " flex-col gap-1 p-1", children: [jsxRuntime.jsx(reactFormComponents.Search, { autoFocus: true, dataTestId: "starred-filters-menu-search", fieldSize: "small", id: "search-filters-list", onChange: e => setSearchText(e.currentTarget.value), onClear: () => setSearchText(""), placeholder: t("filtersBar.searchFiltersPlaceholder"), value: searchText }), jsxRuntime.jsxs("div", { className: "flex h-7 items-center justify-between gap-1 px-3", children: [jsxRuntime.jsx(reactComponents.Text, { className: "text-secondary-400", size: "small", children: jsxRuntime.jsx(FiltersAppliedCountLabel, { filterBarConfig: filterBarConfig }) }), filterBarConfig.appliedFilterKeys().length > 0 ? (jsxRuntime.jsx(ResetFiltersButton, { resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })) : null] })] }), jsxRuntime.jsx(Separator, {}), jsxRuntime.jsxs(reactComponents.CardBody, { className: "gap-1 p-1", density: "none", disableGap: true, children: [jsxRuntime.jsx(GroupedFiltersList, { className: "flex flex-col gap-1", filterBarConfig: filterBarConfig, filtersGrouped: searchText
890
+ ? searchResultsGrouped
891
+ : showCustomFilters
892
+ ? filtersToShowGrouped
893
+ : removeCustomFieldsGroup(filtersToShowGrouped) }), hasCustomFields && !showCustomFilters && !searchText ? (jsxRuntime.jsx(CustomFieldsHiddenGroup, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, onShow: () => {
894
+ setShowCustomFilters(true);
895
+ } })) : null] })] }));
896
+ };
897
+ const Separator = () => jsxRuntime.jsx("hr", { className: "border-secondary-200", role: "separator" });
898
+ const FiltersAppliedCountLabel = ({ filterBarConfig, }) => {
899
+ const [t] = useTranslation();
900
+ switch (filterBarConfig.appliedFilterKeys().length) {
901
+ case 0:
902
+ return t("filtersBar.appliedFiltersTooltip.none");
903
+ case 1:
904
+ return filterBarConfig.appliedFilterKeys()[0] ? t("filtersMenu.appliedFiltersLabel.singular") : null;
905
+ default:
906
+ return jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("filtersMenu.appliedFiltersLabel.plural", { count: filterBarConfig.appliedFilterKeys().length }) });
907
+ }
908
+ };
909
+ const CustomFieldsHiddenGroup = ({ appliedCustomFields, filterBarConfig, onShow, }) => {
910
+ const [t] = useTranslation();
911
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Separator, {}), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(reactComponents.Button, { "aria-controls": "filters-list", className: "text-primary-600 w-full justify-between px-3", onClick: onShow, prefix: jsxRuntime.jsx(reactComponents.Text, { className: "text-secondary-400", size: "small", uppercase: true, weight: "bold", children: t("filtersBar.groups.CUSTOM_FIELDS") }), size: "small", variant: "ghost-neutral", children: t("filtersBar.showAll") }), appliedCustomFields.length > 0 ? (jsxRuntime.jsx("ul", { "aria-label": "Visible custom fields", "data-testid": "applied-custom-fields-list", children: jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: appliedCustomFields, visualStyle: "list-item" }) })) : null] })] }));
912
+ };
913
+
782
914
  /**
783
915
  * TooltipValues component that displays formatted tooltip values based on the provided input.
784
916
  *
@@ -872,100 +1004,31 @@ const MultipleFilterTooltipLabel = ({ filterBarConfig, filterKeys, }) => {
872
1004
  }
873
1005
  };
874
1006
 
875
- /**
876
- * FiltersList is a React component that displays a list of filters within a filter bar.
877
- *
878
- * @returns {ReactElement} - Returns the FiltersList component.
879
- */
880
- const GroupedFiltersList = ({ filterBarConfig, filtersGrouped, className, dataTestId = "grouped-filters-list", }) => {
881
- return (jsxRuntime.jsx("div", { className: className, "data-testid": dataTestId, role: "menu", children: filtersGrouped.map((group, idx) => {
882
- const isLastGroup = idx === filtersGrouped.length - 1;
883
- return (jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(reactComponents.Text, { className: "text-secondary-400 h-7 px-3 py-2", dataTestId: `${group.key}-group-title`, size: "small", uppercase: true, weight: "bold", children: group.title }), jsxRuntime.jsx("ul", { "aria-labelledby": `${group.key}-group-title`, className: "grid", "data-testid": `${group.key}-group-list`, children: jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: group.filters, visualStyle: "list-item" }) })] }), isLastGroup ? null : jsxRuntime.jsx("div", { className: "bg-secondary-200 h-[1px] w-full", role: "separator" })] }, group.key));
884
- }) }));
885
- };
886
-
887
- /**
888
- * ResetFiltersButton is a React component that provides a button for resetting filters.
889
- *
890
- * @returns {ReactElement | null} The rendered ResetFiltersButton component, or null if no filters have been applied.
891
- */
892
- const ResetFiltersButton = ({ resetFiltersToInitialState, dataTestId, className, }) => {
893
- const [t] = useTranslation();
894
- return (jsxRuntime.jsxs(reactComponents.Button, { className: className, dataTestId: dataTestId ?? "reset-filters-button", onClick: () => {
895
- resetFiltersToInitialState();
896
- }, size: "small", variant: "ghost", children: [t("filtersBar.resetFilters"), jsxRuntime.jsx("span", { className: "sr-only", children: "Resets all applied filters" })] }));
897
- };
898
-
899
1007
  /**
900
1008
  * FilterMenu is a React component that displays a list of filters in a popover menu based on the provided filter bar configuration.
901
1009
  *
902
1010
  * @template TFilterBarDefinition - The type representing the filter bar definition.
903
1011
  * @returns {ReactElement} - Returns the FilterMenu component.
904
1012
  */
905
- const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact, title, dataTestId = "filters-menu", className, }) => {
1013
+ const FiltersMenu = ({ filterBarDefinition, filterBarConfig, hiddenFilters = [], compact, title, dataTestId = "filters-menu", className, showAppliedFiltersCount = true, buttonProps, allowShowFiltersDirectly = true, }) => {
906
1014
  const [t] = useTranslation();
907
1015
  const { isSm } = reactComponents.useViewportBreakpoints();
908
1016
  const [showCustomFilters, setShowCustomFilters] = react.useState(false);
909
- // TODO: Add analytics if event requirements are defined
910
- // const { logEvent } = useAnalytics(FilterEvents);
911
- const hideInMenu = react.useMemo(() => {
912
- return sharedUtils.objectValues(filterBarDefinition)
913
- .map(filter => {
914
- const showInFilterBar = filter.showInFilterBar ? filter.showInFilterBar() : true;
915
- const showInStarredMenu = filter.showInStarredMenu ? filter.showInStarredMenu() : true; // TODO: Starred menu concept should be completely removed everywhere
916
- const showMenuAnywayBecauseFilterHasChanged = filterBarConfig.appliedFilterKeys().includes(filter.filterKey);
917
- return (!showInFilterBar || !showInStarredMenu) && !showMenuAnywayBecauseFilterHasChanged
918
- ? filter.filterKey
919
- : null;
920
- })
921
- .filter(sharedUtils.truthy);
922
- }, [filterBarConfig, filterBarDefinition]);
923
- const { filtersGrouped } = useGroupFilters(sharedUtils.objectValues(filterBarDefinition), [...hideInMenu, ...hiddenFilters]);
924
- const { appliedFilters, filtersToShow, showDirectlyFilters, hasCustomFields } = react.useMemo(() => {
925
- const allFilters = filtersGrouped.map(group => group.filters).flat();
926
- return {
927
- appliedFilters: allFilters.filter(filter => filterBarConfig.appliedFilterKeys().includes(filter.filterKey)),
928
- filtersToShow: allFilters.filter(filter => !filter.showDirectly),
929
- showDirectlyFilters: allFilters.filter(filter => filter.showDirectly),
930
- hasCustomFields: allFilters.some(filter => filter.group === "CUSTOM_FIELDS"),
931
- };
932
- }, [filterBarConfig, filtersGrouped]);
933
- const [searchResults, searchText, setSearchText] = reactCoreHooks.useTextSearch(filtersToShow, item => [item.title]);
934
- const { filtersGrouped: searchResultsGrouped } = useGroupFilters(searchResults, []);
935
- const { filtersGrouped: filtersToShowGrouped } = useGroupFilters(filtersToShow, []);
936
- const appliedCustomFields = react.useMemo(() => appliedFilters.filter(filter => filter.group === "CUSTOM_FIELDS"), [appliedFilters]);
1017
+ const { appliedFilters, showDirectlyFilters, hasCustomFields, filtersToShowGrouped, searchResultsGrouped, searchText, appliedCustomFields, removeCustomFieldsGroup, setSearchText, } = useFiltersMenu({
1018
+ filterBarDefinition,
1019
+ filterBarConfig,
1020
+ hiddenFilters,
1021
+ allowShowFiltersDirectly,
1022
+ });
937
1023
  return (jsxRuntime.jsxs("div", { className: tailwindMerge.twMerge("flex items-center gap-2", className), "data-testid": dataTestId, children: [jsxRuntime.jsx(reactComponents.Popover, { onOpenStateChange: open => {
938
1024
  if (!open) {
939
1025
  setShowCustomFilters(false);
940
1026
  setSearchText("");
941
1027
  }
942
1028
  }, placement: "bottom-start", children: modalState => {
943
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx("div", { "data-testid": "starred-filters-menu-trigger", id: "starred-filters-menu-trigger", children: jsxRuntime.jsx(reactComponents.Tooltip, { disabled: !compact || modalState.isOpen, label: jsxRuntime.jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig }), children: jsxRuntime.jsx(reactComponents.Button, { prefix: jsxRuntime.jsx(reactComponents.Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Filter", size: "small" }), size: "small", suffix: compact && filterBarConfig.appliedFilterKeys().length > 0 && isSm ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("span", { "aria-hidden": true, children: ["(", filterBarConfig.appliedFilterKeys().length, ")"] }), jsxRuntime.jsxs("span", { className: "sr-only", children: [filterBarConfig.appliedFilterKeys().length, " filters applied"] })] })) : undefined, variant: "secondary", children: jsxRuntime.jsx("span", { className: "hidden sm:block", children: title ?? t("filtersBar.filtersHeading") }) }) }) }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { cellPadding: 100, children: jsxRuntime.jsxs(reactComponents.Card, { className: "max-h-[min(600px,_calc(100dvh-32px))] w-[300px] overflow-y-hidden", dataTestId: "starred-filters-menu-popover", children: [jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 p-1", children: [jsxRuntime.jsx(reactFormComponents.Search, { autoFocus: true, dataTestId: "starred-filters-menu-search", fieldSize: "small", id: "search-filters-list", onChange: e => setSearchText(e.currentTarget.value), onClear: () => setSearchText(""), placeholder: t("filtersBar.searchFiltersPlaceholder"), value: searchText }), jsxRuntime.jsxs("div", { className: "flex h-7 items-center justify-between gap-1 px-3", children: [jsxRuntime.jsx(reactComponents.Text, { className: "text-secondary-400", size: "small", children: jsxRuntime.jsx(FiltersAppliedCountLabel, { filterBarConfig: filterBarConfig }) }), filterBarConfig.appliedFilterKeys().length > 0 ? (jsxRuntime.jsx(ResetFiltersButton, { resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })) : null] })] }), jsxRuntime.jsx(Separator, {}), jsxRuntime.jsxs(reactComponents.CardBody, { className: "gap-1 p-1", density: "none", disableGap: true, children: [jsxRuntime.jsx(GroupedFiltersList, { className: "flex flex-col gap-1", filterBarConfig: filterBarConfig, filtersGrouped: searchText
944
- ? searchResultsGrouped
945
- : showCustomFilters
946
- ? filtersToShowGrouped
947
- : removeCustomFields(filtersToShowGrouped) }), hasCustomFields && !showCustomFilters && !searchText ? (jsxRuntime.jsx(CustomFieldsHiddenGroup, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, onShow: () => {
948
- setShowCustomFilters(true);
949
- } })) : null] })] }) })] }));
1029
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(reactComponents.PopoverTrigger, { children: jsxRuntime.jsx("div", { "data-testid": "starred-filters-menu-trigger", id: "starred-filters-menu-trigger", children: jsxRuntime.jsx(reactComponents.Tooltip, { disabled: !compact || modalState.isOpen, label: jsxRuntime.jsx(MultipleFilterTooltipLabel, { filterBarConfig: filterBarConfig }), children: jsxRuntime.jsx(reactComponents.Button, { prefix: jsxRuntime.jsx(reactComponents.Icon, { ariaHidden: true, color: filterBarConfig.appliedFilterKeys().length > 0 ? "primary" : undefined, name: "Filter", size: "small" }), size: "small", suffix: compact && showAppliedFiltersCount && filterBarConfig.appliedFilterKeys().length > 0 && isSm ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("span", { "aria-hidden": true, children: ["(", filterBarConfig.appliedFilterKeys().length, ")"] }), jsxRuntime.jsxs("span", { className: "sr-only", children: [filterBarConfig.appliedFilterKeys().length, " filters applied"] })] })) : undefined, variant: "secondary", ...buttonProps, children: title !== "" ? (jsxRuntime.jsx("span", { className: "hidden sm:block", children: title ?? t("filtersBar.filtersHeading") })) : null }) }) }) }), jsxRuntime.jsx(reactComponents.PopoverContent, { cellPadding: 100, children: jsxRuntime.jsx(FiltersMenuContent, { appliedCustomFields: appliedCustomFields, filterBarConfig: filterBarConfig, filtersToShowGrouped: filtersToShowGrouped, hasCustomFields: hasCustomFields, removeCustomFieldsGroup: removeCustomFieldsGroup, searchResultsGrouped: searchResultsGrouped, searchText: searchText, setSearchText: setSearchText, setShowCustomFilters: setShowCustomFilters, showCustomFilters: showCustomFilters }) })] }));
950
1030
  } }), showDirectlyFilters.length > 0 ? (jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: showDirectlyFilters })) : null, !compact ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [appliedFilters.filter(filter => !filter.showDirectly).length > 0 ? (jsxRuntime.jsx("div", { className: "h-4 w-[1px] bg-slate-300", "data-testid": "applied-filters-buttons" })) : null, jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: appliedFilters }), filterBarConfig.appliedFilterKeys().length > 0 ? (jsxRuntime.jsx(ResetFiltersButton, { resetFiltersToInitialState: filterBarConfig.resetFiltersToInitialState })) : null] })) : null] }));
951
1031
  };
952
- const Separator = () => jsxRuntime.jsx("hr", { className: "border-secondary-200", role: "separator" });
953
- const FiltersAppliedCountLabel = ({ filterBarConfig, }) => {
954
- const [t] = useTranslation();
955
- switch (filterBarConfig.appliedFilterKeys().length) {
956
- case 0:
957
- return t("filtersBar.appliedFiltersTooltip.none");
958
- case 1:
959
- return filterBarConfig.appliedFilterKeys()[0] ? t("filtersMenu.appliedFiltersLabel.singular") : null;
960
- default:
961
- return jsxRuntime.jsx(jsxRuntime.Fragment, { children: t("filtersMenu.appliedFiltersLabel.plural", { count: filterBarConfig.appliedFilterKeys().length }) });
962
- }
963
- };
964
- const CustomFieldsHiddenGroup = ({ appliedCustomFields, filterBarConfig, onShow, }) => {
965
- const [t] = useTranslation();
966
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(Separator, {}), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx(reactComponents.Button, { "aria-controls": "filters-list", className: "text-primary-600 w-full justify-between px-3", onClick: onShow, prefix: jsxRuntime.jsx(reactComponents.Text, { className: "text-secondary-400", size: "small", uppercase: true, weight: "bold", children: t("filtersBar.groups.CUSTOM_FIELDS") }), size: "small", variant: "ghost-neutral", children: t("filtersBar.showAll") }), appliedCustomFields.length > 0 ? (jsxRuntime.jsx("ul", { "aria-label": "Visible custom fields", "data-testid": "applied-custom-fields-list", children: jsxRuntime.jsx(FiltersRenderer, { filterBarConfig: filterBarConfig, filters: appliedCustomFields, visualStyle: "list-item" }) })) : null] })] }));
967
- };
968
- const removeCustomFields = (filtersGrouped) => filtersGrouped.filter(group => group.key !== "CUSTOM_FIELDS");
969
1032
 
970
1033
  /**
971
1034
  * Filter is a React component that renders a filter element based on the provided filter definition and state.
@@ -1224,8 +1287,8 @@ const HierarchicalCheckboxFilter = ({ filterDefinition, filterBarActions, option
1224
1287
  /**
1225
1288
  * The FilterBar component serves as a wrapper for managing filters.
1226
1289
  */
1227
- const FilterBar = ({ hiddenFilters, className, filterBarDefinition, filterBarConfig, compact = true, title, }) => {
1228
- return (jsxRuntime.jsx(FiltersMenu, { className: className, compact: compact, dataTestId: `${filterBarConfig.name}-filterbar`, filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters, title: title }));
1290
+ const FilterBar = ({ hiddenFilters, className, filterBarDefinition, filterBarConfig, compact = true, title, allowShowFiltersDirectly = true, }) => {
1291
+ return (jsxRuntime.jsx(FiltersMenu, { allowShowFiltersDirectly: allowShowFiltersDirectly, className: className, compact: compact, dataTestId: `${filterBarConfig.name}-filterbar`, filterBarConfig: filterBarConfig, filterBarDefinition: filterBarDefinition, hiddenFilters: hiddenFilters, title: title }));
1229
1292
  };
1230
1293
 
1231
1294
  // Can't import jest.fn so must define a function that does nothing but mimics the jest.fn
@@ -1887,8 +1950,8 @@ const useFilterBar = ({ name, onValuesChange, filterBarDefinition, }) => {
1887
1950
  setValue: (key, callback) => setValue(setFilterBarConfig, key, callback),
1888
1951
  });
1889
1952
  react.useEffect(() => {
1890
- onValuesChange?.(filterBarConfig.values);
1891
1953
  saveData(filterBarConfig, filterBarDefinition);
1954
+ onValuesChange?.(filterBarConfig.values);
1892
1955
  }, [filterBarConfig, filterBarDefinition, onValuesChange, saveData]);
1893
1956
  return react.useMemo(() => {
1894
1957
  return {
@@ -2047,12 +2110,14 @@ exports.DefaultMinMaxFilter = DefaultMinMaxFilter;
2047
2110
  exports.DefaultRadioFilter = DefaultRadioFilter;
2048
2111
  exports.DynamicFilterList = DynamicFilterList;
2049
2112
  exports.FilterBar = FilterBar;
2113
+ exports.FilterButtonTooltipLabel = FilterButtonTooltipLabel;
2050
2114
  exports.FilterComponent = FilterComponent;
2051
2115
  exports.FilterEvents = FilterEvents;
2052
2116
  exports.FilterHeader = FilterHeader;
2053
2117
  exports.FilterResults = FilterResults;
2054
2118
  exports.FilterTableComponent = FilterTableComponent;
2055
2119
  exports.FiltersMenu = FiltersMenu;
2120
+ exports.FiltersMenuContent = FiltersMenuContent;
2056
2121
  exports.FiltersRenderer = FiltersRenderer;
2057
2122
  exports.GroupedFiltersList = GroupedFiltersList;
2058
2123
  exports.HierarchicalCheckboxFilter = HierarchicalCheckboxFilter;
@@ -2071,5 +2136,6 @@ exports.mockFilterBar = mockFilterBar;
2071
2136
  exports.toggleFilterValue = toggleFilterValue;
2072
2137
  exports.useFilterBar = useFilterBar;
2073
2138
  exports.useFilterBarAsync = useFilterBarAsync;
2139
+ exports.useFiltersMenu = useFiltersMenu;
2074
2140
  exports.useSearchParamAsFilter = useSearchParamAsFilter;
2075
2141
  exports.validateFilter = validateFilter;