@transferwise/components 46.89.2 → 46.90.0

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.
@@ -290,6 +290,65 @@ export const MultipleCurrencies: Story<Currency, true> = {
290
290
  },
291
291
  };
292
292
 
293
+ export const WithSelectAll: Story<Currency, true> = {
294
+ args: {
295
+ ...MultipleCurrencies.args,
296
+ },
297
+ render: function Render(args) {
298
+ const [selectedItems, setSelectedItems] = useState<Currency[]>([]);
299
+
300
+ const allSelected = (items: Currency[]) => {
301
+ const selectedSet = new Set(selectedItems);
302
+ return items.every((item) => selectedSet.has(item));
303
+ };
304
+
305
+ const toggleItems = (items: Currency[]) => {
306
+ setSelectedItems((currentItems) =>
307
+ allSelected(items)
308
+ ? currentItems.filter((item) => !items.includes(item))
309
+ : [...new Set([...currentItems, ...items])],
310
+ );
311
+ };
312
+
313
+ return (
314
+ <SelectInput
315
+ {...args}
316
+ items={[
317
+ {
318
+ type: 'group',
319
+ label: 'Popular currencies',
320
+ options: popularCurrencies.map((currency) => currencyOption(currency)),
321
+ action: {
322
+ label: allSelected(popularCurrencies) ? 'Deselect all' : 'Select all',
323
+ onClick: () => {
324
+ toggleItems(popularCurrencies);
325
+ },
326
+ },
327
+ },
328
+ {
329
+ type: 'group',
330
+ label: 'Other currencies',
331
+ options: otherCurrencies.map((currency) => currencyOption(currency)),
332
+ action: {
333
+ label: allSelected(otherCurrencies) ? 'Deselect all' : 'Select all',
334
+ onClick: () => {
335
+ toggleItems(otherCurrencies);
336
+ },
337
+ },
338
+ },
339
+ ]}
340
+ value={selectedItems}
341
+ onChange={(currencies) => {
342
+ setSelectedItems(currencies);
343
+ }}
344
+ onClear={() => {
345
+ setSelectedItems([]);
346
+ }}
347
+ />
348
+ );
349
+ },
350
+ };
351
+
293
352
  export const CustomTrigger: Story<Month> = {
294
353
  args: {
295
354
  placeholder: 'Month',
@@ -30,6 +30,9 @@ import { useInputAttributes, WithInputAttributesProps } from './contexts';
30
30
  import { InputGroup } from './InputGroup';
31
31
  import { SearchInput } from './SearchInput';
32
32
  import messages from './SelectInput.messages';
33
+ import Header from '../header';
34
+ import Section from '../section';
35
+ import { ButtonProps } from '../button/Button.types';
33
36
 
34
37
  const MAX_ITEMS_WITHOUT_VIRTUALIZATION = 50;
35
38
 
@@ -62,6 +65,10 @@ export interface SelectInputGroupItem<T = string> {
62
65
  type: 'group';
63
66
  label: string;
64
67
  options: readonly SelectInputOptionItem<T>[];
68
+ action?: {
69
+ label: string;
70
+ onClick: ButtonProps['onClick'];
71
+ };
65
72
  }
66
73
 
67
74
  export interface SelectInputSeparatorItem {
@@ -810,23 +817,33 @@ function SelectInputGroupItemView<T = string>({
810
817
  }: SelectInputGroupItemViewProps<T>) {
811
818
  const headerId = useId();
812
819
 
820
+ const header = (
821
+ <Header
822
+ as="header"
823
+ role="none"
824
+ id={headerId}
825
+ title={item.label}
826
+ // @ts-expect-error when we migrate ActionButton to new Button this should be sorted
827
+ action={
828
+ item.action && {
829
+ text: item.action.label,
830
+ onClick: item.action.onClick,
831
+ }
832
+ }
833
+ className="np-select-input-group-item-header p-x-1"
834
+ />
835
+ );
836
+
813
837
  return (
814
838
  // An empty container may be rendered when no options match `needle`
815
839
  // However, pre-filtering would result in worse performance overall
816
- <section
840
+ <Section
841
+ as="section"
817
842
  role="group"
818
843
  aria-labelledby={headerId}
819
- className={clsx(needle === null && 'np-select-input-group-item--without-needle')}
844
+ className={clsx('m-y-0', needle === null && 'np-select-input-group-item--without-needle')}
820
845
  >
821
- {needle == null ? (
822
- <header
823
- id={headerId}
824
- role="none"
825
- className="np-select-input-group-item-header np-text-title-group"
826
- >
827
- {item.label}
828
- </header>
829
- ) : null}
846
+ {needle == null ? header : null}
830
847
  {item.options.map((option, index) => (
831
848
  <SelectInputItemView
832
849
  // eslint-disable-next-line react/no-array-index-key
@@ -836,7 +853,7 @@ function SelectInputGroupItemView<T = string>({
836
853
  needle={needle}
837
854
  />
838
855
  ))}
839
- </section>
856
+ </Section>
840
857
  );
841
858
  }
842
859
 
package/src/main.css CHANGED
@@ -2943,10 +2943,6 @@ html:not([dir="rtl"]) .np-flow-navigation--sm .np-flow-navigation__stepper {
2943
2943
  z-index: 10;
2944
2944
  background-color: #ffffff;
2945
2945
  background-color: var(--color-background-elevated);
2946
- padding: 8px 16px 4px;
2947
- padding: var(--size-8) var(--size-16) var(--size-4);
2948
- color: #5d7079;
2949
- color: var(--color-content-secondary);
2950
2946
  }
2951
2947
  .np-select-input-option-container {
2952
2948
  display: flex;
@@ -1,14 +1,15 @@
1
1
  import { clsx } from 'clsx';
2
- import { PropsWithChildren } from 'react';
2
+ import { HTMLAttributes, PropsWithChildren } from 'react';
3
3
 
4
4
  import { CommonProps } from '../common';
5
5
 
6
6
  type SectionProps = PropsWithChildren<
7
7
  CommonProps & {
8
- as?: 'div' | 'fieldset';
8
+ as?: 'div' | 'fieldset' | 'section';
9
9
  withHorizontalPadding?: boolean;
10
10
  }
11
- >;
11
+ > &
12
+ Pick<HTMLAttributes<HTMLDivElement>, 'role' | 'aria-labelledby'>;
12
13
 
13
14
  /**
14
15
  *
@@ -20,12 +21,16 @@ const Section = ({
20
21
  children,
21
22
  className,
22
23
  withHorizontalPadding = false,
24
+ role = undefined,
25
+ ...restProps
23
26
  }: SectionProps) => {
24
27
  return (
25
28
  <Element
26
29
  className={clsx('np-section', className, {
27
30
  'np-section--with-horizontal-padding': withHorizontalPadding,
28
31
  })}
32
+ role={role}
33
+ {...restProps}
29
34
  >
30
35
  {children}
31
36
  </Element>
@@ -18,7 +18,7 @@ type Props = LabelHTMLAttributes<HTMLHeadingElement | HTMLSpanElement | HTMLLabe
18
18
  /**
19
19
  * Default value will based one `type` prop
20
20
  */
21
- as?: 'span' | 'label' | 'li' | 'legend' | Heading;
21
+ as?: 'span' | 'label' | 'li' | 'legend' | 'header' | Heading;
22
22
  /**
23
23
  * Default value: {@link DEFAULT_TYPE}
24
24
  */