@sproutsocial/seeds-react-token-input 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@sproutsocial/seeds-react-token-input",
3
+ "version": "1.0.0",
4
+ "description": "Seeds React TokenInput",
5
+ "author": "Sprout Social, Inc.",
6
+ "license": "MIT",
7
+ "main": "dist/index.js",
8
+ "module": "dist/esm/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "scripts": {
11
+ "build": "tsup --dts",
12
+ "build:debug": "tsup --dts --metafile",
13
+ "dev": "tsup --watch --dts",
14
+ "clean": "rm -rf .turbo dist",
15
+ "clean:modules": "rm -rf node_modules",
16
+ "typecheck": "tsc --noEmit",
17
+ "test": "jest",
18
+ "test:watch": "jest --watch --coverage=false"
19
+ },
20
+ "dependencies": {
21
+ "@sproutsocial/seeds-react-theme": "*",
22
+ "@sproutsocial/seeds-react-system-props": "*",
23
+ "@sproutsocial/seeds-react-mixins": "*",
24
+ "@sproutsocial/seeds-react-input": "*",
25
+ "@sproutsocial/seeds-react-box": "*",
26
+ "@sproutsocial/seeds-react-icon": "*",
27
+ "@sproutsocial/seeds-react-token": "*",
28
+ "lodash.uniqueid": "^4.0.1"
29
+ },
30
+ "devDependencies": {
31
+ "@types/react": "^18.0.0",
32
+ "@types/styled-components": "^5.1.26",
33
+ "@types/lodash.uniqueid": "^4.0.1",
34
+ "@sproutsocial/eslint-config-seeds": "*",
35
+ "react": "^18.0.0",
36
+ "styled-components": "^5.2.3",
37
+ "tsup": "^8.0.2",
38
+ "typescript": "^5.6.2",
39
+ "@sproutsocial/seeds-tsconfig": "*",
40
+ "@sproutsocial/seeds-testing": "*",
41
+ "@sproutsocial/seeds-react-testing-library": "*"
42
+ },
43
+ "peerDependencies": {
44
+ "styled-components": "^5.2.3"
45
+ },
46
+ "engines": {
47
+ "node": ">=18"
48
+ }
49
+ }
@@ -0,0 +1,235 @@
1
+ import React, { Fragment, useState } from "react";
2
+ import { Icon } from "@sproutsocial/seeds-react-icon";
3
+ import { TokenInput, type TypeTokenInputProps, type TypeTokenSpec } from "./";
4
+ import type { Meta, StoryObj } from "@storybook/react";
5
+
6
+ const MinimalStatefulTokenInput = (props: TypeTokenInputProps) => {
7
+ const { tokens, ...rest } = props;
8
+ const [tokenSpecs, setTokenSpecs] = useState(tokens || []);
9
+ return (
10
+ <Fragment>
11
+ <TokenInput
12
+ {...rest}
13
+ id="example"
14
+ name="example"
15
+ tokens={tokenSpecs}
16
+ onChangeTokens={setTokenSpecs}
17
+ autocomplete="off"
18
+ />
19
+ </Fragment>
20
+ );
21
+ };
22
+
23
+ const MeddlingStatefulTokenInput = (props: TypeTokenInputProps) => {
24
+ const { delimiters, value, tokens, ...rest } = props;
25
+ const [tokenSpecs, setTokenSpecs] = useState(tokens || []);
26
+ const [text, setText] = useState(value);
27
+
28
+ const handleAddToken = (newToken: TypeTokenSpec) => {
29
+ setTokenSpecs((tokens) => tokens.concat(newToken));
30
+ setText("");
31
+ };
32
+
33
+ const handleRemoveToken = (id: TypeTokenSpec["id"]) => {
34
+ setTokenSpecs((tokens) => tokens.filter((token) => token.id !== id));
35
+ };
36
+
37
+ // @ts-expect-error: event isn't used but it's required by the type
38
+ const handleChange = (e, newValue: TypeTokenSpec["value"]) => {
39
+ setText(newValue);
40
+ };
41
+
42
+ // @ts-expect-error: event isn't used but it's required by the type
43
+ const handleClickToken = (e, id: TypeTokenSpec["id"]) => {
44
+ handleRemoveToken(id);
45
+ };
46
+
47
+ return (
48
+ <Fragment>
49
+ <TokenInput
50
+ {...rest}
51
+ id="fine-grain"
52
+ name="fine-grain"
53
+ value={text}
54
+ tokens={tokenSpecs}
55
+ delimiters={delimiters}
56
+ onChange={handleChange}
57
+ onAddToken={handleAddToken}
58
+ onRemoveToken={handleRemoveToken}
59
+ onClickToken={handleClickToken}
60
+ />
61
+ </Fragment>
62
+ );
63
+ };
64
+
65
+ const meta: Meta<typeof TokenInput> = {
66
+ title: "Components/Form Elements/TokenInput",
67
+ component: TokenInput,
68
+ args: {
69
+ ariaLabel: "Enter names of fruits",
70
+ value: "",
71
+ disabled: undefined,
72
+ isInvalid: undefined,
73
+ },
74
+ };
75
+
76
+ export default meta;
77
+
78
+ type Story = StoryObj<typeof TokenInput>;
79
+
80
+ export const Default: Story = {
81
+ render: (args) => (
82
+ <Fragment>
83
+ <MinimalStatefulTokenInput {...args} />
84
+ </Fragment>
85
+ ),
86
+ name: "Default",
87
+ };
88
+
89
+ export const HighControl: Story = {
90
+ args: {
91
+ tokens: [
92
+ {
93
+ id: "0",
94
+ value: "Cherries",
95
+ },
96
+ {
97
+ id: "1",
98
+ value: "Mangos",
99
+ },
100
+ ],
101
+ },
102
+ render: (args) => (
103
+ <Fragment>
104
+ <MeddlingStatefulTokenInput {...args} />
105
+ </Fragment>
106
+ ),
107
+ name: "Fine-grained state control",
108
+ };
109
+
110
+ export const TrimmedTokens: Story = {
111
+ args: {
112
+ tokenMaxLength: 4,
113
+ },
114
+ render: (args) => (
115
+ <Fragment>
116
+ <MinimalStatefulTokenInput {...args} />
117
+ </Fragment>
118
+ ),
119
+ name: "Trimming tokens",
120
+ };
121
+
122
+ export const WithIcon: Story = {
123
+ args: {
124
+ iconName: "lock-outline",
125
+ },
126
+ render: (args) => (
127
+ <Fragment>
128
+ <MinimalStatefulTokenInput {...args} />
129
+ </Fragment>
130
+ ),
131
+ name: "Tokens With Icon",
132
+ };
133
+
134
+ export const WithIndividualIcons: Story = {
135
+ args: {
136
+ iconName: "triangle-outline",
137
+ tokens: [
138
+ {
139
+ id: "0",
140
+ iconName: "face-smile-solid",
141
+ iconProps: {
142
+ "aria-label": "informative emoji icon",
143
+ },
144
+ value: "Cherries",
145
+ },
146
+ {
147
+ id: "1",
148
+ value: "Mangos",
149
+ },
150
+ ],
151
+ },
152
+ render: (args) => (
153
+ <Fragment>
154
+ <MeddlingStatefulTokenInput {...args} />
155
+ </Fragment>
156
+ ),
157
+ name: "Tokens With Individual Icons",
158
+ };
159
+
160
+ export const Disabled: Story = {
161
+ args: {
162
+ tokens: [
163
+ {
164
+ id: "0",
165
+ value: "One",
166
+ },
167
+ {
168
+ id: "1",
169
+ value: "Two",
170
+ },
171
+ ],
172
+ disabled: true,
173
+ },
174
+ render: (args) => <MinimalStatefulTokenInput {...args} />,
175
+ name: "Disabled",
176
+ };
177
+
178
+ export const Error: Story = {
179
+ args: {
180
+ isInvalid: true,
181
+ tokens: [
182
+ {
183
+ id: "0",
184
+ value: "One",
185
+ },
186
+ {
187
+ id: "1",
188
+ value: "Two",
189
+ },
190
+ ],
191
+ },
192
+ render: (args) => <MinimalStatefulTokenInput {...args} />,
193
+ name: "Error",
194
+ };
195
+
196
+ export const LeftIcon: Story = {
197
+ args: {
198
+ elemBefore: <Icon fixedWidth name="lock-outline" aria-hidden />,
199
+ },
200
+ render: (args) => <MinimalStatefulTokenInput {...args} />,
201
+ name: "Left Icon",
202
+ };
203
+
204
+ export const RightIcon: Story = {
205
+ args: {
206
+ elemAfter: <Icon fixedWidth name="lock-outline" aria-hidden />,
207
+ },
208
+ render: (args) => <MinimalStatefulTokenInput {...args} />,
209
+ name: "Right Icon",
210
+ };
211
+
212
+ export const LeftAndRightIcons: Story = {
213
+ args: {
214
+ elemBefore: <Icon fixedWidth name="magnifying-glass-outline" aria-hidden />,
215
+ elemAfter: <Icon fixedWidth name="lock-outline" aria-hidden />,
216
+ },
217
+ render: (args) => <MinimalStatefulTokenInput {...args} />,
218
+ name: "Left and right icons",
219
+ };
220
+
221
+ export const Autofocus: Story = {
222
+ args: {
223
+ autoFocus: true,
224
+ },
225
+ render: (args) => <MinimalStatefulTokenInput {...args} />,
226
+ name: "Autofocus",
227
+ };
228
+
229
+ export const WithPlaceholder: Story = {
230
+ args: {
231
+ placeholder: "Enter a token...",
232
+ },
233
+ render: (args) => <MinimalStatefulTokenInput {...args} />,
234
+ name: "With Placeholder",
235
+ };
@@ -0,0 +1,302 @@
1
+ import * as React from "react";
2
+ import { Accessory } from "@sproutsocial/seeds-react-input";
3
+ import Box from "@sproutsocial/seeds-react-box";
4
+ import Icon from "@sproutsocial/seeds-react-icon";
5
+ import Token from "@sproutsocial/seeds-react-token";
6
+ import Container from "./styles";
7
+ import { asTokenSpec, delimitersAsRegExp } from "./util";
8
+ import type { TypeTokenInputProps, TypeTokenSpec } from "./TokenInputTypes";
9
+ import { TokenScreenReaderStatus } from "./TokenScreenReaderStatus";
10
+
11
+ type TypeState = {
12
+ prevProps: TypeTokenInputProps;
13
+ hasFocus: boolean;
14
+ activeToken: string | null | undefined;
15
+ value: string;
16
+ };
17
+
18
+ const DefaultDelimiters = [",", "Enter"];
19
+ const ControlledPropNames: (keyof TypeState)[] = [
20
+ "value",
21
+ "hasFocus",
22
+ "activeToken",
23
+ ];
24
+
25
+ export default class TokenInput extends React.Component<
26
+ TypeTokenInputProps,
27
+ TypeState
28
+ > {
29
+ delimiterMatcher: RegExp;
30
+
31
+ constructor(props: TypeTokenInputProps) {
32
+ super(props);
33
+ const { hasFocus, activeToken, value, delimiters } = props;
34
+ this.delimiterMatcher = delimitersAsRegExp(delimiters || DefaultDelimiters);
35
+ this.state = {
36
+ prevProps: props,
37
+ hasFocus: hasFocus || false,
38
+ activeToken: activeToken,
39
+ value: value || "",
40
+ };
41
+ }
42
+
43
+ static getDerivedStateFromProps(
44
+ props: Readonly<TypeTokenInputProps>,
45
+ state: TypeState
46
+ ) {
47
+ const { prevProps } = state;
48
+ const modifiedState: Partial<TypeState> = { prevProps: props };
49
+ ControlledPropNames.forEach((propName) => {
50
+ const currentProp = props[propName as keyof TypeTokenInputProps];
51
+
52
+ // @ts-ignore: TODO - fix state types for prevProps
53
+ if (currentProp !== prevProps[propName]) {
54
+ modifiedState[propName] = currentProp;
55
+ }
56
+ });
57
+ modifiedState.prevProps = props;
58
+ return modifiedState;
59
+ }
60
+
61
+ isDelimiter(keyName: string) {
62
+ const { delimiters = DefaultDelimiters } = this.props;
63
+ return delimiters.includes(keyName);
64
+ }
65
+
66
+ spawnNewTokens(texts: string[]) {
67
+ const {
68
+ onAddToken,
69
+ onChangeTokens,
70
+ tokenMaxLength: max = Infinity,
71
+ tokens = [],
72
+ } = this.props;
73
+ const tokenSpecs = texts.map((text) => asTokenSpec(text.slice(0, max)));
74
+
75
+ if (onAddToken) {
76
+ tokenSpecs.forEach(onAddToken);
77
+ } else if (onChangeTokens) {
78
+ onChangeTokens(tokens.concat(tokenSpecs));
79
+ }
80
+
81
+ this.setState({
82
+ value: "",
83
+ });
84
+ }
85
+
86
+ deleteToken(tokenId?: string) {
87
+ const { onRemoveToken, onChangeTokens, tokens = [] } = this.props;
88
+ const count = tokens.length;
89
+ if (count === 0) return;
90
+ const id = tokenId ?? tokens[count - 1]?.id;
91
+
92
+ if (onRemoveToken) {
93
+ onRemoveToken(id ? id : "");
94
+ } else if (onChangeTokens) {
95
+ onChangeTokens(tokens.filter((tokenSpec) => tokenSpec.id !== id));
96
+ }
97
+
98
+ this.setState({
99
+ value: "",
100
+ });
101
+ }
102
+
103
+ handleChangeText = (e: React.SyntheticEvent<HTMLInputElement>) => {
104
+ const { value } = e.currentTarget;
105
+ const { onChange } = this.props;
106
+ this.setState({
107
+ value,
108
+ });
109
+ onChange?.(e, value);
110
+ };
111
+
112
+ handleFocus = (e: React.FocusEvent<HTMLInputElement>) => {
113
+ const { onFocus } = this.props;
114
+ this.setState({
115
+ hasFocus: true,
116
+ });
117
+ onFocus?.(e);
118
+ };
119
+
120
+ handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
121
+ const { onBlur } = this.props;
122
+ if (onBlur) onBlur(e);
123
+ this.setState({
124
+ hasFocus: false,
125
+ });
126
+ };
127
+
128
+ handleKeyUp = (e: React.KeyboardEvent<HTMLInputElement>) => {
129
+ this.props.onKeyUp?.(e, e.currentTarget.value);
130
+ };
131
+
132
+ handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
133
+ const { onKeyDown } = this.props;
134
+ const { key, currentTarget } = e;
135
+ const text = currentTarget.value;
136
+ if (onKeyDown) onKeyDown(e, text);
137
+
138
+ // keyPress event runs before change
139
+ // Prevent event from bubbling up and calling change, which can lead to comma in value
140
+ if (this.isDelimiter(key)) {
141
+ if (text) {
142
+ this.spawnNewTokens([text]);
143
+ e.preventDefault();
144
+ }
145
+ } else if (key === "Backspace") {
146
+ if (text === "") {
147
+ this.deleteToken();
148
+ }
149
+ }
150
+ };
151
+
152
+ handlePaste = (e: React.ClipboardEvent<HTMLInputElement>) => {
153
+ const text = e.clipboardData.getData("text");
154
+ const { onPaste } = this.props;
155
+ if (onPaste) onPaste(e, text);
156
+ const subtexts = text.split(this.delimiterMatcher);
157
+ const texts = subtexts.filter((subtext) => subtext.length);
158
+
159
+ if (texts.length > 1) {
160
+ this.spawnNewTokens(texts);
161
+ e.preventDefault();
162
+ }
163
+ };
164
+
165
+ handleClickToken = (
166
+ e: React.SyntheticEvent<HTMLButtonElement>,
167
+ token: TypeTokenSpec
168
+ ) => {
169
+ const { onClickToken, disabled } = this.props;
170
+ if (onClickToken) onClickToken(e, token.id);
171
+
172
+ if (!disabled) {
173
+ this.deleteToken(token.id);
174
+ }
175
+ };
176
+
177
+ renderToken(token: TypeTokenSpec): React.ReactNode {
178
+ const { iconName: defaultIconName, disabled } = this.props;
179
+ const activeId = this.state.activeToken;
180
+ const {
181
+ id,
182
+ iconName: tokenIconName,
183
+ iconProps = { "aria-hidden": true },
184
+ value,
185
+ valid,
186
+ } = token;
187
+ const iconName = tokenIconName || defaultIconName;
188
+ const isActive = activeId === id;
189
+ return (
190
+ <Token
191
+ id={id}
192
+ onClick={(e) => this.handleClickToken(e, token)}
193
+ valid={valid}
194
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
195
+ // @ts-ignore
196
+ active={isActive}
197
+ disabled={disabled}
198
+ >
199
+ <Box display="flex" alignItems="center">
200
+ {iconName && (
201
+ <Icon name={iconName} size="mini" pr={300} {...iconProps} />
202
+ )}
203
+ {value}
204
+ </Box>
205
+ </Token>
206
+ );
207
+ }
208
+
209
+ renderTokens(tokens: TypeTokenSpec[]): React.ReactNode {
210
+ return tokens.map<React.ReactNode>((token) => (
211
+ <div key={token.id} className="TokenInput-token">
212
+ {this.renderToken(token)}
213
+ </div>
214
+ ));
215
+ }
216
+
217
+ override render() {
218
+ const {
219
+ autoFocus,
220
+ autocomplete,
221
+ disabled,
222
+ isInvalid,
223
+ hasWarning,
224
+ id,
225
+ name,
226
+ placeholder,
227
+ required,
228
+ elemBefore,
229
+ elemAfter,
230
+ maxLength,
231
+ ariaDescribedby,
232
+ ariaLabel,
233
+ innerRef,
234
+ // These functions are used in the class functions above, but need to be extracted in order for `rest` to be correct
235
+ /* eslint-disable @typescript-eslint/no-unused-vars */
236
+ value,
237
+ onAddToken,
238
+ onRemoveToken,
239
+ onChangeTokens,
240
+ onClickToken,
241
+ onBlur,
242
+ onChange,
243
+ onFocus,
244
+ onKeyDown,
245
+ onKeyUp,
246
+ onPaste,
247
+ /* eslint-enable @typescript-eslint/no-unused-vars */
248
+ inputProps = {},
249
+ qa = {},
250
+ tokens,
251
+ ...rest
252
+ } = this.props;
253
+ const { state } = this;
254
+ return (
255
+ <Container
256
+ hasBeforeElement={!!elemBefore}
257
+ hasAfterElement={!!elemAfter}
258
+ disabled={disabled}
259
+ invalid={!!isInvalid}
260
+ warning={hasWarning}
261
+ focused={state.hasFocus}
262
+ {...rest}
263
+ >
264
+ {elemBefore && <Accessory before>{elemBefore}</Accessory>}
265
+
266
+ {tokens && this.renderTokens(tokens)}
267
+
268
+ <TokenScreenReaderStatus tokens={tokens} />
269
+
270
+ <input
271
+ aria-describedby={ariaDescribedby}
272
+ aria-invalid={!!isInvalid}
273
+ aria-label={ariaLabel}
274
+ autoFocus={autoFocus}
275
+ autoComplete={autocomplete}
276
+ disabled={disabled}
277
+ id={id}
278
+ name={name}
279
+ placeholder={placeholder}
280
+ type="text"
281
+ required={required}
282
+ value={state.value}
283
+ maxLength={maxLength}
284
+ onBlur={this.handleBlur}
285
+ onChange={this.handleChangeText}
286
+ onFocus={this.handleFocus}
287
+ onKeyDown={this.handleKeyDown}
288
+ onKeyUp={this.handleKeyUp}
289
+ onPaste={this.handlePaste}
290
+ ref={innerRef}
291
+ data-qa-input={name || ""}
292
+ data-qa-input-isdisabled={!!disabled}
293
+ data-qa-input-isrequired={!!required}
294
+ {...qa}
295
+ {...inputProps}
296
+ />
297
+
298
+ {elemAfter && <Accessory after>{elemAfter}</Accessory>}
299
+ </Container>
300
+ );
301
+ }
302
+ }
@@ -0,0 +1,119 @@
1
+ import * as React from "react";
2
+ import type {
3
+ TypeStyledComponentsCommonProps,
4
+ TypeSystemCommonProps,
5
+ } from "@sproutsocial/seeds-react-system-props";
6
+ import type {
7
+ TypeIconProps,
8
+ TypeIconName,
9
+ } from "@sproutsocial/seeds-react-icon";
10
+
11
+ export interface TypeTokenSpec {
12
+ id: string;
13
+ iconName?: TypeIconName;
14
+ iconProps?: Omit<TypeIconProps, "name">;
15
+ value: string;
16
+ valid?: boolean;
17
+ }
18
+
19
+ export interface TypeBaseTokenInputProps {
20
+ /** ID of the form element, should match the "for" value of the associated label */
21
+ id: string;
22
+ name: string;
23
+ iconName?: TypeIconName;
24
+
25
+ /** Array of delimiter key names */
26
+ delimiters?: string[];
27
+
28
+ /** Current input text. Required when controlling the input text */
29
+ value?: string;
30
+
31
+ /** Current focus state. Required when controlling the focus via onFocus and onBlur */
32
+ hasFocus?: boolean;
33
+
34
+ /** Id of the currently selected token */
35
+ activeToken?: string;
36
+
37
+ /** Array of current tokens */
38
+ tokens?: TypeTokenSpec[];
39
+
40
+ /** Standard control of changing tokens. For fine-grain control use onAddToken and onRemoveToken, instead */
41
+ onChangeTokens?: (tokens: TypeTokenSpec[]) => void;
42
+
43
+ /** Fine-grained control of adding tokens. Use with onRemoveToken instead of onChangeTokens */
44
+ onAddToken?: (tokenSpec: TypeTokenSpec) => void;
45
+
46
+ /** Fine-grained control of removing tokens. Use with onAddToken instead of onChangeTokens */
47
+ onRemoveToken?: (tokenId: string) => void;
48
+
49
+ /** Controls clicking on a token. When absent, clicking a token removes itself */
50
+ onClickToken?: (
51
+ e: React.SyntheticEvent<HTMLButtonElement>,
52
+ tokenId: string
53
+ ) => void;
54
+
55
+ /** Fine-grained control of the input text used to create tokens */
56
+ onChange?: (e: React.SyntheticEvent<HTMLInputElement>, value: string) => void;
57
+
58
+ /** Fine-grained control of pasted text */
59
+ onPaste?: (e: React.ClipboardEvent<HTMLInputElement>, value: string) => void;
60
+ onBlur?: (e: React.FocusEvent<HTMLInputElement>) => void;
61
+ onFocus?: (e: React.FocusEvent<HTMLInputElement>) => void;
62
+ onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>, value: string) => void;
63
+ onKeyUp?: (e: React.KeyboardEvent<HTMLInputElement>, value: string) => void;
64
+
65
+ /** Attribute used to associate other elements that describe the Input, like an error */
66
+ ariaDescribedby?: string;
67
+
68
+ /** Label used to describe the input if not used with an accompanying visual label */
69
+ ariaLabel?: string;
70
+
71
+ /** Placeholder text for when value is undefined or empty */
72
+ placeholder?: string;
73
+
74
+ /** Will autofocus the element when mounted to the DOM */
75
+ autoFocus?: boolean;
76
+
77
+ /** HTML disabled attribute */
78
+ disabled?: boolean;
79
+
80
+ /** Whether or not the current contents of the input are invalid */
81
+ isInvalid?: boolean;
82
+
83
+ /** Whether or not the current contents of the input has any warnings */
84
+ hasWarning?: boolean;
85
+
86
+ /** HTML required attribute */
87
+ required?: boolean;
88
+
89
+ /** 16x16 element, such as an icon */
90
+ elemBefore?: React.ReactNode;
91
+
92
+ /** 16x16 element, such as an icon */
93
+ elemAfter?: React.ReactNode;
94
+
95
+ /** Max input text length */
96
+ maxLength?: number;
97
+
98
+ /** Max length of the token */
99
+ tokenMaxLength?: number;
100
+
101
+ /** Props to spread onto the underlying input element */
102
+ inputProps?: React.ComponentPropsWithoutRef<"input">;
103
+
104
+ /** Used to get a reference to the underlying element */
105
+ innerRef?: React.Ref<HTMLInputElement>;
106
+ qa?: object;
107
+
108
+ /** Browser autocomplete support */
109
+ autocomplete?: string;
110
+ }
111
+
112
+ export interface TypeTokenInputProps
113
+ extends TypeBaseTokenInputProps,
114
+ TypeStyledComponentsCommonProps,
115
+ TypeSystemCommonProps,
116
+ Omit<
117
+ React.ComponentPropsWithoutRef<"div">,
118
+ keyof TypeBaseTokenInputProps | "color"
119
+ > {}