@transferwise/components 46.57.1 → 46.58.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.
Files changed (44) hide show
  1. package/build/i18n/tr.json +1 -1
  2. package/build/i18n/tr.json.js +1 -1
  3. package/build/i18n/tr.json.mjs +1 -1
  4. package/build/index.js +2 -0
  5. package/build/index.js.map +1 -1
  6. package/build/index.mjs +1 -0
  7. package/build/index.mjs.map +1 -1
  8. package/build/inlineAlert/InlineAlert.js +3 -1
  9. package/build/inlineAlert/InlineAlert.js.map +1 -1
  10. package/build/inlineAlert/InlineAlert.mjs +3 -1
  11. package/build/inlineAlert/InlineAlert.mjs.map +1 -1
  12. package/build/moneyInput/MoneyInput.js +8 -3
  13. package/build/moneyInput/MoneyInput.js.map +1 -1
  14. package/build/moneyInput/MoneyInput.mjs +8 -3
  15. package/build/moneyInput/MoneyInput.mjs.map +1 -1
  16. package/build/types/index.d.ts +5 -0
  17. package/build/types/index.d.ts.map +1 -1
  18. package/build/types/moneyInput/MoneyInput.d.ts +7 -3
  19. package/build/types/moneyInput/MoneyInput.d.ts.map +1 -1
  20. package/build/types/withId/index.d.ts +3 -0
  21. package/build/types/withId/index.d.ts.map +1 -0
  22. package/build/types/withId/story/source.d.ts +2 -0
  23. package/build/types/withId/story/source.d.ts.map +1 -0
  24. package/build/types/withId/withId.d.ts +17 -0
  25. package/build/types/withId/withId.d.ts.map +1 -0
  26. package/build/withId/withId.js +19 -0
  27. package/build/withId/withId.js.map +1 -0
  28. package/build/withId/withId.mjs +17 -0
  29. package/build/withId/withId.mjs.map +1 -0
  30. package/package.json +3 -3
  31. package/src/i18n/tr.json +1 -1
  32. package/src/index.ts +6 -0
  33. package/src/inlineAlert/InlineAlert.story.tsx +10 -23
  34. package/src/inlineAlert/InlineAlert.tsx +1 -1
  35. package/src/moneyInput/MoneyInput.spec.js +19 -6
  36. package/src/moneyInput/MoneyInput.story.tsx +8 -9
  37. package/src/moneyInput/MoneyInput.tsx +8 -5
  38. package/src/radioGroup/RadioGroup.story.tsx +70 -69
  39. package/src/withId/index.ts +2 -0
  40. package/src/withId/story/source.tsx +22 -0
  41. package/src/withId/withId.docs.mdx +33 -0
  42. package/src/withId/withId.spec.tsx +28 -0
  43. package/src/withId/withId.story.tsx +41 -0
  44. package/src/withId/withId.tsx +27 -0
@@ -1,5 +1,5 @@
1
1
  import { WrappedComponentProps } from 'react-intl';
2
- import { SizeLarge, SizeMedium, SizeSmall } from '../common/propsValues/size';
2
+ import { SizeLarge, SizeMedium, SizeSmall } from '../common';
3
3
  import { WithInputAttributesProps } from '../inputs/contexts';
4
4
  import { SelectInputProps } from '../inputs/SelectInput';
5
5
  export interface CurrencyOptionItem {
@@ -39,8 +39,12 @@ export interface MoneyInputProps extends WrappedComponentProps {
39
39
  selectProps?: Partial<SelectInputProps<CurrencyOptionItem>>;
40
40
  }
41
41
  type MoneyInputPropsWithInputAttributes = MoneyInputProps & Partial<WithInputAttributesProps>;
42
- declare const _default: import("react").FC<import("react-intl").WithIntlProps<Omit<MoneyInputPropsWithInputAttributes, "inputAttributes">>> & {
43
- WrappedComponent: import("react").ComponentType<Omit<MoneyInputPropsWithInputAttributes, "inputAttributes">>;
42
+ declare const _default: import("react").FC<import("react-intl").WithIntlProps<Omit<MoneyInputPropsWithInputAttributes, "inputAttributes"> & {
43
+ id?: string;
44
+ }>> & {
45
+ WrappedComponent: import("react").ComponentType<Omit<MoneyInputPropsWithInputAttributes, "inputAttributes"> & {
46
+ id?: string;
47
+ }>;
44
48
  };
45
49
  export default _default;
46
50
  //# sourceMappingURL=MoneyInput.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MoneyInput.d.ts","sourceRoot":"","sources":["../../../src/moneyInput/MoneyInput.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAc,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAG/D,OAAO,EAAQ,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACpF,OAAO,EAAuB,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAEnF,OAAO,EAKL,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAM/B,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AA0CnE,MAAM,WAAW,eAAgB,SAAQ,qBAAqB;IAC5D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,SAAS,YAAY,EAAE,CAAC;IACpC,gBAAgB,EAAE,kBAAkB,CAAC;IACrC,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IAC1C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,YAAY,EAAE,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3F,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,CAAC;CAC7D;AAED,KAAK,kCAAkC,GAAG,eAAe,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;;;;AAuY9F,wBAAmF"}
1
+ {"version":3,"file":"MoneyInput.d.ts","sourceRoot":"","sources":["../../../src/moneyInput/MoneyInput.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAc,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE/D,OAAO,EAAoB,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAC/E,OAAO,EAAuB,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAEnF,OAAO,EAKL,gBAAgB,EACjB,MAAM,uBAAuB,CAAC;AAO/B,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,YAAY,GAAG,kBAAkB,GAAG,kBAAkB,CAAC;AA0CnE,MAAM,WAAW,eAAgB,SAAQ,qBAAqB;IAC5D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,SAAS,YAAY,EAAE,CAAC;IACpC,gBAAgB,EAAE,kBAAkB,CAAC;IACrC,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;IAC1C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;OAEG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,YAAY,EAAE,CAAA;KAAE,KAAK,IAAI,CAAC;IAC3F,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,WAAW,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,CAAC;CAC7D;AAED,KAAK,kCAAkC,GAAG,eAAe,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAAC;;;;;;;;AA0Y9F,wBAA2F"}
@@ -0,0 +1,3 @@
1
+ export { default } from './withId';
2
+ export type { WithIdProps } from './withId';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/withId/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,YAAY,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const withIdSource = "\nimport { withId, Button, type WithIdProps } from '@transferwise/components';\n\ntype DescribedButtonProps = { id?: string };\ntype DescribedButtonWithIdProps = WithIdProps<DescribedButtonProps>;\n\nfunction DescribedButton({ id }: DescribedButtonWithIdProps) {\n return (\n <>\n <Button aria-describedby={id}>Continue</Button>\n\n <p id={id} className=\"text-xs-center m-t-2\">\n Enter an amount in either GBP or PLN\n <br />\n This paragraph has id of <code>{id}</code>\n </p>\n </>\n );\n}\n\nexport default withId<DescribedButtonProps>(DescribedButton);\n";
2
+ //# sourceMappingURL=source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"source.d.ts","sourceRoot":"","sources":["../../../../src/withId/story/source.tsx"],"names":[],"mappings":"AAAA,eAAO,MAAM,YAAY,imBAqBxB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { ComponentType } from 'react';
2
+ type ChildProps<Props> = Props & {
3
+ id?: string;
4
+ };
5
+ export type WithIdProps<Props> = Props & {
6
+ id: string;
7
+ };
8
+ /**
9
+ * @internal
10
+ * @param {ReactNode} WrappedComponent
11
+ */
12
+ export default function withId<Props extends object>(WrappedComponent: ComponentType<WithIdProps<Props>>): {
13
+ (props: ChildProps<Props>): import("react").JSX.Element;
14
+ displayName: string;
15
+ };
16
+ export {};
17
+ //# sourceMappingURL=withId.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withId.d.ts","sourceRoot":"","sources":["../../../src/withId/withId.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAS,aAAa,EAAE,MAAM,OAAO,CAAC;AAE7C,KAAK,UAAU,CAAC,KAAK,IAAI,KAAK,GAAG;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,KAAK,IAAI,KAAK,GAAG;IACvC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,KAAK,SAAS,MAAM,EACjD,gBAAgB,EAAE,aAAa,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;YAEnB,UAAU,CAAC,KAAK,CAAC;;EASlD"}
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var jsxRuntime = require('react/jsx-runtime');
5
+
6
+ function withId(WrappedComponent) {
7
+ function WithIdComponent(props) {
8
+ const id = React.useId();
9
+ return /*#__PURE__*/jsxRuntime.jsx(WrappedComponent, {
10
+ ...props,
11
+ id: props.id || id
12
+ });
13
+ }
14
+ WithIdComponent.displayName = `withId(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
15
+ return WithIdComponent;
16
+ }
17
+
18
+ module.exports = withId;
19
+ //# sourceMappingURL=withId.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withId.js","sources":["../../src/withId/withId.tsx"],"sourcesContent":["import { useId, ComponentType } from 'react';\n\ntype ChildProps<Props> = Props & {\n id?: string;\n};\n\nexport type WithIdProps<Props> = Props & {\n id: string;\n};\n\n/**\n * @internal\n * @param {ReactNode} WrappedComponent\n */\nexport default function withId<Props extends object>(\n WrappedComponent: ComponentType<WithIdProps<Props>>,\n) {\n function WithIdComponent(props: ChildProps<Props>) {\n const id = useId();\n\n return <WrappedComponent {...props} id={props.id || id} />;\n }\n\n WithIdComponent.displayName = `withId(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;\n\n return WithIdComponent;\n}\n"],"names":["withId","WrappedComponent","WithIdComponent","props","id","useId","_jsx","displayName","name"],"mappings":";;;;;AAcwB,SAAAA,MAAMA,CAC5BC,gBAAmD,EAAA;EAEnD,SAASC,eAAeA,CAACC,KAAwB,EAAA;AAC/C,IAAA,MAAMC,EAAE,GAAGC,WAAK,EAAE,CAAA;IAElB,oBAAOC,cAAA,CAACL,gBAAgB,EAAA;AAAA,MAAA,GAAKE,KAAK;AAAEC,MAAAA,EAAE,EAAED,KAAK,CAACC,EAAE,IAAIA,EAAAA;AAAG,MAAG,CAAA;AAC5D,GAAA;AAEAF,EAAAA,eAAe,CAACK,WAAW,GAAG,CAAA,OAAA,EAAUN,gBAAgB,CAACM,WAAW,IAAIN,gBAAgB,CAACO,IAAI,IAAI,WAAW,CAAG,CAAA,CAAA,CAAA;AAE/G,EAAA,OAAON,eAAe,CAAA;AACxB;;;;"}
@@ -0,0 +1,17 @@
1
+ import { useId } from 'react';
2
+ import { jsx } from 'react/jsx-runtime';
3
+
4
+ function withId(WrappedComponent) {
5
+ function WithIdComponent(props) {
6
+ const id = useId();
7
+ return /*#__PURE__*/jsx(WrappedComponent, {
8
+ ...props,
9
+ id: props.id || id
10
+ });
11
+ }
12
+ WithIdComponent.displayName = `withId(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;
13
+ return WithIdComponent;
14
+ }
15
+
16
+ export { withId as default };
17
+ //# sourceMappingURL=withId.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"withId.mjs","sources":["../../src/withId/withId.tsx"],"sourcesContent":["import { useId, ComponentType } from 'react';\n\ntype ChildProps<Props> = Props & {\n id?: string;\n};\n\nexport type WithIdProps<Props> = Props & {\n id: string;\n};\n\n/**\n * @internal\n * @param {ReactNode} WrappedComponent\n */\nexport default function withId<Props extends object>(\n WrappedComponent: ComponentType<WithIdProps<Props>>,\n) {\n function WithIdComponent(props: ChildProps<Props>) {\n const id = useId();\n\n return <WrappedComponent {...props} id={props.id || id} />;\n }\n\n WithIdComponent.displayName = `withId(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;\n\n return WithIdComponent;\n}\n"],"names":["withId","WrappedComponent","WithIdComponent","props","id","useId","_jsx","displayName","name"],"mappings":";;;AAcwB,SAAAA,MAAMA,CAC5BC,gBAAmD,EAAA;EAEnD,SAASC,eAAeA,CAACC,KAAwB,EAAA;AAC/C,IAAA,MAAMC,EAAE,GAAGC,KAAK,EAAE,CAAA;IAElB,oBAAOC,GAAA,CAACL,gBAAgB,EAAA;AAAA,MAAA,GAAKE,KAAK;AAAEC,MAAAA,EAAE,EAAED,KAAK,CAACC,EAAE,IAAIA,EAAAA;AAAG,MAAG,CAAA;AAC5D,GAAA;AAEAF,EAAAA,eAAe,CAACK,WAAW,GAAG,CAAA,OAAA,EAAUN,gBAAgB,CAACM,WAAW,IAAIN,gBAAgB,CAACO,IAAI,IAAI,WAAW,CAAG,CAAA,CAAA,CAAA;AAE/G,EAAA,OAAON,eAAe,CAAA;AACxB;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transferwise/components",
3
- "version": "46.57.1",
3
+ "version": "46.58.1",
4
4
  "description": "Neptune React components",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -93,8 +93,8 @@
93
93
  "rollup-preserve-directives": "^1.1.1",
94
94
  "storybook": "^8.2.2",
95
95
  "@transferwise/less-config": "3.1.0",
96
- "@wise/components-theming": "1.6.0",
97
- "@transferwise/neptune-css": "14.13.2"
96
+ "@transferwise/neptune-css": "14.13.4",
97
+ "@wise/components-theming": "1.6.0"
98
98
  },
99
99
  "peerDependencies": {
100
100
  "@transferwise/icons": "^3.7.0",
package/src/i18n/tr.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "neptune.DateLookup.year": "yıl",
19
19
  "neptune.FlowNavigation.back": "önceki adıma dön",
20
20
  "neptune.Info.ariaLabel": "Daha fazla bilgi",
21
- "neptune.Label.optional": "(Isteğe bağlı)",
21
+ "neptune.Label.optional": "(İsteğe bağlı)",
22
22
  "neptune.Link.opensInNewTab": "(yeni sekmede açılır)",
23
23
  "neptune.MoneyInput.Select.placeholder": "Bir seçenek seçin...",
24
24
  "neptune.PhoneNumberInput.SelectInput.placeholder": "Bir seçenek seçin...",
package/src/index.ts CHANGED
@@ -89,6 +89,7 @@ export type { TooltipProps } from './tooltip';
89
89
  export type { TypeaheadOption, TypeaheadProps } from './typeahead';
90
90
  export type { UploadProps } from './upload';
91
91
  export type { UploadError, UploadResponse, UploadedFile } from './uploadInput/types';
92
+ export type { WithIdProps } from './withId';
92
93
 
93
94
  /**
94
95
  * Components
@@ -244,3 +245,8 @@ export {
244
245
  * Translations
245
246
  */
246
247
  export { default as translations } from './i18n';
248
+
249
+ /**
250
+ * HoCs
251
+ */
252
+ export { default as withId } from './withId';
@@ -1,10 +1,8 @@
1
- import { select, text } from '@storybook/addon-knobs';
2
1
  import { Meta } from '@storybook/react';
3
2
 
4
- import { Sentiment } from '../common';
5
-
6
3
  import InlineAlert, { InlineAlertProps } from './InlineAlert';
7
- import { lorem40 } from '../test-utils';
4
+ import { lorem10, lorem40 } from '../test-utils';
5
+ import Link from '../link';
8
6
 
9
7
  export default {
10
8
  component: InlineAlert,
@@ -12,23 +10,6 @@ export default {
12
10
  } as Meta<InlineAlertProps>;
13
11
 
14
12
  export const Basic = () => {
15
- const type = select(
16
- 'type',
17
- [
18
- Sentiment.POSITIVE,
19
- Sentiment.NEGATIVE,
20
- Sentiment.NEUTRAL,
21
- Sentiment.WARNING,
22
- Sentiment.PENDING,
23
- Sentiment.ERROR,
24
- Sentiment.INFO,
25
- Sentiment.SUCCESS,
26
- ],
27
- Sentiment.WARNING,
28
- );
29
-
30
- const message = text('message', 'Please enter a password over 5 characters');
31
-
32
13
  return (
33
14
  <>
34
15
  <p>
@@ -36,8 +17,14 @@ export const Basic = () => {
36
17
  control validation, info messaging please use <code>Field</code> component
37
18
  <pre>{'<Field sentiment={..} message={..}>'}</pre>
38
19
  </p>
39
- <InlineAlert type={type}>{message}</InlineAlert>
40
- <InlineAlert type="negative">{message}</InlineAlert>
20
+ <InlineAlert type="neutral">
21
+ You have <Link href="#">20.22 EUR</Link> available in <strong>My savings</strong>
22
+ </InlineAlert>
23
+ <InlineAlert type="warning">
24
+ You have <Link href="#">20.22 EUR</Link> available in <strong>My savings</strong>
25
+ </InlineAlert>
26
+ <InlineAlert type="warning">{lorem10}</InlineAlert>
27
+ <InlineAlert type="negative">{lorem10}</InlineAlert>
41
28
  <InlineAlert type="positive">{lorem40}</InlineAlert>
42
29
  </>
43
30
  );
@@ -47,7 +47,7 @@ export default function InlineAlert({
47
47
  )}
48
48
  >
49
49
  {iconTypes.has(type) && <StatusIcon sentiment={type} size={Size.SMALL} />}
50
- {children}
50
+ <div>{children}</div>
51
51
  </Body>
52
52
  );
53
53
  }
@@ -1,4 +1,5 @@
1
1
  import { shallow } from 'enzyme';
2
+ import { render, screen } from '@testing-library/react';
2
3
 
3
4
  import { MoneyInput, Title, Input, SelectInput } from '..';
4
5
  import { mockMatchMedia, mockResizeObserver } from '../test-utils';
@@ -87,7 +88,9 @@ describe('Money Input', () => {
87
88
  onAmountChange: jest.fn(),
88
89
  onCurrencyChange: jest.fn(),
89
90
  };
90
- component = shallow(<MoneyInput {...props} />).dive();
91
+ component = shallow(<MoneyInput {...props} />)
92
+ .dive()
93
+ .dive();
91
94
  jest.clearAllMocks();
92
95
  });
93
96
 
@@ -207,10 +210,10 @@ describe('Money Input', () => {
207
210
  ]);
208
211
  });
209
212
 
210
- it('renders Select component with undefined id when id is not provided', () => {
213
+ it('renders Select component with generated id when id is not provided', () => {
211
214
  const select = component.find('SelectInput');
212
215
 
213
- expect(select.prop('id')).toBeUndefined();
216
+ expect(select.prop('id')).toBeTruthy();
214
217
  });
215
218
 
216
219
  it('shows the currently active currency as active and hides its note', () => {
@@ -570,9 +573,7 @@ describe('Money Input', () => {
570
573
  );
571
574
 
572
575
  it('passes the id given to the input element', () => {
573
- expect(amountInput().prop('id')).toBeUndefined();
574
576
  component.setProps({ id: 'some-id' });
575
-
576
577
  expect(amountInput().prop('id')).toBe('some-id');
577
578
  });
578
579
 
@@ -804,7 +805,9 @@ describe('Money Input', () => {
804
805
  }}
805
806
  {...props}
806
807
  />,
807
- ).dive();
808
+ )
809
+ .dive()
810
+ .dive();
808
811
  });
809
812
 
810
813
  it('renders Select component with expected props', () => {
@@ -815,4 +818,14 @@ describe('Money Input', () => {
815
818
  });
816
819
  });
817
820
  });
821
+
822
+ describe('withId', () => {
823
+ it('should guarantee id and connect the input with the selected currency', () => {
824
+ render(<MoneyInput {...props} />);
825
+ const input = screen.getByRole('textbox');
826
+ const button = screen.getByRole('combobox');
827
+ expect(input.getAttribute('id')).toBeTruthy();
828
+ expect(input).toHaveAttribute('aria-describedby', button.getAttribute('id'));
829
+ });
830
+ });
818
831
  });
@@ -1,5 +1,5 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
- import { expect, within, userEvent } from '@storybook/test';
2
+ import { within, userEvent } from '@storybook/test';
3
3
  import { Lock } from '@transferwise/icons';
4
4
  import { useState } from 'react';
5
5
 
@@ -9,14 +9,13 @@ import { Field } from '../field/Field';
9
9
  export default {
10
10
  component: MoneyInput,
11
11
  title: 'Forms/MoneyInput',
12
- render: (args) => {
13
- // eslint-disable-next-line react-hooks/rules-of-hooks
12
+ render: function Render(args) {
14
13
  const [selectedCurrency, setSelectedCurrency] = useState(args.selectedCurrency);
15
14
 
16
15
  const handleOnCurrencyChange = (value: CurrencyOptionItem) => setSelectedCurrency(value);
17
16
 
18
17
  return (
19
- <Field id={args.id} label="Editable money input label" required>
18
+ <Field label="Editable money input label" required>
20
19
  <MoneyInput
21
20
  {...args}
22
21
  selectedCurrency={selectedCurrency}
@@ -26,8 +25,8 @@ export default {
26
25
  );
27
26
  },
28
27
  args: {
29
- id: 'money-input',
30
28
  amount: 1000,
29
+ id: 'moneyInput',
31
30
  onAmountChange: () => {},
32
31
  onCurrencyChange: () => {},
33
32
  },
@@ -61,28 +60,28 @@ const exampleCurrency = {
61
60
  hkd: {
62
61
  value: 'HKD',
63
62
  label: 'HKD',
64
- note: 'Hong Kong',
63
+ note: 'Hong Kong dollar',
65
64
  currency: 'hkd',
66
65
  searchable: 'Hong Kong, Saudi Arabia',
67
66
  },
68
67
  aud: {
69
68
  value: 'AUD',
70
69
  label: 'AUD',
71
- note: 'Australia',
70
+ note: 'Australian dollar',
72
71
  currency: 'aud',
73
72
  searchable: 'Kenguru',
74
73
  },
75
74
  cny: {
76
75
  value: 'CNY',
77
76
  label: 'CNY',
78
- note: 'China',
77
+ note: 'Chinese yuan',
79
78
  currency: 'cny',
80
79
  searchable: 'China',
81
80
  },
82
81
  jpy: {
83
82
  value: 'JPY',
84
83
  label: 'JPY',
85
- note: 'Japan',
84
+ note: 'Japanese yen',
86
85
  currency: 'jpy',
87
86
  searchable: 'Japan',
88
87
  },
@@ -4,8 +4,7 @@ import { clsx } from 'clsx';
4
4
  import { Component } from 'react';
5
5
  import { injectIntl, WrappedComponentProps } from 'react-intl';
6
6
 
7
- import { Typography } from '../common';
8
- import { Size, SizeLarge, SizeMedium, SizeSmall } from '../common/propsValues/size';
7
+ import { Typography, Size, SizeLarge, SizeMedium, SizeSmall } from '../common';
9
8
  import { withInputAttributes, WithInputAttributesProps } from '../inputs/contexts';
10
9
  import { Input } from '../inputs/Input';
11
10
  import {
@@ -19,6 +18,7 @@ import Title from '../title';
19
18
 
20
19
  import messages from './MoneyInput.messages';
21
20
  import { formatAmount, parseAmount } from './currencyFormatting';
21
+ import withId from '../withId';
22
22
 
23
23
  export interface CurrencyOptionItem {
24
24
  header?: never;
@@ -288,7 +288,6 @@ class MoneyInput extends Component<MoneyInputPropsWithInputAttributes, MoneyInpu
288
288
  selectProps,
289
289
  } = this.props;
290
290
  const ariaLabelledBy = ariaLabelledByProp ?? inputAttributes?.['aria-labelledby'];
291
-
292
291
  const selectOptions = this.getSelectOptions();
293
292
 
294
293
  const hasSingleCurrency = () => {
@@ -314,8 +313,9 @@ class MoneyInput extends Component<MoneyInputPropsWithInputAttributes, MoneyInpu
314
313
  };
315
314
 
316
315
  const isFixedCurrency = (!this.state.searchQuery && hasSingleCurrency()) || !onCurrencyChange;
317
-
318
316
  const disabled = !this.props.onAmountChange;
317
+ const selectedCurrencyElementId = `${amountInputId}SelectedCurrency`;
318
+
319
319
  return (
320
320
  <div
321
321
  role="group"
@@ -338,6 +338,7 @@ class MoneyInput extends Component<MoneyInputPropsWithInputAttributes, MoneyInpu
338
338
  locale: this.state.locale,
339
339
  })}
340
340
  autoComplete="off"
341
+ aria-describedby={selectedCurrencyElementId}
341
342
  onKeyDown={this.handleKeyDown}
342
343
  onChange={this.onAmountChange}
343
344
  onFocus={this.onAmountFocus}
@@ -363,6 +364,7 @@ class MoneyInput extends Component<MoneyInputPropsWithInputAttributes, MoneyInpu
363
364
  this.style('tw-money-input__fixed-currency'),
364
365
  disabled ? this.style('disabled') : '',
365
366
  )}
367
+ id={selectedCurrencyElementId}
366
368
  >
367
369
  {(size === 'lg' || size === 'md') && (
368
370
  <span className={clsx(this.style('money-input-currency-flag'), this.style('m-r-2'))}>
@@ -385,6 +387,7 @@ class MoneyInput extends Component<MoneyInputPropsWithInputAttributes, MoneyInpu
385
387
  )}
386
388
  >
387
389
  <SelectInput
390
+ id={selectedCurrencyElementId}
388
391
  items={selectOptions}
389
392
  value={selectedCurrency}
390
393
  compareValues="currency"
@@ -488,4 +491,4 @@ function sortOptionsLabelsToFirst(options: readonly CurrencyOptionItem[], query:
488
491
  });
489
492
  }
490
493
 
491
- export default injectIntl(withInputAttributes(MoneyInput, { nonLabelable: true }));
494
+ export default injectIntl(withId(withInputAttributes(MoneyInput, { nonLabelable: true })));
@@ -1,81 +1,82 @@
1
- import { action } from '@storybook/addon-actions';
2
- import { boolean } from '@storybook/addon-knobs';
3
1
  import { Flag } from '@wise/art';
4
2
 
5
3
  import Avatar, { AvatarType } from '../avatar';
6
4
 
7
- import RadioGroup from './RadioGroup';
5
+ import RadioGroup, { RadioGroupProps, RadioGroupRadio } from './RadioGroup';
8
6
  import { Field } from '../field/Field';
7
+ import { Meta, StoryObj } from '@storybook/react';
8
+ import { fn } from '@storybook/test';
9
+ import { useState } from 'react';
9
10
 
10
- export default {
11
+ const meta = {
11
12
  component: RadioGroup,
12
13
  title: 'Forms/RadioGroup',
13
- };
14
+ } satisfies Meta<typeof RadioGroup>;
14
15
 
15
- export const Basic = () => {
16
- const showAvatars = boolean('avatar', false);
17
- const hasError = boolean('hasError', false);
16
+ export default meta;
17
+ type Story<T extends string | number = string> = StoryObj<RadioGroupProps<T>>;
18
18
 
19
- const avatar = showAvatars ? (
20
- <Avatar type={AvatarType.THUMBNAIL}>
21
- <Flag code="NZD" />
22
- </Avatar>
23
- ) : undefined;
19
+ export const Basic = {
20
+ args: {
21
+ selectedValue: 'radio-2',
22
+ name: 'radio-group',
23
+ radios: [
24
+ {
25
+ value: 'radio-1',
26
+ label: 'Radio1',
27
+ secondary: 'Secondary line 1',
28
+ disabled: false,
29
+ },
30
+ {
31
+ value: 'radio-2',
32
+ label: 'Radio2',
33
+ disabled: false,
34
+ },
35
+ {
36
+ value: 'radio-3',
37
+ label: 'Radio3',
38
+ secondary: 'Secondary line 3',
39
+ disabled: true,
40
+ },
41
+ ],
42
+ onChange: fn(),
43
+ },
44
+ } satisfies Story;
24
45
 
25
- return (
26
- <div className={`form-group ${hasError ? 'has-error' : ''}`}>
27
- <RadioGroup
28
- selectedValue="radio-2"
29
- name="radio-group"
30
- radios={[
31
- {
32
- value: 'radio-1',
33
- label: 'Radio1',
34
- secondary: 'Secondary line 1',
35
- disabled: false,
36
- avatar,
37
- },
38
- {
39
- value: 'radio-2',
40
- label: 'Radio2',
41
- disabled: false,
42
- avatar,
43
- },
44
- {
45
- value: 'radio-3',
46
- label: 'Radio3',
47
- secondary: 'Secondary line 3',
48
- disabled: true,
49
- avatar,
50
- },
51
- ]}
52
- onChange={(v) => action(v)}
53
- />
54
- </div>
55
- );
56
- };
46
+ export const WithAvatars = {
47
+ ...Basic,
48
+ args: {
49
+ ...Basic.args,
50
+ radios: Basic.args.radios.map(
51
+ (radio) =>
52
+ ({
53
+ ...radio,
54
+ avatar: (
55
+ <Avatar type={AvatarType.THUMBNAIL}>
56
+ <Flag code="NZD" />
57
+ </Avatar>
58
+ ),
59
+ }) satisfies RadioGroupRadio,
60
+ ),
61
+ },
62
+ } satisfies Story;
57
63
 
58
- export const Labeled = () => {
59
- return (
60
- <Field label="Do you like our product?">
61
- <RadioGroup
62
- name="radio-group"
63
- radios={[
64
- {
65
- value: 'yes',
66
- label: 'Yes',
67
- },
68
- {
69
- value: 'definitely',
70
- label: 'Definitely',
71
- },
72
- {
73
- value: 'absolutely',
74
- label: 'Absolutely',
75
- },
76
- ]}
77
- onChange={(v) => action(v)}
78
- />
79
- </Field>
80
- );
81
- };
64
+ export const WithinField = {
65
+ ...Basic,
66
+ render: function Render(args) {
67
+ const [selectedValue, setSelectedValue] = useState('radio-2');
68
+ const hasError = selectedValue === 'radio-2';
69
+ return (
70
+ <Field
71
+ {...(hasError
72
+ ? {
73
+ message: 'Something went wrong',
74
+ sentiment: 'negative',
75
+ }
76
+ : undefined)}
77
+ >
78
+ <RadioGroup {...args} selectedValue={selectedValue} onChange={setSelectedValue} />
79
+ </Field>
80
+ );
81
+ },
82
+ } satisfies Story;
@@ -0,0 +1,2 @@
1
+ export { default } from './withId';
2
+ export type { WithIdProps } from './withId';
@@ -0,0 +1,22 @@
1
+ export const withIdSource = `
2
+ import { withId, Button, type WithIdProps } from '@transferwise/components';
3
+
4
+ type DescribedButtonProps = { id?: string };
5
+ type DescribedButtonWithIdProps = WithIdProps<DescribedButtonProps>;
6
+
7
+ function DescribedButton({ id }: DescribedButtonWithIdProps) {
8
+ return (
9
+ <>
10
+ <Button aria-describedby={id}>Continue</Button>
11
+
12
+ <p id={id} className="text-xs-center m-t-2">
13
+ Enter an amount in either GBP or PLN
14
+ <br />
15
+ This paragraph has id of <code>{id}</code>
16
+ </p>
17
+ </>
18
+ );
19
+ }
20
+
21
+ export default withId<DescribedButtonProps>(DescribedButton);
22
+ `;
@@ -0,0 +1,33 @@
1
+ import { Meta, Canvas, Source } from '@storybook/blocks';
2
+ import { WithoutId, WithCustomId, WithEmptyId } from './withId.story';
3
+ import { withIdSource } from './story/source';
4
+
5
+ <Meta title="HoCs/withId" />
6
+
7
+ # withId
8
+
9
+ This Higher Order Component injects an SSR-friendly `id` prop to any given component. It's especially useful for class-based components where you need to connect a number of elements via aria attributes, yet `useId` hook is unavailable.
10
+
11
+ > **Please note:** this component will be eventually deprecated as we move away from the class components to function components, so use with care.
12
+
13
+ ## Usage
14
+
15
+ Given a dummy `DescribedButtonProps` component, when wrapped in the `widthId` HoC…
16
+
17
+ <Source code={withIdSource} dark />
18
+
19
+ …it will be automatically provided with a generated `id` prop…
20
+
21
+ <Canvas of={WithoutId} language="tsx" />
22
+
23
+ …but it will also respect a custom `id`, if provided.
24
+
25
+ <Canvas of={WithCustomId} language="tsx" />
26
+
27
+ ### Empty strings
28
+
29
+ The HTML spec considers [empty 'id' attributes as invalid](https://html.spec.whatwg.org/multipage/dom.html#the-id-attribute), and given that the main purpose of this HoC is to offer cross-element connectivity for accessibility purposes, any falsy value provided to the `id` prop for the wrapped component would most likely cause a fault.
30
+
31
+ Because of that, all falsy `id` prop values will be overridden by this HoC.
32
+
33
+ <Canvas of={WithEmptyId} language="tsx" />