@abcagency/hc-ui-components 1.6.6 → 1.6.7

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 (35) hide show
  1. package/dist/components/HireControlMap.js +43 -2
  2. package/dist/components/HireControlMap.js.map +1 -1
  3. package/dist/components/containers/accordions/filter-item-container.js +3 -1
  4. package/dist/components/containers/accordions/filter-item-container.js.map +1 -1
  5. package/dist/components/containers/filter/filter-item-container.js +29 -15
  6. package/dist/components/containers/filter/filter-item-container.js.map +1 -1
  7. package/dist/components/modules/accordions/filterItem.js +8 -3
  8. package/dist/components/modules/accordions/filterItem.js.map +1 -1
  9. package/dist/components/modules/filter/index.js +87 -20
  10. package/dist/components/modules/filter/index.js.map +1 -1
  11. package/dist/components/modules/filter/item.js +8 -3
  12. package/dist/components/modules/filter/item.js.map +1 -1
  13. package/dist/components/modules/filter/search.js +6 -1
  14. package/dist/components/modules/filter/search.js.map +1 -1
  15. package/dist/contexts/mapListContext.js +48 -3
  16. package/dist/contexts/mapListContext.js.map +1 -1
  17. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +51 -1
  18. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
  19. package/dist/styles/index.css +1 -1
  20. package/dist/types/util/filterUtil.d.ts +9 -3
  21. package/dist/util/filterUtil.js +77 -14
  22. package/dist/util/filterUtil.js.map +1 -1
  23. package/dist/util/twMerge.js +9 -0
  24. package/dist/util/twMerge.js.map +1 -0
  25. package/package.json +1 -1
  26. package/src/components/HireControlMap.js +48 -2
  27. package/src/components/containers/accordions/filter-item-container.js +1 -1
  28. package/src/components/containers/filter/filter-item-container.js +27 -14
  29. package/src/components/modules/accordions/filterItem.js +16 -3
  30. package/src/components/modules/filter/index.js +114 -41
  31. package/src/components/modules/filter/item.js +15 -3
  32. package/src/components/modules/filter/search.js +7 -1
  33. package/src/contexts/mapListContext.tsx +96 -3
  34. package/src/util/filterUtil.js +63 -17
  35. package/src/util/twMerge.js +6 -0
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react';
2
2
  import { useTrackEvent } from '~/contexts/trackEventContext';
3
3
  import FilterItem from '~/components/modules/filter/item';
4
4
  import { useMapList } from '~/contexts/mapListContext';
5
+ import { twMerge } from '~/util/twMerge';
5
6
 
6
7
  const FilterItemContainer = ({
7
8
  className,
@@ -17,18 +18,37 @@ const FilterItemContainer = ({
17
18
  subcategoryRequireCategory = false,
18
19
  ...rest
19
20
  }) => {
21
+ // All hooks MUST be called before any early returns
20
22
  const { trackEvent, eventTypes } = useTrackEvent();
21
- const itemName = item.name ? item.name : item;
22
- itemKey = itemKey === null ? itemName : itemKey;
23
- const isActive = selectedFilters != undefined && !!selectedFilters[field]?.[itemKey];
23
+ const { filteredListings, filterConfig } = useMapList();
24
+
25
+ // Initialize itemName and itemKey safely
26
+ const itemName = item?.name ? item.name : item;
27
+ const safeItemKey = itemKey === null ? itemName : itemKey;
28
+ const isActive = selectedFilters != undefined && !!selectedFilters[field]?.[safeItemKey];
24
29
  const [activeItem, setActiveItem] = useState(isActive);
25
30
 
26
31
  useEffect(() => {
27
32
  if (!selectedFilters || isExternalLink) return;
28
- setActiveItem(!!selectedFilters[field]?.[itemKey]);
29
- }, [selectedFilters, field, itemKey]);
33
+ setActiveItem(!!selectedFilters[field]?.[safeItemKey]);
34
+ }, [selectedFilters, field, safeItemKey, isExternalLink]);
35
+
36
+ useEffect(() => {
37
+ if (selectedFilters && selectedFilters[field] && Object.keys(selectedFilters[field])?.length > 0) return;
38
+ else if (activeItem === true) {
39
+ setActiveItem(false);
40
+ }
41
+ }, [selectedFilters, activeItem, field]);
42
+
43
+ // NOW we can do the guard check after all hooks
44
+ if (!item) {
45
+ console.warn('FilterItemContainer received undefined item for field:', field);
46
+ return null;
47
+ }
48
+
49
+ // Re-assign itemKey for the rest of the component
50
+ itemKey = safeItemKey;
30
51
 
31
- const { filteredListings } = useMapList();
32
52
  const changeHandler = () => {
33
53
  if (!isActive || isExternalLink) {
34
54
  trackEvent(eventTypes.FILTER_APPLIED, { filterType: field, filterChecked: itemKey });
@@ -87,16 +107,9 @@ const FilterItemContainer = ({
87
107
  });
88
108
  };
89
109
 
90
- useEffect(() => {
91
- if (selectedFilters && selectedFilters[field] && Object.keys(selectedFilters[field])?.length > 0) return;
92
- else if (activeItem === true) {
93
- setActiveItem(false);
94
- }
95
- }, [selectedFilters]);
96
-
97
110
  return (
98
111
  <FilterItem
99
- className={className}
112
+ className={twMerge(className, filterConfig?.classNames?.filterItem)}
100
113
  item={item}
101
114
  type={type}
102
115
  itemKey={itemKey}
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
2
  import Accordion from '~/components/modules/accordions/default';
3
+ import { twMerge } from '~/util/twMerge';
4
+ import { useMapList } from '~/contexts/mapListContext';
3
5
 
4
6
  const AccordionFilterItem = ({
5
7
  id,
@@ -7,17 +9,28 @@ const AccordionFilterItem = ({
7
9
  header,
8
10
  body
9
11
  }) => {
12
+ const { filterConfig } = useMapList();
13
+
10
14
  return (
11
15
  <Accordion.Item key={id} id={id}>
12
16
  <Accordion.Trigger.HasHeader
13
17
  onClick={() => setDefaultValue(id)}
14
- className="hc-stretched-link hc-text-left"
18
+ className={twMerge(
19
+ "hc-stretched-link hc-text-left",
20
+ filterConfig?.classNames?.filterAccordion
21
+ )}
15
22
  iconClassName="hc-order-last"
16
- headerClassName="hc-relative hc-py-2 hc-rounded hc-border hc-border-uiAccent/20 hc-bg-white hc-text-sm hc-transition data-[state=open]:hc-border-b-transparent data-[state=open]:hc-rounded-b-none"
23
+ headerClassName={twMerge(
24
+ "hc-relative hc-py-2 hc-rounded hc-border hc-border-uiAccent/20 hc-bg-white hc-text-sm hc-transition data-[state=open]:hc-border-b-transparent data-[state=open]:hc-rounded-b-none",
25
+ filterConfig?.classNames?.filterAccordionHeader
26
+ )}
17
27
  >
18
28
  {header}
19
29
  </Accordion.Trigger.HasHeader>
20
- <Accordion.Content bodyClassName="hc-px-2 hc-py-1 hc-bg-white hc-rounded-b hc-border hc-border-uiAccent/20 hc-border-t-0 hc-max-h-[20vh] md:hc-max-h-[25vh] hc-overflow-auto">
30
+ <Accordion.Content bodyClassName={twMerge(
31
+ "hc-px-2 hc-py-1 hc-bg-white hc-rounded-b hc-border hc-border-uiAccent/20 hc-border-t-0 hc-max-h-[20vh] md:hc-max-h-[25vh] hc-overflow-auto",
32
+ filterConfig?.classNames?.filterAccordionContent
33
+ )}>
21
34
  {body}
22
35
  </Accordion.Content>
23
36
  </Accordion.Item>
@@ -1,6 +1,9 @@
1
1
  "use client";
2
2
  import Button from "~/components/modules/buttons/default";
3
3
  import React, { useEffect, useRef, useState } from "react";
4
+ import { twMerge } from '~/util/twMerge';
5
+ import { useMapList } from "~/contexts/mapListContext";
6
+
4
7
  const Filter = ({
5
8
  className,
6
9
  hasActiveFilters,
@@ -12,36 +15,83 @@ const Filter = ({
12
15
  style,
13
16
  children
14
17
  }) => {
18
+ const { filterConfig } = useMapList();
15
19
  const contentRef = useRef(null);
20
+ const containerRef = useRef(null);
16
21
  const [hasOverflow, setHasOverflow] = useState(false);
22
+ const [showFixedFooter, setShowFixedFooter] = useState(false);
23
+ const transitionTimeoutRef = useRef(null);
17
24
 
18
25
  useEffect(() => {
19
26
  const checkOverflow = () => {
20
27
  if (contentRef.current) {
21
- const hasScroll = contentRef.current.scrollHeight > contentRef.current.clientHeight;
22
- setHasOverflow(hasScroll);
28
+ // Add a small tolerance (5px) to avoid false positives from rounding/padding
29
+ const hasScroll = contentRef.current.scrollHeight > contentRef.current.clientHeight + 5;
30
+
31
+ if (hasScroll !== hasOverflow) {
32
+ setHasOverflow(hasScroll);
33
+
34
+ // Clear any pending transitions
35
+ if (transitionTimeoutRef.current) {
36
+ clearTimeout(transitionTimeoutRef.current);
37
+ }
38
+
39
+ // Add a delay when transitioning to fixed footer to smooth the transition
40
+ if (hasScroll) {
41
+ transitionTimeoutRef.current = setTimeout(() => setShowFixedFooter(true), 200);
42
+ } else {
43
+ // Delay hiding fixed footer to allow animation to complete and prevent flashing
44
+ transitionTimeoutRef.current = setTimeout(() => setShowFixedFooter(false), 200);
45
+ }
46
+ }
23
47
  }
24
48
  };
25
49
 
26
- checkOverflow();
50
+ // Delay initial check to ensure layout is complete
51
+ setTimeout(checkOverflow, 100);
52
+
53
+ // Check on resize
27
54
  window.addEventListener('resize', checkOverflow);
28
-
55
+
56
+ // Use ResizeObserver to detect container size changes
57
+ const resizeObserver = new ResizeObserver(() => {
58
+ setTimeout(checkOverflow, 50);
59
+ });
60
+ if (containerRef.current) {
61
+ resizeObserver.observe(containerRef.current);
62
+ }
63
+ if (contentRef.current) {
64
+ resizeObserver.observe(contentRef.current);
65
+ }
66
+
29
67
  // Use MutationObserver to detect content changes
30
- const observer = new MutationObserver(checkOverflow);
68
+ const mutationObserver = new MutationObserver(() => {
69
+ setTimeout(checkOverflow, 50);
70
+ });
31
71
  if (contentRef.current) {
32
- observer.observe(contentRef.current, { childList: true, subtree: true });
72
+ mutationObserver.observe(contentRef.current, { childList: true, subtree: true, attributes: true });
33
73
  }
34
74
 
35
75
  return () => {
36
76
  window.removeEventListener('resize', checkOverflow);
37
- observer.disconnect();
77
+ resizeObserver.disconnect();
78
+ mutationObserver.disconnect();
79
+ // Clear timeout on unmount
80
+ if (transitionTimeoutRef.current) {
81
+ clearTimeout(transitionTimeoutRef.current);
82
+ }
38
83
  };
39
- }, [children]);
84
+ }, [children, hasOverflow]);
40
85
 
41
86
  // Reusable button component
42
87
  const MobileButtons = () => (
43
88
  <>
44
- <Button.Btn onClick={handleReset} variant="outline" size="sm">
89
+ <Button.Btn
90
+ onClick={handleReset}
91
+ variant="outline"
92
+ size="sm"
93
+ className={filterConfig?.classNames?.resetButton}
94
+ >
45
95
  Reset All
46
96
  </Button.Btn>
47
97
  {selectedFilters && Object.keys(selectedFilters).length > 0 && (
@@ -49,9 +99,10 @@ const Filter = ({
49
99
  onClick={() => setMobileTab("listTab")}
50
100
  variant="primary"
51
101
  size="sm"
52
- className={`
53
- ${hasActiveFilters ? "hc-opacity-0 hc-pointer-events-none" : "hc-opacity-100"}
54
- `}
102
+ className={twMerge(
103
+ `${hasActiveFilters ? "hc-opacity-0 hc-pointer-events-none" : "hc-opacity-100"}`,
104
+ filterConfig?.classNames?.showJobsButton
105
+ )}
55
106
  >
56
107
  <Button.Body>
57
108
  <Button.Icon icon="fluent:search-12-filled" size="hc-size-3.5" />
@@ -64,58 +115,80 @@ const Filter = ({
64
115
 
65
116
  return (
66
117
  <div
118
+ ref={containerRef}
67
119
  style={isDesktop ? style : undefined}
68
- className={`
69
- /* Mobile layout */
120
+ className={twMerge(
121
+ `/* Mobile layout */
70
122
  hc-relative hc-w-full hc-h-full hc-flex hc-flex-col
71
123
 
72
124
  /* Desktop layout */
73
- md:hc-block md:hc-max-h-[100%] md:hc-overflow-y-auto md:hc-overflow-x-auto
125
+ md:hc-relative md:hc-flex md:hc-flex-col md:hc-max-h-[100%]
74
126
 
75
- ${className ?? ""}
76
- `}
127
+ ${className ?? ""}`,
128
+ filterConfig?.classNames?.filterContainer
129
+ )}
77
130
  >
78
131
  {/* Content area with scroll */}
79
- <div
132
+ <div
80
133
  ref={contentRef}
81
- className={`hc-w-full hc-flex-grow hc-overflow-auto ${hasOverflow ? 'hc-pb-16' : ''} md:hc-pb-0`}
134
+ className={twMerge(
135
+ `hc-w-full hc-flex-grow hc-max-h-full hc-overflow-auto ${hasOverflow ? 'hc-pb-16' : ''} md:hc-overflow-y-auto ${hasOverflow ? 'md:hc-pb-16' : 'md:hc-pb-0'}`,
136
+ filterConfig?.classNames?.filterContent
137
+ )}
82
138
  >
83
139
  <div className="hc-px-4 md:hc-pt-4 hc-space-y-4">
84
140
  {children}
85
-
86
- {/* Mobile buttons inline when no overflow */}
87
- {!hasOverflow && (
88
- <div className="hc-flex hc-items-center hc-justify-between hc-gap-2 hc-py-2 hc-border-t hc-border-gray-200 md:hc-hidden">
89
- <MobileButtons />
141
+
142
+ {/* Inline buttons when no overflow - both mobile and desktop */}
143
+ {!hasOverflow && !showFixedFooter && (
144
+ <div className={twMerge(
145
+ "hc-flex hc-items-center hc-justify-between hc-gap-2 hc-py-2 hc-border-t hc-border-gray-200 hc-animate-in hc-fade-in hc-duration-300",
146
+ filterConfig?.classNames?.filterFooter
147
+ )}>
148
+ {/* Mobile buttons */}
149
+ <div className="hc-flex hc-items-center hc-justify-between hc-gap-2 hc-w-full md:hc-hidden">
150
+ <MobileButtons />
151
+ </div>
152
+ {/* Desktop button */}
153
+ <div className="hc-hidden md:hc-flex">
154
+ <Button.Btn
155
+ onClick={handleReset}
156
+ variant="outline"
157
+ size="sm"
158
+ className={filterConfig?.classNames?.resetButton}
159
+ >
160
+ Reset All
161
+ </Button.Btn>
162
+ </div>
90
163
  </div>
91
164
  )}
92
165
  </div>
93
166
  </div>
94
167
 
95
- {/* Desktop Reset Button - left aligned */}
96
- <div className="hc-hidden md:hc-flex md:hc-justify-start hc-px-4 hc-py-4">
97
- <Button.Btn onClick={handleReset} variant="outline" size="sm">
98
- Reset All
99
- </Button.Btn>
100
- </div>
101
-
102
168
  {/* Mobile Footer - fixed at bottom (only when overflow) */}
103
- {hasOverflow && (
169
+ {showFixedFooter && (
104
170
  <div
105
- className="
106
- hc-w-full
107
- hc-absolute hc-bottom-0 hc-left-0 hc-right-0
108
- hc-flex hc-items-center hc-justify-between hc-gap-2 hc-py-2 hc-px-4
109
- hc-bg-white hc-border-t hc-border-gray-200
110
- hc-z-10
111
- md:hc-hidden
112
- "
171
+ className="hc-w-full hc-absolute hc-bottom-0 hc-left-0 hc-right-0 hc-flex hc-items-center hc-justify-between hc-gap-2 hc-py-2 hc-px-4 hc-bg-gray-100 hc-border-t hc-border-gray-200 hc-z-50 md:hc-hidden hc-animate-in hc-fade-in hc-slide-in-from-bottom-2 hc-duration-200"
113
172
  >
114
173
  <MobileButtons />
115
174
  </div>
116
175
  )}
176
+
177
+ {/* Desktop Footer - fixed at bottom (only when overflow) */}
178
+ {showFixedFooter && (
179
+ <div className="hc-hidden md:hc-flex md:hc-justify-start hc-px-4 hc-py-4 hc-absolute hc-bottom-0 hc-left-0 hc-right-0 hc-bg-gray-100 hc-border-t hc-border-gray-200 hc-z-50 hc-animate-in hc-fade-in hc-slide-in-from-bottom-2 hc-duration-200">
180
+ <Button.Btn
181
+ onClick={handleReset}
182
+ variant="outline"
183
+ size="sm"
184
+ className={filterConfig?.classNames?.resetButton}
185
+ >
186
+ Reset All
187
+ </Button.Btn>
188
+ </div>
189
+ )}
117
190
  </div>
118
191
  );
119
192
  };
120
193
 
121
- export default Filter;
194
+ export default Filter;
@@ -1,5 +1,7 @@
1
1
  import React from 'react';
2
2
  import Icon from '~/components/modules/icon';
3
+ import { twMerge } from '~/util/twMerge';
4
+ import { useMapList } from '~/contexts/mapListContext';
3
5
 
4
6
  const FilterItem = ({
5
7
  className,
@@ -17,6 +19,7 @@ const FilterItem = ({
17
19
  eventTypes,
18
20
  ...rest
19
21
  }) => {
22
+ const { filterConfig } = useMapList();
20
23
  const itemName = item.name ? item.name : item;
21
24
 
22
25
  return (
@@ -47,7 +50,10 @@ const FilterItem = ({
47
50
  disabled={item.count === 0}
48
51
  value={itemName}
49
52
  type={type}
50
- className="hc-size-4 hc-mt-px hc-text-primary hc-border-uiAccent/30 hc-transition-colors hc-rounded-sm"
53
+ className={twMerge(
54
+ "hc-size-4 hc-mt-px hc-text-primary hc-border-uiAccent/30 hc-transition-colors hc-rounded-sm",
55
+ filterConfig?.classNames?.filterItemCheckbox
56
+ )}
51
57
  checked={activeItem}
52
58
  onChange={() => {
53
59
  setActiveItem(!activeItem);
@@ -56,9 +62,15 @@ const FilterItem = ({
56
62
  />
57
63
  )}
58
64
 
59
- <span className="hc-text-left hc-font-medium">{itemName}</span>
65
+ <span className={twMerge(
66
+ "hc-text-left hc-font-medium",
67
+ filterConfig?.classNames?.filterItemLabel
68
+ )}>{itemName}</span>
60
69
  {hasCount && !isExternalLink && (
61
- <span className="hc-inline-block hc-mt-1 hc-ml-auto hc-text-xs hc-leading-none hc-text-primary">
70
+ <span className={twMerge(
71
+ "hc-inline-block hc-mt-1 hc-ml-auto hc-text-xs hc-leading-none hc-text-primary",
72
+ filterConfig?.classNames?.filterItemCount
73
+ )}>
62
74
  ({item.count})
63
75
  </span>
64
76
  )}
@@ -2,6 +2,8 @@ import React, { useRef } from 'react';
2
2
  import Button from '~/components/modules/buttons/default';
3
3
  import Icon from '~/components/modules/icon';
4
4
  import FilterCard from '~/components/modules/cards/filter';
5
+ import { twMerge } from '~/util/twMerge';
6
+ import { useMapList } from '~/contexts/mapListContext';
5
7
 
6
8
  const Search = ({
7
9
  inputPlaceholder,
@@ -14,6 +16,7 @@ const Search = ({
14
16
  handleReset,
15
17
  label
16
18
  }) => {
19
+ const { filterConfig } = useMapList();
17
20
  const inputRef = useRef(null);
18
21
  const buttonRef = useRef(null);
19
22
 
@@ -47,7 +50,10 @@ const Search = ({
47
50
  }}
48
51
  placeholder={inputPlaceholder}
49
52
  value={inputValue}
50
- className="hc-w-full hc-px-0 hc-py-2 hc-text-sm hc-border-0 hc-transition-colors placeholder:hc-text-uiText/50 focus:hc-ring-0 focus:hc-outline-none"
53
+ className={twMerge(
54
+ "hc-w-full hc-px-0 hc-py-2 hc-text-sm hc-border-0 hc-transition-colors placeholder:hc-text-uiText/50 focus:hc-ring-0 focus:hc-outline-none",
55
+ filterConfig?.classNames?.searchInput
56
+ )}
51
57
  onChange={handleInputChange}
52
58
  />
53
59
 
@@ -55,6 +55,28 @@ interface MapListContextProps {
55
55
  containerStyle?: any;
56
56
  ExpandListComponent?: React.ComponentType<{ listing: any }> | ((listing: any) => JSX.Element) | null;
57
57
  noEntities?: boolean;
58
+ filterConfig?: {
59
+ hideZeroResults?: boolean;
60
+ dynamicCounts?: boolean;
61
+ showFavorites?: boolean;
62
+ collapsedByDefault?: boolean;
63
+ sortAlphabetically?: boolean;
64
+ classNames?: {
65
+ filterContainer?: string;
66
+ filterContent?: string;
67
+ filterAccordion?: string;
68
+ filterAccordionHeader?: string;
69
+ filterAccordionContent?: string;
70
+ filterItem?: string;
71
+ filterItemLabel?: string;
72
+ filterItemCheckbox?: string;
73
+ filterItemCount?: string;
74
+ searchInput?: string;
75
+ resetButton?: string;
76
+ showJobsButton?: string;
77
+ filterFooter?: string;
78
+ };
79
+ };
58
80
  }
59
81
 
60
82
  const MapListContext = createContext<MapListContextProps | undefined>(undefined);
@@ -101,6 +123,28 @@ interface MapListProviderProps {
101
123
  hideMap?: boolean;
102
124
  hideFilters?: boolean;
103
125
  noEntities?: boolean;
126
+ filterConfig?: {
127
+ hideZeroResults?: boolean;
128
+ dynamicCounts?: boolean;
129
+ showFavorites?: boolean;
130
+ collapsedByDefault?: boolean;
131
+ sortAlphabetically?: boolean;
132
+ classNames?: {
133
+ filterContainer?: string;
134
+ filterContent?: string;
135
+ filterAccordion?: string;
136
+ filterAccordionHeader?: string;
137
+ filterAccordionContent?: string;
138
+ filterItem?: string;
139
+ filterItemLabel?: string;
140
+ filterItemCheckbox?: string;
141
+ filterItemCount?: string;
142
+ searchInput?: string;
143
+ resetButton?: string;
144
+ showJobsButton?: string;
145
+ filterFooter?: string;
146
+ };
147
+ };
104
148
  }
105
149
 
106
150
  export const MapListProvider: React.FC<MapListProviderProps> = ({
@@ -128,7 +172,29 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
128
172
  localStorageKey,
129
173
  hideMap = false,
130
174
  hideFilters = false,
131
- noEntities = false
175
+ noEntities = false,
176
+ filterConfig = {
177
+ hideZeroResults: false,
178
+ dynamicCounts: true,
179
+ showFavorites: true,
180
+ collapsedByDefault: false,
181
+ sortAlphabetically: false,
182
+ classNames: {
183
+ filterContainer: '',
184
+ filterContent: '',
185
+ filterAccordion: '',
186
+ filterAccordionHeader: '',
187
+ filterAccordionContent: '',
188
+ filterItem: '',
189
+ filterItemLabel: '',
190
+ filterItemCheckbox: '',
191
+ filterItemCount: '',
192
+ searchInput: '',
193
+ resetButton: '',
194
+ showJobsButton: '',
195
+ filterFooter: ''
196
+ }
197
+ }
132
198
  }) => {
133
199
  const firstLoadFilters = () =>{
134
200
  let urlData = filtersFromURL(window.location);
@@ -318,6 +384,31 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
318
384
  if (tempSelectedFilters) {
319
385
  const keys = Object.keys(tempSelectedFilters);
320
386
  const lastKey = keys[keys.length - 1];
387
+
388
+ // Ensure all filterConfig properties have values
389
+ const normalizedFilterConfig = {
390
+ hideZeroResults: filterConfig?.hideZeroResults ?? false,
391
+ dynamicCounts: filterConfig?.dynamicCounts ?? true,
392
+ showFavorites: filterConfig?.showFavorites ?? true,
393
+ collapsedByDefault: filterConfig?.collapsedByDefault ?? false,
394
+ sortAlphabetically: filterConfig?.sortAlphabetically ?? false,
395
+ classNames: {
396
+ filterContainer: filterConfig?.classNames?.filterContainer ?? '',
397
+ filterContent: filterConfig?.classNames?.filterContent ?? '',
398
+ filterAccordion: filterConfig?.classNames?.filterAccordion ?? '',
399
+ filterAccordionHeader: filterConfig?.classNames?.filterAccordionHeader ?? '',
400
+ filterAccordionContent: filterConfig?.classNames?.filterAccordionContent ?? '',
401
+ filterItem: filterConfig?.classNames?.filterItem ?? '',
402
+ filterItemLabel: filterConfig?.classNames?.filterItemLabel ?? '',
403
+ filterItemCheckbox: filterConfig?.classNames?.filterItemCheckbox ?? '',
404
+ filterItemCount: filterConfig?.classNames?.filterItemCount ?? '',
405
+ searchInput: filterConfig?.classNames?.searchInput ?? '',
406
+ resetButton: filterConfig?.classNames?.resetButton ?? '',
407
+ showJobsButton: filterConfig?.classNames?.showJobsButton ?? '',
408
+ filterFooter: filterConfig?.classNames?.filterFooter ?? ''
409
+ }
410
+ };
411
+
321
412
  const options = generateFilterOptions(
322
413
  filteredListings,
323
414
  allListings,
@@ -325,7 +416,8 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
325
416
  filterOptions,
326
417
  lastKey,
327
418
  favorites,
328
- tempSelectedFilters
419
+ tempSelectedFilters,
420
+ normalizedFilterConfig
329
421
  );
330
422
  if (options) {
331
423
  setFilterOptions(options);
@@ -403,7 +495,8 @@ export const MapListProvider: React.FC<MapListProviderProps> = ({
403
495
  hiddenFilters,
404
496
  containerStyle,
405
497
  ExpandListComponent,
406
- noEntities
498
+ noEntities,
499
+ filterConfig
407
500
  }}>
408
501
  {children}
409
502
  </MapListContext.Provider>