@transferwise/components 0.0.0-experimental-e95c8a5 → 0.0.0-experimental-39ab3ec

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 (90) hide show
  1. package/build/index.esm.js +65 -688
  2. package/build/index.esm.js.map +1 -1
  3. package/build/index.js +65 -691
  4. package/build/index.js.map +1 -1
  5. package/build/main.css +1 -1
  6. package/build/styles/common/closeButton/CloseButton.css +1 -1
  7. package/build/styles/inputs/Input.css +1 -1
  8. package/build/styles/inputs/InputGroup.css +1 -1
  9. package/build/styles/inputs/TextArea.css +1 -1
  10. package/build/styles/main.css +1 -1
  11. package/build/styles/promoCard/PromoCard.css +1 -1
  12. package/build/types/common/card/index.d.ts +1 -0
  13. package/build/types/common/card/index.d.ts.map +1 -1
  14. package/build/types/dateLookup/dateTrigger/DateTrigger.messages.d.ts +7 -7
  15. package/build/types/dateLookup/dateTrigger/DateTrigger.messages.d.ts.map +1 -1
  16. package/build/types/index.d.ts +0 -4
  17. package/build/types/index.d.ts.map +1 -1
  18. package/build/types/inputs/Input.d.ts +0 -1
  19. package/build/types/inputs/Input.d.ts.map +1 -1
  20. package/build/types/inputs/_common.d.ts.map +1 -1
  21. package/build/types/promoCard/PromoCard.d.ts +9 -3
  22. package/build/types/promoCard/PromoCard.d.ts.map +1 -1
  23. package/build/types/promoCard/PromoCardIndicator.d.ts +5 -3
  24. package/build/types/promoCard/PromoCardIndicator.d.ts.map +1 -1
  25. package/package.json +22 -28
  26. package/src/avatar/Avatar.story.tsx +16 -14
  27. package/src/common/card/Card.tsx +1 -1
  28. package/src/common/card/index.ts +1 -0
  29. package/src/common/closeButton/CloseButton.css +1 -1
  30. package/src/index.ts +0 -8
  31. package/src/inputs/Input.css +1 -1
  32. package/src/inputs/Input.less +0 -14
  33. package/src/inputs/Input.tsx +2 -6
  34. package/src/inputs/InputGroup.css +1 -1
  35. package/src/inputs/InputGroup.less +0 -5
  36. package/src/inputs/TextArea.css +1 -1
  37. package/src/inputs/TextArea.less +0 -5
  38. package/src/inputs/_common.less +4 -0
  39. package/src/inputs/_common.ts +1 -0
  40. package/src/main.css +1 -1
  41. package/src/main.less +0 -4
  42. package/src/navigationOption/NavigationOption.story.js +3 -5
  43. package/src/promoCard/PromoCard.css +1 -1
  44. package/src/promoCard/PromoCard.less +9 -9
  45. package/src/promoCard/PromoCard.spec.tsx +1 -0
  46. package/src/promoCard/PromoCard.story.tsx +86 -29
  47. package/src/promoCard/PromoCard.tsx +69 -22
  48. package/src/promoCard/PromoCardIndicator.tsx +20 -8
  49. package/src/radio/Radio.story.js +3 -2
  50. package/src/radioGroup/RadioGroup.story.js +2 -1
  51. package/src/select/searchBox/__snapshots__/SearchBox.spec.js.snap +1 -1
  52. package/src/ssr.spec.js +0 -7
  53. package/build/styles/inputs/SelectInput.css +0 -1
  54. package/build/types/common/hooks/useMedia.d.ts +0 -2
  55. package/build/types/common/hooks/useMedia.d.ts.map +0 -1
  56. package/build/types/common/hooks/useScreenSize.d.ts +0 -3
  57. package/build/types/common/hooks/useScreenSize.d.ts.map +0 -1
  58. package/build/types/common/preventScroll/PreventScroll.d.ts +0 -2
  59. package/build/types/common/preventScroll/PreventScroll.d.ts.map +0 -1
  60. package/build/types/inputs/SearchInput.d.ts +0 -10
  61. package/build/types/inputs/SearchInput.d.ts.map +0 -1
  62. package/build/types/inputs/SelectInput.d.ts +0 -41
  63. package/build/types/inputs/SelectInput.d.ts.map +0 -1
  64. package/build/types/inputs/_BottomSheet.d.ts +0 -17
  65. package/build/types/inputs/_BottomSheet.d.ts.map +0 -1
  66. package/build/types/inputs/_ButtonInput.d.ts +0 -6
  67. package/build/types/inputs/_ButtonInput.d.ts.map +0 -1
  68. package/build/types/inputs/_Popover.d.ts +0 -18
  69. package/build/types/inputs/_Popover.d.ts.map +0 -1
  70. package/build/types/utilities/wrapInFragment.d.ts +0 -3
  71. package/build/types/utilities/wrapInFragment.d.ts.map +0 -1
  72. package/src/common/hooks/useMedia.spec.ts +0 -39
  73. package/src/common/hooks/useMedia.ts +0 -15
  74. package/src/common/hooks/useScreenSize.ts +0 -7
  75. package/src/common/preventScroll/PreventScroll.tsx +0 -6
  76. package/src/inputs/SearchInput.story.tsx +0 -40
  77. package/src/inputs/SearchInput.tsx +0 -35
  78. package/src/inputs/SelectInput.css +0 -1
  79. package/src/inputs/SelectInput.less +0 -183
  80. package/src/inputs/SelectInput.spec.tsx +0 -120
  81. package/src/inputs/SelectInput.story.tsx +0 -259
  82. package/src/inputs/SelectInput.tsx +0 -565
  83. package/src/inputs/_BottomSheet.less +0 -107
  84. package/src/inputs/_BottomSheet.tsx +0 -128
  85. package/src/inputs/_ButtonInput.less +0 -7
  86. package/src/inputs/_ButtonInput.tsx +0 -27
  87. package/src/inputs/_Popover.less +0 -38
  88. package/src/inputs/_Popover.tsx +0 -118
  89. package/src/utilities/wrapInFragment.tsx +0 -3
  90. /package/src/dateLookup/dateTrigger/{DateTrigger.messages.ts → DateTrigger.messages.js} +0 -0
@@ -1,41 +0,0 @@
1
- /// <reference types="react" />
2
- interface SelectInputOptionItem<T = string> {
3
- type: 'option';
4
- value: T;
5
- filterMatchers?: readonly string[];
6
- disabled?: boolean;
7
- }
8
- interface SelectInputGroupItem<T = string> {
9
- type: 'group';
10
- label: string;
11
- options: readonly SelectInputOptionItem<T>[];
12
- }
13
- interface SelectInputSeparatorItem {
14
- type: 'separator';
15
- }
16
- export type SelectInputItem<T = string> = SelectInputOptionItem<T> | SelectInputGroupItem<T> | SelectInputSeparatorItem;
17
- export interface SelectInputProps<T = string> {
18
- name?: string;
19
- placeholder?: string;
20
- items: readonly SelectInputItem<NonNullable<T>>[];
21
- defaultValue?: T;
22
- value?: T;
23
- renderValue?: (value: NonNullable<T>, compact: boolean) => React.ReactNode;
24
- compareValues?: (keyof NonNullable<T> & string) | ((a: T | undefined, b: T | undefined) => boolean);
25
- filterable?: boolean;
26
- filterPlaceholder?: string;
27
- disabled?: boolean;
28
- className?: string;
29
- onChange?: (value: T) => void;
30
- onClear?: () => void;
31
- }
32
- export declare function SelectInput<T>({ name, placeholder, items, defaultValue, value: controlledValue, renderValue, compareValues, filterable, filterPlaceholder, disabled, className, onChange, onClear, }: SelectInputProps<T>): import("react").JSX.Element;
33
- export interface SelectInputOptionContentProps {
34
- title: string;
35
- note?: string;
36
- description?: string;
37
- icon?: React.ReactNode;
38
- }
39
- export declare function SelectInputOptionContent({ title, note, description, icon, }: SelectInputOptionContentProps): import("react").JSX.Element;
40
- export {};
41
- //# sourceMappingURL=SelectInput.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SelectInput.d.ts","sourceRoot":"","sources":["../../../src/inputs/SelectInput.tsx"],"names":[],"mappings":";AA0CA,UAAU,qBAAqB,CAAC,CAAC,GAAG,MAAM;IACxC,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,CAAC,CAAC;IACT,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,UAAU,oBAAoB,CAAC,CAAC,GAAG,MAAM;IACvC,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,SAAS,qBAAqB,CAAC,CAAC,CAAC,EAAE,CAAC;CAC9C;AAED,UAAU,wBAAwB;IAChC,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,MAAM,eAAe,CAAC,CAAC,GAAG,MAAM,IAClC,qBAAqB,CAAC,CAAC,CAAC,GACxB,oBAAoB,CAAC,CAAC,CAAC,GACvB,wBAAwB,CAAC;AAuC7B,MAAM,WAAW,gBAAgB,CAAC,CAAC,GAAG,MAAM;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,KAAK,EAAE,SAAS,eAAe,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAClD,YAAY,CAAC,EAAE,CAAC,CAAC;IACjB,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC,SAAS,CAAC;IAC3E,aAAa,CAAC,EACV,CAAC,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GAC/B,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,CAAC,GAAG,SAAS,KAAK,OAAO,CAAC,CAAC;IACtD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAC7B,IAAI,EACJ,WAAW,EACX,KAAK,EACL,YAAY,EACZ,KAAK,EAAE,eAAe,EACtB,WAA4B,EAC5B,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,OAAO,GACR,EAAE,gBAAgB,CAAC,CAAC,CAAC,+BA2GrB;AA2QD,MAAM,WAAW,6BAA6B;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACxB;AAED,wBAAgB,wBAAwB,CAAC,EACvC,KAAK,EACL,IAAI,EACJ,WAAW,EACX,IAAI,GACL,EAAE,6BAA6B,+BA4C/B"}
@@ -1,17 +0,0 @@
1
- /// <reference types="react" />
2
- export interface BottomSheetProps {
3
- open: boolean;
4
- renderTrigger?: (args: {
5
- ref: React.RefCallback<Element>;
6
- getInteractionProps: (customEventHandlers?: React.HTMLProps<Element>) => {
7
- [key: string]: unknown;
8
- };
9
- }) => React.ReactNode;
10
- title?: string;
11
- initialFocusRef?: React.RefObject<HTMLElement>;
12
- padding?: 'none' | 'md';
13
- children?: React.ReactNode;
14
- onClose?: () => void;
15
- }
16
- export declare function BottomSheet({ open, renderTrigger, title, initialFocusRef, padding, children, onClose, }: BottomSheetProps): import("react").JSX.Element;
17
- //# sourceMappingURL=_BottomSheet.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_BottomSheet.d.ts","sourceRoot":"","sources":["../../../src/inputs/_BottomSheet.tsx"],"names":[],"mappings":";AAgBA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE;QACrB,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAChC,mBAAmB,EAAE,CAAC,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK;YACvE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CAAC;KACH,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,aAAa,EACb,KAAK,EACL,eAAe,EACf,OAAc,EACd,QAAQ,EACR,OAAO,GACR,EAAE,gBAAgB,+BAwFlB"}
@@ -1,6 +0,0 @@
1
- /// <reference types="react" />
2
- export interface ButtonInputProps extends React.ComponentPropsWithRef<'button'> {
3
- size?: 'sm' | 'md' | 'lg';
4
- }
5
- export declare const ButtonInput: import("react").ForwardRefExoticComponent<Omit<ButtonInputProps, "ref"> & import("react").RefAttributes<HTMLButtonElement>>;
6
- //# sourceMappingURL=_ButtonInput.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_ButtonInput.d.ts","sourceRoot":"","sources":["../../../src/inputs/_ButtonInput.tsx"],"names":[],"mappings":";AAMA,MAAM,WAAW,gBAAiB,SAAQ,KAAK,CAAC,qBAAqB,CAAC,QAAQ,CAAC;IAC7E,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC3B;AAED,eAAO,MAAM,WAAW,6HAgBtB,CAAC"}
@@ -1,18 +0,0 @@
1
- /// <reference types="react" />
2
- import { type Side } from '@floating-ui/react';
3
- export interface PopoverProps {
4
- placement?: Side;
5
- open: boolean;
6
- renderTrigger: (args: {
7
- ref: React.RefCallback<Element>;
8
- getInteractionProps: (customEventHandlers?: React.HTMLProps<Element>) => {
9
- [key: string]: unknown;
10
- };
11
- }) => React.ReactNode;
12
- title?: string;
13
- padding?: 'none' | 'md';
14
- children?: React.ReactNode;
15
- onClose?: () => void;
16
- }
17
- export declare function Popover({ placement, open, renderTrigger, title, padding, children, onClose, }: PopoverProps): import("react").JSX.Element;
18
- //# sourceMappingURL=_Popover.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"_Popover.d.ts","sourceRoot":"","sources":["../../../src/inputs/_Popover.tsx"],"names":[],"mappings":";AAAA,OAAO,EAOL,KAAK,IAAI,EAMV,MAAM,oBAAoB,CAAC;AAO5B,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,aAAa,EAAE,CAAC,IAAI,EAAE;QACpB,GAAG,EAAE,KAAK,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAChC,mBAAmB,EAAE,CAAC,mBAAmB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK;YACvE,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;SACxB,CAAC;KACH,KAAK,KAAK,CAAC,SAAS,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAID,wBAAgB,OAAO,CAAC,EACtB,SAAS,EACT,IAAI,EACJ,aAAa,EACb,KAAK,EACL,OAAc,EACd,QAAQ,EACR,OAAO,GACR,EAAE,YAAY,+BAwEd"}
@@ -1,3 +0,0 @@
1
- /// <reference types="react" />
2
- export declare function wrapInFragment(value: unknown): import("react").JSX.Element;
3
- //# sourceMappingURL=wrapInFragment.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrapInFragment.d.ts","sourceRoot":"","sources":["../../../src/utilities/wrapInFragment.tsx"],"names":[],"mappings":";AAAA,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,+BAE5C"}
@@ -1,39 +0,0 @@
1
- import { renderHook } from '@testing-library/react-hooks';
2
-
3
- import { useMedia } from './useMedia';
4
-
5
- Object.defineProperty(window, 'matchMedia', {
6
- writable: true,
7
- value: jest.fn().mockImplementation((query: string) => {
8
- const matches = /^\(min-width: ([0-9]+)px\)$/.exec(query);
9
- const minWidth = matches != null ? Number(matches[1]) : undefined;
10
- return {
11
- matches: minWidth != null ? window.innerWidth >= minWidth : false,
12
- media: query,
13
- onchange: null,
14
- addListener: jest.fn(), // deprecated
15
- removeListener: jest.fn(), // deprecated
16
- addEventListener: jest.fn(),
17
- removeEventListener: jest.fn(),
18
- dispatchEvent: jest.fn(),
19
- };
20
- }),
21
- });
22
-
23
- describe('useMedia', () => {
24
- it('tracks changes of window size', () => {
25
- const { result, rerender } = renderHook(() => useMedia('(min-width: 1024px)'));
26
-
27
- window.innerWidth = 1920;
28
- rerender();
29
- expect(result.current).toBeTruthy();
30
-
31
- window.innerWidth = 1024;
32
- rerender();
33
- expect(result.current).toBeTruthy();
34
-
35
- window.innerWidth = 800;
36
- rerender();
37
- expect(result.current).toBeFalsy();
38
- });
39
- });
@@ -1,15 +0,0 @@
1
- import { useSyncExternalStore } from 'use-sync-external-store/shim';
2
-
3
- export function useMedia(query: string) {
4
- return useSyncExternalStore(
5
- (onStoreChange) => {
6
- const mediaQueryList = window.matchMedia(query);
7
- mediaQueryList.addEventListener('change', onStoreChange);
8
- return () => {
9
- mediaQueryList.removeEventListener('change', onStoreChange);
10
- };
11
- },
12
- () => (typeof window !== 'undefined' ? window.matchMedia(query).matches : undefined),
13
- () => undefined,
14
- );
15
- }
@@ -1,7 +0,0 @@
1
- import { Breakpoint } from '../propsValues/breakpoint';
2
-
3
- import { useMedia } from './useMedia';
4
-
5
- export function useScreenSize(size: Breakpoint) {
6
- return useMedia(`(min-width: ${size}px)`);
7
- }
@@ -1,6 +0,0 @@
1
- import { usePreventScroll } from '@react-aria/overlays';
2
-
3
- export function PreventScroll() {
4
- usePreventScroll();
5
- return null;
6
- }
@@ -1,40 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react';
2
- import { useState } from 'react';
3
-
4
- import { SearchInput } from './SearchInput';
5
-
6
- export default {
7
- component: SearchInput,
8
- title: 'Forms/SearchInput',
9
- } satisfies Meta<typeof SearchInput>;
10
-
11
- type Story = StoryObj<typeof SearchInput>;
12
-
13
- export const Basic: Story = {
14
- render: (args) => <SearchInputBasic {...args} />,
15
- args: {
16
- size: 'md',
17
- shape: 'pill',
18
- disabled: false,
19
- },
20
- argTypes: {
21
- onChange: {
22
- action: 'changed',
23
- },
24
- },
25
- };
26
-
27
- function SearchInputBasic({ onChange, ...restProps }: NonNullable<Story['args']>) {
28
- const [value, setValue] = useState('Text value');
29
-
30
- return (
31
- <SearchInput
32
- {...restProps}
33
- value={value}
34
- onChange={(event) => {
35
- setValue(event.currentTarget.value);
36
- onChange?.(event);
37
- }}
38
- />
39
- );
40
- }
@@ -1,35 +0,0 @@
1
- import { Search } from '@transferwise/icons';
2
- import { forwardRef } from 'react';
3
-
4
- import { Merge } from '../utils';
5
-
6
- import { Input } from './Input';
7
- import { InputGroup } from './InputGroup';
8
-
9
- export interface SearchInputProps
10
- extends Merge<
11
- React.ComponentPropsWithRef<'input'>,
12
- {
13
- size?: 'sm' | 'md';
14
- shape?: 'rectangle' | 'pill';
15
- 'aria-invalid'?: boolean;
16
- }
17
- > {}
18
-
19
- export const SearchInput = forwardRef(function SearchInput(
20
- { shape = 'pill', disabled, className, ...restProps }: SearchInputProps,
21
- ref: React.ForwardedRef<HTMLInputElement>,
22
- ) {
23
- return (
24
- <InputGroup
25
- addonStart={{
26
- content: <Search size={24} />,
27
- initialContentWidth: 24,
28
- }}
29
- disabled={disabled}
30
- className={className}
31
- >
32
- <Input ref={ref} role="searchbox" inputMode="search" shape={shape} {...restProps} />
33
- </InputGroup>
34
- );
35
- });
@@ -1 +0,0 @@
1
- .np-select-input-placeholder{color:#768e9c;color:var(--color-content-tertiary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.np-select-input-options-container{display:flex;flex-direction:column;height:100%}.np-select-input-options-container:focus{outline:none}@media (min-width:576px){.np-select-input-options-container{max-height:28rem}}.np-select-input-query-container{display:flex;flex-direction:column;padding:8px;padding:var(--size-8);padding-top:0}@media (min-width:576px){.np-select-input-query-container{padding-top:8px;padding-top:var(--size-8)}}.np-select-input-listbox-container{height:var(--initial-height);overflow-y:auto;position:relative;scroll-padding-bottom:8px;scroll-padding-bottom:var(--size-8);scroll-padding-top:8px;scroll-padding-top:var(--size-8)}@media (min-width:576px){.np-select-input-listbox-container{height:auto}}.np-select-input-listbox-container--has-group{scroll-padding-top:32px;scroll-padding-top:var(--size-32)}.np-select-input-listbox{padding:8px;padding:var(--size-8)}.np-select-input-listbox:focus{outline:none}.np-select-input-separator-item{border-top-width:1px;margin:8px;margin:var(--size-8)}.np-select-input-group-item--without-needle:first-child{margin-top:-8px;margin-top:calc(var(--size-8)*-1)}.np-select-input-group-item-header{background-color:#fff;background-color:var(--color-background-elevated);color:#5d7079;color:var(--color-content-secondary);padding:8px 16px 4px;padding:var(--size-8) var(--size-16) var(--size-4);position:sticky;top:0;z-index:10}.np-select-input-option-container{align-items:center;border-radius:10px;border-radius:var(--radius-small);color:#37517e;color:var(--color-content-primary);-moz-column-gap:8px;column-gap:8px;-moz-column-gap:var(--size-8);column-gap:var(--size-8);display:flex;padding:12px 16px;padding:var(--size-12) var(--size-16)}.np-select-input-option-container--active{background-color:var(--color-background-screen-hover)}.np-select-input-option-container--disabled{opacity:.45}.np-select-input-option-check--not-selected{visibility:hidden}.np-select-input-option{flex:1}.np-select-input-option-content-container{align-items:center;color:#37517e;color:var(--color-content-primary);-moz-column-gap:8px;column-gap:8px;-moz-column-gap:var(--size-8);column-gap:var(--size-8);display:flex}.np-select-input-option-content-icon{display:flex}.np-select-input-option-content-icon--not-compact{align-self:flex-start}.np-select-input-option-content-text{display:flex;flex:1;flex-direction:column;overflow:hidden}.np-select-input-option-content-text-secondary{color:#5d7079;color:var(--color-content-secondary)}.np-select-input-option-content-text-compact{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.np-select-input-option-content-text-line-1>:not([hidden])~:not([hidden]){margin-left:8px;margin-left:var(--size-8);margin-right:8px;margin-right:var(--size-8)}.np-select-input-addon-container{align-items:center;display:inline-flex;pointer-events:none}.np-select-input-addon{align-items:center;background:none;border-radius:.125rem;border-width:0;display:inline-flex;height:32px;height:var(--size-32);justify-content:center;width:32px;width:var(--size-32)}.np-select-input-addon--interactive{color:#c9cbce;color:var(--color-interactive-secondary);pointer-events:auto}.np-select-input-addon--interactive:hover{color:#b5b7ba;color:var(--color-interactive-secondary-hover)}.np-select-input-addon--interactive:focus{outline:none}.np-select-input-addon--interactive:focus-visible{outline:var(--ring-outline-color) solid var(--ring-outline-width);outline-offset:var(--ring-outline-offset)}.np-select-input-addon-separator{border-inline-start-width:1px;height:24px;height:var(--size-24)}
@@ -1,183 +0,0 @@
1
- @import (reference) "../../node_modules/@transferwise/neptune-css/src/less/ring.less";
2
-
3
- .np-select-input-placeholder {
4
- overflow: hidden;
5
- text-overflow: ellipsis;
6
- white-space: nowrap;
7
- color: var(--color-content-tertiary);
8
- }
9
-
10
- .np-select-input-options-container {
11
- display: flex;
12
- height: 100%;
13
- flex-direction: column;
14
-
15
- &:focus {
16
- outline: none;
17
- }
18
-
19
- @media (--screen-sm) {
20
- & {
21
- max-height: 28rem /* 448px */;
22
- }
23
- }
24
- }
25
-
26
- .np-select-input-query-container {
27
- display: flex;
28
- flex-direction: column;
29
- padding: var(--size-8);
30
- padding-top: 0px;
31
-
32
- @media (--screen-sm) {
33
- & {
34
- padding-top: var(--size-8);
35
- }
36
- }
37
- }
38
-
39
- .np-select-input-listbox-container {
40
- position: relative;
41
- height: var(--initial-height);
42
- scroll-padding-top: var(--size-8);
43
- scroll-padding-bottom: var(--size-8);
44
- overflow-y: auto;
45
-
46
- @media (--screen-sm) {
47
- & {
48
- height: auto;
49
- }
50
- }
51
-
52
- &--has-group {
53
- scroll-padding-top: var(--size-32);
54
- }
55
- }
56
-
57
- .np-select-input-listbox {
58
- padding: var(--size-8);
59
-
60
- &:focus {
61
- outline: none;
62
- }
63
- }
64
-
65
- .np-select-input-separator-item {
66
- margin: var(--size-8);
67
- border-top-width: 1px;
68
- }
69
-
70
- .np-select-input-group-item {
71
- &--without-needle:first-child {
72
- margin-top: calc(-1 * var(--size-8));
73
- }
74
- }
75
-
76
- .np-select-input-group-item-header {
77
- position: sticky;
78
- top: 0px;
79
- z-index: 10;
80
- background-color: var(--color-background-elevated);
81
- padding: var(--size-8) var(--size-16) var(--size-4);
82
- color: var(--color-content-secondary);
83
- }
84
-
85
- .np-select-input-option-container {
86
- display: flex;
87
- align-items: center;
88
- column-gap: var(--size-8);
89
- border-radius: var(--radius-small);
90
- padding: var(--size-12) var(--size-16);
91
- color: var(--color-content-primary);
92
-
93
- &--active {
94
- background-color: var(--color-background-screen-hover);
95
- }
96
-
97
- &--disabled {
98
- opacity: 0.45;
99
- }
100
- }
101
-
102
- .np-select-input-option-check {
103
- &--not-selected {
104
- visibility: hidden;
105
- }
106
- }
107
-
108
- .np-select-input-option {
109
- flex: 1;
110
- }
111
-
112
- .np-select-input-option-content-container {
113
- display: flex;
114
- align-items: center;
115
- column-gap: var(--size-8);
116
- color: var(--color-content-primary);
117
- }
118
-
119
- .np-select-input-option-content-icon {
120
- display: flex;
121
-
122
- &--not-compact {
123
- align-self: flex-start;
124
- }
125
- }
126
-
127
- .np-select-input-option-content-text {
128
- display: flex;
129
- flex: 1;
130
- flex-direction: column;
131
- overflow: hidden;
132
- }
133
-
134
- .np-select-input-option-content-text-secondary {
135
- color: var(--color-content-secondary);
136
- }
137
-
138
- .np-select-input-option-content-text-compact {
139
- overflow: hidden;
140
- text-overflow: ellipsis;
141
- white-space: nowrap;
142
- }
143
-
144
- .np-select-input-option-content-text-line-1 {
145
- > :not([hidden]) ~ :not([hidden]) {
146
- margin-right: var(--size-8);
147
- margin-left: var(--size-8);
148
- }
149
- }
150
-
151
- .np-select-input-addon-container {
152
- pointer-events: none;
153
- display: inline-flex;
154
- align-items: center;
155
- }
156
-
157
- .np-select-input-addon {
158
- border-width: 0;
159
- background: none;
160
-
161
- display: inline-flex;
162
- height: var(--size-32);
163
- width: var(--size-32);
164
- align-items: center;
165
- justify-content: center;
166
- border-radius: 0.125rem /* 2px */; /* TODO: Tokenize */
167
-
168
- &--interactive {
169
- pointer-events: auto;
170
- color: var(--color-interactive-secondary);
171
-
172
- &:hover {
173
- color: var(--color-interactive-secondary-hover);
174
- }
175
-
176
- .focus-ring();
177
- }
178
- }
179
-
180
- .np-select-input-addon-separator {
181
- height: var(--size-24);
182
- border-inline-start-width: 1px;
183
- }
@@ -1,120 +0,0 @@
1
- import { act, screen, within } from '@testing-library/react';
2
- import userEvent, { specialChars } from '@testing-library/user-event';
3
-
4
- import { render } from '../test-utils';
5
-
6
- import { SelectInput } from './SelectInput';
7
-
8
- Object.defineProperty(window, 'matchMedia', {
9
- writable: true,
10
- value: jest.fn((query: string) => {
11
- const matches = /^\(min-width: ([0-9]+)px\)$/.exec(query);
12
- const minWidth = matches != null ? Number(matches[1]) : undefined;
13
- return {
14
- matches: minWidth != null ? window.innerWidth >= minWidth : false,
15
- media: query,
16
- onchange: null,
17
- addListener: jest.fn(), // deprecated
18
- removeListener: jest.fn(), // deprecated
19
- addEventListener: jest.fn(),
20
- removeEventListener: jest.fn(),
21
- dispatchEvent: jest.fn(),
22
- };
23
- }),
24
- });
25
-
26
- Object.defineProperty(window, 'ResizeObserver', {
27
- writable: true,
28
- value: jest.fn(() => ({
29
- observe: jest.fn(),
30
- unobserve: jest.fn(),
31
- disconnect: jest.fn(),
32
- })),
33
- });
34
-
35
- describe('SelectInput', () => {
36
- it('renders placeholder', () => {
37
- render(
38
- <SelectInput
39
- placeholder="Currency"
40
- items={[
41
- { type: 'option', value: 'USD' },
42
- { type: 'option', value: 'EUR' },
43
- ]}
44
- />,
45
- );
46
-
47
- expect(screen.getByText('Currency')).toBeInTheDocument();
48
- });
49
-
50
- it('shows item selected via mouse', async () => {
51
- render(
52
- <SelectInput
53
- items={[
54
- { type: 'option', value: 'USD' },
55
- { type: 'option', value: 'EUR' },
56
- ]}
57
- />,
58
- );
59
-
60
- expect(screen.queryByText('EUR')).not.toBeInTheDocument();
61
-
62
- const trigger = screen.getAllByRole('button')[0];
63
- // eslint-disable-next-line @typescript-eslint/require-await
64
- await act(async () => {
65
- userEvent.click(trigger);
66
- });
67
-
68
- const listbox = screen.getByRole('listbox');
69
- const option = within(listbox).getByRole('option', { name: 'EUR' });
70
- userEvent.click(option);
71
-
72
- expect(trigger).toHaveTextContent('EUR');
73
- });
74
-
75
- it('filters items via keyboard', async () => {
76
- render(
77
- <SelectInput
78
- items={[
79
- {
80
- type: 'group',
81
- label: 'Popular currencies',
82
- options: [
83
- { type: 'option', value: 'USD' },
84
- { type: 'option', value: 'EUR' },
85
- { type: 'option', value: 'GBP' },
86
- ],
87
- },
88
- ]}
89
- filterable
90
- />,
91
- );
92
-
93
- const trigger = screen.getAllByRole('button')[0];
94
- // eslint-disable-next-line @typescript-eslint/require-await
95
- await act(async () => {
96
- userEvent.tab();
97
- userEvent.keyboard(specialChars.enter);
98
- });
99
-
100
- const listbox = screen.getByRole('listbox');
101
- expect(within(listbox).getAllByRole('option')).toHaveLength(3);
102
-
103
- userEvent.keyboard('u');
104
- expect(within(listbox).getAllByRole('option')).toHaveLength(2);
105
-
106
- userEvent.keyboard('r');
107
- expect(within(listbox).getByRole('option')).toBeInTheDocument();
108
-
109
- userEvent.keyboard('x');
110
- expect(within(listbox).queryByRole('option')).not.toBeInTheDocument();
111
-
112
- userEvent.keyboard(specialChars.backspace);
113
- expect(within(listbox).getByRole('option')).toBeInTheDocument();
114
-
115
- const option = within(listbox).getAllByRole('option')[0];
116
- userEvent.click(option);
117
-
118
- expect(trigger).toHaveTextContent('EUR');
119
- });
120
- });