@transferwise/components 46.51.0 → 46.52.1
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/i18n/pt.json +2 -0
- package/build/i18n/pt.json.js +2 -0
- package/build/i18n/pt.json.js.map +1 -1
- package/build/i18n/pt.json.mjs +2 -0
- package/build/i18n/pt.json.mjs.map +1 -1
- package/build/i18n/zh-CN.json +2 -0
- package/build/i18n/zh-CN.json.js +2 -0
- package/build/i18n/zh-CN.json.js.map +1 -1
- package/build/i18n/zh-CN.json.mjs +2 -0
- package/build/i18n/zh-CN.json.mjs.map +1 -1
- package/build/inputs/SelectInput.js +107 -36
- package/build/inputs/SelectInput.js.map +1 -1
- package/build/inputs/SelectInput.mjs +109 -38
- package/build/inputs/SelectInput.mjs.map +1 -1
- package/build/main.css +10 -0
- package/build/styles/inputs/SelectInput.css +10 -0
- package/build/styles/main.css +10 -0
- package/build/typeahead/Typeahead.js +63 -59
- package/build/typeahead/Typeahead.js.map +1 -1
- package/build/typeahead/Typeahead.messages.js +12 -0
- package/build/typeahead/Typeahead.messages.js.map +1 -0
- package/build/typeahead/Typeahead.messages.mjs +10 -0
- package/build/typeahead/Typeahead.messages.mjs.map +1 -0
- package/build/typeahead/Typeahead.mjs +63 -59
- package/build/typeahead/Typeahead.mjs.map +1 -1
- package/build/types/inputs/SelectInput.d.ts.map +1 -1
- package/build/types/typeahead/Typeahead.d.ts +2 -1
- package/build/types/typeahead/Typeahead.d.ts.map +1 -1
- package/build/types/typeahead/Typeahead.messages.d.ts +9 -0
- package/build/types/typeahead/Typeahead.messages.d.ts.map +1 -0
- package/package.json +5 -4
- package/src/i18n/pt.json +2 -0
- package/src/i18n/zh-CN.json +2 -0
- package/src/inputs/SelectInput.css +10 -0
- package/src/inputs/SelectInput.less +12 -0
- package/src/inputs/SelectInput.story.tsx +20 -0
- package/src/inputs/SelectInput.tsx +144 -46
- package/src/main.css +10 -0
- package/src/typeahead/Typeahead.messages.ts +9 -0
- package/src/typeahead/Typeahead.rtl.spec.tsx +13 -1
- package/src/typeahead/Typeahead.spec.js +12 -10
- package/src/typeahead/Typeahead.story.tsx +194 -195
- package/src/typeahead/Typeahead.tsx +16 -9
|
@@ -1,125 +1,122 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { StoryContext } from '@storybook/react';
|
|
3
|
-
import { userEvent, within } from '@storybook/test';
|
|
1
|
+
import { Meta, StoryObj } from '@storybook/react';
|
|
4
2
|
import { Search as SearchIcon } from '@transferwise/icons';
|
|
5
|
-
import {
|
|
3
|
+
import { userEvent, within, fn } from '@storybook/test';
|
|
6
4
|
|
|
7
|
-
import {
|
|
5
|
+
import Typeahead, { type TypeaheadOption } from './Typeahead';
|
|
6
|
+
import { Size } from '../common';
|
|
7
|
+
import { useState } from 'react';
|
|
8
8
|
import { Input } from '../inputs/Input';
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
type Story = StoryObj<typeof Typeahead>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Checks if provided TypeaheadOption contains an HTML5-compliant email address
|
|
14
|
+
* @see https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address
|
|
15
|
+
*/
|
|
16
|
+
const validateOptionAsEmail = (option: TypeaheadOption) => {
|
|
17
|
+
return /^[\w.!#$%&'*+/=?^`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i.test(
|
|
18
|
+
option.label,
|
|
19
|
+
);
|
|
20
|
+
};
|
|
11
21
|
|
|
12
22
|
export default {
|
|
13
23
|
component: Typeahead,
|
|
14
24
|
title: 'Forms/Typeahead',
|
|
25
|
+
args: {
|
|
26
|
+
allowNew: false,
|
|
27
|
+
autoFillOnBlur: true,
|
|
28
|
+
autoFocus: false,
|
|
29
|
+
chipSeparators: [',', ' '],
|
|
30
|
+
clearable: true,
|
|
31
|
+
inputAutoComplete: 'new-password',
|
|
32
|
+
minQueryLength: 3,
|
|
33
|
+
multiple: false,
|
|
34
|
+
searchDelay: 200,
|
|
35
|
+
showSuggestions: true,
|
|
36
|
+
showNewEntry: true,
|
|
37
|
+
size: Size.MEDIUM,
|
|
38
|
+
initialValue: [],
|
|
39
|
+
id: 'myTypeahead',
|
|
40
|
+
name: 'typeahead-input-name',
|
|
41
|
+
placeholder: 'placeholder',
|
|
42
|
+
onChange: fn(),
|
|
43
|
+
onBlur: fn(),
|
|
44
|
+
onFocus: fn(),
|
|
45
|
+
onInputChange: fn(),
|
|
46
|
+
onSearch: fn(),
|
|
47
|
+
},
|
|
48
|
+
argTypes: {
|
|
49
|
+
size: {
|
|
50
|
+
control: 'inline-radio',
|
|
51
|
+
options: [Size.MEDIUM, Size.LARGE],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
} satisfies Meta<typeof Typeahead>;
|
|
55
|
+
|
|
56
|
+
export const Basic: Story = {
|
|
57
|
+
render: function Render(args) {
|
|
58
|
+
const [options, setOptions] = useState([
|
|
59
|
+
{
|
|
60
|
+
label: 'A thing',
|
|
61
|
+
note: 'with a note',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: 'Another thing',
|
|
65
|
+
secondary: 'with secondary text this time',
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
label: 'Profile',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
label: 'Globe',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
label: 'British pound',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
label: 'Euro',
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
label: 'Something else',
|
|
81
|
+
},
|
|
82
|
+
]);
|
|
83
|
+
|
|
84
|
+
const validateChipWhenMultiple = () =>
|
|
85
|
+
args.multiple && args.allowNew
|
|
86
|
+
? (option: TypeaheadOption) => validateOptionAsEmail(option)
|
|
87
|
+
: undefined;
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Typeahead
|
|
91
|
+
{...args}
|
|
92
|
+
initialValue={[]}
|
|
93
|
+
validateChip={validateChipWhenMultiple()}
|
|
94
|
+
addon={<SearchIcon size={24} />}
|
|
95
|
+
options={options}
|
|
96
|
+
onSearch={() => {
|
|
97
|
+
setTimeout(() => setOptions(options), 1500);
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
);
|
|
101
|
+
},
|
|
15
102
|
};
|
|
16
103
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
return /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
|
|
20
|
-
option.label,
|
|
21
|
-
);
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
export const createable = () => {
|
|
25
|
-
return (
|
|
104
|
+
export const Creatable: Story = {
|
|
105
|
+
render: (args) => (
|
|
26
106
|
<Typeahead
|
|
27
|
-
|
|
28
|
-
name="typeahead-input-name"
|
|
29
|
-
size="md"
|
|
30
|
-
maxHeight={100}
|
|
31
|
-
footer={<div>Want a footer? Style it!</div>}
|
|
32
|
-
multiple
|
|
33
|
-
clearable
|
|
107
|
+
{...args}
|
|
34
108
|
allowNew
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
chipSeparators={[',', ' ']}
|
|
39
|
-
validateChip={validateChip}
|
|
109
|
+
multiple
|
|
110
|
+
initialValue={[]}
|
|
111
|
+
validateChip={validateOptionAsEmail}
|
|
40
112
|
addon={<SearchIcon size={24} />}
|
|
41
113
|
options={[]}
|
|
42
|
-
onChange={() => {}}
|
|
43
|
-
onBlur={() => {}}
|
|
44
114
|
/>
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
await userEvent.type(canvas.getByRole('combobox'), 'chip{Enter}chip2{Enter}');
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export const Basic = () => {
|
|
54
|
-
const [options, setOptions] = useState([
|
|
55
|
-
{
|
|
56
|
-
label: 'A thing',
|
|
57
|
-
note: 'with a note',
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
label: 'Another thing',
|
|
61
|
-
secondary: 'with secondary text this time',
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
label: 'Profile',
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
label: 'Globe',
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
label: 'British pound',
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
label: 'Euro',
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
label: 'Something else',
|
|
77
|
-
},
|
|
78
|
-
]);
|
|
79
|
-
|
|
80
|
-
const validateChipWhenMultiple = () => {
|
|
81
|
-
return multiple && allowNew ? (option: TypeaheadOption) => validateChip(option) : undefined;
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const multiple = boolean('multiple', false);
|
|
85
|
-
const clearable = boolean('clearable', false);
|
|
86
|
-
const allowNew = boolean('allowNew', false);
|
|
87
|
-
const showSuggestions = boolean('showSuggestions', true);
|
|
88
|
-
const showNewEntry = boolean('showNewEntry', true);
|
|
89
|
-
const showAlert = boolean('alert', false);
|
|
90
|
-
const alertType = select('alert type', [Sentiment.ERROR, Sentiment.WARNING], Sentiment.ERROR);
|
|
91
|
-
|
|
92
|
-
return (
|
|
93
|
-
<Typeahead
|
|
94
|
-
id="typeahead"
|
|
95
|
-
name="typeahead-input-name"
|
|
96
|
-
size="md"
|
|
97
|
-
maxHeight={100}
|
|
98
|
-
footer={<div>Want a footer? Style it!</div>}
|
|
99
|
-
multiple={multiple}
|
|
100
|
-
clearable={clearable}
|
|
101
|
-
allowNew={allowNew}
|
|
102
|
-
showSuggestions={showSuggestions}
|
|
103
|
-
showNewEntry={showNewEntry}
|
|
104
|
-
placeholder="placeholder"
|
|
105
|
-
chipSeparators={[',', ' ']}
|
|
106
|
-
validateChip={validateChipWhenMultiple()}
|
|
107
|
-
alert={showAlert ? { message: `Couldn't add item`, type: alertType } : undefined}
|
|
108
|
-
addon={<SearchIcon size={24} />}
|
|
109
|
-
options={options}
|
|
110
|
-
inputAutoComplete="off"
|
|
111
|
-
onSearch={() => {
|
|
112
|
-
setTimeout(() => setOptions(options), 1500);
|
|
113
|
-
}}
|
|
114
|
-
onChange={() => {}}
|
|
115
|
-
onBlur={() => {}}
|
|
116
|
-
/>
|
|
117
|
-
);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
Basic.play = async ({ canvasElement }: StoryContext) => {
|
|
121
|
-
const canvas = within(canvasElement);
|
|
122
|
-
await userEvent.type(canvas.getByRole('combobox'), 'abc{ArrowDown}');
|
|
115
|
+
),
|
|
116
|
+
play: async ({ canvasElement }) => {
|
|
117
|
+
const canvas = within(canvasElement);
|
|
118
|
+
await userEvent.type(canvas.getByRole('combobox'), 'chip{Enter}hello@wise.com{Enter}');
|
|
119
|
+
},
|
|
123
120
|
};
|
|
124
121
|
|
|
125
122
|
type Result =
|
|
@@ -134,98 +131,100 @@ type Result =
|
|
|
134
131
|
|
|
135
132
|
type SearchState = 'success' | 'idle' | 'error' | 'loading';
|
|
136
133
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
134
|
+
/**
|
|
135
|
+
* @FIXME This story feels incomplete. It ignores bunch of props
|
|
136
|
+
* and seems very opinionated. Surely we can do better?
|
|
137
|
+
*/
|
|
138
|
+
export const Search: Story = {
|
|
139
|
+
render: function Render(args) {
|
|
140
|
+
const [results, setResults] = useState<Result[]>([]);
|
|
141
|
+
const [state, setState] = useState<SearchState>('idle');
|
|
142
|
+
const [filledValue, setFilledValue] = useState<string | null>(null);
|
|
143
|
+
|
|
144
|
+
const handleInputChange = (query: string) => {
|
|
145
|
+
args?.onInputChange?.(query);
|
|
146
|
+
|
|
147
|
+
if (query === 'loading' || query === 'error' || query === 'nothing') {
|
|
148
|
+
setState(query === 'nothing' ? 'success' : query);
|
|
149
|
+
setResults([]);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
141
152
|
|
|
142
|
-
const onChange = (query: string) => {
|
|
143
|
-
if (query === 'loading') {
|
|
144
|
-
setState('loading');
|
|
145
|
-
setResults([]);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
if (query === 'error') {
|
|
149
|
-
setState('error');
|
|
150
|
-
setResults([]);
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
if (query === 'nothing') {
|
|
154
153
|
setState('success');
|
|
155
|
-
setResults(
|
|
156
|
-
|
|
157
|
-
}
|
|
154
|
+
setResults(getResults(query));
|
|
155
|
+
};
|
|
158
156
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
addon={<SearchIcon />}
|
|
196
|
-
options={results.map((option) => ({
|
|
197
|
-
value: option,
|
|
198
|
-
label: option.value,
|
|
199
|
-
keepFocusOnSelect: option.type === 'search',
|
|
200
|
-
clearQueryOnSelect: option.type === 'action',
|
|
201
|
-
}))}
|
|
202
|
-
onChange={(values) => {
|
|
203
|
-
if (values.length > 0) {
|
|
204
|
-
const [updatedValue] = values;
|
|
205
|
-
if (updatedValue.value) {
|
|
206
|
-
onResultSelected(updatedValue.value);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}}
|
|
210
|
-
onInputChange={onChange}
|
|
211
|
-
/>
|
|
212
|
-
{filledValue != null ? <Input value={filledValue} /> : null}
|
|
213
|
-
</>
|
|
214
|
-
);
|
|
215
|
-
};
|
|
157
|
+
const handleResultSelected = (option: Result) => {
|
|
158
|
+
if (option.type === 'search') {
|
|
159
|
+
setResults([
|
|
160
|
+
{ type: 'action', value: `${option.value} Result #1` },
|
|
161
|
+
{ type: 'action', value: `${option.value} Result #2` },
|
|
162
|
+
{ type: 'action', value: `${option.value} Result #3` },
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
if (option.type === 'action') {
|
|
166
|
+
setFilledValue(option.value);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const handleChange = (values: TypeaheadOption<Result>[]) => {
|
|
171
|
+
args?.onChange?.(values);
|
|
172
|
+
|
|
173
|
+
if (values.length > 0) {
|
|
174
|
+
const [updatedValue] = values;
|
|
175
|
+
|
|
176
|
+
if (updatedValue.value) {
|
|
177
|
+
handleResultSelected(updatedValue.value);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const getResults = (query: string): Result[] => {
|
|
183
|
+
return [
|
|
184
|
+
{ type: 'action', value: `${query} Result #1` },
|
|
185
|
+
{ type: 'action', value: `${query} Result #2` },
|
|
186
|
+
{ type: 'action', value: `${query} Result #3` },
|
|
187
|
+
{ type: 'search', value: `Search for more: '${query}'` },
|
|
188
|
+
];
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const renderFooter = (options: Result[]) => {
|
|
192
|
+
let output = null;
|
|
216
193
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
194
|
+
if (state === 'loading') {
|
|
195
|
+
output = 'Loading…';
|
|
196
|
+
}
|
|
221
197
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
198
|
+
if (state === 'success' && options.length === 0) {
|
|
199
|
+
output = 'No results found';
|
|
200
|
+
}
|
|
225
201
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
202
|
+
if (state === 'error' && options.length === 0) {
|
|
203
|
+
output = 'Something went wrong';
|
|
204
|
+
}
|
|
229
205
|
|
|
230
|
-
|
|
231
|
-
}
|
|
206
|
+
return <p className="m-y-2 m-x-2">{output}</p>;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<>
|
|
211
|
+
<Typeahead<Result>
|
|
212
|
+
{...args}
|
|
213
|
+
initialValue={undefined}
|
|
214
|
+
footer={renderFooter(results)}
|
|
215
|
+
addon={<SearchIcon />}
|
|
216
|
+
options={results.map((option) => ({
|
|
217
|
+
value: option,
|
|
218
|
+
label: option.value,
|
|
219
|
+
keepFocusOnSelect: option.type === 'search',
|
|
220
|
+
clearQueryOnSelect: option.type === 'action',
|
|
221
|
+
}))}
|
|
222
|
+
onChange={handleChange}
|
|
223
|
+
onInputChange={handleInputChange}
|
|
224
|
+
/>
|
|
225
|
+
|
|
226
|
+
{filledValue != null ? <Input value={filledValue} /> : null}
|
|
227
|
+
</>
|
|
228
|
+
);
|
|
229
|
+
},
|
|
230
|
+
};
|
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
/* eslint-disable jsx-a11y/anchor-is-valid */
|
|
2
|
-
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
|
3
|
-
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
|
4
|
-
|
|
5
1
|
import { Cross as CrossIcon } from '@transferwise/icons';
|
|
6
2
|
import { clsx } from 'clsx';
|
|
7
3
|
import { DebouncedFunc } from 'lodash';
|
|
8
4
|
import clamp from 'lodash.clamp';
|
|
9
5
|
import debounce from 'lodash.debounce';
|
|
10
6
|
import { Component, ReactNode } from 'react';
|
|
7
|
+
import { injectIntl, WrappedComponentProps } from 'react-intl';
|
|
11
8
|
|
|
12
9
|
import Chip from '../chips/Chip';
|
|
13
|
-
import { Size, Sentiment, SizeMedium, SizeLarge } from '../common';
|
|
14
10
|
import {
|
|
11
|
+
Size,
|
|
12
|
+
Sentiment,
|
|
13
|
+
SizeMedium,
|
|
14
|
+
SizeLarge,
|
|
15
15
|
addClickClassToDocumentOnIos,
|
|
16
16
|
removeClickClassFromDocumentOnIos,
|
|
17
17
|
stopPropagation,
|
|
18
|
-
} from '../common
|
|
18
|
+
} from '../common';
|
|
19
19
|
import InlineAlert from '../inlineAlert';
|
|
20
20
|
import { InlineAlertProps } from '../inlineAlert/InlineAlert';
|
|
21
21
|
import { withInputAttributes, WithInputAttributesProps } from '../inputs/contexts';
|
|
22
22
|
|
|
23
23
|
import TypeaheadInput from './typeaheadInput/TypeaheadInput';
|
|
24
24
|
import TypeaheadOption from './typeaheadOption/TypeaheadOption';
|
|
25
|
+
import messages from './Typeahead.messages';
|
|
25
26
|
|
|
26
27
|
const DEFAULT_MIN_QUERY_LENGTH = 3;
|
|
27
28
|
const SEARCH_DELAY = 200;
|
|
@@ -35,7 +36,7 @@ export type TypeaheadOption<T = string> = {
|
|
|
35
36
|
keepFocusOnSelect?: boolean;
|
|
36
37
|
};
|
|
37
38
|
|
|
38
|
-
export interface TypeaheadProps<T> {
|
|
39
|
+
export interface TypeaheadProps<T> extends WrappedComponentProps {
|
|
39
40
|
id: string;
|
|
40
41
|
name: string;
|
|
41
42
|
addon?: ReactNode;
|
|
@@ -464,6 +465,7 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
|
|
|
464
465
|
const hasWarning = displayAlert && alertType === Sentiment.WARNING;
|
|
465
466
|
const hasInfo = displayAlert && alertType === Sentiment.NEUTRAL;
|
|
466
467
|
return (
|
|
468
|
+
/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */
|
|
467
469
|
<div
|
|
468
470
|
role="group"
|
|
469
471
|
{...inputAttributes}
|
|
@@ -513,7 +515,12 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
|
|
|
513
515
|
|
|
514
516
|
{clearButton && (
|
|
515
517
|
<div className="input-group-addon">
|
|
516
|
-
<button
|
|
518
|
+
<button
|
|
519
|
+
type="button"
|
|
520
|
+
className="btn-unstyled"
|
|
521
|
+
aria-label={this.props.intl.formatMessage(messages.clearLabel)}
|
|
522
|
+
onClick={this.clear}
|
|
523
|
+
>
|
|
517
524
|
<CrossIcon />
|
|
518
525
|
</button>
|
|
519
526
|
</div>
|
|
@@ -526,6 +533,6 @@ class Typeahead<T> extends Component<TypeaheadPropsWithInputAttributes<T>, Typea
|
|
|
526
533
|
}
|
|
527
534
|
}
|
|
528
535
|
|
|
529
|
-
export default withInputAttributes(Typeahead, { nonLabelable: true }) as <T>(
|
|
536
|
+
export default injectIntl(withInputAttributes(Typeahead, { nonLabelable: true })) as <T>(
|
|
530
537
|
props: TypeaheadProps<T>,
|
|
531
538
|
) => React.ReactElement;
|