@transferwise/components 0.0.0-experimental-a899b88 → 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.
- package/build/Typeahead/Typeahead.js +53 -27
- package/build/Typeahead/Typeahead.js.map +1 -1
- package/build/Typeahead/Typeahead.messages.js +3 -0
- package/build/Typeahead/Typeahead.messages.js.map +1 -1
- package/build/Typeahead/Typeahead.messages.mjs +3 -0
- package/build/Typeahead/Typeahead.messages.mjs.map +1 -1
- package/build/Typeahead/Typeahead.mjs +53 -27
- package/build/Typeahead/Typeahead.mjs.map +1 -1
- package/build/Typeahead/TypeaheadInput/TypeaheadInput.js +2 -2
- package/build/Typeahead/TypeaheadInput/TypeaheadInput.js.map +1 -1
- package/build/Typeahead/TypeaheadInput/TypeaheadInput.mjs +2 -2
- package/build/Typeahead/TypeaheadInput/TypeaheadInput.mjs.map +1 -1
- package/build/Typeahead/TypeaheadOption/TypeaheadOption.js +3 -1
- package/build/Typeahead/TypeaheadOption/TypeaheadOption.js.map +1 -1
- package/build/Typeahead/TypeaheadOption/TypeaheadOption.mjs +3 -1
- package/build/Typeahead/TypeaheadOption/TypeaheadOption.mjs.map +1 -1
- package/build/i18n/en.json +1 -0
- package/build/i18n/en.json.js +1 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +1 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/main.css +10 -0
- package/build/styles/Typeahead/Typeahead.css +4 -0
- package/build/styles/Typeahead/TypeaheadOption/TypeaheadOption.css +5 -0
- package/build/styles/main.css +10 -0
- package/build/types/Body/Body.d.ts +2 -2
- package/build/types/IconButton/IconButton.d.ts +1 -1
- package/build/types/MoneyInput/MoneyInput.d.ts +1 -1
- package/build/types/MoneyInput/MoneyInput.d.ts.map +1 -1
- package/build/types/Title/Title.d.ts +2 -2
- package/build/types/Typeahead/Typeahead.d.ts.map +1 -1
- package/build/types/Typeahead/Typeahead.messages.d.ts +5 -0
- package/build/types/Typeahead/Typeahead.messages.d.ts.map +1 -1
- package/build/types/Typeahead/TypeaheadInput/TypeaheadInput.d.ts.map +1 -1
- package/build/types/Typeahead/TypeaheadOption/TypeaheadOption.d.ts +1 -0
- package/build/types/Typeahead/TypeaheadOption/TypeaheadOption.d.ts.map +1 -1
- package/build/types/Upload/Steps/UploadImageStep/UploadImageStep.d.ts +1 -1
- package/build/types/UploadInput/UploadItem/UploadItemLink.d.ts +1 -1
- package/build/types/test-utils/index.d.ts +2 -0
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Typeahead/Typeahead.css +4 -0
- package/src/Typeahead/Typeahead.less +2 -0
- package/src/Typeahead/Typeahead.messages.ts +5 -0
- package/src/Typeahead/Typeahead.test.tsx +80 -3
- package/src/Typeahead/Typeahead.tsx +50 -27
- package/src/Typeahead/TypeaheadInput/TypeaheadInput.tsx +2 -6
- package/src/Typeahead/TypeaheadOption/TypeaheadOption.css +5 -0
- package/src/Typeahead/TypeaheadOption/TypeaheadOption.less +9 -0
- package/src/Typeahead/TypeaheadOption/TypeaheadOption.tsx +4 -2
- package/src/main.css +10 -0
|
@@ -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,
|
|
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"}
|
|
@@ -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,
|
|
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"}
|
|
@@ -15,7 +15,7 @@ export default class UploadImageStep extends PureComponent<UploadImageStepProps>
|
|
|
15
15
|
uploadInputRef: import("react").RefObject<HTMLInputElement>;
|
|
16
16
|
onManualUpload: () => void;
|
|
17
17
|
onKeyDown: (event: React.KeyboardEvent<HTMLLabelElement>) => void;
|
|
18
|
-
getImage: () => number | true |
|
|
18
|
+
getImage: () => number | true | import("react").JSX.Element | Iterable<import("react").ReactNode>;
|
|
19
19
|
render(): import("react").JSX.Element;
|
|
20
20
|
}
|
|
21
21
|
//# sourceMappingURL=UploadImageStep.d.ts.map
|
|
@@ -5,5 +5,5 @@ export declare const UploadItemLink: import("react").ForwardRefExoticComponent<{
|
|
|
5
5
|
singleFileUpload: boolean;
|
|
6
6
|
} & {
|
|
7
7
|
children?: import("react").ReactNode | undefined;
|
|
8
|
-
} & import("react").RefAttributes<
|
|
8
|
+
} & import("react").RefAttributes<HTMLDivElement | HTMLAnchorElement>>;
|
|
9
9
|
//# sourceMappingURL=UploadItemLink.d.ts.map
|
|
@@ -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
|
|
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
|
@@ -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,
|
|
@@ -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('
|
|
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('
|
|
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
|
|
220
|
-
const {
|
|
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.
|
|
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
|
|
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={
|
|
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-${
|
|
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,
|
|
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
|
+
}
|
|
@@ -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={
|
|
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 && (
|
package/src/main.css
CHANGED
|
@@ -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
|
}
|