Skip to content

Commit 2025036

Browse files
authored
Fix TextInput types (#1959)
* Add failing TextInput type test * Create loud-schools-own.md * Update TextInput types * Add another TextInput type test * Update TextInput props type * Update TextInputWithTokens types * Fix incorrect uses of TextInputWithTokens * Update SelectMenu types test * Fix TextInput tests * Update snapshot tests * autocomplete -> autoComplete * Remove theme prop SelectMenuFilter * Fix TextInput in Overlay story * Fix TextInputWithTokens stories
1 parent 1b01485 commit 2025036

16 files changed

+564
-526
lines changed

.changeset/loud-schools-own.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": patch
3+
---
4+
5+
Fix `TextInput` types

src/Autocomplete/AutocompleteInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ const AutocompleteInput = React.forwardRef(
166166
aria-expanded={showMenu}
167167
aria-haspopup="listbox"
168168
aria-owns={`${id}-listbox`}
169-
autocomplete="off"
169+
autoComplete="off"
170170
id={id}
171171
{...props}
172172
/>

src/TextInput.tsx

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
1+
import {ForwardRefComponent as PolymorphicForwardRefComponent} from '@radix-ui/react-polymorphic'
12
import classnames from 'classnames'
23
import React from 'react'
3-
import {ComponentProps, Merge} from './utils/types'
4+
import {Merge} from './utils/types'
5+
import TextInputWrapper, {StyledWrapperProps} from './_TextInputWrapper'
46
import UnstyledTextInput from './_UnstyledTextInput'
5-
import TextInputWrapper from './_TextInputWrapper'
67

78
type NonPassthroughProps = {
8-
className?: string
99
/** @deprecated Use `leadingVisual` or `trailingVisual` prop instead */
1010
icon?: React.ComponentType<{className?: string}>
1111
leadingVisual?: string | React.ComponentType<{className?: string}>
1212
trailingVisual?: string | React.ComponentType<{className?: string}>
1313
} & Pick<
14-
ComponentProps<typeof TextInputWrapper>,
15-
'block' | 'contrast' | 'disabled' | 'monospace' | 'sx' | 'width' | 'maxWidth' | 'minWidth' | 'variant' | 'size'
14+
StyledWrapperProps,
15+
| 'block'
16+
| 'contrast'
17+
| 'disabled'
18+
| 'monospace'
19+
| 'sx'
20+
| 'width'
21+
| 'maxWidth'
22+
| 'minWidth'
23+
| 'variant'
24+
| 'size'
25+
| 'validationStatus'
1626
>
1727

18-
// Note: using ComponentProps instead of ComponentPropsWithoutRef here would cause a type issue where `css` is a required prop.
19-
type TextInputInternalProps = Merge<React.ComponentPropsWithoutRef<typeof UnstyledTextInput>, NonPassthroughProps>
28+
export type TextInputProps = Merge<React.ComponentPropsWithoutRef<'input'>, NonPassthroughProps>
2029

2130
// using forwardRef is important so that other components (ex. SelectMenu) can autofocus the input
22-
const TextInput = React.forwardRef<HTMLInputElement, TextInputInternalProps>(
31+
const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
2332
(
2433
{
2534
icon: IconComponent,
@@ -78,13 +87,12 @@ const TextInput = React.forwardRef<HTMLInputElement, TextInputInternalProps>(
7887
</TextInputWrapper>
7988
)
8089
}
81-
)
90+
) as PolymorphicForwardRefComponent<'input', TextInputProps>
8291

8392
TextInput.defaultProps = {
8493
type: 'text'
8594
}
8695

8796
TextInput.displayName = 'TextInput'
8897

89-
export type TextInputProps = ComponentProps<typeof TextInput>
9098
export default TextInput

src/TextInputWithTokens.tsx

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
1-
import React, {FocusEventHandler, KeyboardEventHandler, MouseEventHandler, RefObject, useRef, useState} from 'react'
2-
import {omit} from '@styled-system/props'
31
import {FocusKeys} from '@primer/behaviors'
2+
import {isFocusable} from '@primer/behaviors/utils'
3+
import {omit} from '@styled-system/props'
4+
import React, {FocusEventHandler, KeyboardEventHandler, MouseEventHandler, RefObject, useRef, useState} from 'react'
5+
import Box from './Box'
6+
import {useProvidedRefOrCreate} from './hooks'
47
import {useCombinedRefs} from './hooks/useCombinedRefs'
58
import {useFocusZone} from './hooks/useFocusZone'
6-
import {ComponentProps} from './utils/types'
9+
import Text from './Text'
10+
import {TextInputProps} from './TextInput'
711
import Token from './Token/Token'
812
import {TokenSizeKeys} from './Token/TokenBase'
9-
import {TextInputProps} from './TextInput'
10-
import {useProvidedRefOrCreate} from './hooks'
11-
import UnstyledTextInput from './_UnstyledTextInput'
1213
import TextInputWrapper, {textInputHorizPadding, TextInputSizes} from './_TextInputWrapper'
13-
import Box from './Box'
14-
import Text from './Text'
15-
import {isFocusable} from '@primer/behaviors/utils'
14+
import UnstyledTextInput from './_UnstyledTextInput'
1615

1716
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1817
type AnyReactComponent = React.ComponentType<any>
1918

2019
// NOTE: if these props or their JSDoc comments are updated, be sure to also update
2120
// the prop table in docs/content/TextInputTokens.mdx
22-
type TextInputWithTokensInternalProps<TokenComponentType extends AnyReactComponent> = {
21+
export type TextInputWithTokensProps<TokenComponentType extends AnyReactComponent = typeof Token> = {
2322
/**
2423
* The array of tokens to render
2524
*/
@@ -53,7 +52,7 @@ type TextInputWithTokensInternalProps<TokenComponentType extends AnyReactCompone
5352
* The number of tokens to display before truncating
5453
*/
5554
visibleTokenCount?: number
56-
} & TextInputProps
55+
} & Omit<TextInputProps, 'size'>
5756

5857
const overflowCountFontSizeMap: Record<TokenSizeKeys, number> = {
5958
small: 0,
@@ -72,7 +71,6 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
7271
className,
7372
block,
7473
disabled,
75-
theme,
7674
sx: sxProp,
7775
tokens,
7876
onTokenRemove,
@@ -88,10 +86,7 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
8886
variant: variantProp, // deprecated. use `size` instead
8987
visibleTokenCount,
9088
...rest
91-
}: TextInputWithTokensInternalProps<TokenComponentType> & {
92-
selectedTokenIndex: number | undefined
93-
setSelectedTokenIndex: React.Dispatch<React.SetStateAction<number | undefined>>
94-
},
89+
}: TextInputWithTokensProps<TokenComponentType>,
9590
externalRef: React.ForwardedRef<HTMLInputElement>
9691
) {
9792
const {onBlur, onFocus, onKeyDown, ...inputPropsRest} = omit(rest)
@@ -252,7 +247,6 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
252247
disabled={disabled}
253248
hasLeadingVisual={Boolean(LeadingVisual)}
254249
hasTrailingVisual={Boolean(TrailingVisual)}
255-
theme={theme}
256250
width={widthProp}
257251
minWidth={minWidthProp}
258252
maxWidth={maxWidthProp}
@@ -372,5 +366,4 @@ TextInputWithTokens.defaultProps = {
372366

373367
TextInputWithTokens.displayName = 'TextInputWithTokens'
374368

375-
export type TextInputWithTokensProps = ComponentProps<typeof TextInputWithTokens>
376369
export default TextInputWithTokens

src/__tests__/FormControl.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ describe('FormControl', () => {
140140
{text: 'one', id: 1},
141141
{text: 'two', id: 2}
142142
]}
143-
onRemove={onRemoveMock}
143+
onTokenRemove={onRemoveMock}
144144
/>
145145
</FormControl>
146146
</SSRProvider>

src/__tests__/SelectMenu.types.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function shouldNotAcceptSystemProps() {
1414
<SelectMenu.List backgroundColor="lightgreen" />
1515
{/* @ts-expect-error system props should not be accepted */}
1616
<SelectMenu.Divider backgroundColor="lightgrey" />
17-
{/* This will not error for now, but once TextInputProps is fixed, a ts-expect-error can be added */}
17+
{/* @ts-expect-error system props should not be accepted */}
1818
<SelectMenu.Filter backgroundColor="lightpink" />
1919
{/* @ts-expect-error system props should not be accepted */}
2020
<SelectMenu.Footer backgroundColor="lightsalmon" />

src/__tests__/TextInput.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,11 @@ describe('TextInput', () => {
3838
})
3939

4040
it('renders warning', () => {
41-
expect(render(<TextInput name="zipcode" status="warning" />)).toMatchSnapshot()
41+
expect(render(<TextInput name="zipcode" validationStatus="warning" />)).toMatchSnapshot()
4242
})
4343

4444
it('renders error', () => {
45-
expect(render(<TextInput name="zipcode" status="error" />)).toMatchSnapshot()
45+
expect(render(<TextInput name="zipcode" validationStatus="error" />)).toMatchSnapshot()
4646
})
4747

4848
it('renders contrast', () => {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import React from 'react'
2+
import {TextInput} from '..'
3+
4+
export function shouldNotAcceptInvalidDomProps() {
5+
// @ts-expect-error invalid DOM props should not be accepted
6+
return <TextInput onKeyDown={true} />
7+
}
8+
9+
export function shouldNotAcceptInvalidSize() {
10+
// @ts-expect-error invalid size value should not be accepted
11+
return <TextInput size="big" />
12+
}

0 commit comments

Comments
 (0)