@transferwise/components 46.160.5 → 46.160.7

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 (110) hide show
  1. package/build/ExpressiveMoneyInput/AmountInput/AmountInput.js +2 -0
  2. package/build/ExpressiveMoneyInput/AmountInput/AmountInput.js.map +1 -1
  3. package/build/ExpressiveMoneyInput/AmountInput/AmountInput.mjs +2 -0
  4. package/build/ExpressiveMoneyInput/AmountInput/AmountInput.mjs.map +1 -1
  5. package/build/ExpressiveMoneyInput/hooks/useInputStyle.js +1 -1
  6. package/build/ExpressiveMoneyInput/hooks/useInputStyle.js.map +1 -1
  7. package/build/ExpressiveMoneyInput/hooks/useInputStyle.mjs +1 -1
  8. package/build/ExpressiveMoneyInput/hooks/useInputStyle.mjs.map +1 -1
  9. package/build/Inputs/Input/Input.js +1 -0
  10. package/build/Inputs/Input/Input.js.map +1 -1
  11. package/build/Inputs/Input/Input.mjs +1 -0
  12. package/build/Inputs/Input/Input.mjs.map +1 -1
  13. package/build/Inputs/TextArea/TextArea.js +1 -0
  14. package/build/Inputs/TextArea/TextArea.js.map +1 -1
  15. package/build/Inputs/TextArea/TextArea.mjs +1 -0
  16. package/build/Inputs/TextArea/TextArea.mjs.map +1 -1
  17. package/build/MoneyInput/MoneyInput.js +1 -0
  18. package/build/MoneyInput/MoneyInput.js.map +1 -1
  19. package/build/MoneyInput/MoneyInput.mjs +1 -0
  20. package/build/MoneyInput/MoneyInput.mjs.map +1 -1
  21. package/build/PhoneNumberInput/PhoneNumberInput.js +3 -2
  22. package/build/PhoneNumberInput/PhoneNumberInput.js.map +1 -1
  23. package/build/PhoneNumberInput/PhoneNumberInput.messages.js +3 -0
  24. package/build/PhoneNumberInput/PhoneNumberInput.messages.js.map +1 -1
  25. package/build/PhoneNumberInput/PhoneNumberInput.messages.mjs +3 -0
  26. package/build/PhoneNumberInput/PhoneNumberInput.messages.mjs.map +1 -1
  27. package/build/PhoneNumberInput/PhoneNumberInput.mjs +3 -2
  28. package/build/PhoneNumberInput/PhoneNumberInput.mjs.map +1 -1
  29. package/build/Prompt/InfoPrompt/InfoPrompt.js +13 -6
  30. package/build/Prompt/InfoPrompt/InfoPrompt.js.map +1 -1
  31. package/build/Prompt/InfoPrompt/InfoPrompt.mjs +13 -6
  32. package/build/Prompt/InfoPrompt/InfoPrompt.mjs.map +1 -1
  33. package/build/Prompt/PrimitivePrompt/PrimitivePrompt.js +1 -0
  34. package/build/Prompt/PrimitivePrompt/PrimitivePrompt.js.map +1 -1
  35. package/build/Prompt/PrimitivePrompt/PrimitivePrompt.mjs +1 -0
  36. package/build/Prompt/PrimitivePrompt/PrimitivePrompt.mjs.map +1 -1
  37. package/build/Typeahead/TypeaheadInput/TypeaheadInput.js +50 -20
  38. package/build/Typeahead/TypeaheadInput/TypeaheadInput.js.map +1 -1
  39. package/build/Typeahead/TypeaheadInput/TypeaheadInput.mjs +50 -20
  40. package/build/Typeahead/TypeaheadInput/TypeaheadInput.mjs.map +1 -1
  41. package/build/i18n/en.json +1 -0
  42. package/build/i18n/en.json.js +1 -0
  43. package/build/i18n/en.json.js.map +1 -1
  44. package/build/i18n/en.json.mjs +1 -0
  45. package/build/i18n/en.json.mjs.map +1 -1
  46. package/build/main.css +68 -1
  47. package/build/styles/ExpressiveMoneyInput/AmountInput/AmountInput.css +6 -0
  48. package/build/styles/ExpressiveMoneyInput/ExpressiveMoneyInput.css +6 -0
  49. package/build/styles/Inputs/Input/Input.css +19 -0
  50. package/build/styles/Inputs/TextArea/TextArea.css +16 -1
  51. package/build/styles/MoneyInput/MoneyInput.css +3 -0
  52. package/build/styles/PhoneNumberInput/PhoneNumberInput.css +7 -0
  53. package/build/styles/Prompt/PrimitivePrompt/PrimitivePrompt.css +17 -0
  54. package/build/styles/main.css +68 -1
  55. package/build/types/ExpressiveMoneyInput/AmountInput/AmountInput.d.ts.map +1 -1
  56. package/build/types/ExpressiveMoneyInput/hooks/useInputStyle.d.ts.map +1 -1
  57. package/build/types/Inputs/Input/Input.d.ts.map +1 -1
  58. package/build/types/Inputs/TextArea/TextArea.d.ts.map +1 -1
  59. package/build/types/MoneyInput/MoneyInput.d.ts.map +1 -1
  60. package/build/types/PhoneNumberInput/PhoneNumberInput.d.ts.map +1 -1
  61. package/build/types/PhoneNumberInput/PhoneNumberInput.messages.d.ts +5 -0
  62. package/build/types/PhoneNumberInput/PhoneNumberInput.messages.d.ts.map +1 -1
  63. package/build/types/Prompt/InfoPrompt/InfoPrompt.d.ts.map +1 -1
  64. package/build/types/Prompt/PrimitivePrompt/PrimitivePrompt.d.ts.map +1 -1
  65. package/build/types/Typeahead/TypeaheadInput/TypeaheadInput.d.ts +1 -1
  66. package/build/types/Typeahead/TypeaheadInput/TypeaheadInput.d.ts.map +1 -1
  67. package/build/types/test-utils/index.d.ts +2 -0
  68. package/build/types/test-utils/index.d.ts.map +1 -1
  69. package/package.json +1 -1
  70. package/src/ExpressiveMoneyInput/AmountInput/AmountInput.css +6 -0
  71. package/src/ExpressiveMoneyInput/AmountInput/AmountInput.less +7 -0
  72. package/src/ExpressiveMoneyInput/AmountInput/AmountInput.tsx +2 -1
  73. package/src/ExpressiveMoneyInput/ExpressiveMoneyInput.css +6 -0
  74. package/src/ExpressiveMoneyInput/hooks/useInputStyle.ts +1 -2
  75. package/src/Field/_stories/Field.test.story.tsx +27 -0
  76. package/src/Inputs/Input/Input.css +19 -0
  77. package/src/Inputs/Input/Input.less +16 -0
  78. package/src/Inputs/Input/Input.tsx +1 -0
  79. package/src/Inputs/{_stories → InputGroup/_stories}/InputGroup.story.tsx +9 -9
  80. package/src/Inputs/InputGroup/_stories/InputGroup.test.story.tsx +80 -0
  81. package/src/Inputs/{_stories → SearchInput/_stories}/SearchInput.story.tsx +4 -4
  82. package/src/Inputs/SearchInput/_stories/SearchInput.test.story.tsx +38 -0
  83. package/src/Inputs/SelectInput/_stories/SelectInput.rtl.test.story.tsx +31 -19
  84. package/src/Inputs/TextArea/TextArea.css +16 -1
  85. package/src/Inputs/TextArea/TextArea.less +6 -1
  86. package/src/Inputs/TextArea/TextArea.tsx +1 -0
  87. package/src/Inputs/{_stories → TextArea/_stories}/TextArea.story.tsx +4 -4
  88. package/src/Inputs/{_stories → TextArea/_stories}/TextArea.test.story.tsx +50 -26
  89. package/src/MoneyInput/MoneyInput.css +3 -0
  90. package/src/MoneyInput/MoneyInput.less +6 -0
  91. package/src/MoneyInput/MoneyInput.tsx +1 -0
  92. package/src/MoneyInput/_stories/MoneyInput.rtl.test.story.tsx +14 -10
  93. package/src/PhoneNumberInput/PhoneNumberInput.css +7 -0
  94. package/src/PhoneNumberInput/PhoneNumberInput.less +13 -0
  95. package/src/PhoneNumberInput/PhoneNumberInput.messages.ts +5 -0
  96. package/src/PhoneNumberInput/PhoneNumberInput.tsx +3 -2
  97. package/src/PhoneNumberInput/_stories/PhoneNumberInput.rtl.test.story.tsx +15 -7
  98. package/src/Prompt/InfoPrompt/InfoPrompt.test.tsx +56 -2
  99. package/src/Prompt/InfoPrompt/InfoPrompt.tsx +14 -7
  100. package/src/Prompt/InfoPrompt/_stories/InfoPrompt.story.tsx +6 -1
  101. package/src/Prompt/PrimitivePrompt/PrimitivePrompt.css +17 -0
  102. package/src/Prompt/PrimitivePrompt/PrimitivePrompt.less +15 -0
  103. package/src/Prompt/PrimitivePrompt/PrimitivePrompt.tsx +1 -0
  104. package/src/TextareaWithDisplayFormat/_stories/TextareaWithDisplayFormat.test.story.tsx +49 -34
  105. package/src/Typeahead/TypeaheadInput/TypeaheadInput.tsx +31 -20
  106. package/src/Typeahead/_stories/Typeahead.rtl.test.story.tsx +90 -57
  107. package/src/_deprecated/Select/_stories/Select.rtl.test.story.tsx +8 -1
  108. package/src/main.css +68 -1
  109. package/src/Inputs/_stories/InputGroup.test.story.tsx +0 -69
  110. package/src/Inputs/_stories/SearchInput.test.story.tsx +0 -32
@@ -15,6 +15,11 @@
15
15
  color: var(--color-interactive-primary);
16
16
  overflow: hidden;
17
17
  margin-bottom: 0 !important;
18
+
19
+ [dir="rtl"] & {
20
+ display: inline-grid;
21
+ grid-template-columns: auto 1fr;
22
+ }
18
23
  }
19
24
 
20
25
  @media (prefers-reduced-motion: reduce) {
@@ -30,6 +35,8 @@
30
35
  text-align: right;
31
36
  background-color: transparent;
32
37
  line-height: inherit;
38
+ field-sizing: content;
39
+ height: inherit;
33
40
 
34
41
  &:focus-visible {
35
42
  outline: none;
@@ -296,13 +296,14 @@ export const AmountInput = ({
296
296
  });
297
297
 
298
298
  return (
299
- <div className="wds-amount-input-container">
299
+ <div className="wds-amount-input-container" dir="ltr">
300
300
  <div
301
301
  className={clsx('wds-amount-input-input-container', 'np-text-display-large--forced')}
302
302
  style={style}
303
303
  >
304
304
  <input
305
305
  ref={ref}
306
+ dir="auto"
306
307
  className="wds-amount-input-input"
307
308
  id={id}
308
309
  autoComplete="off"
@@ -10,6 +10,10 @@
10
10
  overflow: hidden;
11
11
  margin-block-end: 0 !important;
12
12
  }
13
+ [dir="rtl"] .wds-amount-input-input-container {
14
+ display: inline-grid;
15
+ grid-template-columns: auto 1fr;
16
+ }
13
17
  @media (prefers-reduced-motion: reduce) {
14
18
  .wds-amount-input-input-container {
15
19
  transition: none;
@@ -22,6 +26,8 @@
22
26
  text-align: end;
23
27
  background-color: transparent;
24
28
  line-height: inherit;
29
+ field-sizing: content;
30
+ block-size: inherit;
25
31
  }
26
32
  .wds-amount-input-input:focus-visible {
27
33
  outline: none;
@@ -16,14 +16,13 @@ type InputStyleObject = {
16
16
 
17
17
  export const useInputStyle = ({ value, focus, inputElement, loading }: InputStyleObject) => {
18
18
  const initialRender = !useTimeout(300);
19
- const inputWidth = useFirstDefinedValue(inputElement?.current?.clientWidth, [
19
+ const inputWidth = useFirstDefinedValue(inputElement?.current?.parentElement?.clientWidth, [
20
20
  inputElement?.current,
21
21
  value,
22
22
  ]);
23
23
 
24
24
  const getStyle = useCallback((): CSSProperties => {
25
25
  const fontSize = getFontSize(value, focus, inputWidth);
26
-
27
26
  return {
28
27
  fontSize,
29
28
  height: fontSize,
@@ -62,6 +62,22 @@ export const RTLDiff: Story = {
62
62
  const i18n = useRtlDiffI18n();
63
63
  return (
64
64
  <>
65
+ <Field label="Localised placeholder">
66
+ <Input placeholder={i18n.placeholder_ar} />
67
+ </Field>
68
+
69
+ <Field label="Localised value">
70
+ <Input defaultValue={i18n.value_ar} />
71
+ </Field>
72
+
73
+ <Field label="LTR placeholder (email input)">
74
+ <Input placeholder={i18n.placeholder_en} type="email" />
75
+ </Field>
76
+
77
+ <Field label="LTR value (email input)">
78
+ <Input defaultValue={i18n.value_en} />
79
+ </Field>
80
+
65
81
  <Field label={i18n.requiredLabel}>
66
82
  <Input defaultValue={i18n.inputValue} />
67
83
  </Field>
@@ -95,7 +111,18 @@ export const RTLDiff: Story = {
95
111
  );
96
112
  },
97
113
  ...withRTLDiff({
114
+ width: 'stretch',
98
115
  i18n: {
116
+ placeholder_en: { ltr: 'LTR placeholder...', rtl: 'LTR placeholder...' },
117
+ placeholder_ar: { ltr: 'Localised placeholder...', rtl: 'نص بديل مترجم...' },
118
+ value_en: {
119
+ ltr: 'ltr@value.com',
120
+ rtl: 'ltr@value.com',
121
+ },
122
+ value_ar: {
123
+ ltr: 'localised value',
124
+ rtl: 'قيمة مُكيَّفة محلياً',
125
+ },
99
126
  requiredLabel: { ltr: 'Required field', rtl: 'حقل مطلوب' },
100
127
  optionalLabel: { ltr: 'Optional field', rtl: 'حقل اختياري' },
101
128
  descriptionLabel: { ltr: 'Field with description and message', rtl: 'حقل مع وصف ورسالة' },
@@ -147,6 +147,25 @@
147
147
  color: #768e9c;
148
148
  color: var(--color-content-tertiary);
149
149
  }
150
+ [dir="rtl"] .np-input::-moz-placeholder {
151
+ direction: rtl !important;
152
+ }
153
+ [dir="rtl"] .np-input:-moz-placeholder {
154
+ direction: rtl !important;
155
+ }
156
+ [dir="rtl"] .np-input::placeholder,
157
+ [dir="rtl"] .np-input:placeholder-shown {
158
+ direction: rtl !important;
159
+ }
160
+ [dir="rtl"] .np-input:-moz-placeholder[type="email"], [dir="rtl"] .np-input:-moz-placeholder[type="tel"], [dir="rtl"] .np-input:-moz-placeholder[type="number"], [dir="rtl"] .np-input:-moz-placeholder[type="url"] {
161
+ direction: ltr !important;
162
+ }
163
+ [dir="rtl"] .np-input:placeholder-shown[type="email"],
164
+ [dir="rtl"] .np-input:placeholder-shown[type="tel"],
165
+ [dir="rtl"] .np-input:placeholder-shown[type="number"],
166
+ [dir="rtl"] .np-input:placeholder-shown[type="url"] {
167
+ direction: ltr !important;
168
+ }
150
169
  .np-input--shape-rectangle {
151
170
  border-radius: 10px !important;
152
171
  border-radius: var(--radius-small) !important;
@@ -5,6 +5,22 @@
5
5
  color: var(--color-content-tertiary);
6
6
  }
7
7
 
8
+ &::placeholder,
9
+ &:placeholder-shown{
10
+ [dir="rtl"] & {
11
+ direction: rtl !important;
12
+ }
13
+ }
14
+
15
+ &:placeholder-shown[type="email"],
16
+ &:placeholder-shown[type="tel"],
17
+ &:placeholder-shown[type="number"],
18
+ &:placeholder-shown[type="url"] {
19
+ [dir="rtl"] & {
20
+ direction: ltr !important;
21
+ }
22
+ }
23
+
8
24
  &--shape-rectangle {
9
25
  border-radius: var(--radius-small) !important;
10
26
  }
@@ -26,6 +26,7 @@ export const Input = forwardRef(function Input(
26
26
  return (
27
27
  <input
28
28
  ref={reference}
29
+ dir="auto"
29
30
  className={clsx(className, inputClassNameBase({ size }), 'np-input', {
30
31
  'np-input--shape-rectangle': shape === 'rectangle',
31
32
  'np-input--shape-pill': shape === 'pill',
@@ -1,19 +1,19 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
- import { Search } from '@transferwise/icons';
3
2
  import { useRef, useState } from 'react';
3
+ import { Search } from '@transferwise/icons';
4
+ import { Flag } from '@wise/art';
4
5
 
5
6
  import {
6
7
  createSandboxStory,
7
8
  globalScope,
8
- } from '../../../.storybook/components/sandbox/SandboxEditor';
9
- import Field from '../../Field';
9
+ } from '../../../../.storybook/components/sandbox/SandboxEditor';
10
10
 
11
- import { Input } from '../Input/Input';
12
- import { Flag } from '@wise/art';
13
- import { InputGroup } from '../InputGroup/InputGroup';
14
- import AvatarView from '../../AvatarView';
15
- import PhoneNumberInput from '../../PhoneNumberInput';
16
- import Button from '../../Button';
11
+ import Field from '../../../Field';
12
+ import AvatarView from '../../../AvatarView';
13
+ import PhoneNumberInput from '../../../PhoneNumberInput';
14
+ import Button from '../../../Button';
15
+ import { Input } from '../../Input/Input';
16
+ import { InputGroup } from '../InputGroup';
17
17
 
18
18
  export default {
19
19
  component: InputGroup,
@@ -0,0 +1,80 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+ import { Search } from '@transferwise/icons';
3
+
4
+ import { useRtlDiffI18n, withRTLDiff } from '../../../../.storybook/decorators/RtlDiffDecorator';
5
+ import Button from '../../../Button';
6
+ import { Input } from '../../Input/Input';
7
+ import { InputGroup } from '../InputGroup';
8
+
9
+ export default {
10
+ component: InputGroup,
11
+ title: 'Forms/InputGroup/Tests',
12
+ tags: ['!autodocs', '!manifest'],
13
+ } satisfies Meta<typeof InputGroup>;
14
+
15
+ type Story = StoryObj<typeof InputGroup>;
16
+
17
+ /**
18
+ * InputGroup positions addons at the logical start or end of an input.
19
+ * In RTL mode the start and end sides are mirrored, so an addonStart icon
20
+ * should appear on the right and an addonEnd button should appear on the left.
21
+ * All three placement combinations are included to verify each case.
22
+ */
23
+ export const RTLDiff: Story = {
24
+ render: () => {
25
+ const i18n = useRtlDiffI18n();
26
+
27
+ return (
28
+ <>
29
+ <InputGroup
30
+ addonStart={{
31
+ content: <Search size={24} />,
32
+ initialContentWidth: 24,
33
+ }}
34
+ >
35
+ <Input placeholder={i18n.placeholder_start} />
36
+ </InputGroup>
37
+
38
+ <InputGroup
39
+ addonEnd={{
40
+ content: (
41
+ <Button v2 size="sm">
42
+ Copy
43
+ </Button>
44
+ ),
45
+ interactive: true,
46
+ padding: 'sm',
47
+ }}
48
+ >
49
+ <Input placeholder={i18n.placeholder_end} />
50
+ </InputGroup>
51
+
52
+ <InputGroup
53
+ addonStart={{
54
+ content: <Search size={24} />,
55
+ initialContentWidth: 24,
56
+ }}
57
+ addonEnd={{
58
+ content: (
59
+ <Button v2 size="sm">
60
+ Clear
61
+ </Button>
62
+ ),
63
+ interactive: true,
64
+ padding: 'sm',
65
+ }}
66
+ >
67
+ <Input placeholder={i18n.placeholder_both} />
68
+ </InputGroup>
69
+ </>
70
+ );
71
+ },
72
+ ...withRTLDiff({
73
+ i18n:{
74
+ placeholder_both: { ltr: 'addonStart and addonEnd', rtl: 'البادئة والنهاية' },
75
+ placeholder_start: { ltr: 'addonStart only', rtl: 'البادئة فقط' },
76
+ placeholder_end: { ltr: 'addonEnd only', rtl: 'النهاية فقط' },
77
+
78
+ }
79
+ }),
80
+ };
@@ -4,11 +4,11 @@ import { useState } from 'react';
4
4
  import {
5
5
  createSandboxStory,
6
6
  globalScope,
7
- } from '../../../.storybook/components/sandbox/SandboxEditor';
8
- import { Size } from '../../common';
9
- import Field from '../../Field';
7
+ } from '../../../../.storybook/components/sandbox/SandboxEditor';
8
+ import { Size } from '../../../common';
9
+ import Field from '../../../Field';
10
10
 
11
- import { SearchInput } from '../SearchInput/SearchInput';
11
+ import { SearchInput } from '../SearchInput';
12
12
 
13
13
  export default {
14
14
  component: SearchInput,
@@ -0,0 +1,38 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
+
3
+ import { useRtlDiffI18n, withRTLDiff } from '../../../../.storybook/decorators/RtlDiffDecorator';
4
+ import { Size } from '../../../common';
5
+ import { SearchInput } from '../SearchInput';
6
+
7
+ export default {
8
+ component: SearchInput,
9
+ title: 'Forms/SearchInput/Tests',
10
+ tags: ['!autodocs', '!manifest'],
11
+ } satisfies Meta<typeof SearchInput>;
12
+
13
+ type Story = StoryObj<typeof SearchInput>;
14
+
15
+ export const RTLDiff: Story = {
16
+ render: () => {
17
+ const i18n = useRtlDiffI18n();
18
+
19
+ return (
20
+ <>
21
+ {/* sm — search icon (addonStart) position flips to trailing side in RTL */}
22
+ <SearchInput size={Size.SMALL} shape="pill" placeholder={i18n.placeholder} />
23
+
24
+ {/* md — search icon (addonStart) position flips to trailing side in RTL */}
25
+ <SearchInput size={Size.MEDIUM} shape="pill" placeholder={i18n.placeholder} />
26
+
27
+ {/* sm rectangle — icon flip with rectangle shape */}
28
+ <SearchInput size={Size.SMALL} shape="rectangle" placeholder={i18n.placeholder} />
29
+
30
+ {/* md rectangle — icon flip with rectangle shape */}
31
+ <SearchInput size={Size.MEDIUM} shape="rectangle" placeholder={i18n.placeholder} />
32
+ </>
33
+ );
34
+ },
35
+ ...withRTLDiff({i18n:{
36
+ placeholder: { ltr: 'Search', rtl: 'بحث' },
37
+ }}),
38
+ };
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { userEvent, within } from 'storybook/test';
3
3
  import { Flag } from '@wise/art';
4
4
 
5
- import { withRTLDiff } from '../../../../.storybook/decorators/RtlDiffDecorator';
5
+ import { useRtlDiffI18n, withRTLDiff } from '../../../../.storybook/decorators/RtlDiffDecorator';
6
6
  import { SelectInput, SelectInputOptionContent } from '..';
7
7
 
8
8
  export default {
@@ -108,28 +108,40 @@ const groupedItems = [
108
108
  ];
109
109
 
110
110
  export const OpenDropdown: Story = {
111
- render: () => (
112
- <SelectInput
113
- size="lg"
114
- filterable
115
- filterPlaceholder="Type a currency / country"
116
- placeholder="Select currency"
117
- defaultValue={rtlCurrencies[0]}
118
- items={groupedItems}
119
- renderValue={(currency, withinTrigger) => (
120
- <SelectInputOptionContent
121
- title={currency.code}
122
- note={withinTrigger ? undefined : currency.name}
123
- icon={<Flag code={currency.code} intrinsicSize={24} />}
124
- />
125
- )}
126
- />
127
- ),
111
+ render: () => {
112
+ const i18n = useRtlDiffI18n();
113
+ return (
114
+ <SelectInput
115
+ size="lg"
116
+ filterable
117
+ filterPlaceholder={i18n.filterPlaceholder}
118
+ placeholder="Select currency"
119
+ defaultValue={rtlCurrencies[0]}
120
+ items={groupedItems}
121
+ renderValue={(currency, withinTrigger) => (
122
+ <SelectInputOptionContent
123
+ title={currency.code}
124
+ note={withinTrigger ? undefined : currency.name}
125
+ icon={<Flag code={currency.code} intrinsicSize={24} />}
126
+ />
127
+ )}
128
+ />
129
+ );
130
+ },
128
131
  play: async ({ canvasElement }) => {
129
132
  const canvas = within(canvasElement);
130
133
  const trigger = canvas.queryByRole('combobox');
131
134
  if (!trigger) return;
132
135
  await userEvent.click(trigger);
133
136
  },
134
- ...withRTLDiff({ renderAs: 'iframe', width: '80%' }),
137
+ ...withRTLDiff({
138
+ renderAs: 'iframe',
139
+ width: '80%',
140
+ i18n: {
141
+ filterPlaceholder: {
142
+ ltr: 'Type a currency / country',
143
+ rtl: 'اكتب عملة / دولة',
144
+ },
145
+ },
146
+ }),
135
147
  };
@@ -154,7 +154,22 @@
154
154
  color: #768e9c;
155
155
  color: var(--color-content-tertiary);
156
156
  }
157
- .np-text-area::placeholder {
157
+ .np-text-area:-moz-placeholder {
158
158
  color: #768e9c;
159
159
  color: var(--color-content-tertiary);
160
160
  }
161
+ .np-text-area::placeholder,
162
+ .np-text-area:placeholder-shown {
163
+ color: #768e9c;
164
+ color: var(--color-content-tertiary);
165
+ }
166
+ [dir="rtl"] .np-text-area::-moz-placeholder {
167
+ direction: rtl !important;
168
+ }
169
+ [dir="rtl"] .np-text-area:-moz-placeholder {
170
+ direction: rtl !important;
171
+ }
172
+ [dir="rtl"] .np-text-area::placeholder,
173
+ [dir="rtl"] .np-text-area:placeholder-shown {
174
+ direction: rtl !important;
175
+ }
@@ -7,7 +7,12 @@
7
7
  overscroll-behavior: none;
8
8
  border-radius: var(--radius-small) !important;
9
9
 
10
- &::placeholder {
10
+ &::placeholder,
11
+ &:placeholder-shown{
11
12
  color: var(--color-content-tertiary);
13
+
14
+ [dir="rtl"] & {
15
+ direction: rtl !important;
16
+ }
12
17
  }
13
18
  }
@@ -25,6 +25,7 @@ export const TextArea = forwardRef(function TextArea(
25
25
  return (
26
26
  <textarea
27
27
  ref={reference}
28
+ dir="auto"
28
29
  className={clsx(className, inputClassNameBase(), 'np-text-area')}
29
30
  maxLength={maxLength}
30
31
  {...inputAttributes}
@@ -4,10 +4,10 @@ import { Meta, StoryObj } from '@storybook/react-webpack5';
4
4
  import {
5
5
  createSandboxStory,
6
6
  globalScope,
7
- } from '../../../.storybook/components/sandbox/SandboxEditor';
8
- import Field from '../../Field';
9
- import { Sentiment } from '../../common';
10
- import { TextArea } from '../TextArea/TextArea';
7
+ } from '../../../../.storybook/components/sandbox/SandboxEditor';
8
+ import Field from '../../../Field';
9
+ import { Sentiment } from '../../../common';
10
+ import { TextArea } from '../TextArea';
11
11
 
12
12
  /**
13
13
  * TextArea must be the only input with `maxLength` inside a given Field.
@@ -2,10 +2,10 @@ import { useState } from 'react';
2
2
  import { userEvent, within } from 'storybook/test';
3
3
  import { Meta, StoryObj } from '@storybook/react-webpack5';
4
4
 
5
- import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
6
- import Field from '../../Field';
7
- import { Sentiment } from '../../common';
8
- import { TextArea } from '../TextArea/TextArea';
5
+ import { useRtlDiffI18n, withRTLDiff } from '../../../../.storybook/decorators/RtlDiffDecorator';
6
+ import Field from '../../../Field';
7
+ import { Sentiment } from '../../../common';
8
+ import { TextArea } from '../TextArea';
9
9
 
10
10
  const meta: Meta<typeof TextArea> = {
11
11
  title: 'Forms/TextArea/Tests',
@@ -144,26 +144,50 @@ export const CJKTypingBlocked: Story = {
144
144
  };
145
145
 
146
146
  export const RTLDiff: Story = {
147
- render: () => (
148
- <>
149
- <Field label="Message" required={false}>
150
- <TextArea placeholder="Type something..." />
151
- </Field>
152
- <Field label="Message" required={false}>
153
- <TextArea value="This is some entered text content in the textarea field." readOnly />
154
- </Field>
155
- <Field
156
- label="Message"
157
- required={false}
158
- sentiment={Sentiment.NEGATIVE}
159
- message="This field has an error message"
160
- >
161
- <TextArea value="Invalid input text" readOnly />
162
- </Field>
163
- <Field label="Message with character count" required={false}>
164
- <TextArea maxLength={200} value="Text with a character counter displayed below." readOnly />
165
- </Field>
166
- </>
167
- ),
168
- ...withRTLDiff(),
147
+ render: () => {
148
+ const i18n = useRtlDiffI18n();
149
+
150
+ return (
151
+ <>
152
+ <Field label="Localised placeholder">
153
+ <TextArea placeholder={i18n.placeholder} />
154
+ </Field>
155
+ <Field label="Localised value">
156
+ <TextArea value={i18n.value_ar} readOnly />
157
+ </Field>
158
+ <Field label="LTR value">
159
+ <TextArea value={i18n.value_en} readOnly />
160
+ </Field>
161
+ <Field label="Message" sentiment={Sentiment.NEGATIVE} message={i18n.errorMessage}>
162
+ <TextArea value={i18n.invalidInputText} readOnly />
163
+ </Field>
164
+ <Field label="Message with character count">
165
+ <TextArea maxLength={200} value={i18n.characterCounter} readOnly />
166
+ </Field>
167
+ </>
168
+ );
169
+ },
170
+ ...withRTLDiff({
171
+ width: 'stretch',
172
+ i18n: {
173
+ placeholder: { ltr: 'Translated placeholder...', rtl: 'نص بديل مترجم...' },
174
+ value_en: {
175
+ ltr: 'This is LTR text content in the RTL textarea field, showing the input adapting to the content rather than page setting.',
176
+ rtl: 'This is LTR text content in the RTL textarea field, showing the input adapting to the content rather than page setting.',
177
+ },
178
+ value_ar: {
179
+ ltr: 'This is some entered text content in the textarea field.',
180
+ rtl: 'هذا هو بعض المحتوى النصي المدخل في حقل منطقة النص.',
181
+ },
182
+ errorMessage: {
183
+ ltr: 'This field has an error message',
184
+ rtl: 'هذا الحقل يحتوي على رسالة خطأ',
185
+ },
186
+ invalidInputText: { ltr: 'Invalid input text', rtl: 'نص الإدخال غير صالح' },
187
+ characterCounter: {
188
+ ltr: 'Text with a character counter displayed below.',
189
+ rtl: 'نص مع عداد أحرف معروض أدناه.',
190
+ },
191
+ },
192
+ }),
169
193
  };
@@ -10,6 +10,9 @@
10
10
  font-weight: 600;
11
11
  font-weight: var(--font-weight-semi-bold);
12
12
  }
13
+ [dir="rtl"] .tw-money-input .money-input__number-input {
14
+ text-align: end;
15
+ }
13
16
  .tw-money-input .money-input-currency-flag {
14
17
  display: flex;
15
18
  align-items: center;
@@ -11,6 +11,12 @@
11
11
  }
12
12
  }
13
13
 
14
+ .money-input__number-input{
15
+ [dir="rtl"] & {
16
+ text-align: end;
17
+ }
18
+ }
19
+
14
20
  .money-input-currency-flag {
15
21
  display: flex;
16
22
  align-items: center;
@@ -367,6 +367,7 @@ class MoneyInput extends Component<MoneyInputPropsWithInputAttributes, MoneyInpu
367
367
  locale: this.state.locale,
368
368
  decimals: this.props.decimals,
369
369
  })}
370
+ className="money-input__number-input"
370
371
  autoComplete="off"
371
372
  aria-describedby={selectedCurrencyElementId}
372
373
  onKeyDown={this.handleKeyDown}
@@ -1,7 +1,7 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-webpack5';
2
2
  import { userEvent, within } from 'storybook/test';
3
3
 
4
- import { withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
4
+ import { useRtlDiffI18n, withRTLDiff } from '../../../.storybook/decorators/RtlDiffDecorator';
5
5
  import Field from '../../Field/Field';
6
6
  import MoneyInput from '..';
7
7
 
@@ -70,19 +70,23 @@ export const TriggerVariants: Story = {
70
70
  };
71
71
 
72
72
  export const OpenDropdown: Story = {
73
- render: () => (
74
- <MoneyInput
75
- {...sharedProps}
76
- size="lg"
77
- currencies={multipleCurrencies}
78
- selectedCurrency={eurCurrency}
79
- />
80
- ),
73
+ render: () => {
74
+ const i18n = useRtlDiffI18n();
75
+ return (
76
+ <MoneyInput
77
+ {...sharedProps}
78
+ size="lg"
79
+ currencies={multipleCurrencies}
80
+ selectedCurrency={eurCurrency}
81
+ searchPlaceholder={i18n.searchPlaceholder}
82
+ />
83
+ );
84
+ },
81
85
  play: async ({ canvasElement }) => {
82
86
  const canvas = within(canvasElement);
83
87
  const trigger = canvas.queryByRole('combobox');
84
88
  if (!trigger) return;
85
89
  await userEvent.click(trigger);
86
90
  },
87
- ...withRTLDiff({ renderAs: 'iframe', width: '40%' }),
91
+ ...withRTLDiff({ renderAs: 'iframe', width: '40%', i18n: { searchPlaceholder: { ltr: 'Search currencies or countries', rtl: 'ابحث عن العملات أو الدول' } } }),
88
92
  };
@@ -5,6 +5,9 @@
5
5
  margin-inline-start: 12px;
6
6
  margin-inline-start: var(--size-12);
7
7
  }
8
+ [dir="rtl"] .tw-telephone__number-input-field {
9
+ text-align: end;
10
+ }
8
11
  .tw-telephone__country-select {
9
12
  flex-basis: 120px;
10
13
  flex-shrink: 0;
@@ -12,6 +15,10 @@
12
15
  .tw-telephone__country-select .np-input-group {
13
16
  inline-size: 100%;
14
17
  }
18
+ [dir="rtl"] .tw-telephone__country-select .np-select-input-content {
19
+ text-align: end;
20
+ direction: ltr;
21
+ }
15
22
  .tw-telephone__country-select--with-input-group-addon-start .np-button-input {
16
23
  padding-inline-start: 48px;
17
24
  padding-inline-start: var(--size-48);