@transferwise/components 46.71.2 → 46.71.4

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 (86) hide show
  1. package/build/avatar/Avatar.js +7 -2
  2. package/build/avatar/Avatar.js.map +1 -1
  3. package/build/avatar/Avatar.mjs +7 -2
  4. package/build/avatar/Avatar.mjs.map +1 -1
  5. package/build/badge/Badge.js +13 -2
  6. package/build/badge/Badge.js.map +1 -1
  7. package/build/badge/Badge.mjs +13 -2
  8. package/build/badge/Badge.mjs.map +1 -1
  9. package/build/circularButton/CircularButton.js +10 -2
  10. package/build/circularButton/CircularButton.js.map +1 -1
  11. package/build/circularButton/CircularButton.mjs +10 -2
  12. package/build/circularButton/CircularButton.mjs.map +1 -1
  13. package/build/common/circle/Circle.js +29 -0
  14. package/build/common/circle/Circle.js.map +1 -0
  15. package/build/common/circle/Circle.mjs +27 -0
  16. package/build/common/circle/Circle.mjs.map +1 -0
  17. package/build/inputs/SelectInput.js +38 -5
  18. package/build/inputs/SelectInput.js.map +1 -1
  19. package/build/inputs/SelectInput.mjs +38 -5
  20. package/build/inputs/SelectInput.mjs.map +1 -1
  21. package/build/main.css +7 -114
  22. package/build/moneyInput/MoneyInput.js +1 -1
  23. package/build/moneyInput/MoneyInput.js.map +1 -1
  24. package/build/moneyInput/MoneyInput.mjs +1 -1
  25. package/build/moneyInput/MoneyInput.mjs.map +1 -1
  26. package/build/statusIcon/StatusIcon.js +16 -3
  27. package/build/statusIcon/StatusIcon.js.map +1 -1
  28. package/build/statusIcon/StatusIcon.mjs +16 -3
  29. package/build/statusIcon/StatusIcon.mjs.map +1 -1
  30. package/build/styles/avatar/Avatar.css +0 -29
  31. package/build/styles/badge/Badge.css +0 -10
  32. package/build/styles/circularButton/CircularButton.css +0 -37
  33. package/build/styles/common/circle/Circle.css +7 -0
  34. package/build/styles/main.css +7 -114
  35. package/build/styles/statusIcon/StatusIcon.css +0 -33
  36. package/build/types/avatar/Avatar.d.ts.map +1 -1
  37. package/build/types/badge/Badge.d.ts +9 -2
  38. package/build/types/badge/Badge.d.ts.map +1 -1
  39. package/build/types/circularButton/CircularButton.d.ts.map +1 -1
  40. package/build/types/common/circle/Circle.d.ts +34 -0
  41. package/build/types/common/circle/Circle.d.ts.map +1 -0
  42. package/build/types/common/circle/index.d.ts +3 -0
  43. package/build/types/common/circle/index.d.ts.map +1 -0
  44. package/build/types/inputs/SelectInput.d.ts.map +1 -1
  45. package/build/types/statusIcon/StatusIcon.d.ts +1 -1
  46. package/build/types/statusIcon/StatusIcon.d.ts.map +1 -1
  47. package/package.json +2 -2
  48. package/src/alert/Alert.spec.tsx +3 -1
  49. package/src/avatar/Avatar.css +0 -29
  50. package/src/avatar/Avatar.less +0 -12
  51. package/src/avatar/Avatar.tsx +9 -4
  52. package/src/avatarWrapper/AvatarWrapper.spec.tsx +3 -1
  53. package/src/avatarWrapper/__snapshots__/AvatarWrapper.spec.tsx.snap +22 -11
  54. package/src/badge/Badge.css +0 -10
  55. package/src/badge/Badge.less +0 -13
  56. package/src/badge/Badge.tsx +23 -3
  57. package/src/checkbox/Checkbox.spec.tsx +3 -1
  58. package/src/circularButton/CircularButton.css +0 -37
  59. package/src/circularButton/CircularButton.less +0 -27
  60. package/src/circularButton/CircularButton.spec.tsx +3 -1
  61. package/src/circularButton/CircularButton.tsx +10 -3
  62. package/src/circularButton/__snapshots__/CircularButton.spec.tsx.snap +20 -10
  63. package/src/common/circle/Circle.css +7 -0
  64. package/src/common/circle/Circle.less +6 -0
  65. package/src/common/circle/Circle.story.tsx +86 -0
  66. package/src/common/circle/Circle.tsx +46 -0
  67. package/src/common/circle/index.ts +2 -0
  68. package/src/flowNavigation/__snapshots__/FlowNavigation.spec.js.snap +8 -4
  69. package/src/inlineAlert/InlineAlert.spec.tsx +3 -1
  70. package/src/inputs/SelectInput.docs.mdx +21 -0
  71. package/src/inputs/SelectInput.spec.tsx +94 -1
  72. package/src/inputs/SelectInput.story.tsx +4 -4
  73. package/src/inputs/SelectInput.tsx +45 -4
  74. package/src/main.css +7 -114
  75. package/src/main.less +1 -0
  76. package/src/moneyInput/MoneyInput.rtl.spec.tsx +10 -1
  77. package/src/moneyInput/MoneyInput.tsx +1 -1
  78. package/src/overlayHeader/__snapshots__/OverlayHeader.spec.tsx.snap +4 -2
  79. package/src/radio/__snapshots__/Radio.rtl.spec.tsx.snap +4 -2
  80. package/src/statusIcon/StatusIcon.css +0 -33
  81. package/src/statusIcon/StatusIcon.less +0 -24
  82. package/src/statusIcon/StatusIcon.spec.tsx +5 -3
  83. package/src/statusIcon/StatusIcon.tsx +17 -6
  84. package/src/summary/Summary.spec.tsx +3 -1
  85. package/src/typeahead/Typeahead.spec.js +3 -0
  86. package/src/upload/Upload.spec.js +3 -0
@@ -3,7 +3,7 @@ import { userEvent } from '@testing-library/user-event';
3
3
 
4
4
  import { render, mockMatchMedia, mockResizeObserver } from '../test-utils';
5
5
 
6
- import { SelectInput } from './SelectInput';
6
+ import { SelectInput, type SelectInputOptionItem, type SelectInputProps } from './SelectInput';
7
7
  import { Field } from '../field/Field';
8
8
 
9
9
  mockMatchMedia();
@@ -216,4 +216,97 @@ describe('SelectInput', () => {
216
216
  );
217
217
  expect(screen.getByLabelText(/Currency/)).toHaveAttribute('aria-haspopup');
218
218
  });
219
+
220
+ describe('listbox label', () => {
221
+ const fieldLabel = 'Fruits';
222
+ const triggerLabel = 'Select fruit';
223
+ const options: SelectInputOptionItem[] = [
224
+ { type: 'option', value: 'Banana' },
225
+ { type: 'option', value: 'Orange' },
226
+ { type: 'option', value: 'Olive' },
227
+ ];
228
+ const requiredTriggerButtonProps = {
229
+ id: undefined,
230
+ 'aria-labelledby': undefined,
231
+ 'aria-describedby': undefined,
232
+ 'aria-invalid': undefined,
233
+ 'aria-label': undefined,
234
+ };
235
+
236
+ const renderSelectInput = (props: Omit<SelectInputProps<string | null>, 'items'> = {}) =>
237
+ render(
238
+ <Field label={fieldLabel} id="selectId">
239
+ <SelectInput {...props} items={options} />
240
+ </Field>,
241
+ );
242
+
243
+ it("should propagate trigger's label if nothing is selected", async () => {
244
+ renderSelectInput({
245
+ UNSAFE_triggerButtonProps: {
246
+ ...requiredTriggerButtonProps,
247
+ 'aria-label': triggerLabel,
248
+ },
249
+ });
250
+ const trigger = screen.getByRole('combobox');
251
+ await userEvent.click(trigger);
252
+ expect(screen.getByRole('listbox', { name: triggerLabel })).toBeInTheDocument();
253
+ });
254
+
255
+ it("should propagate trigger's label if an option is selected", async () => {
256
+ renderSelectInput({
257
+ UNSAFE_triggerButtonProps: {
258
+ ...requiredTriggerButtonProps,
259
+ 'aria-label': triggerLabel,
260
+ },
261
+ value: options[1].value,
262
+ });
263
+ const trigger = screen.getByRole('combobox');
264
+ await userEvent.click(trigger);
265
+ expect(screen.getByRole('listbox', { name: triggerLabel })).toBeInTheDocument();
266
+ });
267
+
268
+ it("should propagate trigger's label by id", async () => {
269
+ const customLabelId = 'customLabelId';
270
+ renderSelectInput({
271
+ UNSAFE_triggerButtonProps: {
272
+ ...requiredTriggerButtonProps,
273
+ 'aria-labelledby': customLabelId,
274
+ },
275
+ });
276
+ const trigger = screen.getByRole('combobox');
277
+ await userEvent.click(trigger);
278
+ expect(screen.getByRole('listbox')).toHaveAttribute('aria-labelledby', customLabelId);
279
+ });
280
+
281
+ it("should propagate input's label by id", async () => {
282
+ renderSelectInput();
283
+ const trigger = screen.getByRole('combobox');
284
+ await userEvent.click(trigger);
285
+ expect(screen.getByRole('listbox', { name: fieldLabel })).toBeInTheDocument();
286
+ });
287
+
288
+ it('should prefer explicit label over label ids', async () => {
289
+ const customLabelId = 'customLabelId';
290
+ renderSelectInput({
291
+ UNSAFE_triggerButtonProps: {
292
+ ...requiredTriggerButtonProps,
293
+ 'aria-labelledby': customLabelId,
294
+ 'aria-label': triggerLabel,
295
+ },
296
+ });
297
+ const trigger = screen.getByRole('combobox');
298
+ await userEvent.click(trigger);
299
+ expect(screen.getByRole('listbox', { name: triggerLabel })).toBeInTheDocument();
300
+ expect(screen.getByRole('listbox')).not.toHaveAttribute('aria-labelledby');
301
+ });
302
+
303
+ it('should have no label if none of the above are provided', async () => {
304
+ render(<SelectInput items={options} />);
305
+ const trigger = screen.getByRole('combobox');
306
+ await userEvent.click(trigger);
307
+ const listBox = screen.getByRole('listbox');
308
+ expect(listBox).not.toHaveAttribute('aria-label');
309
+ expect(listBox).not.toHaveAttribute('aria-labelledby');
310
+ });
311
+ });
219
312
  });
@@ -20,7 +20,7 @@ import {
20
20
  } from './SelectInput';
21
21
 
22
22
  const meta = {
23
- title: 'components/SelectInput',
23
+ title: 'Forms/SelectInput',
24
24
  component: SelectInput,
25
25
  tags: ['autodocs'],
26
26
  parameters: { actions: { argTypesRegex: '' } },
@@ -208,8 +208,8 @@ export const Currencies: Story<Currency> = {
208
208
  await userEvent.type(input, '{Backspace}{Backspace}');
209
209
  await expect(within(screen.getByRole('listbox')).queryAllByRole('option')).toHaveLength(2);
210
210
 
211
- await userEvent.type(input, '{Backspace}eu');
212
- await expect(input).toHaveAttribute('aria-activedescendant');
211
+ await userEvent.type(input, '{Backspace}e');
212
+ await expect(input).not.toHaveAttribute('aria-activedescendant');
213
213
  });
214
214
  },
215
215
  };
@@ -347,7 +347,7 @@ export const WithinField = {
347
347
  args: Months.args,
348
348
  decorators: [
349
349
  (Story) => (
350
- <Field message="Something went wrong" sentiment="negative">
350
+ <Field message="Something went wrong" sentiment="negative" label="Month">
351
351
  <Story />
352
352
  </Field>
353
353
  ),
@@ -258,8 +258,9 @@ export function SelectInput<T = string, M extends boolean = false>({
258
258
  onClose,
259
259
  onClear,
260
260
  }: SelectInputProps<T, M>) {
261
- const inputAttributes = useInputAttributes();
261
+ const inputAttributes = useInputAttributes({ nonLabelable: true });
262
262
  const id = idProp ?? inputAttributes.id;
263
+
263
264
  const [open, setOpen] = useState(false);
264
265
 
265
266
  const initialized = useRef(false);
@@ -295,6 +296,35 @@ export function SelectInput<T = string, M extends boolean = false>({
295
296
  const listboxRef = useRef<HTMLDivElement>(null);
296
297
  const controllerRef = filterable ? searchInputRef : listboxRef;
297
298
 
299
+ /**
300
+ * Attempts to resolve the `listbox` label
301
+ * @see https://storybook.wise.design/?path=/docs/forms-selectinput-accessibility--docs#labelling
302
+ */
303
+ const getListBoxLabelProps = (): {
304
+ listBoxLabel?: string;
305
+ listBoxLabelledBy?: string;
306
+ } => {
307
+ if (UNSAFE_triggerButtonProps?.['aria-label']) {
308
+ return {
309
+ listBoxLabel: UNSAFE_triggerButtonProps['aria-label'],
310
+ };
311
+ }
312
+
313
+ if (UNSAFE_triggerButtonProps?.['aria-labelledby']) {
314
+ return {
315
+ listBoxLabelledBy: UNSAFE_triggerButtonProps['aria-labelledby'],
316
+ };
317
+ }
318
+
319
+ if (inputAttributes['aria-labelledby']) {
320
+ return {
321
+ listBoxLabelledBy: inputAttributes['aria-labelledby'],
322
+ };
323
+ }
324
+
325
+ return {};
326
+ };
327
+
298
328
  return (
299
329
  <ListboxBase
300
330
  name={name}
@@ -388,7 +418,7 @@ export function SelectInput<T = string, M extends boolean = false>({
388
418
  }}
389
419
  >
390
420
  <SelectInputOptions
391
- id={`${id}Search`}
421
+ id={id ? `${id}Search` : undefined}
392
422
  items={items}
393
423
  renderValue={renderValue}
394
424
  renderFooter={renderFooter}
@@ -398,6 +428,7 @@ export function SelectInput<T = string, M extends boolean = false>({
398
428
  listboxRef={listboxRef}
399
429
  filterQuery={deferredFilterQuery}
400
430
  onFilterChange={setFilterQuery}
431
+ {...getListBoxLabelProps()}
401
432
  />
402
433
  </OptionsOverlay>
403
434
  );
@@ -496,6 +527,8 @@ interface SelectInputOptionsProps<T = string>
496
527
  listboxRef: React.MutableRefObject<HTMLDivElement | null>;
497
528
  filterQuery: string;
498
529
  onFilterChange: (query: string) => void;
530
+ listBoxLabel?: string;
531
+ listBoxLabelledBy?: string;
499
532
  }
500
533
 
501
534
  function SelectInputOptions<T = string>({
@@ -509,10 +542,12 @@ function SelectInputOptions<T = string>({
509
542
  listboxRef,
510
543
  filterQuery,
511
544
  onFilterChange,
545
+ listBoxLabel,
546
+ listBoxLabelledBy,
512
547
  }: SelectInputOptionsProps<T>) {
513
548
  const intl = useIntl();
514
-
515
549
  const controllerRef = filterable ? searchInputRef : listboxRef;
550
+ const [initialRender, setInitialRender] = useState(true);
516
551
 
517
552
  const needle = useMemo(() => {
518
553
  if (filterable) {
@@ -576,6 +611,10 @@ function SelectInputOptions<T = string>({
576
611
  }
577
612
  }, []);
578
613
 
614
+ useEffect(() => {
615
+ setInitialRender(false);
616
+ }, []);
617
+
579
618
  const showStatus = resultsEmpty;
580
619
  const statusId = useId();
581
620
  const listboxId = useId();
@@ -600,7 +639,7 @@ function SelectInputOptions<T = string>({
600
639
  className="np-select-input-options-container"
601
640
  onAriaActiveDescendantChange={(value: React.AriaAttributes['aria-activedescendant']) => {
602
641
  if (controllerRef.current != null) {
603
- if (value != null) {
642
+ if (!initialRender && value != null) {
604
643
  controllerRef.current.setAttribute('aria-activedescendant', value);
605
644
  } else {
606
645
  controllerRef.current.removeAttribute('aria-activedescendant');
@@ -662,6 +701,8 @@ function SelectInputOptions<T = string>({
662
701
  id={listboxId}
663
702
  role="listbox"
664
703
  aria-orientation="vertical"
704
+ aria-label={listBoxLabel}
705
+ aria-labelledby={listBoxLabelledBy}
665
706
  tabIndex={0}
666
707
  className="np-select-input-listbox"
667
708
  >
package/src/main.css CHANGED
@@ -207,27 +207,18 @@ div.critical-comms .critical-comms-body {
207
207
  }
208
208
  .tw-avatar {
209
209
  position: relative;
210
- border-radius: 50%;
211
210
  -webkit-user-select: none;
212
211
  -moz-user-select: none;
213
212
  user-select: none;
214
213
  box-sizing: border-box;
215
214
  }
216
215
  .tw-avatar .tw-avatar__content {
217
- align-items: center;
218
216
  background-color: rgba(134,167,189,0.10196);
219
217
  background-color: #86a7bd1a;
220
218
  background-color: var(--color-background-neutral);
221
- border-radius: 50%;
222
- color: #37517e;
223
- color: var(--color-content-primary);
224
- display: flex;
225
- height: 100%;
226
- justify-content: center;
227
219
  max-height: 100%;
228
220
  max-width: 100%;
229
221
  overflow: hidden;
230
- width: 100%;
231
222
  }
232
223
  .tw-avatar--outlined {
233
224
  border: 1px solid #00a2dd;
@@ -270,11 +261,6 @@ div.critical-comms .critical-comms-body {
270
261
  color: var(--color-dark-charcoal);
271
262
  line-height: 1;
272
263
  }
273
- .tw-avatar--24 {
274
- min-width: 24px;
275
- width: 24px;
276
- height: 24px;
277
- }
278
264
  .tw-avatar--24.tw-avatar--emoji,
279
265
  .tw-avatar--24.tw-avatar--icon {
280
266
  font-size: 12px;
@@ -305,11 +291,6 @@ div.critical-comms .critical-comms-body {
305
291
  content: "";
306
292
  border-radius: 50%;
307
293
  }
308
- .tw-avatar--40 {
309
- min-width: 40px;
310
- width: 40px;
311
- height: 40px;
312
- }
313
294
  .tw-avatar--40.tw-avatar--emoji,
314
295
  .tw-avatar--40.tw-avatar--icon {
315
296
  font-size: 20px;
@@ -340,11 +321,6 @@ div.critical-comms .critical-comms-body {
340
321
  content: "";
341
322
  border-radius: 50%;
342
323
  }
343
- .tw-avatar--48 {
344
- min-width: 48px;
345
- width: 48px;
346
- height: 48px;
347
- }
348
324
  .tw-avatar--48.tw-avatar--emoji,
349
325
  .tw-avatar--48.tw-avatar--icon {
350
326
  font-size: 24px;
@@ -375,11 +351,6 @@ div.critical-comms .critical-comms-body {
375
351
  content: "";
376
352
  border-radius: 50%;
377
353
  }
378
- .tw-avatar--56 {
379
- min-width: 56px;
380
- width: 56px;
381
- height: 56px;
382
- }
383
354
  .tw-avatar--56.tw-avatar--emoji,
384
355
  .tw-avatar--56.tw-avatar--icon {
385
356
  font-size: 28px;
@@ -410,11 +381,6 @@ div.critical-comms .critical-comms-body {
410
381
  content: "";
411
382
  border-radius: 50%;
412
383
  }
413
- .tw-avatar--72 {
414
- min-width: 72px;
415
- width: 72px;
416
- height: 72px;
417
- }
418
384
  .tw-avatar--72.tw-avatar--emoji,
419
385
  .tw-avatar--72.tw-avatar--icon {
420
386
  font-size: 36px;
@@ -463,10 +429,6 @@ div.critical-comms .critical-comms-body {
463
429
  --badge-mask-offset: calc(var(--badge-size) / 2);
464
430
  --badge-border-color: rgba(255, 255, 255, 0.08);
465
431
  }
466
- .tw-badge.tw-badge-md {
467
- --badge-size: 20px;
468
- --badge-mask: 2px;
469
- }
470
432
  .tw-badge.tw-badge-lg {
471
433
  --badge-size: 24px;
472
434
  --badge-mask: 3px;
@@ -484,16 +446,10 @@ div.critical-comms .critical-comms-body {
484
446
  }
485
447
  .tw-badge > .tw-badge__content {
486
448
  position: absolute;
487
- width: var(--badge-size);
488
- height: var(--badge-size);
489
449
  bottom: 0;
490
450
  right: 0;
491
451
  box-sizing: border-box;
492
- border-radius: 50%;
493
452
  text-align: center;
494
- display: flex;
495
- align-items: center;
496
- justify-content: center;
497
453
  overflow: hidden;
498
454
  -webkit-user-select: none;
499
455
  -moz-user-select: none;
@@ -1029,36 +985,6 @@ div.critical-comms .critical-comms-body {
1029
985
  cursor: pointer;
1030
986
  position: relative;
1031
987
  }
1032
- .np-circular-btn input[type="button"] {
1033
- min-height: 40px;
1034
- min-height: var(--size-40);
1035
- width: 40px;
1036
- width: var(--size-40);
1037
- height: 40px;
1038
- height: var(--size-40);
1039
- padding: 0;
1040
- border-radius: 50%;
1041
- margin-bottom: 8px;
1042
- margin-bottom: var(--size-8);
1043
- }
1044
- .np-theme-personal .np-circular-btn input[type="button"] {
1045
- min-height: 56px;
1046
- min-height: var(--size-56);
1047
- width: 56px;
1048
- width: var(--size-56);
1049
- height: 56px;
1050
- height: var(--size-56);
1051
- }
1052
- @media (max-width: 320px) {
1053
- .np-theme-personal .np-circular-btn input[type="button"] {
1054
- min-height: 64px;
1055
- min-height: var(--size-64);
1056
- width: 64px;
1057
- width: var(--size-64);
1058
- height: 64px;
1059
- height: var(--size-64);
1060
- }
1061
- }
1062
988
  .np-circular-btn .tw-icon {
1063
989
  position: absolute;
1064
990
  top: 16px;
@@ -1099,13 +1025,6 @@ div.critical-comms .critical-comms-body {
1099
1025
  .np-theme-personal .np-circular-btn.negative.secondary:not(.disabled):not(:disabled) .tw-icon {
1100
1026
  color: var(--color-sentiment-negative) !important;
1101
1027
  }
1102
- .np-circular-btn__label {
1103
- transition: color 0.15s ease-in-out;
1104
- font-size: 0.875rem;
1105
- font-size: var(--font-size-14);
1106
- font-weight: 600;
1107
- font-weight: var(--font-weight-semi-bold);
1108
- }
1109
1028
  .np-circular-btn.accent .np-circular-btn__label {
1110
1029
  color: #00a2dd;
1111
1030
  color: var(--color-interactive-accent);
@@ -1232,6 +1151,13 @@ div.critical-comms .critical-comms-body {
1232
1151
  .np-theme-personal .np-circular-btn.negative.secondary:active input[type="button"] + .tw-icon {
1233
1152
  color: var(--color-interactive-control);
1234
1153
  }
1154
+ .np-circle {
1155
+ border-radius: 9999px;
1156
+ border-radius: var(--radius-full);
1157
+ width: var(--circle-size);
1158
+ height: var(--circle-size);
1159
+ flex-shrink: 0;
1160
+ }
1235
1161
  .np-bottom-sheet {
1236
1162
  border-radius: 10px 10px 0 0;
1237
1163
  }
@@ -4224,13 +4150,6 @@ html:not([dir="rtl"]) .np-navigation-option {
4224
4150
  width: 32px;
4225
4151
  width: var(--size-32);
4226
4152
  }
4227
- .status-circle {
4228
- border-radius: 50%;
4229
- display: flex;
4230
- align-items: center;
4231
- justify-content: center;
4232
- flex-shrink: 0;
4233
- }
4234
4153
  .status-circle .light {
4235
4154
  color: var(--color-contrast);
4236
4155
  }
@@ -4246,24 +4165,6 @@ html:not([dir="rtl"]) .np-navigation-option {
4246
4165
  .np-theme-personal .status-circle.pending .status-icon {
4247
4166
  color: var(--color-dark);
4248
4167
  }
4249
- .status-circle-lg {
4250
- width: 48px;
4251
- width: var(--size-48);
4252
- height: 48px;
4253
- height: var(--size-48);
4254
- }
4255
- .status-circle-md {
4256
- width: 40px;
4257
- width: var(--size-40);
4258
- height: 40px;
4259
- height: var(--size-40);
4260
- }
4261
- .status-circle-sm {
4262
- width: 16px;
4263
- width: var(--size-16);
4264
- height: 16px;
4265
- height: var(--size-16);
4266
- }
4267
4168
  .status-circle-sm .status-icon > svg {
4268
4169
  height: 14px;
4269
4170
  height: var(--size-14);
@@ -4278,14 +4179,6 @@ html:not([dir="rtl"]) .np-navigation-option {
4278
4179
  height: var(--size-24);
4279
4180
  }
4280
4181
  }
4281
- @media (max-width: 320px) {
4282
- .status-circle-sm {
4283
- width: 32px;
4284
- width: var(--size-32);
4285
- height: 32px;
4286
- height: var(--size-32);
4287
- }
4288
- }
4289
4182
  .status-circle.negative,
4290
4183
  .status-circle.error {
4291
4184
  background-color: var(--color-sentiment-negative);
package/src/main.less CHANGED
@@ -9,6 +9,7 @@
9
9
  @import "./checkboxButton/CheckboxButton.less";
10
10
  @import "./chips/Chip.less";
11
11
  @import "./circularButton/CircularButton.less";
12
+ @import "./common/circle/Circle.less";
12
13
  @import "./common/bottomSheet/BottomSheet.less";
13
14
  @import "./common/card/Card.less";
14
15
  @import "./common/closeButton/CloseButton.less";
@@ -81,7 +81,7 @@ describe('MoneyInput', () => {
81
81
  </>,
82
82
  );
83
83
 
84
- expect(screen.getByLabelText('Prioritized label')).toHaveClass('input-group');
84
+ expect(screen.getAllByLabelText('Prioritized label')[0]).toHaveClass('input-group');
85
85
  });
86
86
 
87
87
  it('supports `Field` for labeling', () => {
@@ -137,4 +137,13 @@ describe('MoneyInput', () => {
137
137
  messages.selectCurrencyLabel.defaultMessage,
138
138
  );
139
139
  });
140
+
141
+ it('should have a listbox label', async () => {
142
+ render(<MoneyInput {...props} />);
143
+ const trigger = screen.getByRole('combobox');
144
+ await userEvent.click(trigger);
145
+ const triggerLabel = trigger.getAttribute('aria-label');
146
+ expect(triggerLabel).toBeTruthy();
147
+ expect(screen.getByRole('listbox', { name: triggerLabel ?? '' })).toBeInTheDocument();
148
+ });
140
149
  });
@@ -391,7 +391,7 @@ class MoneyInput extends Component<MoneyInputPropsWithInputAttributes, MoneyInpu
391
391
  UNSAFE_triggerButtonProps={{
392
392
  id: undefined,
393
393
  'aria-labelledby': undefined,
394
- 'aria-describedby': undefined,
394
+ 'aria-describedby': ariaLabelledBy,
395
395
  'aria-invalid': undefined,
396
396
  'aria-label': this.props.intl.formatMessage(messages.selectCurrencyLabel),
397
397
  }}
@@ -18,10 +18,12 @@ exports[`OverlayHeader renders as expected 1`] = `
18
18
  class="d-flex align-items-center order-2"
19
19
  >
20
20
  <div
21
- class="tw-avatar tw-avatar--48 tw-avatar--initials np-text-title-body"
21
+ class="np-circle d-flex align-items-center justify-content-center tw-avatar tw-avatar--48 tw-avatar--initials np-text-title-body"
22
+ style="--circle-size: 48px;"
22
23
  >
23
24
  <div
24
- class="tw-avatar__content"
25
+ class="np-circle d-flex align-items-center justify-content-center tw-avatar__content"
26
+ style="--circle-size: 48px;"
25
27
  >
26
28
  TM
27
29
  </div>
@@ -34,10 +34,12 @@ exports[`Radio shows the avatar when supplied 1`] = `
34
34
  class="np-radio__avatar m-l-auto"
35
35
  >
36
36
  <div
37
- class="tw-avatar tw-avatar--48 tw-avatar--initials np-text-title-body"
37
+ class="np-circle d-flex align-items-center justify-content-center tw-avatar tw-avatar--48 tw-avatar--initials np-text-title-body"
38
+ style="--circle-size: 48px;"
38
39
  >
39
40
  <div
40
- class="tw-avatar__content"
41
+ class="np-circle d-flex align-items-center justify-content-center tw-avatar__content"
42
+ style="--circle-size: 48px;"
41
43
  >
42
44
  HD
43
45
  </div>
@@ -4,13 +4,6 @@
4
4
  width: 32px;
5
5
  width: var(--size-32);
6
6
  }
7
- .status-circle {
8
- border-radius: 50%;
9
- display: flex;
10
- align-items: center;
11
- justify-content: center;
12
- flex-shrink: 0;
13
- }
14
7
  .status-circle .light {
15
8
  color: var(--color-contrast);
16
9
  }
@@ -26,24 +19,6 @@
26
19
  .np-theme-personal .status-circle.pending .status-icon {
27
20
  color: var(--color-dark);
28
21
  }
29
- .status-circle-lg {
30
- width: 48px;
31
- width: var(--size-48);
32
- height: 48px;
33
- height: var(--size-48);
34
- }
35
- .status-circle-md {
36
- width: 40px;
37
- width: var(--size-40);
38
- height: 40px;
39
- height: var(--size-40);
40
- }
41
- .status-circle-sm {
42
- width: 16px;
43
- width: var(--size-16);
44
- height: 16px;
45
- height: var(--size-16);
46
- }
47
22
  .status-circle-sm .status-icon > svg {
48
23
  height: 14px;
49
24
  height: var(--size-14);
@@ -58,14 +33,6 @@
58
33
  height: var(--size-24);
59
34
  }
60
35
  }
61
- @media (max-width: 320px) {
62
- .status-circle-sm {
63
- width: 32px;
64
- width: var(--size-32);
65
- height: 32px;
66
- height: var(--size-32);
67
- }
68
- }
69
36
  .status-circle.negative,
70
37
  .status-circle.error {
71
38
  background-color: var(--color-sentiment-negative);
@@ -4,12 +4,6 @@
4
4
  }
5
5
 
6
6
  .status-circle {
7
- border-radius: 50%;
8
- display: flex;
9
- align-items: center;
10
- justify-content: center;
11
- flex-shrink: 0;
12
-
13
7
  .light {
14
8
  color: var(--color-contrast);
15
9
  }
@@ -29,20 +23,7 @@
29
23
  }
30
24
  }
31
25
 
32
- &-lg {
33
- width: var(--size-48);
34
- height: var(--size-48);
35
- }
36
-
37
- &-md {
38
- width: var(--size-40);
39
- height: var(--size-40);
40
- }
41
-
42
26
  &-sm {
43
- width: var(--size-16);
44
- height: var(--size-16);
45
-
46
27
  .status-icon > svg {
47
28
  height: var(--size-14);
48
29
  width: var(--size-14);
@@ -52,11 +33,6 @@
52
33
  height: var(--size-24);
53
34
  }
54
35
  }
55
-
56
- @media (--screen-400-zoom) {
57
- width: var(--size-32);
58
- height: var(--size-32);
59
- }
60
36
  }
61
37
  }
62
38
 
@@ -1,8 +1,10 @@
1
1
  import { Sentiment, Size, SizeLarge, SizeMedium, SizeSmall } from '../common';
2
- import { render, cleanup, screen } from '../test-utils';
2
+ import { render, cleanup, screen, mockMatchMedia } from '../test-utils';
3
3
 
4
4
  import StatusIcon from '.';
5
5
 
6
+ mockMatchMedia();
7
+
6
8
  describe('StatusIcon', () => {
7
9
  const renderStatusIcon = (props?: {
8
10
  sentiment?: Sentiment;
@@ -33,7 +35,7 @@ describe('StatusIcon', () => {
33
35
  ])(
34
36
  "if prop 'size' equals '%s' is passed, renders the status icon with class '%s'",
35
37
  (size, expectedClass) => {
36
- renderStatusIcon({ size: size });
38
+ renderStatusIcon({ size });
37
39
 
38
40
  expect(screen.getByTestId('status-icon')).toHaveClass(expectedClass);
39
41
  cleanup();
@@ -49,7 +51,7 @@ describe('StatusIcon', () => {
49
51
  ])(
50
52
  "if prop 'sentiment' equals '%s' is passed, renders the status icon with class '%s'",
51
53
  (sentiment, expectedClass) => {
52
- renderStatusIcon({ sentiment: sentiment });
54
+ renderStatusIcon({ sentiment });
53
55
 
54
56
  expect(screen.getByTestId('status-icon')).toHaveClass(expectedClass);
55
57
  cleanup();