@abgov/jsonforms-components 2.4.7 → 2.5.1

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/index.esm.js CHANGED
@@ -7482,8 +7482,6 @@ const FormStepperReviewer = props => {
7482
7482
  };
7483
7483
  const FormStepperReviewControl = withAjvProps(withTranslateProps(withJsonFormsLayoutProps(FormStepperReviewer)));
7484
7484
 
7485
- const TABLE_CONTEXT_ID = 100001;
7486
-
7487
7485
  const isErrorPathIncluded = (errorPaths, path) => {
7488
7486
  return errorPaths.some(ePath => {
7489
7487
  /**
@@ -7576,16 +7574,15 @@ const stepperReducer = (state, action) => {
7576
7574
  id
7577
7575
  } = action.payload;
7578
7576
  state.activeId = id;
7579
- if (id === TABLE_CONTEXT_ID) {
7580
- return Object.assign({}, state);
7581
- }
7582
7577
  if (id === lastId + 1) {
7583
7578
  state.isOnReview = true;
7584
7579
  state.hasNextButton = false;
7585
7580
  state.hasPrevButton = true;
7586
7581
  return Object.assign({}, state);
7587
7582
  } else {
7588
- state.categories[id].isVisited = true;
7583
+ if (state.categories[id]) {
7584
+ state.categories[id].isVisited = true;
7585
+ }
7589
7586
  state.hasNextButton = id <= lastId;
7590
7587
  state.hasPrevButton = id !== 0;
7591
7588
  state.isOnReview = false;
@@ -7600,7 +7597,7 @@ const stepperReducer = (state, action) => {
7600
7597
  ajv,
7601
7598
  errors
7602
7599
  } = action.payload;
7603
- if (id === state.categories.length || id === TABLE_CONTEXT_ID) {
7600
+ if (id === state.categories.length || id === state.categories.length + 1) {
7604
7601
  return Object.assign({}, state);
7605
7602
  }
7606
7603
  /*
@@ -7608,9 +7605,11 @@ const stepperReducer = (state, action) => {
7608
7605
  */
7609
7606
  const incompletePaths = getIncompletePaths(ajv, ((_a = state.categories[id]) === null || _a === void 0 ? void 0 : _a.scopes) || []);
7610
7607
  const errorsInCategory = getErrorsInScopes(errors, ((_b = state.categories[id]) === null || _b === void 0 ? void 0 : _b.scopes) || []);
7611
- state.categories[id].isCompleted = (incompletePaths === null || incompletePaths === void 0 ? void 0 : incompletePaths.length) === 0;
7612
- state.categories[id].isValid = errorsInCategory.length === 0;
7613
- state.categories[id].isVisited = true;
7608
+ if (state.categories[id]) {
7609
+ state.categories[id].isCompleted = (incompletePaths === null || incompletePaths === void 0 ? void 0 : incompletePaths.length) === 0;
7610
+ state.categories[id].isValid = errorsInCategory.length === 0;
7611
+ state.categories[id].isVisited = true;
7612
+ }
7614
7613
  return Object.assign({}, state);
7615
7614
  }
7616
7615
  case 'validate/form':
@@ -7664,7 +7663,7 @@ const createStepperContextInitData = props => {
7664
7663
  visible
7665
7664
  };
7666
7665
  });
7667
- const activeId = (props === null || props === void 0 ? void 0 : props.activeId) || (isPage ? TABLE_CONTEXT_ID : 0);
7666
+ const activeId = (props === null || props === void 0 ? void 0 : props.activeId) || (isPage ? categories.length + 1 : 0);
7668
7667
  return {
7669
7668
  categories: categories,
7670
7669
  activeId,
@@ -7714,7 +7713,7 @@ const JsonFormsStepperContextProvider = ({
7714
7713
  stepperDispatch({
7715
7714
  type: 'page/to/index',
7716
7715
  payload: {
7717
- id: TABLE_CONTEXT_ID
7716
+ id: stepperState.categories.length + 1
7718
7717
  }
7719
7718
  });
7720
7719
  },
@@ -7778,7 +7777,7 @@ const JsonFormsStepperContextProvider = ({
7778
7777
  }))
7779
7778
  }
7780
7779
  });
7781
- if (stepperState.activeId != TABLE_CONTEXT_ID) {
7780
+ if (stepperState.activeId !== stepperState.categories.length + 1) {
7782
7781
  context.goToPage(stepperState.maxReachedStep);
7783
7782
  context.goToPage(stepperState.activeId);
7784
7783
  }
@@ -8373,7 +8372,7 @@ const FormPagesView = props => {
8373
8372
  const handleGoToPage = index => {
8374
8373
  goToPage(index);
8375
8374
  };
8376
- if (TABLE_CONTEXT_ID === activeId) {
8375
+ if (categories.length + 1 === activeId) {
8377
8376
  const tocProps = {
8378
8377
  categories,
8379
8378
  onClick: handleGoToPage,
@@ -8535,7 +8534,8 @@ const FileUploader = _ref => {
8535
8534
  deleteTrigger(fileInList, propertyId);
8536
8535
  }
8537
8536
  }
8538
- uploadTrigger(file, propertyId);
8537
+ // To support multipleFileUploader, the propertyId (path) is in propertyId.index format
8538
+ uploadTrigger(file, `${propertyId}.${fileListLength}`);
8539
8539
  setDeleteHide(false);
8540
8540
  }
8541
8541
  }
@@ -10223,15 +10223,15 @@ const ListItem = styled.li(_t6 || (_t6 = _$1`
10223
10223
  padding: var(--goa-space-xs) var(--goa-space-2xs) var(--goa-space-xs) var(--goa-space-xs);
10224
10224
  margin-left: 0.25rem;
10225
10225
  `), ({
10226
- selectedIndex,
10226
+ selected,
10227
10227
  index
10228
- }) => selectedIndex === index ? 'var(--goa-color-interactive-default)' : '', ({
10229
- selectedIndex,
10228
+ }) => selected === index ? 'var(--goa-color-interactive-default)' : '', ({
10229
+ selected,
10230
10230
  index
10231
- }) => selectedIndex === index ? 'var(--goa-color-greyscale-white) !important' : '', ({
10232
- selectedIndex,
10231
+ }) => selected === index ? 'var(--goa-color-greyscale-white) !important' : '', ({
10232
+ selected,
10233
10233
  index
10234
- }) => selectedIndex === index ? '600' : '');
10234
+ }) => selected === index ? '600' : '');
10235
10235
 
10236
10236
  const AddressInputs = ({
10237
10237
  address,
@@ -10488,6 +10488,21 @@ function formatPostalCodeIfNeeded(input) {
10488
10488
  const after = cleaned.slice(3);
10489
10489
  return `${before} ${after}`;
10490
10490
  }
10491
+ function handleAddressKeyDown(key, value, activeIndex, suggestions, onInputChange, onSelect) {
10492
+ if (key === 'ArrowDown') {
10493
+ const newIndex = activeIndex < suggestions.length - 1 ? activeIndex + 1 : 0;
10494
+ onInputChange(value);
10495
+ return newIndex;
10496
+ } else if (key === 'ArrowUp') {
10497
+ const newIndex = activeIndex > 0 ? activeIndex - 1 : suggestions.length - 1;
10498
+ onInputChange(value);
10499
+ return newIndex;
10500
+ } else if (key === 'Enter' && suggestions[activeIndex]) {
10501
+ onInputChange(value);
10502
+ onSelect(suggestions[activeIndex]);
10503
+ }
10504
+ return activeIndex;
10505
+ }
10491
10506
 
10492
10507
  let _ = t => t,
10493
10508
  _t,
@@ -10727,6 +10742,7 @@ const HelpContentTester = rankWith(1, uiTypeIs('HelpContent'));
10727
10742
  const HelpContent = withJsonFormsControlProps(HelpContentComponent);
10728
10743
  const HelpReviewContent = withJsonFormsControlProps(HelpContentReviewComponent);
10729
10744
 
10745
+ //type DebounceValueType = string | boolean | number | Record<string, unknown>;
10730
10746
  /**
10731
10747
  * A helper util to return a value at a certain delay. The delay is reset if the value arg changes
10732
10748
  * @param value value to be returned after a period of delay
@@ -10736,11 +10752,9 @@ const HelpReviewContent = withJsonFormsControlProps(HelpContentReviewComponent);
10736
10752
  function useDebounce(value, delay) {
10737
10753
  const [debouncedValue, setDebouncedValue] = useState(value);
10738
10754
  useEffect(() => {
10739
- // Update debounced value after delay
10740
10755
  const handler = setTimeout(() => {
10741
10756
  setDebouncedValue(value);
10742
10757
  }, delay);
10743
- // Cancel the timeout if value changes (also on delay change or unmount)
10744
10758
  return () => {
10745
10759
  clearTimeout(handler);
10746
10760
  };
@@ -10786,10 +10800,9 @@ const AddressLookUpControl = props => {
10786
10800
  const requiredFields = schema.required;
10787
10801
  const [dropdownSelected, setDropdownSelected] = useState(false);
10788
10802
  const updateFormData = updatedAddress => {
10789
- setAddress(updatedAddress);
10790
10803
  handleChange(path, updatedAddress);
10791
10804
  };
10792
- const debouncedRenderAddress = useDebounce(searchTerm, 300);
10805
+ const debouncedRenderAddress = useDebounce(searchTerm, 200);
10793
10806
  const [activeIndex, setActiveIndex] = useState(0);
10794
10807
  const dropdownRef = useRef(null);
10795
10808
  const handleInputChange = (field, value) => {
@@ -10825,29 +10838,38 @@ const AddressLookUpControl = props => {
10825
10838
  }));
10826
10839
  };
10827
10840
  useEffect(() => {
10828
- handleInputChange('addressLine1', searchTerm);
10841
+ handleInputChange('addressLine1', debouncedRenderAddress);
10829
10842
  }, [debouncedRenderAddress]); // eslint-disable-line react-hooks/exhaustive-deps
10830
10843
  useEffect(() => {
10831
10844
  const fetchSuggestions = async () => {
10832
- if (searchTerm.length > 2 && dropdownSelected === false) {
10845
+ if (debouncedRenderAddress.length > 2 && dropdownSelected === false) {
10833
10846
  setLoading(true);
10834
10847
  setOpen(true);
10835
- await fetchAddressSuggestions(formUrl, formatPostalCodeIfNeeded(searchTerm), isAlbertaAddress).then(response => {
10836
- const suggestions = filterSuggestionsWithoutAddressCount(response);
10837
- if (isAlbertaAddress) {
10838
- setSuggestions(filterAlbertaAddresses(suggestions));
10839
- } else {
10840
- setSuggestions(suggestions);
10848
+ try {
10849
+ await fetchAddressSuggestions(formUrl, formatPostalCodeIfNeeded(searchTerm), isAlbertaAddress).then(response => {
10850
+ const suggestions = filterSuggestionsWithoutAddressCount(response);
10851
+ if (isAlbertaAddress) {
10852
+ setSuggestions(filterAlbertaAddresses(suggestions));
10853
+ } else {
10854
+ setSuggestions(suggestions);
10855
+ }
10856
+ setLoading(false);
10857
+ });
10858
+ // eslint-disable-next-line
10859
+ } catch (error) {
10860
+ if (error.name !== 'AbortError') {
10861
+ console.error('Address fetch failed:', error);
10841
10862
  }
10863
+ } finally {
10842
10864
  setLoading(false);
10843
- });
10865
+ }
10844
10866
  } else {
10845
10867
  setSuggestions([]);
10846
10868
  setOpen(false);
10847
10869
  }
10848
10870
  };
10849
10871
  fetchSuggestions();
10850
- }, [searchTerm]); // eslint-disable-line react-hooks/exhaustive-deps
10872
+ }, [debouncedRenderAddress]); // eslint-disable-line react-hooks/exhaustive-deps
10851
10873
  const handleDropdownChange = value => {
10852
10874
  setSearchTerm(value);
10853
10875
  };
@@ -10882,29 +10904,6 @@ const AddressLookUpControl = props => {
10882
10904
  }
10883
10905
  }
10884
10906
  }, [activeIndex]);
10885
- const handleKeyDown = (e, value, key) => {
10886
- var _a;
10887
- if (key === 'ArrowDown') {
10888
- setActiveIndex(prevIndex => prevIndex < suggestions.length - 1 ? prevIndex + 1 : 0);
10889
- handleInputChange('addressLine1', value);
10890
- } else if (key === 'ArrowUp') {
10891
- setActiveIndex(prevIndex => prevIndex > 0 ? prevIndex - 1 : suggestions.length - 1);
10892
- handleInputChange('addressLine1', value);
10893
- } else if (key === 'Enter') {
10894
- handleInputChange('addressLine1', value);
10895
- setLoading(false);
10896
- if (activeIndex >= 0) {
10897
- (_a = document.getElementById('goaInput')) === null || _a === void 0 ? void 0 : _a.blur();
10898
- const suggestion = suggestions[activeIndex];
10899
- if (suggestion) {
10900
- setTimeout(() => {
10901
- handleSuggestionClick(suggestion);
10902
- setOpen(false);
10903
- }, 50);
10904
- }
10905
- }
10906
- }
10907
- };
10908
10907
  const readOnly = (_f = (_e = (_d = uischema === null || uischema === void 0 ? void 0 : uischema.options) === null || _d === void 0 ? void 0 : _d.componentProps) === null || _e === void 0 ? void 0 : _e.readOnly) !== null && _f !== void 0 ? _f : false;
10909
10908
  return jsxs("div", {
10910
10909
  children: [renderHelp(), jsx("h3", {
@@ -10932,7 +10931,8 @@ const AddressLookUpControl = props => {
10932
10931
  width: "100%",
10933
10932
  onKeyPress: (e, value, key) => {
10934
10933
  if (open) {
10935
- handleKeyDown(e, value, key);
10934
+ const newIndex = handleAddressKeyDown(key, value, activeIndex, suggestions, val => handleInputChange('addressLine1', val), suggestion => handleSuggestionClick(suggestion));
10935
+ setActiveIndex(newIndex);
10936
10936
  }
10937
10937
  }
10938
10938
  }), loading && jsx("div", {
@@ -10954,8 +10954,9 @@ const AddressLookUpControl = props => {
10954
10954
  onClick: () => {
10955
10955
  handleSuggestionClick(suggestion);
10956
10956
  },
10957
- selectedIndex: activeIndex,
10957
+ selected: activeIndex,
10958
10958
  index: index,
10959
+ "data-testId": `listItem-${index}`,
10959
10960
  children: `${suggestion.Text} ${suggestion.Description}`
10960
10961
  }, index))
10961
10962
  })]
@@ -11375,6 +11376,37 @@ const FullNameControlReview = props => {
11375
11376
  };
11376
11377
  const GoAInputBaseFullNameControlReview = withJsonFormsAllOfProps(FullNameControlReview);
11377
11378
 
11379
+ function useSyncAutofillFields(fields,
11380
+ // eslint-disable-next-line
11381
+ formData,
11382
+ // eslint-disable-next-line
11383
+ updateFormData,
11384
+ // eslint-disable-next-line
11385
+ handleRequiredFieldBlur) {
11386
+ useEffect(() => {
11387
+ const rAF = requestAnimationFrame(() => {
11388
+ const timeout = setTimeout(() => {
11389
+ const updated = {};
11390
+ fields.forEach(field => {
11391
+ const input = document.querySelector(`goa-input[name="${field}"]`);
11392
+ if (input && input.value && !(formData === null || formData === void 0 ? void 0 : formData[field])) {
11393
+ updated[field] = input.value;
11394
+ }
11395
+ });
11396
+ if (Object.keys(updated).length > 0) {
11397
+ const mergedData = Object.assign({}, formData, updated);
11398
+ updateFormData(mergedData);
11399
+ Object.keys(updated).forEach(name => {
11400
+ handleRequiredFieldBlur(name, mergedData);
11401
+ });
11402
+ }
11403
+ }, 50);
11404
+ return () => clearTimeout(timeout);
11405
+ });
11406
+ return () => cancelAnimationFrame(rAF);
11407
+ }, [formData]);
11408
+ }
11409
+
11378
11410
  const FullNameDobControl = props => {
11379
11411
  var _a, _b, _c, _d, _e, _f;
11380
11412
  const {
@@ -11398,33 +11430,29 @@ const FullNameDobControl = props => {
11398
11430
  const [formData, setFormData] = useState(data || defaultNameAndDob);
11399
11431
  const updateFormData = updatedData => {
11400
11432
  updatedData = Object.fromEntries(Object.entries(updatedData).filter(([_, value]) => value !== ''));
11433
+ setFormData(updatedData);
11401
11434
  handleChange(path, updatedData);
11402
11435
  };
11403
11436
  const handleInputChange = (field, value) => {
11404
11437
  const updatedData = Object.assign({}, formData, {
11405
11438
  [field]: value
11406
11439
  });
11407
- setFormData(updatedData);
11408
- updateFormData(updatedData);
11409
- };
11410
- const handleDobChange = (field, value) => {
11411
- const updatedData = Object.assign({}, formData, {
11412
- [field]: value
11413
- });
11414
- setFormData(updatedData);
11415
11440
  updateFormData(updatedData);
11416
11441
  };
11417
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
11442
+ // eslint-disable-next-line
11418
11443
  const handleRequiredFieldBlur = (name, updatedData) => {
11444
+ var _a, _b, _c, _d;
11419
11445
  const err = Object.assign({}, errors);
11420
- if ((!(data === null || data === void 0 ? void 0 : data[name]) || (data === null || data === void 0 ? void 0 : data[name]) === '') && requiredFields.includes(name) && (!updatedData)) {
11421
- const modifiedName = name === 'firstName' ? 'First name' : name === 'lastName' ? 'Last name' : name === 'dateOfBirth' ? 'Date of birth' : '';
11446
+ const liveInputValue = (_d = (_b = (_a = updatedData === null || updatedData === void 0 ? void 0 : updatedData[name]) !== null && _a !== void 0 ? _a : data === null || data === void 0 ? void 0 : data[name]) !== null && _b !== void 0 ? _b : (_c = document.querySelector(`goa-input[name="${name}"]`)) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : '';
11447
+ if (requiredFields.includes(name) && liveInputValue.trim() === '') {
11448
+ const modifiedName = name === 'firstName' ? 'First name' : name === 'lastName' ? 'Last name' : name === 'dateOfBirth' ? 'Date of birth' : name;
11422
11449
  err[name] = `${modifiedName} is required`;
11423
11450
  } else {
11424
- err[name] = '';
11451
+ delete err[name];
11425
11452
  }
11426
11453
  setErrors(err);
11427
11454
  };
11455
+ useSyncAutofillFields(['firstName', 'middleName', 'lastName', 'dateOfBirth'], formData, updateFormData, handleRequiredFieldBlur);
11428
11456
  return jsxs(Fragment, {
11429
11457
  children: [jsxs(GoAGrid, {
11430
11458
  minChildWidth: "0ch",
@@ -11434,13 +11462,14 @@ const FullNameDobControl = props => {
11434
11462
  label: "First name",
11435
11463
  requirement: ((_a = schema === null || schema === void 0 ? void 0 : schema.required) === null || _a === void 0 ? void 0 : _a.includes('firstName')) ? 'required' : undefined,
11436
11464
  error: (_b = errors === null || errors === void 0 ? void 0 : errors['firstName']) !== null && _b !== void 0 ? _b : '',
11465
+ testId: "form-item-first-name",
11437
11466
  children: jsx(GoAInput, {
11438
11467
  disabled: !enabled,
11439
11468
  type: "text",
11440
11469
  name: "firstName",
11441
11470
  testId: "name-form-first-name",
11442
11471
  ariaLabel: 'name-form-first-name',
11443
- value: formData.firstName || '',
11472
+ value: formData.firstName,
11444
11473
  onChange: (name, value) => {
11445
11474
  handleInputChange(name, value);
11446
11475
  },
@@ -11474,9 +11503,10 @@ const FullNameDobControl = props => {
11474
11503
  testId: "name-form-last-name",
11475
11504
  ariaLabel: 'name-form-last-name',
11476
11505
  value: formData.lastName || '',
11477
- onChange: (name, value) => handleInputChange(name, value),
11506
+ onChange: (name, value) => {
11507
+ handleInputChange(name, value);
11508
+ },
11478
11509
  onBlur: name => {
11479
- /* istanbul ignore next */
11480
11510
  handleRequiredFieldBlur(name);
11481
11511
  },
11482
11512
  width: "100%"
@@ -11500,7 +11530,7 @@ const FullNameDobControl = props => {
11500
11530
  ariaLabel: "dob-form-dateOfBirth",
11501
11531
  placeholder: "YYYY-MM-DD",
11502
11532
  value: formData === null || formData === void 0 ? void 0 : formData.dateOfBirth,
11503
- onChange: (name, value) => handleDobChange(name, value),
11533
+ onChange: (name, value) => handleInputChange(name, value),
11504
11534
  onBlur: name => {
11505
11535
  /* istanbul ignore next */
11506
11536
  handleRequiredFieldBlur(name);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abgov/jsonforms-components",
3
- "version": "2.4.7",
3
+ "version": "2.5.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Government of Alberta - React renderers for JSON Forms based on the design system.",
6
6
  "repository": "https://github.com/GovAlta/adsp-monorepo",
@@ -1,5 +1,5 @@
1
1
  interface ListItemProps {
2
- selectedIndex: number;
2
+ selected: number;
3
3
  index: number;
4
4
  }
5
5
  export declare const SearchBox: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, never>> & string;
@@ -10,3 +10,4 @@ export declare const handlePostalCodeValidation: (validatePc: boolean, message:
10
10
  export declare const formatPostalCode: (value: string) => string;
11
11
  export declare function detectPostalCodeType(input: string): 'full' | 'partial' | 'none';
12
12
  export declare function formatPostalCodeIfNeeded(input: string): string;
13
+ export declare function handleAddressKeyDown(key: string, value: string, activeIndex: number, suggestions: Suggestion[], onInputChange: (value: string) => void, onSelect: (suggestion: Suggestion) => void): number;
@@ -27,4 +27,3 @@ export interface StepperContextDataType {
27
27
  maxReachedStep: number;
28
28
  }
29
29
  export type CategorizationElement = Category | Categorization;
30
- export declare const TABLE_CONTEXT_ID = 100001;
@@ -1,9 +1,7 @@
1
- type DebounceValueType = string | boolean | number | Record<string, unknown>;
2
1
  /**
3
2
  * A helper util to return a value at a certain delay. The delay is reset if the value arg changes
4
3
  * @param value value to be returned after a period of delay
5
4
  * @param delay time in ms to apply the delay
6
5
  * @returns value after the delay timer
7
6
  */
8
- export declare function useDebounce(value: DebounceValueType, delay: number): DebounceValueType;
9
- export {};
7
+ export declare function useDebounce<T>(value: T, delay: number): T;
@@ -0,0 +1 @@
1
+ export declare function useSyncAutofillFields(fields: string[], formData: any, updateFormData: (data: any) => void, handleRequiredFieldBlur: (name: string, updatedData?: any) => void): void;