Skip to content
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
48181d7
chore(TextInput): remove sx
francinelucca Sep 30, 2025
75c5b50
Remove sx prop from TextInput component
francinelucca Sep 30, 2025
bc30458
Merge branch 'main' into chore/remove-sx-from-text-input
francinelucca Oct 1, 2025
3d10343
type fix
francinelucca Oct 1, 2025
607edd3
remove sx from TextInputWIthTokens
francinelucca Oct 1, 2025
a573acd
Merge branch 'main' into chore/remove-sx-from-text-input
francinelucca Oct 1, 2025
1b33c3d
remove unnecessary stories
francinelucca Oct 1, 2025
4b20d80
Merge branch 'chore/remove-sx-from-text-input' of github.com:primer/r…
francinelucca Oct 1, 2025
05785b5
remove sx prop from Textarea
francinelucca Oct 1, 2025
7f53ab9
fix exports
francinelucca Oct 1, 2025
07be57e
fix import
francinelucca Oct 1, 2025
beb0fb1
Merge branch 'main' of github.com:primer/react into chore/remove-sx-f…
francinelucca Oct 1, 2025
a251708
remove sx stories snapshots
francinelucca Oct 1, 2025
b55c591
use component name instead of component reference to associate FormCo…
francinelucca Oct 1, 2025
b03bc50
Revert "use component name instead of component reference to associat…
francinelucca Oct 1, 2025
c3f6d97
Merge branch 'main' into chore/remove-sx-from-text-input
francinelucca Oct 1, 2025
6cc3368
Merge branch 'main' into chore/remove-sx-from-text-input
francinelucca Oct 1, 2025
d98b6c6
Merge branch 'main' of github.com:primer/react into chore/remove-sx-f…
francinelucca Oct 1, 2025
861ffc8
memoize style
francinelucca Oct 1, 2025
55488c7
remove sx from TextInput.Action
francinelucca Oct 1, 2025
d52d8b7
update snapshot
francinelucca Oct 1, 2025
f09d16f
chore: remove sx prop import
francinelucca Oct 6, 2025
f81329e
TextInputWrapper: remove responsive value
francinelucca Oct 6, 2025
aab2106
Merge branch 'main' into chore/remove-sx-from-text-input
francinelucca Oct 6, 2025
f524327
styled-react select component
francinelucca Oct 6, 2025
8f0bdff
Merge branch 'chore/remove-sx-from-text-input' of github.com:primer/r…
francinelucca Oct 6, 2025
b249971
Merge branch 'main' of github.com:primer/react into chore/remove-sx-f…
francinelucca Oct 6, 2025
9418ca0
Merge branch 'main' into chore/remove-sx-from-text-input
francinelucca Oct 7, 2025
b3808a3
Merge branch 'main' into chore/remove-sx-from-text-input
francinelucca Oct 7, 2025
f5a18ab
use styled() wrappr in TextInput
francinelucca Oct 7, 2025
94bad6f
Merge branch 'main' into chore/remove-sx-from-text-input
francinelucca Oct 8, 2025
5dbabf9
use styled wrapper instead of Box in TextInputAction
francinelucca Oct 8, 2025
44d0471
Merge branch 'chore/remove-sx-from-text-input' of github.com:primer/r…
francinelucca Oct 8, 2025
b1732d1
Merge branch 'main' of github.com:primer/react into chore/remove-sx-f…
francinelucca Oct 11, 2025
3a0cb7a
Merge branch 'main' into chore/remove-sx-from-text-input
francinelucca Oct 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/spicy-items-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@primer/react": patch
"@primer/styled-react": patch
---

chore(TextInput): remove sx
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
36 changes: 0 additions & 36 deletions e2e/components/TextInput.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,24 +219,6 @@ test.describe('TextInput', () => {
}
})

test.describe('Dev: With Sx', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-textinput-dev--with-sx',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`TextInput.Dev.WithSx.${theme}.png`)
})
})
}
})

test.describe('Dev: With CSS', () => {
for (const theme of themes) {
test.describe(theme, () => {
Expand All @@ -254,22 +236,4 @@ test.describe('TextInput', () => {
})
}
})

test.describe('Dev: With Sx and CSS', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-textinput-dev--with-sx-and-css',
globals: {
colorScheme: theme,
},
})

// Default state
expect(await page.screenshot()).toMatchSnapshot(`TextInput.Dev.WithSxAndCSS.${theme}.png`)
})
})
}
})
})
18 changes: 0 additions & 18 deletions packages/react/src/TextInput/TextInput.dev.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,3 @@ export const WithCSS = () => (
</FormControl>
</form>
)

export const WithSx = () => (
<form>
<FormControl>
<FormControl.Label>Default label</FormControl.Label>
<TextInput sx={{borderColor: 'red'}} />
</FormControl>
</form>
)

export const WithSxAndCSS = () => (
<form>
<FormControl>
<FormControl.Label>Default label</FormControl.Label>
<TextInput sx={{borderColor: 'red'}} className="testCustomClassnameBorderColor" />
</FormControl>
</form>
)
3 changes: 0 additions & 3 deletions packages/react/src/TextInput/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export type TextInputNonPassthroughProps = {
| 'contrast'
| 'disabled'
| 'monospace'
| 'sx'
| 'width'
| 'maxWidth'
| 'minWidth'
Expand Down Expand Up @@ -75,7 +74,6 @@ const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
loaderText = 'Loading',
monospace,
validationStatus,
sx: sxProp,
size: sizeProp,
onFocus,
onBlur,
Expand Down Expand Up @@ -137,7 +135,6 @@ const TextInput = React.forwardRef<HTMLInputElement, TextInputProps>(
contrast={contrast}
disabled={disabled}
monospace={monospace}
sx={sxProp}
size={sizeProp}
width={widthProp}
minWidth={minWidthProp}
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/TextInput/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export {default} from './TextInput'
export type {TextInputProps, TextInputNonPassthroughProps} from './TextInput'
export type {TextInputActionProps} from '../internal/components/TextInputInnerAction'
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
className,
block,
disabled,
sx: sxProp,
tokens,
onTokenRemove,
tokenComponent: TokenComponent = Token,
Expand Down Expand Up @@ -272,7 +271,6 @@ function TextInputWithTokensInnerComponent<TokenComponentType extends AnyReactCo
data-token-wrapping={Boolean(preventTokenWrapping || maxHeight) || undefined}
className={clsx(className, styles.TextInputWrapper)}
style={maxHeight ? {maxHeight, ...style} : style}
sx={sxProp}
>
{IconComponent && !LeadingVisual && <IconComponent className="TextInput-icon" />}
<TextInputInnerVisualSlot
Expand Down
2 changes: 0 additions & 2 deletions packages/react/src/Textarea/Textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
{
value,
disabled,
sx: sxProp,
required,
validationStatus,
rows = DEFAULT_TEXTAREA_ROWS,
Expand All @@ -76,7 +75,6 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
): ReactElement => {
return (
<TextInputBaseWrapper
sx={sxProp}
validationStatus={validationStatus}
disabled={disabled}
block={block}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ exports[`@primer/react > should not update exports without a semver change 1`] =
"Textarea",
"type TextareaProps",
"TextInput",
"type TextInputActionProps",
"type TextInputProps",
"TextInputWithTokens",
"type TextInputWithTokensProps",
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export type {SubNavProps, SubNavLinkProps, SubNavLinksProps} from './SubNav'
export {default as ToggleSwitch} from './ToggleSwitch'
export type {ToggleSwitchProps} from './ToggleSwitch'
export {default as TextInput} from './TextInput'
export type {TextInputProps} from './TextInput'
export type {TextInputProps, TextInputActionProps} from './TextInput'
export {default as TextInputWithTokens} from './TextInputWithTokens'
export type {TextInputWithTokensProps} from './TextInputWithTokens'
export {default as Text} from './Text'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {clsx} from 'clsx'

import styles from './TextInputInnerAction.module.css'

type TextInputActionProps = Omit<
export type TextInputActionProps = Omit<
React.ButtonHTMLAttributes<HTMLButtonElement>,
'aria-label' | 'size' | 'tooltipDirection'
> & {
Expand Down
15 changes: 7 additions & 8 deletions packages/react/src/internal/components/TextInputWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import type {FormValidationStatus} from '../../utils/types/FormValidationStatus'
import {clsx} from 'clsx'

import styles from './TextInputWrapper.module.css'
import {BoxWithFallback} from './BoxWithFallback'

export type TextInputSizes = 'small' | 'medium' | 'large'

Expand Down Expand Up @@ -59,8 +58,7 @@ export const TextInputBaseWrapper = React.forwardRef<HTMLElement, StyledTextInpu
forwardRef,
) {
return (
<BoxWithFallback
as="span"
<span
ref={forwardRef}
className={clsx(className, styles.TextInputBaseWrapper)}
data-block={block || undefined}
Expand All @@ -72,11 +70,12 @@ export const TextInputBaseWrapper = React.forwardRef<HTMLElement, StyledTextInpu
data-trailing-action={hasTrailingAction || undefined}
data-validation={validationStatus || undefined}
data-variant={variant || undefined}
style={
typeof width === 'string' || typeof minWidth === 'string' || typeof maxWidth === 'string'
? {width, maxWidth, minWidth, ...style}
: style
}
style={{
...(typeof width === 'string' || typeof width === 'number' ? {width} : {}),
...(typeof minWidth === 'string' || typeof minWidth === 'number' ? {minWidth} : {}),
...(typeof maxWidth === 'string' || typeof maxWidth === 'number' ? {maxWidth} : {}),
...style,
}}
Comment thread
francinelucca marked this conversation as resolved.
Outdated
{...restProps}
/>
)
Expand Down
13 changes: 11 additions & 2 deletions packages/styled-react/src/components/Autocomplete.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
Autocomplete as PrimerAutocomplete,
type AutocompleteOverlayProps as PrimerAutocompleteOverlayProps,
type AutocompleteInputProps as PrimerAutocompleteInputProps,
} from '@primer/react'
import {sx, type SxProp} from '../sx'
import styled from 'styled-components'
Expand All @@ -16,17 +17,25 @@ const AutocompleteOverlay: React.ComponentType<AutocompleteOverlayProps> = style
${sx}
`

export type AutocompleteInputProps = PrimerAutocompleteInputProps & SxProp

const AutocompleteInput: React.ComponentType<AutocompleteInputProps> = styled(PrimerAutocomplete.Input).withConfig({
shouldForwardProp: prop => (prop as keyof AutocompleteInputProps) !== 'sx',
})<AutocompleteInputProps>`
${sx}
`

interface AutocompleteExport {
(props: ComponentProps<typeof PrimerAutocomplete>): React.ReactNode
Context: typeof PrimerAutocomplete.Context
Input: typeof PrimerAutocomplete.Input
Input: typeof AutocompleteInput
Menu: typeof PrimerAutocomplete.Menu
Overlay: typeof AutocompleteOverlay
}

const Autocomplete: AutocompleteExport = Object.assign(PrimerAutocomplete, {
Context: PrimerAutocomplete.Context,
Input: PrimerAutocomplete.Input,
Input: AutocompleteInput,
Menu: PrimerAutocomplete.Menu,
Overlay: AutocompleteOverlay,
})
Expand Down
35 changes: 35 additions & 0 deletions packages/styled-react/src/components/TextInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {
TextInput as PrimerTextInput,
type TextInputProps as PrimerTextInputProps,
type TextInputActionProps as PrimerTextInputActionProps,
} from '@primer/react'
import {forwardRef} from 'react'
import {Box} from './Box'
import type {SxProp} from '../sx'
import type {ForwardRefExoticComponent, RefAttributes} from 'react'

export type TextInputProps = PrimerTextInputProps & SxProp & {as?: React.ElementType}
export type TextInputActionProps = PrimerTextInputActionProps & SxProp

const StyledTextInput = forwardRef<HTMLInputElement, TextInputProps>((props, ref) => {
return <Box as={PrimerTextInput} ref={ref} {...props} />
})

const TextInputImpl = forwardRef<HTMLInputElement, TextInputProps>(({as, ...props}, ref) => {
return <StyledTextInput ref={ref} {...props} {...(as ? {forwardedAs: as} : {})} />
Comment thread
francinelucca marked this conversation as resolved.
})

const TextInputAction = forwardRef<HTMLButtonElement, TextInputActionProps>((props, ref) => {
return <Box as={PrimerTextInput.Action} ref={ref} {...props} />
})

type TextInputComposite = ForwardRefExoticComponent<TextInputProps & RefAttributes<HTMLInputElement>> & {
Action: typeof TextInputAction
}

export const TextInput: TextInputComposite = Object.assign(TextInputImpl, {
Action: TextInputAction,
})
Comment on lines +35 to +37

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note the approach we are using with component-wrapping is not compatible with some child type comparisons we do in some parts of the codebase (for example, inputComponent identification in FormControl) and these components will not work in this context. In this PR, this affects TextInput and Textarea.
My approach is to migrate the affected components (https://github.com/github/github-ui/pull/3652, https://github.com/github/github-ui/pull/3649) away from sx and therefore remove the styled-react import. All other usages of the wrapper component work BAU.
I will make a not of this in v38 release notes as well


TextInputAction.displayName = 'TextInputAction'
TextInputImpl.displayName = 'TextInput'
19 changes: 19 additions & 0 deletions packages/styled-react/src/components/Textarea.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {Textarea as PrimerTextarea, type TextareaProps as PrimerTextareaProps} from '@primer/react'
import styled from 'styled-components'
import {sx, type SxProp} from '../sx'
import type {ForwardRefComponent} from '../polymorphic'
import {forwardRef} from 'react'

type TextareaProps = PrimerTextareaProps & SxProp & {as?: React.ElementType}

const StyledTextarea = styled(PrimerTextarea).withConfig<TextareaProps>({
shouldForwardProp: prop => prop !== 'sx',
})`
${sx}
` as ForwardRefComponent<'textarea', TextareaProps>

const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(({as, ...props}, ref) => {
return <StyledTextarea {...props} {...(as ? {forwardedAs: as} : {})} ref={ref} />
}) as ForwardRefComponent<'textarea', TextareaProps>

export {Textarea, type TextareaProps}
5 changes: 2 additions & 3 deletions packages/styled-react/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ export {Details} from '@primer/react'
export {IconButton} from '@primer/react'
export {ProgressBar} from '@primer/react'
export {Select} from '@primer/react'
export {Textarea} from '@primer/react'
export {TextInput} from '@primer/react'
export {type TextInputProps} from '@primer/react'

// theming depends on styled-components
export {ThemeProvider} from '@primer/react'
Expand Down Expand Up @@ -54,6 +51,8 @@ export {Spinner, type SpinnerProps} from './components/Spinner'
export {StateLabel, type StateLabelProps} from './components/StateLabel'
export {SubNav, type SubNavProps, type SubNavLinkProps} from './components/SubNav'
export {Text, type TextProps} from './components/Text'
export {Textarea, type TextareaProps} from './components/Textarea'
export {TextInput, type TextInputProps, type TextInputActionProps} from './components/TextInput'
export {
Timeline,
type TimelineProps,
Expand Down
Loading