@instructure/ui-text-input 11.6.0 → 11.6.1-snapshot-129

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 (62) hide show
  1. package/CHANGELOG.md +47 -305
  2. package/es/TextInput/{index.js → v1/index.js} +2 -2
  3. package/es/TextInput/v2/index.js +308 -0
  4. package/es/TextInput/v2/props.js +26 -0
  5. package/es/TextInput/v2/styles.js +234 -0
  6. package/es/TextInput/v2/theme.js +73 -0
  7. package/es/{index.js → exports/a.js} +1 -1
  8. package/{src/index.ts → es/exports/b.js} +1 -2
  9. package/lib/TextInput/{index.js → v1/index.js} +3 -3
  10. package/lib/TextInput/v2/index.js +319 -0
  11. package/lib/TextInput/v2/props.js +31 -0
  12. package/lib/TextInput/v2/styles.js +239 -0
  13. package/lib/TextInput/v2/theme.js +79 -0
  14. package/lib/{index.js → exports/a.js} +2 -2
  15. package/lib/exports/b.js +12 -0
  16. package/package.json +45 -24
  17. package/src/TextInput/{index.tsx → v1/index.tsx} +2 -2
  18. package/src/TextInput/{props.ts → v1/props.ts} +1 -1
  19. package/src/TextInput/v2/README.md +334 -0
  20. package/src/TextInput/v2/index.tsx +406 -0
  21. package/src/TextInput/v2/props.ts +244 -0
  22. package/src/TextInput/v2/styles.ts +245 -0
  23. package/src/TextInput/v2/theme.ts +84 -0
  24. package/src/exports/a.ts +25 -0
  25. package/src/exports/b.ts +25 -0
  26. package/tsconfig.build.json +0 -3
  27. package/tsconfig.build.tsbuildinfo +1 -1
  28. package/types/TextInput/{index.d.ts → v1/index.d.ts} +1 -1
  29. package/types/TextInput/v1/index.d.ts.map +1 -0
  30. package/types/TextInput/{props.d.ts → v1/props.d.ts} +1 -1
  31. package/types/TextInput/v1/props.d.ts.map +1 -0
  32. package/types/TextInput/v1/styles.d.ts.map +1 -0
  33. package/types/TextInput/v1/theme.d.ts.map +1 -0
  34. package/types/TextInput/v2/index.d.ts +79 -0
  35. package/types/TextInput/v2/index.d.ts.map +1 -0
  36. package/types/TextInput/v2/props.d.ts +138 -0
  37. package/types/TextInput/v2/props.d.ts.map +1 -0
  38. package/types/TextInput/v2/styles.d.ts +16 -0
  39. package/types/TextInput/v2/styles.d.ts.map +1 -0
  40. package/types/TextInput/v2/theme.d.ts +10 -0
  41. package/types/TextInput/v2/theme.d.ts.map +1 -0
  42. package/types/exports/a.d.ts +3 -0
  43. package/types/exports/a.d.ts.map +1 -0
  44. package/types/exports/b.d.ts +3 -0
  45. package/types/exports/b.d.ts.map +1 -0
  46. package/types/TextInput/index.d.ts.map +0 -1
  47. package/types/TextInput/props.d.ts.map +0 -1
  48. package/types/TextInput/styles.d.ts.map +0 -1
  49. package/types/TextInput/theme.d.ts.map +0 -1
  50. package/types/index.d.ts +0 -3
  51. package/types/index.d.ts.map +0 -1
  52. /package/es/TextInput/{props.js → v1/props.js} +0 -0
  53. /package/es/TextInput/{styles.js → v1/styles.js} +0 -0
  54. /package/es/TextInput/{theme.js → v1/theme.js} +0 -0
  55. /package/lib/TextInput/{props.js → v1/props.js} +0 -0
  56. /package/lib/TextInput/{styles.js → v1/styles.js} +0 -0
  57. /package/lib/TextInput/{theme.js → v1/theme.js} +0 -0
  58. /package/src/TextInput/{README.md → v1/README.md} +0 -0
  59. /package/src/TextInput/{styles.ts → v1/styles.ts} +0 -0
  60. /package/src/TextInput/{theme.ts → v1/theme.ts} +0 -0
  61. /package/types/TextInput/{styles.d.ts → v1/styles.d.ts} +0 -0
  62. /package/types/TextInput/{theme.d.ts → v1/theme.d.ts} +0 -0
@@ -0,0 +1,406 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2015 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ import { Component, isValidElement } from 'react'
26
+
27
+ import {
28
+ callRenderProp,
29
+ getInteraction,
30
+ passthroughProps,
31
+ withDeterministicId,
32
+ safeCloneElement
33
+ } from '@instructure/ui-react-utils'
34
+ import {
35
+ isActiveElement,
36
+ addEventListener,
37
+ getCSSStyleDeclaration
38
+ } from '@instructure/ui-dom-utils'
39
+ import { FormField } from '@instructure/ui-form-field/latest'
40
+ import { withStyle } from '@instructure/emotion'
41
+
42
+ import generateStyle from './styles'
43
+ import type {
44
+ TextInputProps,
45
+ TextInputState,
46
+ TextInputStyleProps
47
+ } from './props'
48
+ import { allowedProps } from './props'
49
+ import type { Renderable } from '@instructure/shared-types'
50
+
51
+ /**
52
+ ---
53
+ category: components
54
+ tags: form, field, input
55
+ ---
56
+ **/
57
+ @withDeterministicId()
58
+ @withStyle(generateStyle)
59
+ class TextInput extends Component<TextInputProps, TextInputState> {
60
+ static readonly componentId = 'TextInput'
61
+
62
+ static allowedProps = allowedProps
63
+
64
+ static defaultProps = {
65
+ type: 'text',
66
+ // Leave interaction default undefined so that `disabled` and `readOnly` can also be supplied
67
+ interaction: undefined,
68
+ isRequired: false,
69
+ display: 'block',
70
+ shouldNotWrap: false,
71
+ size: 'medium',
72
+ textAlign: 'start',
73
+ messages: []
74
+ }
75
+
76
+ constructor(props: TextInputProps) {
77
+ super(props)
78
+ this.state = { afterElementHasWidth: undefined }
79
+ this._defaultId = props.deterministicId!()
80
+ this._messagesId = props.deterministicId!('TextInput-messages')
81
+ }
82
+
83
+ ref: Element | null = null
84
+
85
+ private _input: HTMLInputElement | null = null
86
+ private _afterElement: HTMLSpanElement | null = null
87
+
88
+ private readonly _defaultId: string
89
+ private readonly _messagesId: string
90
+
91
+ private _focusListener: { remove(): void } | null = null
92
+
93
+ handleRef = (el: Element | null) => {
94
+ const { elementRef } = this.props
95
+
96
+ this.ref = el
97
+
98
+ if (typeof elementRef === 'function') {
99
+ elementRef(el)
100
+ }
101
+ }
102
+
103
+ componentDidMount() {
104
+ if (this._input) {
105
+ this._focusListener = addEventListener(
106
+ this._input,
107
+ 'focus',
108
+ this.handleFocus
109
+ )
110
+ this.setState({
111
+ afterElementHasWidth: this.getElementHasWidth(this._afterElement)
112
+ })
113
+ }
114
+ this.adjustAfterElementHeight()
115
+ this.props.makeStyles?.(this.makeStyleProps())
116
+ }
117
+
118
+ componentWillUnmount() {
119
+ if (this._focusListener) {
120
+ this._focusListener.remove()
121
+ }
122
+ }
123
+
124
+ componentDidUpdate(prevProps: TextInputProps) {
125
+ if (prevProps.renderAfterInput !== this.props.renderAfterInput) {
126
+ this.setState({
127
+ afterElementHasWidth: this.getElementHasWidth(this._afterElement)
128
+ })
129
+ }
130
+ this.props.makeStyles?.(this.makeStyleProps())
131
+ }
132
+
133
+ renderInstUIIcon(elementToRender: Renderable) {
134
+ if (!elementToRender) {
135
+ return null
136
+ }
137
+ const rendered = callRenderProp(elementToRender)
138
+ // Map sizes to Lucide icon semantic size tokens
139
+ const linkSizeToIconSize = {
140
+ small: 'sm',
141
+ medium: 'md',
142
+ large: 'lg'
143
+ } as const
144
+ const iconSize = linkSizeToIconSize[this.props.size || 'medium']
145
+ if (
146
+ isValidElement(elementToRender) &&
147
+ (elementToRender as React.FunctionComponentElement<unknown>).type
148
+ ?.name === 'WrappedIcon'
149
+ ) {
150
+ return safeCloneElement(rendered, { size: iconSize })
151
+ }
152
+ return rendered
153
+ }
154
+
155
+ adjustAfterElementHeight() {
156
+ const afterElementChild = this._afterElement
157
+ ?.firstElementChild as HTMLElement | null
158
+
159
+ // Check if the child is a button, then get the button's first child (the content span)
160
+ const buttonContentSpan =
161
+ afterElementChild?.tagName === 'BUTTON'
162
+ ? (afterElementChild.firstElementChild as HTMLElement | null)
163
+ : null
164
+
165
+ // This is a necessary workaround for DateInput2 because it uses a Popover, which has a nested Button as an afterElement
166
+ // Check if the child is a Popover's inner span containing a button, then get the button's first child (the content span)
167
+ const popoverContentSpan =
168
+ afterElementChild?.tagName === 'SPAN' &&
169
+ afterElementChild.firstElementChild?.tagName === 'BUTTON'
170
+ ? (afterElementChild.firstElementChild
171
+ .firstElementChild as HTMLElement | null)
172
+ : null
173
+
174
+ const targetSpan = buttonContentSpan ?? popoverContentSpan
175
+
176
+ if (targetSpan) {
177
+ // Set the height to 36px (the height of a medium TextInput) to avoid layout shift when the afterElement content changes
178
+ // this temporary workaround is necessary because otherwise the layout breaks, later on IconButton's default height will be adjusted to the TextInput size
179
+ // so this workaround will not be needed anymore
180
+ targetSpan.style.height = '36px'
181
+ }
182
+ }
183
+
184
+ makeStyleProps = (): TextInputStyleProps => {
185
+ const { afterElementHasWidth } = this.state
186
+ const beforeElement = this.props.renderBeforeInput
187
+ ? callRenderProp(this.props.renderBeforeInput)
188
+ : null
189
+ const success =
190
+ !!this.props.messages &&
191
+ this.props.messages.some((message) => message.type === 'success')
192
+ return {
193
+ interaction: this.interaction,
194
+ invalid: this.invalid,
195
+ success: success,
196
+ afterElementHasWidth: afterElementHasWidth,
197
+ beforeElementExists: !!beforeElement
198
+ }
199
+ }
200
+
201
+ focus() {
202
+ this._input?.focus()
203
+ }
204
+
205
+ get interaction() {
206
+ return getInteraction({ props: this.props })
207
+ }
208
+
209
+ get hasMessages() {
210
+ return !!this.props.messages && this.props.messages.length > 0
211
+ }
212
+
213
+ get invalid() {
214
+ return (
215
+ !!this.props.messages &&
216
+ this.props.messages.findIndex((message) => {
217
+ return message.type === 'error' || message.type === 'newError'
218
+ }) >= 0
219
+ )
220
+ }
221
+
222
+ get focused() {
223
+ return isActiveElement(this._input)
224
+ }
225
+
226
+ get value() {
227
+ return this._input?.value
228
+ }
229
+
230
+ get id() {
231
+ return this.props.id || this._defaultId
232
+ }
233
+
234
+ handleInputRef = (node: HTMLInputElement | null) => {
235
+ this._input = node
236
+
237
+ if (typeof this.props.inputRef === 'function') {
238
+ this.props.inputRef(node)
239
+ }
240
+ }
241
+
242
+ handleChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
243
+ if (typeof this.props.onChange === 'function') {
244
+ this.props.onChange(event, event.target.value)
245
+ }
246
+ }
247
+
248
+ handleBlur: React.FocusEventHandler<HTMLInputElement> = (event) => {
249
+ if (typeof this.props.onBlur === 'function') {
250
+ this.props.onBlur(event)
251
+ }
252
+ }
253
+
254
+ handleFocus: React.FocusEventHandler<HTMLInputElement> = (event) => {
255
+ if (typeof this.props.onFocus === 'function') {
256
+ this.props.onFocus(event)
257
+ }
258
+ }
259
+
260
+ renderInput() {
261
+ const {
262
+ type,
263
+ size,
264
+ htmlSize,
265
+ display,
266
+ textAlign,
267
+ placeholder,
268
+ value,
269
+ defaultValue,
270
+ isRequired,
271
+ onFocus,
272
+ ...rest
273
+ } = this.props
274
+
275
+ const props = passthroughProps(rest)
276
+
277
+ const { interaction } = this
278
+
279
+ let descriptionIds = ''
280
+ if (props['aria-describedby']) {
281
+ descriptionIds = `${props['aria-describedby']}`
282
+ }
283
+
284
+ return (
285
+ <input
286
+ {...props}
287
+ css={this.props.styles?.textInput}
288
+ defaultValue={defaultValue}
289
+ value={value}
290
+ placeholder={interaction === 'enabled' ? placeholder : undefined}
291
+ ref={this.handleInputRef}
292
+ type={type}
293
+ id={this.id}
294
+ required={isRequired}
295
+ aria-invalid={this.invalid ? 'true' : undefined}
296
+ disabled={interaction === 'disabled'}
297
+ readOnly={interaction === 'readonly'}
298
+ aria-describedby={descriptionIds !== '' ? descriptionIds : undefined}
299
+ size={htmlSize}
300
+ onChange={this.handleChange}
301
+ onBlur={this.handleBlur}
302
+ />
303
+ )
304
+ }
305
+
306
+ getElementHasWidth(element: Element | null) {
307
+ if (!element) {
308
+ return undefined
309
+ }
310
+
311
+ const computedStyle = getCSSStyleDeclaration(element)
312
+ if (!computedStyle) {
313
+ return undefined
314
+ }
315
+
316
+ const { width, paddingInlineStart, paddingInlineEnd } = computedStyle
317
+
318
+ if (width === 'auto' || width === '') {
319
+ // This is a workaround for an edge-case, when the TextInput's parent
320
+ // is hidden on load, so the element is not visible either.
321
+ // In this case the computed width is going to be either 'auto' or '',
322
+ // so we assume it has width so that the padding won't be removed.
323
+ return true
324
+ }
325
+
326
+ const elementWidth =
327
+ parseFloat(width) -
328
+ parseFloat(paddingInlineStart) -
329
+ parseFloat(paddingInlineEnd)
330
+
331
+ return elementWidth > 0
332
+ }
333
+
334
+ render() {
335
+ const {
336
+ width,
337
+ display,
338
+ renderLabel,
339
+ renderBeforeInput,
340
+ renderAfterInput,
341
+ messages,
342
+ inputContainerRef,
343
+ isRequired,
344
+ styles
345
+ } = this.props
346
+
347
+ const beforeElement = this.renderInstUIIcon(renderBeforeInput)
348
+ const afterElement = this.renderInstUIIcon(renderAfterInput)
349
+
350
+ const renderBeforeOrAfter =
351
+ !!beforeElement ||
352
+ !!afterElement ||
353
+ renderBeforeInput !== undefined ||
354
+ renderAfterInput !== undefined
355
+
356
+ const label = callRenderProp(renderLabel)
357
+
358
+ return (
359
+ <FormField
360
+ id={this.id}
361
+ label={label}
362
+ messagesId={this._messagesId}
363
+ messages={messages}
364
+ inline={display === 'inline-block'}
365
+ width={width}
366
+ inputContainerRef={inputContainerRef}
367
+ layout={this.props.layout}
368
+ elementRef={this.handleRef}
369
+ margin={this.props.margin}
370
+ isRequired={isRequired}
371
+ disabled={this.interaction === 'disabled'}
372
+ readOnly={this.interaction === 'readonly'}
373
+ data-cid="TextInput"
374
+ >
375
+ <span css={styles?.facade}>
376
+ {renderBeforeOrAfter ? (
377
+ <span css={styles?.layout}>
378
+ {beforeElement}
379
+ {/* The input and content after input should not wrap,
380
+ so they're in their own flex container */}
381
+ <span css={styles?.inputLayout}>
382
+ {this.renderInput()}
383
+ {afterElement && (
384
+ <span
385
+ css={styles?.afterElement}
386
+ ref={(e) => {
387
+ this._afterElement = e
388
+ }}
389
+ >
390
+ {afterElement}
391
+ </span>
392
+ )}
393
+ </span>
394
+ </span>
395
+ ) : (
396
+ /* If no prepended or appended content, don't render Flex layout */
397
+ this.renderInput()
398
+ )}
399
+ </span>
400
+ </FormField>
401
+ )
402
+ }
403
+ }
404
+
405
+ export default TextInput
406
+ export { TextInput }
@@ -0,0 +1,244 @@
1
+ /*
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2015 - present Instructure, Inc.
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in all
14
+ * copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ * SOFTWARE.
23
+ */
24
+
25
+ import { InputHTMLAttributes } from 'react'
26
+
27
+ import type { FormFieldProps, FormMessage } from '@instructure/ui-form-field/latest'
28
+ import type {
29
+ OtherHTMLAttributes,
30
+ TextInputTheme
31
+ } from '@instructure/shared-types'
32
+ import type {
33
+ WithStyleProps,
34
+ ComponentStyle,
35
+ Spacing
36
+ } from '@instructure/emotion'
37
+ import type {
38
+ InteractionType,
39
+ WithDeterministicIdProps
40
+ } from '@instructure/ui-react-utils'
41
+ import { Renderable } from '@instructure/shared-types'
42
+
43
+ type TextInputOwnProps = {
44
+ /**
45
+ * The form field label.
46
+ */
47
+ renderLabel?: Renderable
48
+
49
+ /**
50
+ * Determines the underlying native HTML `<input>` element's `type`.
51
+ *
52
+ * For more see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/url
53
+ */
54
+ type?: 'text' | 'email' | 'url' | 'tel' | 'search' | 'password'
55
+
56
+ /**
57
+ * The id of the text input. One is generated if not supplied.
58
+ */
59
+ id?: string
60
+
61
+ /**
62
+ * the selected value (must be accompanied by an `onChange` prop)
63
+ */
64
+ value?: string // TODO: controllable(PropTypes.string)
65
+
66
+ /**
67
+ * value to set on initial render
68
+ */
69
+ defaultValue?: string
70
+
71
+ /**
72
+ * Specifies if interaction with the input is enabled, disabled, or readonly.
73
+ * When "disabled", the input changes visibly to indicate that it cannot
74
+ * receive user interactions. When "readonly" the input still cannot receive
75
+ * user interactions but it keeps the same styles as if it were enabled.
76
+ */
77
+ interaction?: InteractionType
78
+
79
+ /**
80
+ * Array of objects with shape: `{
81
+ * text: React.ReactNode,
82
+ * type: One of ['error', 'hint', 'success', 'screenreader-only']
83
+ * }`
84
+ */
85
+ messages?: FormMessage[]
86
+
87
+ /**
88
+ * The size of the text input.
89
+ */
90
+ size?: 'small' | 'medium' | 'large'
91
+
92
+ /**
93
+ * The text alignment of the input.
94
+ */
95
+ textAlign?: 'start' | 'center'
96
+
97
+ /**
98
+ * The width of the input.
99
+ */
100
+ width?: string
101
+
102
+ /**
103
+ * The width of the input (integer value 0 or higher), if a width is not explicitly
104
+ * provided via the `width` prop.
105
+ *
106
+ * Only applicable if `display="inline-block"`.
107
+ *
108
+ * For more see https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/size
109
+ */
110
+ htmlSize?: number
111
+
112
+ /**
113
+ * The display of the root element.
114
+ */
115
+ display?: 'inline-block' | 'block'
116
+
117
+ /**
118
+ * Prevents the default behavior of wrapping the input and rendered content
119
+ * when available space is exceeded.
120
+ */
121
+ shouldNotWrap?: boolean
122
+
123
+ /**
124
+ * Html placeholder text to display when the input has no value. This should be hint text, not a label replacement.
125
+ */
126
+ placeholder?: string
127
+
128
+ /**
129
+ * Whether or not the text input is required.
130
+ */
131
+ isRequired?: boolean
132
+
133
+ /**
134
+ * provides a reference to the underlying html root element
135
+ */
136
+ elementRef?: (element: Element | null) => void
137
+
138
+ /**
139
+ * a function that provides a reference to the actual input element
140
+ */
141
+ inputRef?: (inputElement: HTMLInputElement | null) => void
142
+
143
+ /**
144
+ * a function that provides a reference a parent of the input element
145
+ */
146
+ inputContainerRef?: (element: HTMLSpanElement | null) => void
147
+
148
+ /**
149
+ * Content to display before the input text, such as an icon
150
+ */
151
+ renderBeforeInput?: Renderable
152
+
153
+ /**
154
+ * Content to display after the input text, such as an icon
155
+ */
156
+ renderAfterInput?: Renderable
157
+
158
+ /**
159
+ * Callback executed when the input fires a change event.
160
+ * @param {Object} event - the event object
161
+ * @param {string} value - the string value of the input
162
+ */
163
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>, value: string) => void
164
+
165
+ /**
166
+ * Callback fired when input loses focus.
167
+ */
168
+ onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void
169
+
170
+ /**
171
+ * Callback fired when input receives focus.
172
+ */
173
+ onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void
174
+
175
+ /**
176
+ * Margin around the component. Accepts a `Spacing` token. See token values and example usage in [this guide](https://instructure.design/#layout-spacing).
177
+ */
178
+ margin?: Spacing
179
+ }
180
+
181
+ type PropKeys = keyof TextInputOwnProps
182
+
183
+ type AllowedPropKeys = Readonly<Array<PropKeys>>
184
+
185
+ type TextInputProps = TextInputOwnProps &
186
+ WithStyleProps<TextInputTheme, TextInputStyle> &
187
+ OtherHTMLAttributes<
188
+ TextInputOwnProps,
189
+ InputHTMLAttributes<TextInputOwnProps>
190
+ > &
191
+ // The component will handle pass this prop to FormField, but it shouldn't be
192
+ // listed as a prop
193
+ Pick<FormFieldProps, 'layout'> &
194
+ WithDeterministicIdProps
195
+
196
+ type TextInputStyle = ComponentStyle<
197
+ 'textInput' | 'facade' | 'layout' | 'afterElement' | 'inputLayout'
198
+ >
199
+ const allowedProps: AllowedPropKeys = [
200
+ 'renderLabel',
201
+ 'type',
202
+ 'id',
203
+ 'value',
204
+ 'defaultValue',
205
+ 'interaction',
206
+ 'messages',
207
+ 'size',
208
+ 'textAlign',
209
+ 'width',
210
+ 'htmlSize',
211
+ 'display',
212
+ 'shouldNotWrap',
213
+ 'placeholder',
214
+ 'isRequired',
215
+ 'elementRef',
216
+ 'inputRef',
217
+ 'inputContainerRef',
218
+ 'renderBeforeInput',
219
+ 'renderAfterInput',
220
+ 'onChange',
221
+ 'onBlur',
222
+ 'onFocus',
223
+ 'margin'
224
+ ]
225
+
226
+ type TextInputState = {
227
+ afterElementHasWidth?: boolean
228
+ }
229
+
230
+ type TextInputStyleProps = {
231
+ interaction: InteractionType
232
+ success: boolean
233
+ invalid: boolean
234
+ afterElementHasWidth: TextInputState['afterElementHasWidth']
235
+ beforeElementExists: boolean
236
+ }
237
+
238
+ export type {
239
+ TextInputProps,
240
+ TextInputState,
241
+ TextInputStyleProps,
242
+ TextInputStyle
243
+ }
244
+ export { allowedProps }