@okta/odyssey-react-mui 1.12.4 → 1.12.6

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@okta/odyssey-react-mui",
3
- "version": "1.12.4",
3
+ "version": "1.12.6",
4
4
  "description": "React MUI components for Odyssey, Okta's design system",
5
5
  "author": "Okta, Inc.",
6
6
  "license": "Apache-2.0",
@@ -51,7 +51,7 @@
51
51
  "@mui/system": "^5.14.9",
52
52
  "@mui/utils": "^5.11.2",
53
53
  "@mui/x-date-pickers": "^5.0.15",
54
- "@okta/odyssey-design-tokens": "1.12.4",
54
+ "@okta/odyssey-design-tokens": "1.12.6",
55
55
  "date-fns": "^2.30.0",
56
56
  "i18next": "^23.5.1",
57
57
  "material-react-table": "^2.0.2",
@@ -63,5 +63,5 @@
63
63
  "react": ">=17 <19",
64
64
  "react-dom": ">=17 <19"
65
65
  },
66
- "gitHead": "b5b53ea203c3017a6fd6dc6642a1dfc9a19df7fd"
66
+ "gitHead": "00daa33877428125405ce716101dd4c089081922"
67
67
  }
package/src/Select.tsx CHANGED
@@ -212,18 +212,23 @@ const Select = <
212
212
  // data types that might be passed
213
213
  const normalizedOptions = useMemo(
214
214
  () =>
215
- options.map((option) =>
216
- typeof option === "object"
217
- ? {
218
- text: option.text,
219
- value:
220
- option?.value === ""
221
- ? option.value
222
- : option.value || option.text,
223
- type: option.type === "heading" ? "heading" : "option",
224
- }
225
- : { text: option, value: option, type: "option" }
226
- ),
215
+ options.map((option) => {
216
+ if (typeof option === "object") {
217
+ /**
218
+ * If the value of `option?.value is an empty string, we need to make sure that we
219
+ * set an empty string to `value` in the normalized option so that the select component
220
+ * can potentially set it as the selected one in the text input
221
+ */
222
+ const value =
223
+ option?.value === "" ? option.value : option.value || option.text;
224
+ return {
225
+ text: option.text,
226
+ value,
227
+ type: option.type === "heading" ? "heading" : "option",
228
+ };
229
+ }
230
+ return { text: option, value: option, type: "option" };
231
+ }),
227
232
  [options]
228
233
  );
229
234
 
package/src/TextField.tsx CHANGED
@@ -191,7 +191,7 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
191
191
  "aria-errormessage": errorMessageElementId,
192
192
  "aria-labelledby": labelElementId,
193
193
  "data-se": testId,
194
- inputmode: inputMode,
194
+ inputMode,
195
195
  }}
196
196
  inputRef={localInputRef}
197
197
  multiline={isMultiline}
@@ -20,6 +20,7 @@ import {
20
20
  useRef,
21
21
  useState,
22
22
  } from "react";
23
+ import styled from "@emotion/styled";
23
24
  import { Autocomplete } from "../Autocomplete";
24
25
  import { Box } from "../Box";
25
26
  import { TagList } from "../TagList";
@@ -175,22 +176,60 @@ const DataFilters = ({
175
176
  }
176
177
  }, [onChangeSearch, searchValue, searchDelayTime, hasSearchSubmitButton]);
177
178
 
178
- const handleInputChange = useCallback(
179
- (filterId: string, value: DataFilterValue, submit: boolean = false) => {
179
+ const autocompleteOptions = useMemo(() => {
180
+ // Check if filterPopoverCurrentFilter and filterPopoverCurrentFilter.options are defined
181
+ if (
182
+ filterPopoverCurrentFilter?.variant === "autocomplete" &&
183
+ filterPopoverCurrentFilter?.options
184
+ ) {
185
+ return filterPopoverCurrentFilter.options.map((option) => ({
186
+ label: option.label,
187
+ }));
188
+ }
189
+
190
+ // if filterPopoverCurrentFilter or filterPopoverCurrentFilter.options is undefined
191
+ return [];
192
+ }, [filterPopoverCurrentFilter]);
193
+
194
+ const updateInputValue = useCallback(
195
+ ({ filterId, value }: { filterId: string; value: DataFilterValue }) => {
180
196
  setInputValues({ ...inputValues, [filterId]: value });
197
+ },
198
+ [inputValues]
199
+ );
181
200
 
182
- if (submit) {
183
- const updatedFilters = filtersProp.map((filter) => ({
184
- ...filter,
185
- value: filter.id === filterId ? value : inputValues[filter.id],
186
- }));
201
+ const updateFilters = useCallback(
202
+ ({ filterId, value }: { filterId: string; value: DataFilterValue }) => {
203
+ const updatedFilters = filtersProp.map((filter) => ({
204
+ ...filter,
205
+ value: filter.id === filterId ? value : inputValues[filter.id],
206
+ }));
187
207
 
188
- setFilters(updatedFilters);
189
- }
208
+ setFilters(updatedFilters);
190
209
  },
191
210
  [inputValues, filtersProp]
192
211
  );
193
212
 
213
+ const getAutoCompleteLabel = <
214
+ Value extends { label: string } | Array<{ label: string }>
215
+ >(
216
+ value: Value
217
+ ) => {
218
+ if (Array.isArray(value)) {
219
+ // Iterating to find the label
220
+ return value
221
+ .map((valueElement) => {
222
+ if (typeof valueElement === "string") {
223
+ return undefined;
224
+ }
225
+ return valueElement.label;
226
+ })
227
+ .filter((item): item is string => Boolean(item));
228
+ }
229
+
230
+ return value?.label;
231
+ };
232
+
194
233
  const handleMultiSelectChange = useCallback(
195
234
  (filterId: string, value: string, submit: boolean = false) => {
196
235
  const startingValues = filtersProp
@@ -245,6 +284,15 @@ const DataFilters = ({
245
284
  setFilters(updatedFilters);
246
285
  }, [inputValues, filtersProp]);
247
286
 
287
+ const AutocompleteOuterContainer = styled.div`
288
+ display: flex;
289
+ gap: 2;
290
+ align-items: center;
291
+ alignitems: "flex-end";
292
+ `;
293
+ const AutocompleteInnerContainer = styled.div`
294
+ width: "100%";
295
+ `;
248
296
  const filterMenu = useMemo(
249
297
  () => (
250
298
  <>
@@ -367,23 +415,43 @@ const DataFilters = ({
367
415
  {/* Autocomplete */}
368
416
  {filterPopoverCurrentFilter?.variant === "autocomplete" &&
369
417
  filterPopoverCurrentFilter?.options && (
370
- <Autocomplete
371
- label={filterPopoverCurrentFilter.label}
372
- value={
373
- (inputValues[
374
- filterPopoverCurrentFilter.id
375
- ] as string) ?? ""
376
- }
377
- onBlur={function ro() {}}
378
- onChange={function ro() {}}
379
- onFocus={function ro() {}}
380
- onInputChange={function ro() {}}
381
- options={filterPopoverCurrentFilter.options.map(
382
- (option: { label: string }) => ({
383
- label: option.label,
384
- })
385
- )}
386
- />
418
+ <AutocompleteOuterContainer>
419
+ <AutocompleteInnerContainer>
420
+ <Autocomplete
421
+ label={filterPopoverCurrentFilter.label}
422
+ value={
423
+ inputValues[filterPopoverCurrentFilter.id] ?? ""
424
+ }
425
+ onChange={(_, value) => {
426
+ const label =
427
+ typeof value === "string"
428
+ ? getAutoCompleteLabel({ label: value })
429
+ : Array.isArray(value)
430
+ ? getAutoCompleteLabel(
431
+ value.map((item) =>
432
+ typeof item === "string"
433
+ ? { label: item }
434
+ : item
435
+ )
436
+ )
437
+ : value
438
+ ? getAutoCompleteLabel(value)
439
+ : "";
440
+
441
+ updateInputValue({
442
+ filterId: filterPopoverCurrentFilter.id,
443
+ value: label,
444
+ });
445
+ }}
446
+ options={autocompleteOptions}
447
+ />
448
+ </AutocompleteInnerContainer>
449
+ <Button
450
+ variant="primary"
451
+ endIcon={<CheckIcon />}
452
+ type="submit"
453
+ />
454
+ </AutocompleteOuterContainer>
387
455
  )}
388
456
  {/* Text or Number */}
389
457
  {(filterPopoverCurrentFilter?.variant === "text" ||
@@ -410,10 +478,10 @@ const DataFilters = ({
410
478
  ] as string) ?? ""
411
479
  }
412
480
  onChange={(ev) =>
413
- handleInputChange(
414
- filterPopoverCurrentFilter.id,
415
- ev.currentTarget.value
416
- )
481
+ updateInputValue({
482
+ filterId: filterPopoverCurrentFilter.id,
483
+ value: ev.currentTarget.value,
484
+ })
417
485
  }
418
486
  endAdornment={
419
487
  inputValues[filterPopoverCurrentFilter.id] && (
@@ -421,11 +489,15 @@ const DataFilters = ({
421
489
  size="small"
422
490
  aria-label="Clear filter"
423
491
  onClick={() => {
424
- handleInputChange(
425
- filterPopoverCurrentFilter.id,
426
- undefined,
427
- true
428
- );
492
+ updateInputValue({
493
+ filterId: filterPopoverCurrentFilter.id,
494
+ value: undefined,
495
+ });
496
+
497
+ updateFilters({
498
+ filterId: filterPopoverCurrentFilter.id,
499
+ value: undefined,
500
+ });
429
501
  }}
430
502
  >
431
503
  <CloseCircleFilledIcon />
@@ -480,13 +552,17 @@ const DataFilters = ({
480
552
  filterPopoverCurrentFilter?.options && (
481
553
  <RadioGroup
482
554
  label={filterPopoverCurrentFilter.label}
483
- onChange={(_, value) =>
484
- handleInputChange(
485
- filterPopoverCurrentFilter.id,
555
+ onChange={(_, value) => {
556
+ updateInputValue({
557
+ filterId: filterPopoverCurrentFilter.id,
486
558
  value,
487
- true
488
- )
489
- }
559
+ });
560
+
561
+ updateFilters({
562
+ filterId: filterPopoverCurrentFilter.id,
563
+ value,
564
+ });
565
+ }}
490
566
  >
491
567
  <Radio
492
568
  label="Any"
@@ -561,7 +637,7 @@ const DataFilters = ({
561
637
  <Button
562
638
  variant="secondary"
563
639
  label="Clear filters"
564
- onClick={() => clearAllFilters()}
640
+ onClick={clearAllFilters}
565
641
  />
566
642
  </Box>
567
643
  )}
@@ -585,7 +661,17 @@ const DataFilters = ({
585
661
  <Tag
586
662
  key={filter.label}
587
663
  label={`${filter.label}: ${filter.value}`}
588
- onRemove={() => handleInputChange(filter.id, undefined, true)}
664
+ onRemove={() => {
665
+ updateInputValue({
666
+ filterId: filter.id,
667
+ value: undefined,
668
+ });
669
+
670
+ updateFilters({
671
+ filterId: filter.id,
672
+ value: undefined,
673
+ });
674
+ }}
589
675
  />
590
676
  ))}
591
677
  </TagList>