@takaro/lib-components 0.0.1 → 0.0.4

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 (144) hide show
  1. package/package.json +4 -5
  2. package/src/components/actions/Button/index.tsx +68 -70
  3. package/src/components/actions/Button/style.ts +1 -1
  4. package/src/components/actions/ContextMenu/Group.tsx +1 -1
  5. package/src/components/actions/ContextMenu/MenuItem.tsx +4 -1
  6. package/src/components/actions/ContextMenu/index.tsx +11 -12
  7. package/src/components/actions/Dropdown/DropdownMenu.tsx +5 -2
  8. package/src/components/actions/Dropdown/DropdownTrigger.tsx +55 -57
  9. package/src/components/actions/Dropdown/useDropdown.tsx +1 -1
  10. package/src/components/actions/IconButton/index.tsx +11 -10
  11. package/src/components/actions/IconButton/style.ts +5 -2
  12. package/src/components/actions/ToggleButton/ToggleButton.tsx +26 -28
  13. package/src/components/actions/ToggleButton/ToggleButtonGroup.tsx +3 -8
  14. package/src/components/actions/ToggleButton/style.ts +5 -7
  15. package/src/components/charts/AreaChart/index.tsx +7 -8
  16. package/src/components/charts/BarChart/index.tsx +4 -4
  17. package/src/components/charts/GeoMercator/index.tsx +1 -1
  18. package/src/components/charts/Heatmap/index.tsx +3 -4
  19. package/src/components/charts/LineChart/index.tsx +3 -3
  20. package/src/components/charts/PieChart/index.tsx +1 -1
  21. package/src/components/charts/RadarChart/index.tsx +1 -1
  22. package/src/components/charts/RadialBarChart/index.tsx +1 -1
  23. package/src/components/charts/index.tsx +3 -0
  24. package/src/components/data/Avatar/index.tsx +7 -3
  25. package/src/components/data/Avatar/style.ts +2 -2
  26. package/src/components/data/Chip/index.tsx +39 -41
  27. package/src/components/data/Chip/style.ts +3 -2
  28. package/src/components/data/Console/Console.tsx +14 -12
  29. package/src/components/data/CopyId/index.tsx +3 -2
  30. package/src/components/data/Drawer/Drawer.stories.tsx +3 -5
  31. package/src/components/data/Drawer/DrawerContent.tsx +1 -1
  32. package/src/components/data/Drawer/DrawerHeading.tsx +30 -29
  33. package/src/components/data/Drawer/useDrawer.tsx +1 -1
  34. package/src/components/data/InfiniteScroll/index.tsx +37 -36
  35. package/src/components/data/Table/Table.stories.tsx +6 -2
  36. package/src/components/data/Table/index.tsx +63 -44
  37. package/src/components/data/Table/style.ts +3 -2
  38. package/src/components/data/Table/subcomponents/ColumnHeader/ColumnSettings.tsx +22 -19
  39. package/src/components/data/Table/subcomponents/ColumnHeader/index.tsx +3 -3
  40. package/src/components/data/Table/subcomponents/ColumnHeader/style.ts +2 -1
  41. package/src/components/data/Table/subcomponents/Filter/field.tsx +2 -3
  42. package/src/components/data/Table/subcomponents/Filter/index.tsx +6 -10
  43. package/src/components/data/Table/subcomponents/Pagination/{index.tsx → PagePicker.tsx} +7 -7
  44. package/src/components/data/Table/subcomponents/Pagination/PageSizeSelect.tsx +37 -0
  45. package/src/components/data/Table/subcomponents/Pagination/style.ts +1 -1
  46. package/src/components/dialogs/Dialog/Dialog.stories.tsx +1 -2
  47. package/src/components/dialogs/Dialog/DialogBody.tsx +18 -17
  48. package/src/components/dialogs/Dialog/DialogContent.tsx +30 -28
  49. package/src/components/dialogs/Dialog/DialogHeading.tsx +1 -1
  50. package/src/components/dialogs/Dialog/useDialog.tsx +1 -1
  51. package/src/components/feedback/Alert/index.tsx +74 -73
  52. package/src/components/feedback/NotificationBanner/NotificationBanner.test.tsx +1 -1
  53. package/src/components/feedback/NotificationBanner/index.tsx +1 -1
  54. package/src/components/feedback/Popover/PopoverContent.tsx +4 -1
  55. package/src/components/feedback/Popover/PopoverTrigger.tsx +33 -31
  56. package/src/components/feedback/Popover/usePopover.tsx +1 -1
  57. package/src/components/feedback/ProgressBar/index.tsx +1 -1
  58. package/src/components/feedback/QuestionTooltip/index.tsx +1 -2
  59. package/src/components/feedback/Tooltip/TooltipContent.tsx +4 -1
  60. package/src/components/feedback/Tooltip/TooltipTrigger.tsx +3 -3
  61. package/src/components/feedback/Tooltip/useTooltip.tsx +1 -1
  62. package/src/components/feedback/snacks/CookieConsent/index.tsx +5 -2
  63. package/src/components/feedback/snacks/Default/index.tsx +51 -50
  64. package/src/components/feedback/snacks/Drawer/index.tsx +22 -21
  65. package/src/components/feedback/snacks/NetworkDetector/index.tsx +35 -31
  66. package/src/components/inputs/CheckBox/Controlled.tsx +2 -3
  67. package/src/components/inputs/CheckBox/Generic.tsx +18 -72
  68. package/src/components/inputs/CheckBox/style.ts +40 -97
  69. package/src/components/inputs/CodeField/CodeField.stories.tsx +4 -5
  70. package/src/components/inputs/CodeField/index.tsx +1 -0
  71. package/src/components/inputs/Date/DatePicker/DatePicker.stories.tsx +2 -3
  72. package/src/components/inputs/Date/DatePicker/Generic.tsx +20 -10
  73. package/src/components/inputs/Date/DateRangePicker/Context.tsx +12 -1
  74. package/src/components/inputs/Date/DateRangePicker/Controlled.tsx +74 -0
  75. package/src/components/inputs/Date/DateRangePicker/DateRangePicker.stories.tsx +11 -6
  76. package/src/components/inputs/Date/DateRangePicker/DateSelector/Absolute.tsx +3 -3
  77. package/src/components/inputs/Date/DateRangePicker/DateSelector/Relative.tsx +1 -1
  78. package/src/components/inputs/Date/DateRangePicker/DateSelector/index.tsx +1 -1
  79. package/src/components/inputs/Date/DateRangePicker/{index.tsx → Generic.tsx} +43 -11
  80. package/src/components/inputs/Date/DateRangePicker/QuickSelect/index.tsx +1 -1
  81. package/src/components/inputs/Date/DateRangePicker/style.ts +19 -5
  82. package/src/components/inputs/Date/subcomponents/Calendar/index.tsx +1 -1
  83. package/src/components/inputs/Date/subcomponents/RelativePicker/index.tsx +1 -1
  84. package/src/components/inputs/DurationField/Generic.tsx +149 -152
  85. package/src/components/inputs/FileField/FileField.stories.tsx +6 -6
  86. package/src/components/inputs/FileField/Generic.tsx +101 -99
  87. package/src/components/inputs/InputProps.ts +0 -2
  88. package/src/components/inputs/RadioGroup/Controlled.tsx +1 -2
  89. package/src/components/inputs/RadioGroup/RadioGroup.stories.tsx +6 -4
  90. package/src/components/inputs/RadioGroup/RadioItem.tsx +54 -53
  91. package/src/components/inputs/Slider/Generic.tsx +1 -1
  92. package/src/components/inputs/Slider/Slider.stories.tsx +1 -1
  93. package/src/components/inputs/Slider/handle.tsx +1 -0
  94. package/src/components/inputs/Switch/Controlled.tsx +1 -2
  95. package/src/components/inputs/Switch/Generic.tsx +18 -45
  96. package/src/components/inputs/Switch/Switch.stories.tsx +2 -4
  97. package/src/components/inputs/Switch/style.ts +44 -38
  98. package/src/components/inputs/TagField/Generic.tsx +109 -109
  99. package/src/components/inputs/TextAreaField/Generic.tsx +41 -39
  100. package/src/components/inputs/TextAreaField/TextAreaField.stories.tsx +2 -3
  101. package/src/components/inputs/TextField/Generic.tsx +81 -79
  102. package/src/components/inputs/TextField/TextField.stories.tsx +3 -4
  103. package/src/components/inputs/index.ts +3 -2
  104. package/src/components/inputs/layout/Description.tsx +1 -2
  105. package/src/components/inputs/layout/InputWrapper.ts +2 -2
  106. package/src/components/inputs/selects/SelectField/Controlled.tsx +1 -2
  107. package/src/components/inputs/selects/SelectField/Generic/FilterInput.tsx +4 -1
  108. package/src/components/inputs/selects/SelectField/SelectField.stories.tsx +9 -10
  109. package/src/components/inputs/selects/SelectQueryField/Generic/index.tsx +200 -197
  110. package/src/components/inputs/selects/SelectQueryField/SelectQueryField.stories.tsx +1 -1
  111. package/src/components/inputs/selects/SubComponents/Option.tsx +3 -3
  112. package/src/components/inputs/selects/SubComponents/OptionGroup.tsx +1 -1
  113. package/src/components/inputs/selects/SubComponents/style.ts +18 -4
  114. package/src/components/inputs/selects/index.tsx +5 -2
  115. package/src/components/inputs/selects/sharedStyle.ts +1 -0
  116. package/src/components/navigation/HorizontalNav/index.tsx +5 -32
  117. package/src/components/navigation/HorizontalNav/style.ts +11 -26
  118. package/src/components/navigation/IconNav/index.tsx +1 -1
  119. package/src/components/navigation/Steppers/SlimStepper/index.tsx +1 -1
  120. package/src/components/navigation/Steppers/SlimStepper/style.ts +1 -2
  121. package/src/components/navigation/Steppers/Stepper/index.tsx +1 -1
  122. package/src/components/navigation/Steppers/context.tsx +2 -2
  123. package/src/components/navigation/Tabs/Content.tsx +4 -1
  124. package/src/components/navigation/Tabs/Trigger.tsx +24 -23
  125. package/src/components/navigation/index.ts +1 -1
  126. package/src/components/other/ActionMenu/ActionMenu.stories.tsx +1 -2
  127. package/src/components/other/ActionMenu/index.tsx +30 -29
  128. package/src/components/other/Collapsible/CollapsibleTrigger.tsx +8 -4
  129. package/src/components/other/Company/index.tsx +25 -26
  130. package/src/components/other/Empty/Empty.stories.tsx +1 -1
  131. package/src/components/other/Empty/index.tsx +1 -3
  132. package/src/components/other/PermissionsGuard/index.tsx +2 -2
  133. package/src/components/visual/Card/index.tsx +4 -1
  134. package/src/errors/base.ts +4 -1
  135. package/src/errors/errors.ts +19 -2
  136. package/src/helpers/getSnackbarProvider.tsx +3 -3
  137. package/src/helpers/regexprs.ts +1 -0
  138. package/src/hooks/useFocus.tsx +3 -1
  139. package/src/hooks/useOnScreen.ts +1 -1
  140. package/src/hooks/useTableActions.ts +10 -5
  141. package/src/styled/GlobalStyle.ts +2 -1
  142. package/src/styled/zIndex.ts +1 -1
  143. package/src/test/testUtils.tsx +3 -0
  144. package/src/components/data/Table/subcomponents/index.ts +0 -4
@@ -42,7 +42,7 @@ interface SharedSelectQueryFieldProps {
42
42
  placeholder?: string;
43
43
  /// Debounce time in milliseconds (when clientside data it should be 0)
44
44
  debounce?: number;
45
- /// Triggered whenever the input value changes, debounced by 0.5 seconds.
45
+ /// Triggered whenever the input value changes.
46
46
  /// This is used to trigger the API call to get the new options
47
47
  handleInputValueChange: (value: string) => void;
48
48
  /// render inPortal
@@ -84,217 +84,220 @@ export interface InputValue {
84
84
  shouldUpdate: boolean;
85
85
  }
86
86
 
87
- export const GenericSelectQueryField = forwardRef<HTMLInputElement, GenericSelectQueryFieldProps>((props, ref) => {
88
- const {
89
- onBlur = () => {},
90
- onFocus = () => {},
91
- onChange,
92
- name,
93
- disabled,
94
- value,
95
- id,
96
- placeholder = 'Search field',
97
- hasDescription,
98
- inPortal = false,
99
- hasError,
100
- children,
101
- readOnly,
102
- render,
103
- multiple = false,
104
- canClear = false,
105
- debounce = 250,
106
- isLoadingData: isLoading = false,
107
- handleInputValueChange,
108
- } = defaultsApplier(props);
87
+ export const GenericSelectQueryField = forwardRef<HTMLInputElement, GenericSelectQueryFieldProps>(
88
+ function GenericSelectQueryField(props, ref) {
89
+ const {
90
+ onBlur = () => {},
91
+ onFocus = () => {},
92
+ onChange,
93
+ name,
94
+ disabled,
95
+ value,
96
+ id,
97
+ placeholder = 'Search field',
98
+ hasDescription,
99
+ inPortal = false,
100
+ hasError,
101
+ children,
102
+ readOnly,
103
+ render,
104
+ multiple = false,
105
+ canClear = false,
106
+ debounce = 250,
107
+ isLoadingData: isLoading = false,
108
+ handleInputValueChange,
109
+ } = defaultsApplier(props);
109
110
 
110
- const [open, setOpen] = useState<boolean>(false);
111
- const [inputValue, setInputValue] = useState<InputValue>({ value: '', shouldUpdate: false, label: '' });
112
- const [activeIndex, setActiveIndex] = useState<number | null>(null);
113
- const [selectedItems, setSelectedItems] = useState<SelectItem[]>([]);
111
+ const [open, setOpen] = useState<boolean>(false);
112
+ const [inputValue, setInputValue] = useState<InputValue>({ value: '', shouldUpdate: false, label: '' });
113
+ const [activeIndex, setActiveIndex] = useState<number | null>(null);
114
+ const [selectedItems, setSelectedItems] = useState<SelectItem[]>([]);
114
115
 
115
- const debouncedValue = useDebounce(inputValue.value, debounce);
116
- const listItemsRef = useRef<Array<HTMLLIElement | null>>([]);
116
+ const debouncedValue = useDebounce(inputValue.value, debounce);
117
+ const listItemsRef = useRef<Array<HTMLLIElement | null>>([]);
117
118
 
118
- useEffect(() => {
119
- if (debouncedValue && inputValue.shouldUpdate) handleInputValueChange(debouncedValue);
120
- }, [debouncedValue]);
119
+ useEffect(() => {
120
+ if ((inputValue.shouldUpdate && debouncedValue) || (inputValue && debouncedValue === ''))
121
+ handleInputValueChange(debouncedValue);
122
+ }, [debouncedValue]);
121
123
 
122
- const { refs, strategy, x, y, context } = useFloating<HTMLInputElement>({
123
- whileElementsMounted: autoUpdate,
124
- open,
125
- onOpenChange: readOnly || disabled ? () => {} : setOpen,
126
- middleware: [
127
- offset(5),
128
- size({
129
- apply({ rects, elements }) {
130
- Object.assign(elements.floating.style, {
131
- width: `${rects.reference.width}px`,
132
- maxHeight: '255px',
133
- });
134
- },
135
- padding: 10,
136
- }),
137
- ],
138
- });
124
+ const { refs, strategy, x, y, context } = useFloating<HTMLInputElement>({
125
+ whileElementsMounted: autoUpdate,
126
+ open,
127
+ onOpenChange: readOnly || disabled ? () => {} : setOpen,
128
+ middleware: [
129
+ offset(5),
130
+ size({
131
+ apply({ rects, elements }) {
132
+ Object.assign(elements.floating.style, {
133
+ width: `${rects.reference.width}px`,
134
+ maxHeight: '255px',
135
+ });
136
+ },
137
+ padding: 10,
138
+ }),
139
+ ],
140
+ });
139
141
 
140
- const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
141
- useClick(context),
142
- useRole(context, { role: 'listbox' }),
143
- useDismiss(context),
144
- ]);
142
+ const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions([
143
+ useClick(context),
144
+ useRole(context, { role: 'listbox' }),
145
+ useDismiss(context),
146
+ ]);
145
147
 
146
- function onInputChange(event: React.ChangeEvent<HTMLInputElement>) {
147
- const value = event.target.value;
148
- setInputValue({ value, shouldUpdate: true, label: value });
149
- if (value) {
150
- setActiveIndex(0);
148
+ function onInputChange(event: React.ChangeEvent<HTMLInputElement>) {
149
+ const value = event.target.value;
150
+ setInputValue({ value, shouldUpdate: true, label: value });
151
+ if (value) {
152
+ setActiveIndex(0);
153
+ }
151
154
  }
152
- }
153
155
 
154
- const handleClear = (e: MouseEvent) => {
155
- e.preventDefault();
156
- e.stopPropagation();
157
- setSelectedItems([]);
156
+ const handleClear = (e: MouseEvent) => {
157
+ e.preventDefault();
158
+ e.stopPropagation();
159
+ setSelectedItems([]);
158
160
 
159
- // the undefined is an expection
160
- if (onChange) onChange(multiple ? ([] as string[]) : (undefined as any));
161
- };
161
+ // the undefined is an expection
162
+ if (onChange) onChange(multiple ? ([] as string[]) : (undefined as any));
163
+ };
162
164
 
163
- /* This handles the case where the value is changed externally (e.g. from a parent component) */
164
- /* onChange propagates the value to the parent component, but since the value prop is not a required prop, the parent might not reflect the change
165
- * which ends up not running this useEffect. Meaning we still need to update the selectedIndex when clicked on an option.
166
- */
167
- useEffect(() => {
168
- // Function to create an item with a value and label
169
- const createItem = (v: string) => ({ value: v, label: getLabelFromChildren(children, v) as unknown as string });
165
+ /* This handles the case where the value is changed externally (e.g. from a parent component) */
166
+ /* onChange propagates the value to the parent component, but since the value prop is not a required prop, the parent might not reflect the change
167
+ * which ends up not running this useEffect. Meaning we still need to update the selectedIndex when clicked on an option.
168
+ */
169
+ useEffect(() => {
170
+ // Function to create an item with a value and label
171
+ const createItem = (v: string) => ({ value: v, label: getLabelFromChildren(children, v) as unknown as string });
170
172
 
171
- if (Array.isArray(value)) {
172
- const items = value.map(createItem);
173
- setSelectedItems(items);
174
- } else if (typeof value === 'string' && value !== '') {
175
- setSelectedItems([createItem(value)]);
176
- }
177
- }, [value, children]);
173
+ if (Array.isArray(value)) {
174
+ const items = value.map(createItem);
175
+ setSelectedItems(items);
176
+ } else if (typeof value === 'string' && value !== '') {
177
+ setSelectedItems([createItem(value)]);
178
+ }
179
+ }, [value, children]);
180
+
181
+ const renderSelect = () => {
182
+ const hasOptions = options && Children.count(options[0].props.children) > 1;
183
+
184
+ // initialFocus=-1 is used to prevent the first item from being focused when the list opens
185
+ return (
186
+ <FloatingFocusManager context={context} visuallyHiddenDismiss initialFocus={-1}>
187
+ <SelectContainer
188
+ {...getFloatingProps({
189
+ ref: refs.setFloating,
190
+ style: {
191
+ position: strategy,
192
+ top: y ?? 0,
193
+ left: x ?? 0,
194
+ overflow: 'auto',
195
+ borderBottom: '10px solid transparent',
196
+ },
197
+ })}
198
+ >
199
+ <GenericTextField
200
+ id={`${name}-input`}
201
+ name={`${name}-input`}
202
+ hasDescription={false}
203
+ icon={<SearchIcon />}
204
+ hasError={hasError}
205
+ value={inputValue.value}
206
+ onChange={onInputChange}
207
+ placeholder={placeholder}
208
+ ref={ref}
209
+ />
210
+ {/* it will always contain 1 because of the group label */}
211
+ {isLoading && (
212
+ <FeedBackContainer>
213
+ <Spinner size="small" />
214
+ <span style={{ marginLeft: '10px' }}>loading results</span>
215
+ </FeedBackContainer>
216
+ )}
217
+ {hasOptions && options}
218
+ {/* Basically first interaction */}
219
+ {!hasOptions && inputValue.value === '' && <FeedBackContainer>Start typing to search</FeedBackContainer>}
220
+ {/* When there is no result */}
221
+ {!hasOptions && !isLoading && inputValue.value !== '' && (
222
+ <FeedBackContainer>No results found</FeedBackContainer>
223
+ )}
224
+ </SelectContainer>
225
+ </FloatingFocusManager>
226
+ );
227
+ };
178
228
 
179
- const renderSelect = () => {
180
- const hasOptions = options && Children.count(options[0].props.children) > 1;
229
+ const options = useMemo(() => {
230
+ return [
231
+ ...(Children.map(
232
+ children,
233
+ (child) =>
234
+ isValidElement(child) && (
235
+ <ul key={child.props.label} role="group" aria-labelledby={`select-${child.props.label}`}>
236
+ {child.props.label && (
237
+ <GroupLabel role="presentation" id={`select-${child.props.label}`} aria-hidden="true">
238
+ {child.props.label}
239
+ </GroupLabel>
240
+ )}
241
+ {Children.map(child.props.children, (option) => {
242
+ return cloneElement(option, {
243
+ onChange: onChange,
244
+ });
245
+ })}
246
+ </ul>
247
+ ),
248
+ ) ?? []),
249
+ ];
250
+ }, [children]);
181
251
 
182
- // initialFocus=-1 is used to prevent the first item from being focused when the list opens
183
252
  return (
184
- <FloatingFocusManager context={context} visuallyHiddenDismiss initialFocus={-1}>
185
- <SelectContainer
186
- {...getFloatingProps({
187
- ref: refs.setFloating,
188
- style: {
189
- position: strategy,
190
- top: y ?? 0,
191
- left: x ?? 0,
192
- overflow: 'auto',
193
- borderBottom: '10px solid transparent',
194
- },
195
- })}
253
+ <SelectContext.Provider
254
+ value={{
255
+ listRef: listItemsRef,
256
+ setOpen,
257
+ getItemProps,
258
+ setActiveIndex,
259
+ activeIndex,
260
+ dataRef: context.dataRef,
261
+ multiple,
262
+ selectedItems,
263
+ setSelectedItems,
264
+ name,
265
+ }}
266
+ >
267
+ <SelectButton
268
+ id={id}
269
+ ref={refs.setReference}
270
+ disabled={disabled}
271
+ readOnly={readOnly}
272
+ onBlur={onBlur}
273
+ onFocus={onFocus}
274
+ isOpen={open}
275
+ tabIndex={disabled ? -1 : 0}
276
+ hasError={hasError}
277
+ aria-describedby={setAriaDescribedBy(name, hasDescription)}
278
+ {...getReferenceProps()}
196
279
  >
197
- <GenericTextField
198
- id={`${name}-input`}
199
- name={`${name}-input`}
200
- hasDescription={false}
201
- icon={<SearchIcon />}
202
- hasError={hasError}
203
- value={inputValue.value}
204
- onChange={onInputChange}
205
- placeholder={placeholder}
206
- ref={ref}
207
- />
208
- {/* it will always contain 1 because of the group label */}
209
- {isLoading && (
210
- <FeedBackContainer>
211
- <Spinner size="small" />
212
- <span style={{ marginLeft: '10px' }}>loading results</span>
213
- </FeedBackContainer>
280
+ {render ? (
281
+ render(selectedItems)
282
+ ) : (
283
+ <div>{selectedItems.length === 0 ? 'Select' : selectedItems.map((item) => item.label).join(', ')}</div>
214
284
  )}
215
- {hasOptions && options}
216
- {/* Basically first interaction */}
217
- {!hasOptions && inputValue.value === '' && <FeedBackContainer>Start typing to search</FeedBackContainer>}
218
- {/* When there is no result */}
219
- {!hasOptions && !isLoading && inputValue.value !== '' && (
220
- <FeedBackContainer>No results found</FeedBackContainer>
285
+
286
+ {!readOnly && canClear && selectedItems.length > 0 && !open ? (
287
+ <IconButton size="tiny" icon={<ClearIcon />} ariaLabel="clear" onClick={(e) => handleClear(e)} />
288
+ ) : (
289
+ <StyledArrowIcon size={16} />
221
290
  )}
222
- </SelectContainer>
223
- </FloatingFocusManager>
291
+ </SelectButton>
292
+ {open &&
293
+ (!inPortal ? (
294
+ <StyledFloatingOverlay lockScroll style={{ zIndex: 1000 }}>
295
+ {renderSelect()}
296
+ </StyledFloatingOverlay>
297
+ ) : (
298
+ <FloatingPortal>{renderSelect()}</FloatingPortal>
299
+ ))}
300
+ </SelectContext.Provider>
224
301
  );
225
- };
226
-
227
- const options = useMemo(() => {
228
- return [
229
- ...(Children.map(
230
- children,
231
- (child) =>
232
- isValidElement(child) && (
233
- <ul key={child.props.label} role="group" aria-labelledby={`select-${child.props.label}`}>
234
- {child.props.label && (
235
- <GroupLabel role="presentation" id={`select-${child.props.label}`} aria-hidden="true">
236
- {child.props.label}
237
- </GroupLabel>
238
- )}
239
- {Children.map(child.props.children, (option) => {
240
- return cloneElement(option, {
241
- onChange: onChange,
242
- });
243
- })}
244
- </ul>
245
- )
246
- ) ?? []),
247
- ];
248
- }, [children]);
249
-
250
- return (
251
- <SelectContext.Provider
252
- value={{
253
- listRef: listItemsRef,
254
- setOpen,
255
- getItemProps,
256
- setActiveIndex,
257
- activeIndex,
258
- dataRef: context.dataRef,
259
- multiple,
260
- selectedItems,
261
- setSelectedItems,
262
- name,
263
- }}
264
- >
265
- <SelectButton
266
- id={id}
267
- ref={refs.setReference}
268
- disabled={disabled}
269
- readOnly={readOnly}
270
- onBlur={onBlur}
271
- onFocus={onFocus}
272
- isOpen={open}
273
- tabIndex={disabled ? -1 : 0}
274
- hasError={hasError}
275
- aria-describedby={setAriaDescribedBy(name, hasDescription)}
276
- {...getReferenceProps()}
277
- >
278
- {render ? (
279
- render(selectedItems)
280
- ) : (
281
- <div>{selectedItems.length === 0 ? 'Select' : selectedItems.map((item) => item.label).join(', ')}</div>
282
- )}
283
-
284
- {!readOnly && canClear && selectedItems.length > 0 && !open ? (
285
- <IconButton size="tiny" icon={<ClearIcon />} ariaLabel="clear" onClick={(e) => handleClear(e)} />
286
- ) : (
287
- <StyledArrowIcon size={16} />
288
- )}
289
- </SelectButton>
290
- {open &&
291
- (!inPortal ? (
292
- <StyledFloatingOverlay lockScroll style={{ zIndex: 1000 }}>
293
- {renderSelect()}
294
- </StyledFloatingOverlay>
295
- ) : (
296
- <FloatingPortal>{renderSelect()}</FloatingPortal>
297
- ))}
298
- </SelectContext.Provider>
299
- );
300
- });
302
+ },
303
+ );
@@ -42,7 +42,7 @@ export const ServerSideSubmit: StoryFn<SelectQueryFieldProps> = (args) => {
42
42
 
43
43
  setTimeout(() => {
44
44
  const filteredOptions = films.filter((film) =>
45
- film.name.toLowerCase().trim().includes(debouncedValue.toLowerCase())
45
+ film.name.toLowerCase().trim().includes(debouncedValue.toLowerCase()),
46
46
  );
47
47
  setOptions(filteredOptions);
48
48
  setLoading(false);
@@ -26,10 +26,9 @@ function toggleSelectedItem(selectedValues: SelectItem[], itemToToggle: SelectIt
26
26
  if (existingIndex > -1) {
27
27
  // If it exists, remove it from the array
28
28
  return selectedValues.filter((_, index) => index !== existingIndex);
29
- } else {
30
- // If it doesn't exist, add it to the array
31
- return [...selectedValues, itemToToggle];
32
29
  }
30
+ // If it doesn't exist, add it to the array
31
+ return [...selectedValues, itemToToggle];
33
32
  }
34
33
 
35
34
  function hasSelectedItem(selectedItems: SelectItem[], itemToCheck: SelectItem) {
@@ -84,6 +83,7 @@ export const Option: FC<OptionProps> = ({ children, index = 0, value, onChange,
84
83
  tabIndex={activeIndex === index ? 0 : 1}
85
84
  isMultiSelect={multiple}
86
85
  isActive={activeIndex === index}
86
+ disabled={disabled}
87
87
  aria-disabled={disabled}
88
88
  isGrouped={false}
89
89
  aria-selected={activeIndex === index}
@@ -1,4 +1,4 @@
1
- import { FC, PropsWithChildren } from 'react';
1
+ import { FC, PropsWithChildren, JSX } from 'react';
2
2
 
3
3
  export interface OptionGroupProps extends PropsWithChildren {
4
4
  label?: string;
@@ -1,6 +1,11 @@
1
1
  import { styled } from '../../../../styled';
2
2
 
3
- export const OptionContainer = styled.li<{ isActive: boolean; isMultiSelect: boolean; isGrouped: boolean }>`
3
+ export const OptionContainer = styled.li<{
4
+ isActive: boolean;
5
+ isMultiSelect: boolean;
6
+ isGrouped: boolean;
7
+ disabled: boolean;
8
+ }>`
4
9
  padding: ${({ theme }) => `${theme.spacing['0_75']} ${theme.spacing['1']}`};
5
10
  min-height: ${({ theme }) => theme.spacing[4]};
6
11
  cursor: default;
@@ -15,12 +20,13 @@ export const OptionContainer = styled.li<{ isActive: boolean; isMultiSelect: boo
15
20
  scroll-margin: ${({ theme }) => theme.spacing['0_75']};
16
21
  padding-left: ${({ isGrouped, theme }) => (isGrouped ? `calc(1.7 * ${theme.spacing['2']})` : theme.spacing['1'])}};
17
22
 
23
+
18
24
  &:focus {
19
25
  border-color: ${({ theme }) => theme.colors.primary};
20
26
  }
21
27
 
22
28
  &:hover {
23
- border-color: ${({ theme }) => theme.colors.backgroundAccent};
29
+ border-color: ${({ theme, disabled }) => (disabled ? theme.colors.backgroundAlt : theme.colors.backgroundAccent)};
24
30
  span {
25
31
  color: white;
26
32
  }
@@ -32,8 +38,16 @@ export const OptionContainer = styled.li<{ isActive: boolean; isMultiSelect: boo
32
38
  gap: ${({ theme }) => theme.spacing[1]};
33
39
 
34
40
  span {
35
- cursor: pointer;
36
- color: ${({ theme, isActive }) => (isActive ? theme.colors.white : theme.colors.text)};
41
+ cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
42
+ color: ${({ theme, isActive, disabled }) => {
43
+ if (disabled) {
44
+ return theme.colors.backgroundAccent;
45
+ }
46
+ if (isActive) {
47
+ return theme.colors.white;
48
+ }
49
+ return theme.colors.text;
50
+ }};
37
51
  }
38
52
  }
39
53
  `;
@@ -41,7 +41,10 @@ export const getLabelFromChildren = (children: ReactNode, value: string) => {
41
41
  return matchedOption.props.label;
42
42
  }
43
43
  }
44
- throw new Error(
45
- `No label found for value ${value}. This occurs when a value is passed through the defaultValue prop of useForm, but the value is not present in the options.`
44
+
45
+ // eslint-disable-next-line no-console
46
+ console.error(
47
+ `No label found for value ${value}. This occurs when a value is passed through the defaultValue prop of useForm, but the value is not present in the options.`,
46
48
  );
49
+ return undefined;
47
50
  };
@@ -4,6 +4,7 @@ import { AiOutlineDown as ArrowIcon } from 'react-icons/ai';
4
4
 
5
5
  export const Container = styled.div`
6
6
  position: relative;
7
+ width: 100%;
7
8
  `;
8
9
 
9
10
  export const SelectContainer = styled.div`
@@ -1,40 +1,13 @@
1
- import { FC } from 'react';
2
- import { Link, LinkProps } from '@tanstack/react-router';
3
- import { Block, NavBar, Underline } from './style';
1
+ import { FC, ReactNode } from 'react';
2
+ import { NavBar } from './style';
4
3
 
5
4
  export type HorizontalNavVariant = 'underline' | 'block';
6
5
 
7
- export type HorizontalNavLink = Partial<LinkProps> & {
8
- text: string;
9
- to: string;
10
- params?: Record<string, string>;
11
- };
12
-
13
6
  export interface HorizontalNavProps {
14
- links: HorizontalNavLink[];
7
+ children: ReactNode;
15
8
  variant: HorizontalNavVariant;
16
9
  }
17
10
 
18
- export const HorizontalNav: FC<HorizontalNavProps> = ({ links, variant }) => {
19
- return (
20
- <NavBar variant={variant}>
21
- {links.map(({ text, ...rest }) => {
22
- return (
23
- <>
24
- {/*eslint-disable-next-line @typescript-eslint/ban-ts-comment*/}
25
- {/*@ts-ignore reusable link*/}
26
- <Link key={`${rest.to}-${text}`} {...rest}>
27
- {({ isActive }) => (
28
- <>
29
- {isActive && variant === 'block' && <Block layoutId="block" />}
30
- {isActive && variant === 'underline' && <Underline layoutId="underline" />}
31
- <span>{text}</span>
32
- </>
33
- )}
34
- </Link>
35
- </>
36
- );
37
- })}
38
- </NavBar>
39
- );
11
+ export const HorizontalNav: FC<HorizontalNavProps> = ({ children, variant }) => {
12
+ return <NavBar variant={variant}>{children}</NavBar>;
40
13
  };
@@ -1,5 +1,4 @@
1
1
  import { styled } from '../../../styled';
2
- import { motion } from 'framer-motion';
3
2
  import { HorizontalNavVariant } from '.';
4
3
 
5
4
  export const NavBar = styled.nav<{ variant: HorizontalNavVariant }>`
@@ -53,31 +52,17 @@ export const NavBar = styled.nav<{ variant: HorizontalNavVariant }>`
53
52
 
54
53
  &.active {
55
54
  color: ${({ theme }) => theme.colors.text};
55
+
56
+ &:after {
57
+ content: '';
58
+ position: absolute;
59
+ bottom: -8px;
60
+ left: 0;
61
+ right: 0;
62
+ height: 1px;
63
+ width: 100%;
64
+ background-color: ${({ theme }) => theme.colors.primary};
65
+ }
56
66
  }
57
67
  }
58
68
  `;
59
-
60
- export const Underline = styled(motion.div)`
61
- position: absolute;
62
- bottom: -${({ theme }) => theme.spacing['0_75']};
63
- left: 0px;
64
- display: block;
65
- height: 2px;
66
- background-color: ${({ theme }) => theme.colors.primary};
67
- content: '';
68
- width: 100%;
69
- `;
70
-
71
- export const Block = styled(motion.div)`
72
- position: absolute;
73
- top: 0;
74
- left: 0px;
75
- z-index: 1;
76
- display: block;
77
- height: 100%;
78
- border: 1px solid ${({ theme }) => theme.colors.primary};
79
- background-color: ${({ theme }) => theme.colors.primaryShade};
80
- border-radius: ${({ theme }) => theme.borderRadius.medium};
81
- content: '';
82
- width: 100%;
83
- `;
@@ -1,4 +1,4 @@
1
- import { FC, cloneElement } from 'react';
1
+ import { JSX, FC, cloneElement } from 'react';
2
2
  import { styled } from '../../../styled';
3
3
  import { Link, LinkProps } from '@tanstack/react-router';
4
4
  import { Tooltip } from '../../../components';
@@ -26,7 +26,7 @@ const StepperSteps: FC<PropsWithChildren<void>> = ({ children }) => {
26
26
  {children &&
27
27
  Children.map(
28
28
  children,
29
- (child) => steps.length !== 0 && isValidElement(child) && child.props.id === steps[currentStep].id && child
29
+ (child) => steps.length !== 0 && isValidElement(child) && child.props.id === steps[currentStep].id && child,
30
30
  )}
31
31
  </div>
32
32
  );
@@ -28,9 +28,8 @@ export const StepperHeaderItem = styled.div<{
28
28
  ${({ theme, stepState }) => {
29
29
  if (stepState === StepStates.COMPLETE) {
30
30
  return `border-right: .6rem solid ${lighten(0.15, theme.colors.primary)}`;
31
- } else {
32
- return 'border-right: .6rem solid transparent';
33
31
  }
32
+ return 'border-right: .6rem solid transparent';
34
33
  }};
35
34
 
36
35
  cursor: ${({ stepState, canStepBack }) => (stepState === StepStates.COMPLETE && canStepBack ? 'pointer' : 'inherit')};
@@ -27,7 +27,7 @@ const StepperSteps: FC<PropsWithChildren<void>> = ({ children }) => {
27
27
  {children &&
28
28
  Children.map(
29
29
  children,
30
- (child) => steps.length !== 0 && isValidElement(child) && child.props.id === steps[currentStep].id && child
30
+ (child) => steps.length !== 0 && isValidElement(child) && child.props.id === steps[currentStep].id && child,
31
31
  )}
32
32
  </div>
33
33
  );