@transferwise/components 0.0.0-experimental-94710bd → 0.0.0-experimental-d7e8cfa

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 (57) hide show
  1. package/build/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.js +5 -3
  2. package/build/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.js.map +1 -1
  3. package/build/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.mjs +5 -3
  4. package/build/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.mjs.map +1 -1
  5. package/build/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.js.map +1 -1
  6. package/build/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.mjs.map +1 -1
  7. package/build/Typeahead/Typeahead.js +53 -27
  8. package/build/Typeahead/Typeahead.js.map +1 -1
  9. package/build/Typeahead/Typeahead.messages.js +3 -0
  10. package/build/Typeahead/Typeahead.messages.js.map +1 -1
  11. package/build/Typeahead/Typeahead.messages.mjs +3 -0
  12. package/build/Typeahead/Typeahead.messages.mjs.map +1 -1
  13. package/build/Typeahead/Typeahead.mjs +53 -27
  14. package/build/Typeahead/Typeahead.mjs.map +1 -1
  15. package/build/Typeahead/TypeaheadInput/TypeaheadInput.js +2 -2
  16. package/build/Typeahead/TypeaheadInput/TypeaheadInput.js.map +1 -1
  17. package/build/Typeahead/TypeaheadInput/TypeaheadInput.mjs +2 -2
  18. package/build/Typeahead/TypeaheadInput/TypeaheadInput.mjs.map +1 -1
  19. package/build/Typeahead/TypeaheadOption/TypeaheadOption.js +3 -1
  20. package/build/Typeahead/TypeaheadOption/TypeaheadOption.js.map +1 -1
  21. package/build/Typeahead/TypeaheadOption/TypeaheadOption.mjs +3 -1
  22. package/build/Typeahead/TypeaheadOption/TypeaheadOption.mjs.map +1 -1
  23. package/build/i18n/en.json +1 -0
  24. package/build/i18n/en.json.js +1 -0
  25. package/build/i18n/en.json.js.map +1 -1
  26. package/build/i18n/en.json.mjs +1 -0
  27. package/build/i18n/en.json.mjs.map +1 -1
  28. package/build/main.css +10 -0
  29. package/build/styles/Typeahead/Typeahead.css +4 -0
  30. package/build/styles/Typeahead/TypeaheadOption/TypeaheadOption.css +5 -0
  31. package/build/styles/main.css +10 -0
  32. package/build/types/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.d.ts +4 -6
  33. package/build/types/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.d.ts.map +1 -1
  34. package/build/types/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.d.ts +1 -1
  35. package/build/types/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.d.ts.map +1 -1
  36. package/build/types/Typeahead/Typeahead.d.ts.map +1 -1
  37. package/build/types/Typeahead/Typeahead.messages.d.ts +5 -0
  38. package/build/types/Typeahead/Typeahead.messages.d.ts.map +1 -1
  39. package/build/types/Typeahead/TypeaheadInput/TypeaheadInput.d.ts.map +1 -1
  40. package/build/types/Typeahead/TypeaheadOption/TypeaheadOption.d.ts +1 -0
  41. package/build/types/Typeahead/TypeaheadOption/TypeaheadOption.d.ts.map +1 -1
  42. package/build/types/test-utils/index.d.ts +2 -0
  43. package/build/types/test-utils/index.d.ts.map +1 -1
  44. package/package.json +1 -1
  45. package/src/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.tsx +13 -12
  46. package/src/Inputs/SelectInput/SelectInput.test.tsx +0 -37
  47. package/src/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.tsx +1 -1
  48. package/src/Typeahead/Typeahead.css +4 -0
  49. package/src/Typeahead/Typeahead.less +2 -0
  50. package/src/Typeahead/Typeahead.messages.ts +5 -0
  51. package/src/Typeahead/Typeahead.test.tsx +80 -3
  52. package/src/Typeahead/Typeahead.tsx +50 -27
  53. package/src/Typeahead/TypeaheadInput/TypeaheadInput.tsx +2 -6
  54. package/src/Typeahead/TypeaheadOption/TypeaheadOption.css +5 -0
  55. package/src/Typeahead/TypeaheadOption/TypeaheadOption.less +9 -0
  56. package/src/Typeahead/TypeaheadOption/TypeaheadOption.tsx +4 -2
  57. package/src/main.css +10 -0
@@ -34122,6 +34122,10 @@ html:not([dir="rtl"]) .np-navigation-option {
34122
34122
  .typeahead .dropdown-menu {
34123
34123
  max-width: 100%;
34124
34124
  width: 100%;
34125
+ scroll-padding-top: 8px;
34126
+ scroll-padding-top: var(--size-8);
34127
+ scroll-padding-bottom: 8px;
34128
+ scroll-padding-bottom: var(--size-8);
34125
34129
  }
34126
34130
 
34127
34131
  .np-theme-personal--forest-green .typeahead .dropdown-menu,
@@ -34348,6 +34352,12 @@ html:not([dir="rtl"]) .np-navigation-option {
34348
34352
  border-radius: var(--radius-small);
34349
34353
  }
34350
34354
 
34355
+ .dropdown-menu > li.typeahead__option:not(.active):not(.selected) > .dropdown-item:not([disabled]):not(.disabled):hover,
34356
+ .dropdown-menu > li.typeahead__option:not(.active):not(.selected) > .dropdown-item:not([disabled]):not(.disabled):focus,
34357
+ .dropdown-menu > li.typeahead__option:not(.active):not(.selected) > .dropdown-item:not([disabled]):not(.disabled):active {
34358
+ box-shadow: none;
34359
+ }
34360
+
34351
34361
  .tw-droppable-lg {
34352
34362
  min-height: 346px;
34353
34363
  }
@@ -1,6 +1,6 @@
1
1
  import type { AvatarLayoutProps } from '../../AvatarLayout';
2
- import type { NewButtonProps } from '../../Button';
3
2
  import type { CurrencyType, ExpressiveMoneyInputProps } from '../ExpressiveMoneyInput';
3
+ import { type ButtonHTMLAttributes } from 'react';
4
4
  export interface CurrencyOption {
5
5
  label?: string;
6
6
  code: string;
@@ -23,10 +23,8 @@ export type CurrencySelectorProps = {
23
23
  resultCount: number;
24
24
  }) => void;
25
25
  } & Pick<ExpressiveMoneyInputProps, 'currency'>;
26
- type ButtonInputProps = Omit<Extract<NewButtonProps, {
27
- as?: 'button';
28
- }>, 'v2' | 'size' | 'priority' | 'className' | 'href' | 'target'>;
29
26
  export declare const CurrencySelector: ({ id, currency, options, labelId, onChange, addons, onOpen, onSearchChange, }: CurrencySelectorProps) => import("react").JSX.Element;
30
- export declare const ButtonInput: import("react").ForwardRefExoticComponent<ButtonInputProps & import("react").RefAttributes<HTMLButtonElement>>;
31
- export {};
27
+ export declare const ButtonInput: import("react").ForwardRefExoticComponent<ButtonHTMLAttributes<HTMLButtonElement> & {
28
+ children?: import("react").ReactNode | undefined;
29
+ } & import("react").RefAttributes<HTMLButtonElement | null>>;
32
30
  //# sourceMappingURL=CurrencySelector.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CurrencySelector.d.ts","sourceRoot":"","sources":["../../../../src/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAMnD,OAAO,KAAK,EAAE,YAAY,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAUvF,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B;AAED,MAAM,MAAM,eAAe,GAAG,eAAe,EAAE,CAAC;AAEhD,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACtC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC5E,GAAG,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,CAAC;AAEhD,KAAK,gBAAgB,GAAG,IAAI,CAC1B,OAAO,CAAC,cAAc,EAAE;IAAE,EAAE,CAAC,EAAE,QAAQ,CAAA;CAAE,CAAC,EAC1C,IAAI,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,MAAM,GAAG,QAAQ,CAC7D,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,+EAS9B,qBAAqB,gCA+FvB,CAAC;AAEF,eAAO,MAAM,WAAW,gHAgBtB,CAAC"}
1
+ {"version":3,"file":"CurrencySelector.d.ts","sourceRoot":"","sources":["../../../../src/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAO5D,OAAO,KAAK,EAAE,YAAY,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAGvF,OAAO,EACL,KAAK,oBAAoB,EAK1B,MAAM,OAAO,CAAC;AAOf,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B;AAED,MAAM,MAAM,eAAe,GAAG,eAAe,EAAE,CAAC;AAEhD,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,MAAM,CAAC,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;IACtC,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAC5E,GAAG,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,CAAC;AAEhD,eAAO,MAAM,gBAAgB,GAAI,+EAS9B,qBAAqB,gCAgGvB,CAAC;AAEF,eAAO,MAAM,WAAW;;4DAgBtB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { Merge } from '../../../utils';
2
- export type SelectInputTriggerButtonElementType = React.ElementType;
2
+ export type SelectInputTriggerButtonElementType = 'button' | React.ComponentType;
3
3
  export type SelectInputTriggerButtonProps<T extends SelectInputTriggerButtonElementType = 'button'> = Merge<React.ComponentPropsWithoutRef<T>, {
4
4
  as?: T;
5
5
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"SelectInputTriggerButton.d.ts","sourceRoot":"","sources":["../../../../../src/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC,MAAM,MAAM,mCAAmC,GAAG,KAAK,CAAC,WAAW,CAAC;AAEpE,MAAM,MAAM,6BAA6B,CACvC,CAAC,SAAS,mCAAmC,GAAG,QAAQ,IACtD,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE;IAAE,EAAE,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC;AAEzD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,mCAAmC,GAAG,QAAQ,EAAE,EACjG,EAAkB,EAClB,GAAG,SAAS,EACb,EAAE,6BAA6B,CAAC,CAAC,CAAC,+BAclC"}
1
+ {"version":3,"file":"SelectInputTriggerButton.d.ts","sourceRoot":"","sources":["../../../../../src/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC,MAAM,MAAM,mCAAmC,GAAG,QAAQ,GAAG,KAAK,CAAC,aAAa,CAAC;AAEjF,MAAM,MAAM,6BAA6B,CACvC,CAAC,SAAS,mCAAmC,GAAG,QAAQ,IACtD,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE;IAAE,EAAE,CAAC,EAAE,CAAC,CAAA;CAAE,CAAC,CAAC;AAEzD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,SAAS,mCAAmC,GAAG,QAAQ,EAAE,EACjG,EAAkB,EAClB,GAAG,SAAS,EACb,EAAE,6BAA6B,CAAC,CAAC,CAAC,+BAclC"}
@@ -1 +1 @@
1
- {"version":3,"file":"Typeahead.d.ts","sourceRoot":"","sources":["../../../src/Typeahead/Typeahead.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAc,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAG9E,OAAO,EAEL,SAAS,EACT,UAAU,EACV,SAAS,EAIV,MAAM,WAAW,CAAC;AACnB,OAAO,EAAgB,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAU5D,MAAM,MAAM,eAAe,CAAC,CAAC,GAAG,MAAM,IAAI;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,WAAW,cAAc,CAAC,CAAC,CAAE,SAAQ,OAAO,CAAC,qBAAqB,CAAC;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE;QACN,OAAO,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACvC,IAAI,CAAC,EACD,iBAAiB,CAAC,WAAW,CAAC,GAC9B,GAAG,SAAS,CAAC,KAAK,EAAE,GACpB,GAAG,SAAS,CAAC,IAAI,EAAE,GACnB,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;KAC5B,CAAC;IACF,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oBAAoB;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qBAAqB;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kBAAkB;IAClB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,oBAAoB;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,kBAAkB;IAClB,YAAY,CAAC,EAAE,SAAS,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,8BAA8B;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,SAAS,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IACvC,mBAAmB;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oBAAoB;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oBAAoB;IACpB,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC;CACtD;wBAsfoF,CAAC,CAAC,EACrF,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KACrB,KAAK,CAAC,YAAY;AAFvB,wBAEwB"}
1
+ {"version":3,"file":"Typeahead.d.ts","sourceRoot":"","sources":["../../../src/Typeahead/Typeahead.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAc,KAAK,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAG9E,OAAO,EAEL,SAAS,EACT,UAAU,EACV,SAAS,EAIV,MAAM,WAAW,CAAC;AACnB,OAAO,EAAgB,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAU5D,MAAM,MAAM,eAAe,CAAC,CAAC,GAAG,MAAM,IAAI;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,CAAC;IACV,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,MAAM,WAAW,cAAc,CAAC,CAAC,CAAE,SAAQ,OAAO,CAAC,qBAAqB,CAAC;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE;QACN,OAAO,EAAE,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACvC,IAAI,CAAC,EACD,iBAAiB,CAAC,WAAW,CAAC,GAC9B,GAAG,SAAS,CAAC,KAAK,EAAE,GACpB,GAAG,SAAS,CAAC,IAAI,EAAE,GACnB,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;KAC5B,CAAC;IACF,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oBAAoB;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qBAAqB;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,kBAAkB;IAClB,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,oBAAoB;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,kBAAkB;IAClB,YAAY,CAAC,EAAE,SAAS,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7C,8BAA8B;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,SAAS,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IACvC,mBAAmB;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,oBAAoB;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,oBAAoB;IACpB,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC;IAClD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC;CACtD;wBA6gBoF,CAAC,CAAC,EACrF,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KACrB,KAAK,CAAC,YAAY;AAFvB,wBAEwB"}
@@ -4,6 +4,11 @@ declare const _default: {
4
4
  defaultMessage: string;
5
5
  description: string;
6
6
  };
7
+ suggestionsLabel: {
8
+ id: string;
9
+ defaultMessage: string;
10
+ description: string;
11
+ };
7
12
  };
8
13
  export default _default;
9
14
  //# sourceMappingURL=Typeahead.messages.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Typeahead.messages.d.ts","sourceRoot":"","sources":["../../../src/Typeahead/Typeahead.messages.ts"],"names":[],"mappings":";;;;;;;AAEA,wBAMG"}
1
+ {"version":3,"file":"Typeahead.messages.d.ts","sourceRoot":"","sources":["../../../src/Typeahead/Typeahead.messages.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,wBAWG"}
@@ -1 +1 @@
1
- {"version":3,"file":"TypeaheadInput.d.ts","sourceRoot":"","sources":["../../../../src/Typeahead/TypeaheadInput/TypeaheadInput.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAGxD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAI/D,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IACxC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;IACrD,SAAS,EAAE,KAAK,CAAC,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,KAAK,CAAC,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;IACvD,UAAU,EAAE,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IACnE,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,aAAa,GAAG,WAAW,CAAC,CAAC;AAEpG,KAAK,mBAAmB,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3C,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,cAAc,CAAC,CAAC,CAAE,SAAQ,SAAS,CACtD,mBAAmB,CAAC,CAAC,CAAC,EACtB,mBAAmB,CACpB;IACC,QAAQ,8CAAiC;IACzC,QAAQ,4CAA+B;gBAE3B,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAQzC,iBAAiB;IAUjB,kBAAkB,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAMxD,gBAAgB,aAMd;IAEF,WAAW,oCAgDT;IAEF,MAAM;CAyBP"}
1
+ {"version":3,"file":"TypeaheadInput.d.ts","sourceRoot":"","sources":["../../../../src/Typeahead/TypeaheadInput/TypeaheadInput.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAa,SAAS,EAAE,MAAM,OAAO,CAAC;AAGxD,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAI/D,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI;IACnC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,SAAS,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;IACxC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;IACrD,SAAS,EAAE,KAAK,CAAC,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;IACxD,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,EAAE,KAAK,CAAC,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;IACvD,UAAU,EAAE,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IACnE,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,GAAG,aAAa,GAAG,WAAW,CAAC,CAAC;AAEpG,KAAK,mBAAmB,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3C,CAAC;AAEF,MAAM,CAAC,OAAO,OAAO,cAAc,CAAC,CAAC,CAAE,SAAQ,SAAS,CACtD,mBAAmB,CAAC,CAAC,CAAC,EACtB,mBAAmB,CACpB;IACC,QAAQ,8CAAiC;IACzC,QAAQ,4CAA+B;gBAE3B,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAQzC,iBAAiB;IAUjB,kBAAkB,CAAC,aAAa,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAMxD,gBAAgB,aAMd;IAEF,WAAW,oCA4CT;IAEF,MAAM;CAyBP"}
@@ -3,6 +3,7 @@ export type TypeaheadOptionProps<T> = {
3
3
  option: TypeaheadOption<T>;
4
4
  selected?: boolean;
5
5
  onClick?: React.MouseEventHandler;
6
+ onMouseEnter?: React.MouseEventHandler;
6
7
  query?: string;
7
8
  id?: string;
8
9
  };
@@ -1 +1 @@
1
- {"version":3,"file":"TypeaheadOption.d.ts","sourceRoot":"","sources":["../../../../src/Typeahead/TypeaheadOption/TypeaheadOption.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI;IACpC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,QAAA,MAAM,MAAM,qHA2BV,CAAC;AAEH,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"TypeaheadOption.d.ts","sourceRoot":"","sources":["../../../../src/Typeahead/TypeaheadOption/TypeaheadOption.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI;IACpC,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC;IAClC,YAAY,CAAC,EAAE,KAAK,CAAC,iBAAiB,CAAC;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,QAAA,MAAM,MAAM,qHA4BV,CAAC;AAEH,eAAe,MAAM,CAAC"}
@@ -56,6 +56,7 @@ declare function customRender(ui: ReactElement, { locale, messages, ...renderOpt
56
56
  "neptune.Table.loaded": string;
57
57
  "neptune.Table.loading": string;
58
58
  "neptune.Table.refreshPage": string;
59
+ "neptune.Typeahead.suggestionsLabel": string;
59
60
  "neptune.Upload.csButtonText": string;
60
61
  "neptune.Upload.csFailureText": string;
61
62
  "neptune.Upload.csSuccessText": string;
@@ -144,6 +145,7 @@ declare function customRenderHook(callback: () => unknown, { locale, messages }?
144
145
  "neptune.Table.loaded": string;
145
146
  "neptune.Table.loading": string;
146
147
  "neptune.Table.refreshPage": string;
148
+ "neptune.Typeahead.suggestionsLabel": string;
147
149
  "neptune.Upload.csButtonText": string;
148
150
  "neptune.Upload.csFailureText": string;
149
151
  "neptune.Upload.csSuccessText": string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test-utils/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAM,MAAM,OAAO,CAAC;AACzC,OAAO,EAAE,MAAM,EAAc,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAMxD;;;;GAIG;AACH,iBAAS,YAAY,CACnB,EAAE,EAAE,YAAY,EAChB,EAAE,MAAuB,EAAE,QAAa,EAAE,GAAG,aAAa,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAK,GAChE,UAAU,CAAC,OAAO,MAAM,CAAC,CAK3B;AAED;;;GAGG;AACH,iBAAS,gBAAgB,CACvB,QAAQ,EAAE,MAAM,OAAO,EACvB,EAAE,MAAuB,EAAE,QAAa,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAK,uEAKhD;AAED,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,YAAY,IAAI,MAAM,EAAE,gBAAgB,IAAI,UAAU,EAAE,SAAS,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/test-utils/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAM,MAAM,OAAO,CAAC;AACzC,OAAO,EAAE,MAAM,EAAc,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAMxD;;;;GAIG;AACH,iBAAS,YAAY,CACnB,EAAE,EAAE,YAAY,EAChB,EAAE,MAAuB,EAAE,QAAa,EAAE,GAAG,aAAa,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAK,GAChE,UAAU,CAAC,OAAO,MAAM,CAAC,CAK3B;AAED;;;GAGG;AACH,iBAAS,gBAAgB,CACvB,QAAQ,EAAE,MAAM,OAAO,EACvB,EAAE,MAAuB,EAAE,QAAa,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAK,uEAKhD;AAED,cAAc,wBAAwB,CAAC;AACvC,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,YAAY,IAAI,MAAM,EAAE,gBAAgB,IAAI,UAAU,EAAE,SAAS,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transferwise/components",
3
- "version": "0.0.0-experimental-94710bd",
3
+ "version": "0.0.0-experimental-d7e8cfa",
4
4
  "description": "Neptune React components",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -1,6 +1,5 @@
1
1
  import type { AvatarLayoutProps } from '../../AvatarLayout';
2
2
  import Button from '../../Button';
3
- import type { NewButtonProps } from '../../Button';
4
3
  import {
5
4
  SelectInput,
6
5
  SelectInputOptionContent,
@@ -9,7 +8,13 @@ import {
9
8
  import type { CurrencyType, ExpressiveMoneyInputProps } from '../ExpressiveMoneyInput';
10
9
  import { ChevronDown } from '@transferwise/icons';
11
10
  import { Flag } from '@wise/art';
12
- import { forwardRef, type MouseEventHandler, useMemo, useState } from 'react';
11
+ import {
12
+ type ButtonHTMLAttributes,
13
+ forwardRef,
14
+ type MouseEventHandler,
15
+ useMemo,
16
+ useState,
17
+ } from 'react';
13
18
  import { useIntl } from 'react-intl';
14
19
 
15
20
  import messages from '../ExpressiveMoneyInput.messages';
@@ -39,11 +44,6 @@ export type CurrencySelectorProps = {
39
44
  onSearchChange?: (payload: { query: string; resultCount: number }) => void;
40
45
  } & Pick<ExpressiveMoneyInputProps, 'currency'>;
41
46
 
42
- type ButtonInputProps = Omit<
43
- Extract<NewButtonProps, { as?: 'button' }>,
44
- 'v2' | 'size' | 'priority' | 'className' | 'href' | 'target'
45
- >;
46
-
47
47
  export const CurrencySelector = ({
48
48
  id,
49
49
  currency,
@@ -109,6 +109,7 @@ export const CurrencySelector = ({
109
109
  renderTrigger={() => (
110
110
  <SelectInputTriggerButton
111
111
  as={ButtonInput}
112
+ // @ts-expect-error new (v2) ButtonProps
112
113
  addonStart={{
113
114
  type: 'avatar',
114
115
  value: [
@@ -121,11 +122,11 @@ export const CurrencySelector = ({
121
122
  }),
122
123
  },
123
124
  ]
124
- .filter((avatar): avatar is NonNullable<typeof avatar> => avatar != null)
125
+ .filter(Boolean)
125
126
  .filter((avatar) => !(avatar && Object.keys(avatar).length === 0)),
126
127
  }}
127
128
  addonEnd={disabled ? undefined : { type: 'icon', value: <ChevronDown /> }}
128
- onClick={handleTriggerClick}
129
+ onClick={(event) => handleTriggerClick(event)}
129
130
  >
130
131
  <>
131
132
  <span aria-hidden>{formatCurrencyCode(currency)}</span>
@@ -150,9 +151,9 @@ export const CurrencySelector = ({
150
151
  );
151
152
  };
152
153
 
153
- export const ButtonInput = forwardRef<HTMLButtonElement, ButtonInputProps>(function ButtonInput(
154
- { children, ...rest },
155
- ref,
154
+ export const ButtonInput = forwardRef(function ButtonInput(
155
+ { children, ...rest }: React.PropsWithChildren<ButtonHTMLAttributes<HTMLButtonElement>>,
156
+ ref: React.ForwardedRef<HTMLButtonElement | null>,
156
157
  ) {
157
158
  return (
158
159
  <Button
@@ -7,12 +7,9 @@ import { render, mockMatchMedia, mockResizeObserver } from '../../test-utils';
7
7
  import {
8
8
  SelectInput,
9
9
  SelectInputOptionContent,
10
- SelectInputTriggerButton,
11
10
  type SelectInputOptionItem,
12
11
  type SelectInputProps,
13
12
  } from '.';
14
- import Button from '../../Button';
15
- import type { ButtonProps as ResolverButtonProps } from '../../Button/Button.resolver';
16
13
  import Field from '../../Field';
17
14
 
18
15
  mockMatchMedia();
@@ -340,40 +337,6 @@ describe('SelectInput', () => {
340
337
  expect(screen.getByLabelText(/Currency/)).toHaveAttribute('aria-haspopup');
341
338
  });
342
339
 
343
- it('renders addonStart and addonEnd when using trigger as Button', () => {
344
- type V2ButtonProps = Extract<ResolverButtonProps, { v2: true }>;
345
-
346
- render(
347
- <SelectInput
348
- items={[{ type: 'option', value: 'USD' }]}
349
- renderTrigger={({ content, className, size }) => {
350
- const triggerButtonProps: V2ButtonProps = {
351
- v2: true,
352
- size,
353
- className,
354
- addonStart: {
355
- type: 'icon',
356
- value: <span data-testid="select-trigger-addon-start">Start</span>,
357
- },
358
- addonEnd: {
359
- type: 'icon',
360
- value: <span data-testid="select-trigger-addon-end">End</span>,
361
- },
362
- };
363
-
364
- return (
365
- <SelectInputTriggerButton as={Button} {...triggerButtonProps}>
366
- {content}
367
- </SelectInputTriggerButton>
368
- );
369
- }}
370
- />,
371
- );
372
-
373
- expect(screen.getByTestId('select-trigger-addon-start')).toBeInTheDocument();
374
- expect(screen.getByTestId('select-trigger-addon-end')).toBeInTheDocument();
375
- });
376
-
377
340
  it('deduplicates search results across groups using compareValues as key', async () => {
378
341
  interface Currency {
379
342
  code: string;
@@ -5,7 +5,7 @@ import { PolymorphicWithOverrides } from '../../../common/PolymorphicWithOverrid
5
5
  import { Merge } from '../../../utils';
6
6
  import { SelectInputTriggerButtonPropsContext } from '../SelectInput.contexts';
7
7
 
8
- export type SelectInputTriggerButtonElementType = React.ElementType;
8
+ export type SelectInputTriggerButtonElementType = 'button' | React.ComponentType;
9
9
 
10
10
  export type SelectInputTriggerButtonProps<
11
11
  T extends SelectInputTriggerButtonElementType = 'button',
@@ -7,6 +7,10 @@
7
7
  .typeahead .dropdown-menu {
8
8
  max-width: 100%;
9
9
  width: 100%;
10
+ scroll-padding-top: 8px;
11
+ scroll-padding-top: var(--size-8);
12
+ scroll-padding-bottom: 8px;
13
+ scroll-padding-bottom: var(--size-8);
10
14
  }
11
15
  .np-theme-personal--forest-green .typeahead .dropdown-menu,
12
16
  .np-theme-personal--bright-green .typeahead .dropdown-menu,
@@ -18,6 +18,8 @@
18
18
  .dropdown-menu {
19
19
  max-width: 100%;
20
20
  width: 100%;
21
+ scroll-padding-top: var(--size-8);
22
+ scroll-padding-bottom: var(--size-8);
21
23
  .np-theme-personal--forest-green &,
22
24
  .np-theme-personal--bright-green &,
23
25
  .np-theme-personal--dark & {
@@ -6,4 +6,9 @@ export default defineMessages({
6
6
  defaultMessage: 'Clear',
7
7
  description: 'Description of clear button',
8
8
  },
9
+ suggestionsLabel: {
10
+ id: 'neptune.Typeahead.suggestionsLabel',
11
+ defaultMessage: 'Suggestions',
12
+ description: 'Accessible label for the list of typeahead suggestions',
13
+ },
9
14
  });
@@ -1,11 +1,18 @@
1
1
  import Field from '../Field';
2
- import { mockMatchMedia, render, screen, fireEvent } from '../test-utils';
2
+ import { mockMatchMedia, render, screen, fireEvent, userEvent } from '../test-utils';
3
3
  import { wait } from '../test-utils/wait';
4
4
  import Typeahead from './Typeahead';
5
5
 
6
6
  mockMatchMedia();
7
7
 
8
+ const scrollIntoView = jest.fn();
9
+
8
10
  describe('Typeahead', () => {
11
+ beforeEach(() => {
12
+ HTMLElement.prototype.scrollIntoView = scrollIntoView;
13
+ scrollIntoView.mockClear();
14
+ });
15
+
9
16
  it('supports `Field` for labeling', () => {
10
17
  render(
11
18
  <Field id="test" label="Tags">
@@ -22,7 +29,7 @@ describe('Typeahead', () => {
22
29
  <Typeahead id="test" name="test" options={[]} onChange={() => {}} />
23
30
  </Field>,
24
31
  );
25
- expect(screen.queryByRole('menu')).not.toBeInTheDocument();
32
+ expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
26
33
  });
27
34
  it('does render a dropdown when only a footer is provided', () => {
28
35
  render(
@@ -30,7 +37,7 @@ describe('Typeahead', () => {
30
37
  <Typeahead id="test" name="test" options={[]} footer={<p>hello</p>} onChange={() => {}} />
31
38
  </Field>,
32
39
  );
33
- expect(screen.getByRole('menu')).toBeInTheDocument();
40
+ expect(screen.getByRole('listbox')).toBeInTheDocument();
34
41
  });
35
42
  });
36
43
 
@@ -74,6 +81,76 @@ describe('Typeahead', () => {
74
81
  expect(onChange).toHaveBeenCalled();
75
82
  });
76
83
 
84
+ it('scrolls options into view when navigating with arrow keys', async () => {
85
+ render(
86
+ <Typeahead
87
+ id="test"
88
+ name="test"
89
+ options={[{ label: 'Option 1' }, { label: 'Option 2' }]}
90
+ minQueryLength={0}
91
+ onChange={() => {}}
92
+ />,
93
+ );
94
+ const input = screen.getByRole('combobox');
95
+
96
+ await userEvent.click(input);
97
+ await userEvent.keyboard('{ArrowDown}{ArrowDown}');
98
+
99
+ expect(input).toHaveFocus();
100
+ expect(scrollIntoView).toHaveBeenCalledTimes(2);
101
+ expect(scrollIntoView).toHaveBeenNthCalledWith(1, { block: 'nearest' });
102
+ expect(scrollIntoView).toHaveBeenNthCalledWith(2, { block: 'nearest' });
103
+ expect(scrollIntoView.mock.contexts[0]).toBe(screen.getByText('Option 1').closest('li'));
104
+ expect(scrollIntoView.mock.contexts[1]).toBe(screen.getByText('Option 2').closest('li'));
105
+ });
106
+
107
+ it('uses one Tab stop for the suggestions', async () => {
108
+ render(
109
+ <>
110
+ <Typeahead
111
+ id="test"
112
+ name="test"
113
+ options={[{ label: 'Option 1' }, { label: 'Option 2' }]}
114
+ minQueryLength={0}
115
+ onChange={() => {}}
116
+ />
117
+ <button type="button">Next control</button>
118
+ </>,
119
+ );
120
+
121
+ const input = screen.getByRole('combobox');
122
+ await userEvent.click(input);
123
+ await userEvent.tab();
124
+
125
+ expect(screen.getByRole('listbox')).toHaveFocus();
126
+ await userEvent.keyboard('{ArrowDown}');
127
+ expect(screen.getByRole('option', { name: 'Option 1' })).toHaveClass(
128
+ 'tw-dropdown-item--focused',
129
+ );
130
+ expect(screen.getByRole('listbox')).toHaveFocus();
131
+ await userEvent.tab();
132
+ expect(screen.getByRole('button', { name: 'Next control' })).toHaveFocus();
133
+ });
134
+
135
+ it('uses the active option style when hovering a suggestion', async () => {
136
+ render(
137
+ <Typeahead
138
+ id="test"
139
+ name="test"
140
+ options={[{ label: 'Option 1' }, { label: 'Option 2' }]}
141
+ minQueryLength={0}
142
+ onChange={() => {}}
143
+ />,
144
+ );
145
+
146
+ await userEvent.click(screen.getByRole('combobox'));
147
+ await userEvent.hover(screen.getByRole('option', { name: 'Option 2' }));
148
+
149
+ expect(screen.getByRole('option', { name: 'Option 2' })).toHaveClass(
150
+ 'tw-dropdown-item--focused',
151
+ );
152
+ });
153
+
77
154
  it('shows clear button when there is a value', () => {
78
155
  render(
79
156
  <Typeahead
@@ -216,31 +216,15 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
216
216
  };
217
217
 
218
218
  handleOnKeyDown: React.KeyboardEventHandler<HTMLInputElement> = (event) => {
219
- const { showSuggestions, allowNew, multiple, chipSeparators, options } = this.props;
220
- const { keyboardFocusedOptionIndex, query, selected } = this.state;
219
+ const { showSuggestions, allowNew, multiple, chipSeparators } = this.props;
220
+ const { query, selected } = this.state;
221
221
  const chipsMode = !showSuggestions && allowNew && multiple;
222
222
 
223
223
  if (chipsMode && ['Enter', 'Tab', ...chipSeparators].includes(event.key) && query.trim()) {
224
224
  event.preventDefault();
225
225
  this.selectItem({ label: query });
226
- } else {
226
+ } else if (!this.handleOptionNavigation(event)) {
227
227
  switch (event.key) {
228
- case 'ArrowDown':
229
- event.preventDefault();
230
- this.moveFocusedOption(1);
231
- break;
232
- case 'ArrowUp':
233
- event.preventDefault();
234
- this.moveFocusedOption(-1);
235
- break;
236
- case 'Enter':
237
- event.preventDefault();
238
- if (keyboardFocusedOptionIndex != null && options[keyboardFocusedOptionIndex]) {
239
- this.selectItem(options[keyboardFocusedOptionIndex]);
240
- } else if (allowNew && query.trim()) {
241
- this.selectItem({ label: query });
242
- }
243
- break;
244
228
  case 'Backspace':
245
229
  if (multiple && !query && selected.length > 0) {
246
230
  this.updateSelectedValue(selected.slice(0, -1));
@@ -251,6 +235,36 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
251
235
  }
252
236
  }
253
237
  };
238
+
239
+ handleOptionListKeyDown: React.KeyboardEventHandler<HTMLUListElement> = (event) => {
240
+ this.handleOptionNavigation(event);
241
+ };
242
+
243
+ handleOptionNavigation(event: React.KeyboardEvent<HTMLElement>) {
244
+ const { allowNew, options } = this.props;
245
+ const { keyboardFocusedOptionIndex, query } = this.state;
246
+
247
+ switch (event.key) {
248
+ case 'ArrowDown':
249
+ event.preventDefault();
250
+ this.moveFocusedOption(1);
251
+ return true;
252
+ case 'ArrowUp':
253
+ event.preventDefault();
254
+ this.moveFocusedOption(-1);
255
+ return true;
256
+ case 'Enter':
257
+ event.preventDefault();
258
+ if (keyboardFocusedOptionIndex != null && options[keyboardFocusedOptionIndex]) {
259
+ this.selectItem(options[keyboardFocusedOptionIndex]);
260
+ } else if (allowNew && query.trim()) {
261
+ this.selectItem({ label: query });
262
+ }
263
+ return true;
264
+ default:
265
+ return false;
266
+ }
267
+ }
254
268
  moveFocusedOption(offset: number) {
255
269
  this.setState((previousState) => {
256
270
  const { keyboardFocusedOptionIndex } = previousState;
@@ -261,7 +275,7 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
261
275
  }
262
276
  const optionRef = this.optionRefs[index];
263
277
  if (optionRef?.current) {
264
- optionRef.current.focus(); // Set focus on the option element
278
+ optionRef.current.scrollIntoView({ block: 'nearest' });
265
279
  }
266
280
  return {
267
281
  keyboardFocusedOptionIndex: index,
@@ -269,6 +283,10 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
269
283
  });
270
284
  }
271
285
 
286
+ setFocusedOption = (index: number) => {
287
+ this.setState({ keyboardFocusedOptionIndex: index });
288
+ };
289
+
272
290
  selectItem = (item: TypeaheadOption<T>) => {
273
291
  const { multiple } = this.props;
274
292
  let selected = [...this.state.selected];
@@ -423,12 +441,16 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
423
441
  });
424
442
  }
425
443
  return (
426
- <div
427
- className={clsx('dropdown btn-group btn-block', { open: dropdownOpen })}
428
- id={`menu-${id}`}
429
- >
444
+ <div className={clsx('dropdown btn-group btn-block', { open: dropdownOpen })}>
430
445
  {(!!optionsToRender.length || footer) && (
431
- <ul className="dropdown-menu" role="menu">
446
+ <ul
447
+ className="dropdown-menu"
448
+ id={`menu-${id}`}
449
+ role="listbox"
450
+ aria-label={this.props?.intl?.formatMessage(messages.suggestionsLabel)}
451
+ tabIndex={0}
452
+ onKeyDown={this.handleOptionListKeyDown}
453
+ >
432
454
  {optionsToRender.map((option, idx) => {
433
455
  const ref = React.createRef<HTMLLIElement>();
434
456
  this.optionRefs[idx] = ref;
@@ -439,7 +461,8 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
439
461
  query={query}
440
462
  option={option}
441
463
  selected={keyboardFocusedOptionIndex === idx}
442
- id={String(option.label.replace(/\s+/g, '-'))}
464
+ id={`${id}-${idx}`}
465
+ onMouseEnter={() => this.setFocusedOption(idx)}
443
466
  onClick={(event) => {
444
467
  this.onOptionSelected(event, option);
445
468
  }}
@@ -554,7 +577,7 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
554
577
  autoComplete={inputAutoComplete}
555
578
  ariaActivedescendant={
556
579
  keyboardFocusedOptionIndex !== null && options[keyboardFocusedOptionIndex]
557
- ? `option-${options[keyboardFocusedOptionIndex].label.replace(/\s+/g, '-')}`
580
+ ? `option-${id}-${keyboardFocusedOptionIndex}`
558
581
  : undefined
559
582
  }
560
583
  onChange={this.handleOnChange}
@@ -82,7 +82,7 @@ export default class TypeaheadInput<T> extends Component<
82
82
  onFocus,
83
83
  onPaste,
84
84
  autoComplete,
85
- ariaActivedescendant, // Declare the ariaActivedescendant variable
85
+ ariaActivedescendant,
86
86
  } = this.props;
87
87
  const { inputWidth } = this.state;
88
88
 
@@ -93,11 +93,7 @@ export default class TypeaheadInput<T> extends Component<
93
93
  className={clsx(multiple && 'typeahead__input')}
94
94
  name={name}
95
95
  id={`input-${typeaheadId}`}
96
- aria-activedescendant={
97
- ariaActivedescendant
98
- ? `menu-${typeaheadId} ${ariaActivedescendant}`
99
- : `menu-${typeaheadId}`
100
- }
96
+ aria-activedescendant={ariaActivedescendant}
101
97
  autoFocus={autoFocus}
102
98
  placeholder={hasPlaceholder ? placeholder : ''}
103
99
  aria-autocomplete="list"
@@ -7,3 +7,8 @@
7
7
  border-radius: 10px;
8
8
  border-radius: var(--radius-small);
9
9
  }
10
+ .dropdown-menu > li.typeahead__option:not(.active):not(.selected) > .dropdown-item:not([disabled]):not(.disabled):hover,
11
+ .dropdown-menu > li.typeahead__option:not(.active):not(.selected) > .dropdown-item:not([disabled]):not(.disabled):focus,
12
+ .dropdown-menu > li.typeahead__option:not(.active):not(.selected) > .dropdown-item:not([disabled]):not(.disabled):active {
13
+ box-shadow: none;
14
+ }
@@ -12,3 +12,12 @@
12
12
  border-radius: var(--radius-small);
13
13
  }
14
14
  }
15
+ .dropdown-menu
16
+ > li.typeahead__option:not(.active):not(.selected)
17
+ > .dropdown-item:not([disabled]):not(.disabled) {
18
+ &:hover,
19
+ &:focus,
20
+ &:active {
21
+ box-shadow: none;
22
+ }
23
+ }
@@ -8,12 +8,13 @@ export type TypeaheadOptionProps<T> = {
8
8
  option: TypeaheadOption<T>;
9
9
  selected?: boolean;
10
10
  onClick?: React.MouseEventHandler;
11
+ onMouseEnter?: React.MouseEventHandler;
11
12
  query?: string;
12
13
  id?: string;
13
14
  };
14
15
 
15
16
  const Option = forwardRef<HTMLLIElement, TypeaheadOptionProps<any>>((props, ref) => {
16
- const { option, selected = false, onClick = () => {}, query = '', id } = props;
17
+ const { option, selected = false, onClick = () => {}, onMouseEnter, query = '', id } = props;
17
18
  const { label, note, secondary } = option;
18
19
 
19
20
  return (
@@ -25,8 +26,9 @@ const Option = forwardRef<HTMLLIElement, TypeaheadOptionProps<any>>((props, ref)
25
26
  className={clsx('typeahead__option tw-dropdown-item clickable', {
26
27
  'tw-dropdown-item--focused': selected,
27
28
  })}
29
+ onMouseEnter={onMouseEnter}
28
30
  >
29
- <a className="dropdown-item" href="#" tabIndex={0} onClick={onClick}>
31
+ <a className="dropdown-item" href="#" tabIndex={-1} onClick={onClick}>
30
32
  <Highlight value={label} query={query} />
31
33
  {note && <span className="np-text-body-default m-l-1">{note}</span>}
32
34
  {secondary && (