@transferwise/components 0.0.0-experimental-946d3a7 → 0.0.0-experimental-bf4cb26

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 (45) hide show
  1. package/build/index.js +157 -57
  2. package/build/index.js.map +1 -1
  3. package/build/index.mjs +157 -59
  4. package/build/index.mjs.map +1 -1
  5. package/build/types/dateInput/DateInput.d.ts +3 -2
  6. package/build/types/dateInput/DateInput.d.ts.map +1 -1
  7. package/build/types/field/Field.d.ts +12 -0
  8. package/build/types/field/Field.d.ts.map +1 -0
  9. package/build/types/index.d.ts +4 -0
  10. package/build/types/index.d.ts.map +1 -1
  11. package/build/types/inputs/Input.d.ts.map +1 -1
  12. package/build/types/inputs/SelectInput.d.ts +1 -2
  13. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  14. package/build/types/inputs/TextArea.d.ts.map +1 -1
  15. package/build/types/inputs/_common.d.ts +2 -2
  16. package/build/types/inputs/_common.d.ts.map +1 -1
  17. package/build/types/inputs/contexts.d.ts +17 -0
  18. package/build/types/inputs/contexts.d.ts.map +1 -0
  19. package/build/types/label/Label.d.ts +9 -0
  20. package/build/types/label/Label.d.ts.map +1 -0
  21. package/build/types/radioGroup/RadioGroup.d.ts.map +1 -1
  22. package/build/types/switch/Switch.d.ts +6 -3
  23. package/build/types/switch/Switch.d.ts.map +1 -1
  24. package/package.json +1 -1
  25. package/src/dateInput/DateInput.rtl.spec.tsx +17 -0
  26. package/src/dateInput/DateInput.tsx +24 -18
  27. package/src/field/Field.spec.tsx +95 -0
  28. package/src/field/Field.story.tsx +59 -0
  29. package/src/field/Field.tsx +70 -0
  30. package/src/index.ts +4 -0
  31. package/src/inputs/Input.tsx +5 -3
  32. package/src/inputs/SelectInput.spec.tsx +8 -9
  33. package/src/inputs/SelectInput.tsx +9 -9
  34. package/src/inputs/TextArea.tsx +6 -3
  35. package/src/inputs/_ButtonInput.tsx +2 -2
  36. package/src/inputs/_common.ts +2 -2
  37. package/src/inputs/contexts.ts +28 -0
  38. package/src/label/Label.spec.tsx +26 -0
  39. package/src/label/Label.story.tsx +37 -0
  40. package/src/label/Label.tsx +20 -0
  41. package/src/radioGroup/RadioGroup.rtl.spec.tsx +14 -0
  42. package/src/radioGroup/RadioGroup.story.tsx +26 -0
  43. package/src/radioGroup/RadioGroup.tsx +4 -1
  44. package/src/switch/Switch.spec.tsx +10 -0
  45. package/src/switch/Switch.tsx +22 -13
package/build/index.js CHANGED
@@ -2622,6 +2622,26 @@ const isMonthAndYearFormat = dateString => validDateString(dateString) && dateSt
2622
2622
  const MDY = new Set(['en-US']);
2623
2623
  const YMD = new Set(['hu', 'hu-HU', 'zh-HK', 'zh-CN', 'ja', 'ja-JP']);
2624
2624
 
2625
+ const FieldLabelIdContext = /*#__PURE__*/React.createContext(undefined);
2626
+ const FieldLabelIdContextProvider = FieldLabelIdContext.Provider;
2627
+ const InputIdContext = /*#__PURE__*/React.createContext(undefined);
2628
+ const InputIdContextProvider = InputIdContext.Provider;
2629
+ const InputDescribedByContext = /*#__PURE__*/React.createContext(undefined);
2630
+ const InputDescribedByProvider = InputDescribedByContext.Provider;
2631
+ const InputInvalidContext = /*#__PURE__*/React.createContext(undefined);
2632
+ const InputInvalidProvider = InputInvalidContext.Provider;
2633
+ function useInputAttributes({
2634
+ nonLabelable = false
2635
+ } = {}) {
2636
+ const labelId = React.useContext(FieldLabelIdContext);
2637
+ return {
2638
+ id: React.useContext(InputIdContext),
2639
+ 'aria-labelledby': nonLabelable ? labelId : undefined,
2640
+ 'aria-describedby': React.useContext(InputDescribedByContext),
2641
+ 'aria-invalid': React.useContext(InputInvalidContext)
2642
+ };
2643
+ }
2644
+
2625
2645
  var messages$9 = reactIntl.defineMessages({
2626
2646
  monthLabel: {
2627
2647
  id: "neptune.DateInput.month.label"
@@ -2646,7 +2666,7 @@ const convertToLocalMidnight = date => {
2646
2666
  };
2647
2667
 
2648
2668
  const DateInput = ({
2649
- 'aria-labelledby': ariaLabelledBy,
2669
+ 'aria-labelledby': ariaLabelledByProp,
2650
2670
  'aria-label': ariaLabel,
2651
2671
  disabled = false,
2652
2672
  size = exports.Size.MEDIUM,
@@ -2662,9 +2682,14 @@ const DateInput = ({
2662
2682
  onFocus,
2663
2683
  onBlur,
2664
2684
  placeholders,
2665
- id,
2685
+ id: idProp,
2666
2686
  selectProps = {}
2667
2687
  }) => {
2688
+ const inputAttributes = useInputAttributes({
2689
+ nonLabelable: true
2690
+ });
2691
+ const id = idProp ?? inputAttributes.id;
2692
+ const ariaLabelledBy = ariaLabelledByProp ?? inputAttributes['aria-labelledby'];
2668
2693
  const {
2669
2694
  locale,
2670
2695
  formatMessage
@@ -2701,9 +2726,9 @@ const DateInput = ({
2701
2726
  const [year, setYear] = React.useState(() => getInitialDate('year'));
2702
2727
  const [lastBroadcastedValue, setLastBroadcastedValue] = React.useState(getDateObject);
2703
2728
  const monthNames = getMonthNames(locale, monthFormat);
2704
- dayLabel = dayLabel || formatMessage(messages$9.dayLabel);
2705
- monthLabel = monthLabel || formatMessage(messages$9.monthLabel);
2706
- yearLabel = yearLabel || formatMessage(messages$9.yearLabel);
2729
+ dayLabel ||= formatMessage(messages$9.dayLabel);
2730
+ monthLabel ||= formatMessage(messages$9.monthLabel);
2731
+ yearLabel ||= formatMessage(messages$9.yearLabel);
2707
2732
  placeholders = {
2708
2733
  day: placeholders?.day || formatMessage(messages$9.dayPlaceholder),
2709
2734
  month: placeholders?.month || formatMessage(messages$9.monthLabel),
@@ -2894,6 +2919,7 @@ const DateInput = ({
2894
2919
  const yearFirst = YMD.has(locale);
2895
2920
  return /*#__PURE__*/jsxRuntime.jsx("div", {
2896
2921
  className: "tw-date",
2922
+ ...inputAttributes,
2897
2923
  id: id,
2898
2924
  "aria-labelledby": ariaLabelledBy,
2899
2925
  "aria-label": ariaLabel,
@@ -2913,15 +2939,15 @@ const DateInput = ({
2913
2939
  return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2914
2940
  children: [getMonth(), getDay(), getYear()]
2915
2941
  });
2916
- } else if (yearFirst) {
2942
+ }
2943
+ if (yearFirst) {
2917
2944
  return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2918
2945
  children: [getYear(), getMonth(), getDay()]
2919
2946
  });
2920
- } else {
2921
- return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2922
- children: [getDay(), getMonth(), getYear()]
2923
- });
2924
2947
  }
2948
+ return /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
2949
+ children: [getDay(), getMonth(), getYear()]
2950
+ });
2925
2951
  })()
2926
2952
  })
2927
2953
  });
@@ -4903,6 +4929,84 @@ const FlowNavigation = ({
4903
4929
  });
4904
4930
  };
4905
4931
 
4932
+ function InlineAlert({
4933
+ id,
4934
+ type = 'neutral',
4935
+ className,
4936
+ children
4937
+ }) {
4938
+ const danger = type === 'negative' || type === 'error';
4939
+ return /*#__PURE__*/jsxRuntime.jsxs("div", {
4940
+ role: "alert",
4941
+ id: id,
4942
+ className: classNames__default.default('alert alert-detach', `alert-${danger ? 'danger' : type}`, className),
4943
+ children: [danger && /*#__PURE__*/jsxRuntime.jsx(icons.AlertCircle, {}), /*#__PURE__*/jsxRuntime.jsx("div", {
4944
+ children: children
4945
+ })]
4946
+ });
4947
+ }
4948
+
4949
+ const Label = ({
4950
+ id,
4951
+ htmlFor,
4952
+ className,
4953
+ children
4954
+ }) => {
4955
+ return /*#__PURE__*/jsxRuntime.jsx("label", {
4956
+ id: id,
4957
+ htmlFor: htmlFor,
4958
+ className: classNames__default.default('control-label d-flex flex-column gap-y-1 m-b-0', className),
4959
+ children: children
4960
+ });
4961
+ };
4962
+
4963
+ const Field = ({
4964
+ id,
4965
+ label,
4966
+ hint,
4967
+ error,
4968
+ className,
4969
+ children
4970
+ }) => {
4971
+ const hasError = Boolean(error);
4972
+ const hasHint = Boolean(hint) && !hasError;
4973
+ const labelId = reactId.useId();
4974
+ const fallbackInputId = reactId.useId(); // TODO: Use `React.useId()` when react>=18
4975
+ const inputId = id !== null ? id ?? fallbackInputId : undefined;
4976
+ const descriptionId = reactId.useId(); // TODO: Use `React.useId()` when react>=18
4977
+ return /*#__PURE__*/jsxRuntime.jsx(FieldLabelIdContextProvider, {
4978
+ value: labelId,
4979
+ children: /*#__PURE__*/jsxRuntime.jsx(InputIdContextProvider, {
4980
+ value: inputId,
4981
+ children: /*#__PURE__*/jsxRuntime.jsx(InputDescribedByProvider, {
4982
+ value: hasError || hasHint ? descriptionId : undefined,
4983
+ children: /*#__PURE__*/jsxRuntime.jsx(InputInvalidProvider, {
4984
+ value: hasError,
4985
+ children: /*#__PURE__*/jsxRuntime.jsxs("div", {
4986
+ className: classNames__default.default('form-group d-block', {
4987
+ 'has-error': hasError,
4988
+ 'has-info': hasHint
4989
+ }, className),
4990
+ children: [/*#__PURE__*/jsxRuntime.jsxs(Label, {
4991
+ id: labelId,
4992
+ htmlFor: inputId,
4993
+ children: [label, children]
4994
+ }), hasHint && /*#__PURE__*/jsxRuntime.jsx(InlineAlert, {
4995
+ type: exports.Sentiment.NEUTRAL,
4996
+ id: descriptionId,
4997
+ children: hint
4998
+ }), hasError && /*#__PURE__*/jsxRuntime.jsx(InlineAlert, {
4999
+ type: exports.Sentiment.NEGATIVE,
5000
+ id: descriptionId,
5001
+ children: error
5002
+ })]
5003
+ })
5004
+ })
5005
+ })
5006
+ })
5007
+ });
5008
+ };
5009
+
4906
5010
  const HeaderAction = ({
4907
5011
  action
4908
5012
  }) => {
@@ -5235,20 +5339,16 @@ const Info = ({
5235
5339
  });
5236
5340
  };
5237
5341
 
5238
- function InlineAlert({
5239
- id,
5240
- type = 'neutral',
5241
- className,
5242
- children
5243
- }) {
5244
- const danger = type === 'negative' || type === 'error';
5245
- return /*#__PURE__*/jsxRuntime.jsxs("div", {
5246
- role: "alert",
5247
- id: id,
5248
- className: classNames__default.default('alert alert-detach', `alert-${danger ? 'danger' : type}`, className),
5249
- children: [danger && /*#__PURE__*/jsxRuntime.jsx(icons.AlertCircle, {}), /*#__PURE__*/jsxRuntime.jsx("div", {
5250
- children: children
5251
- })]
5342
+ function inputClassNameBase({
5343
+ size = 'auto'
5344
+ } = {}) {
5345
+ return classNames__default.default('form-control',
5346
+ // TODO: Deprecate
5347
+ 'np-form-control', {
5348
+ 'np-form-control--size-auto': size === 'auto',
5349
+ 'np-form-control--size-sm': size === 'sm',
5350
+ 'np-form-control--size-md': size === 'md',
5351
+ 'np-form-control--size-lg': size === 'lg'
5252
5352
  });
5253
5353
  }
5254
5354
 
@@ -5370,29 +5470,17 @@ function InputAddon({
5370
5470
  });
5371
5471
  }
5372
5472
 
5373
- function formControlClassNameBase({
5374
- size = 'auto'
5375
- } = {}) {
5376
- return classNames__default.default('form-control',
5377
- // TODO: Deprecate
5378
- 'np-form-control', {
5379
- 'np-form-control--size-auto': size === 'auto',
5380
- 'np-form-control--size-sm': size === 'sm',
5381
- 'np-form-control--size-md': size === 'md',
5382
- 'np-form-control--size-lg': size === 'lg'
5383
- });
5384
- }
5385
-
5386
5473
  const Input = /*#__PURE__*/React.forwardRef(function Input({
5387
5474
  size = 'auto',
5388
5475
  shape = 'rectangle',
5389
5476
  className,
5390
5477
  ...restProps
5391
5478
  }, reference) {
5479
+ const inputAttributes = useInputAttributes();
5392
5480
  const inputPaddings = useInputPaddings();
5393
5481
  return /*#__PURE__*/jsxRuntime.jsx("input", {
5394
5482
  ref: reference,
5395
- className: classNames__default.default(className, formControlClassNameBase({
5483
+ className: classNames__default.default(className, inputClassNameBase({
5396
5484
  size
5397
5485
  }), 'np-input', {
5398
5486
  'np-input--shape-rectangle': shape === 'rectangle',
@@ -5401,6 +5489,7 @@ const Input = /*#__PURE__*/React.forwardRef(function Input({
5401
5489
  // eslint-disable-next-line react/forbid-dom-props
5402
5490
  ,
5403
5491
  style: inputPaddings,
5492
+ ...inputAttributes,
5404
5493
  ...restProps
5405
5494
  });
5406
5495
  });
@@ -5822,12 +5911,6 @@ const PolymorphicWithOverrides = /*#__PURE__*/React.forwardRef(function Polymorp
5822
5911
  });
5823
5912
  });
5824
5913
 
5825
- var messages$5 = reactIntl.defineMessages({
5826
- noResultsFound: {
5827
- id: "neptune.SelectInput.noResultsFound"
5828
- }
5829
- });
5830
-
5831
5914
  function PreventScroll() {
5832
5915
  overlays.usePreventScroll();
5833
5916
  return null;
@@ -5947,7 +6030,7 @@ const ButtonInput = /*#__PURE__*/React.forwardRef(function ButtonInput({
5947
6030
  return /*#__PURE__*/jsxRuntime.jsx("button", {
5948
6031
  ref: ref,
5949
6032
  type: "button",
5950
- className: classNames__default.default(className, formControlClassNameBase({
6033
+ className: classNames__default.default(className, inputClassNameBase({
5951
6034
  size
5952
6035
  }), 'np-button-input')
5953
6036
  // eslint-disable-next-line react/forbid-dom-props
@@ -6063,6 +6146,12 @@ function Popover({
6063
6146
  });
6064
6147
  }
6065
6148
 
6149
+ var messages$5 = reactIntl.defineMessages({
6150
+ noResultsFound: {
6151
+ id: "neptune.SelectInput.noResultsFound"
6152
+ }
6153
+ });
6154
+
6066
6155
  function searchableString(value) {
6067
6156
  return value.trim().replace(/\s+/gu, ' ').normalize('NFKC').toLowerCase();
6068
6157
  }
@@ -6182,7 +6271,7 @@ function SelectInputClearButton({
6182
6271
  }
6183
6272
  const noop = () => {};
6184
6273
  function SelectInput({
6185
- id,
6274
+ id: idProp,
6186
6275
  name,
6187
6276
  multiple,
6188
6277
  placeholder,
@@ -6200,9 +6289,10 @@ function SelectInput({
6200
6289
  className,
6201
6290
  onFilterChange = noop,
6202
6291
  onChange,
6203
- onClear,
6204
- onBlur
6292
+ onClear
6205
6293
  }) {
6294
+ const inputAttributes = useInputAttributes();
6295
+ const id = idProp ?? inputAttributes.id;
6206
6296
  const [open, setOpen] = React.useState(false);
6207
6297
  const [filterQuery, _setFilterQuery] = React.useState('');
6208
6298
  const setFilterQuery = useEffectEvent(query => {
@@ -6252,6 +6342,7 @@ function SelectInput({
6252
6342
  ref(node);
6253
6343
  triggerRef.current = node;
6254
6344
  },
6345
+ ...inputAttributes,
6255
6346
  id,
6256
6347
  ...mergeProps__default.default({
6257
6348
  onClick: () => {
@@ -6261,9 +6352,6 @@ function SelectInput({
6261
6352
  if (event.key === ' ' || event.key === 'Enter' || event.key === 'ArrowDown' || event.key === 'ArrowUp') {
6262
6353
  setOpen(prev => !prev);
6263
6354
  }
6264
- },
6265
- onBlur: () => {
6266
- onBlur?.();
6267
6355
  }
6268
6356
  }, getInteractionProps())
6269
6357
  },
@@ -6608,9 +6696,11 @@ const TextArea = /*#__PURE__*/React.forwardRef(function TextArea({
6608
6696
  className,
6609
6697
  ...restProps
6610
6698
  }, reference) {
6699
+ const inputAttributes = useInputAttributes();
6611
6700
  return /*#__PURE__*/jsxRuntime.jsx("textarea", {
6612
6701
  ref: reference,
6613
- className: classNames__default.default(className, formControlClassNameBase(), 'np-text-area'),
6702
+ className: classNames__default.default(className, inputClassNameBase(), 'np-text-area'),
6703
+ ...inputAttributes,
6614
6704
  ...restProps
6615
6705
  });
6616
6706
  });
@@ -9356,9 +9446,13 @@ function RadioGroup({
9356
9446
  selectedValue: controlledValue,
9357
9447
  onChange
9358
9448
  }) {
9449
+ const inputAttributes = useInputAttributes({
9450
+ nonLabelable: true
9451
+ });
9359
9452
  const [uncontrolledValue, setUncontrolledValue] = React.useState(controlledValue);
9360
9453
  return radios.length > 0 ? /*#__PURE__*/jsxRuntime.jsx("div", {
9361
9454
  role: "radiogroup",
9455
+ ...inputAttributes,
9362
9456
  children: radios.map(({
9363
9457
  value = '',
9364
9458
  ...restProps
@@ -10470,13 +10564,18 @@ const Summary = ({
10470
10564
  };
10471
10565
 
10472
10566
  const Switch = props => {
10567
+ const inputAttributes = useInputAttributes({
10568
+ nonLabelable: true
10569
+ });
10473
10570
  const {
10474
10571
  isModern
10475
10572
  } = componentsTheming.useTheme();
10476
10573
  const {
10477
10574
  checked,
10478
10575
  className,
10479
- id,
10576
+ id = inputAttributes.id,
10577
+ 'aria-label': ariaLabel,
10578
+ 'aria-labelledby': ariaLabelledbyProp,
10480
10579
  onClick,
10481
10580
  disabled
10482
10581
  } = props;
@@ -10507,19 +10606,18 @@ const Switch = props => {
10507
10606
  })
10508
10607
  });
10509
10608
  };
10510
- const ariaLabel = props['aria-label'];
10511
- const ariaLabelledby = ariaLabel ? undefined : props['aria-labelledby'];
10512
- logActionRequiredIf('Switch now expects either `aria-label` or `aria-labelledby`, and will soon make these props required. Please update your usage to provide one or the other.', !ariaLabel && !ariaLabelledby);
10609
+ const ariaLabelledby = (ariaLabel ? undefined : ariaLabelledbyProp) ?? inputAttributes['aria-labelledby'];
10513
10610
  return /*#__PURE__*/jsxRuntime.jsxs("span", {
10514
10611
  className: classNames__default.default('np-switch', {
10515
10612
  'np-switch--unchecked': !checked,
10516
10613
  'np-switch--checked': checked,
10517
- disabled: disabled
10614
+ disabled
10518
10615
  }, className),
10519
10616
  tabIndex: 0,
10520
10617
  role: "switch",
10521
10618
  "aria-checked": checked,
10522
10619
  "aria-label": ariaLabel,
10620
+ ...inputAttributes,
10523
10621
  "aria-labelledby": ariaLabelledby,
10524
10622
  id: id,
10525
10623
  "aria-disabled": disabled,
@@ -14407,6 +14505,7 @@ exports.Display = Display;
14407
14505
  exports.Drawer = Drawer$1;
14408
14506
  exports.DropFade = DropFade;
14409
14507
  exports.Emphasis = Emphasis;
14508
+ exports.Field = Field;
14410
14509
  exports.FlowNavigation = FlowNavigation;
14411
14510
  exports.Header = Header;
14412
14511
  exports.Image = Image;
@@ -14416,6 +14515,7 @@ exports.Input = Input;
14416
14515
  exports.InputGroup = InputGroup;
14417
14516
  exports.InputWithDisplayFormat = InputWithDisplayFormat;
14418
14517
  exports.InstructionsList = InstructionsList;
14518
+ exports.Label = Label;
14419
14519
  exports.LanguageProvider = LanguageProvider;
14420
14520
  exports.Link = Link;
14421
14521
  exports.ListItem = ListItem$1;