Skip to content

Commit d06b923

Browse files
pawelgrimmclaude
andcommitted
refactor: narrow FieldChromeContainer's public surface
Replace `Omit<ComponentProps<typeof Box>, 'className'>` on FCC with explicit `onClick` + `ObfuscatedClassName`. Move `display:flex`, `align-items:center`, `overflow:hidden` into FCC's CSS. CP keeps its loose public type (Box props) but only forwards `onClick` to FCC; other Box props (maxWidth, etc.) are silently dropped pending a follow-up CP API tightening. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent b6842d3 commit d06b923

3 files changed

Lines changed: 29 additions & 19 deletions

File tree

src/control-presentation/control-presentation.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export type ControlPresentationProps = {
3939
*/
4040
children: React.ReactNode
4141
} & ObfuscatedClassName &
42-
Omit<ComponentProps<typeof FieldChromeContainer>, 'borderRadius'>
42+
Omit<ComponentProps<typeof Box>, 'className'>
4343

4444
/**
4545
* The visual chrome of an inline, single-row, text-field-style input: a
@@ -55,15 +55,15 @@ export const ControlPresentation = forwardRef<HTMLDivElement, ControlPresentatio
5555
{ startSlot, endSlot, exceptionallySetClassName, children, ...rest },
5656
ref,
5757
) {
58+
// Only `onClick` is meaningful to forward to FCC; other Box props are
59+
// silently dropped pending CP API tightening in a follow-up.
60+
const { onClick } = rest as { onClick?: React.MouseEventHandler<HTMLDivElement> }
5861
return (
5962
<FieldChromeContainer
6063
ref={ref}
61-
{...rest}
6264
borderRadius="small"
65+
onClick={onClick}
6366
exceptionallySetClassName={classNames(styles.container, exceptionallySetClassName)}
64-
display="flex"
65-
alignItems="center"
66-
overflow="hidden"
6767
>
6868
{startSlot ? (
6969
<Slot className={[styles.slot, styles.startSlot]}>{startSlot}</Slot>

src/control-presentation/field-chrome-container.module.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
.container {
2+
display: flex;
3+
align-items: center;
4+
overflow: hidden;
5+
26
/* border */
37
border: 1px solid var(--reactist-inputs-idle);
48

src/control-presentation/field-chrome-container.tsx

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react'
2-
import { type ComponentProps, forwardRef } from 'react'
2+
import { forwardRef } from 'react'
33

44
import { Box } from '../box'
55

@@ -9,12 +9,15 @@ import type { ObfuscatedClassName } from '../utils/common-types'
99

1010
export type FieldChromeContainerProps = {
1111
/**
12-
* The control element to wrap. The wrapper chrome (border, hover/focus,
13-
* disabled/read-only background tint, error border) derives from
14-
* attributes on this descendant.
12+
* The control element (or a layout containing one) to wrap. The wrapper
13+
* chrome (border, hover/focus, disabled/read-only background tint, error
14+
* border) derives from attributes on the focusable descendant. Clicking
15+
* the wrapper focuses the first focusable input/select/textarea
16+
* descendant and dispatches a `.click()` (or `.showPicker()` for native
17+
* `<select>`).
1518
*
16-
* Clicking the wrapper focuses the first focusable descendant and
17-
* dispatches a `.click()` (or `.showPicker()` for native `<select>`).
19+
* Contract: assumes a single focusable control descendant. Behavior is
20+
* undefined if multiple focusable controls are inside.
1821
*/
1922
children: React.ReactNode
2023

@@ -24,8 +27,16 @@ export type FieldChromeContainerProps = {
2427
* - `large` — used by `BorderedTextField`'s outlined chrome.
2528
*/
2629
borderRadius?: 'small' | 'large'
27-
} & ObfuscatedClassName &
28-
Omit<ComponentProps<typeof Box>, 'className'>
30+
31+
/**
32+
* Optional click handler. Fires before the click-to-focus dispatch.
33+
* Intended for composition with library wrappers (e.g. Ariakit's
34+
* render-prop pattern) that forward an upstream onClick to the chrome
35+
* element. Component-level click handling normally lives on the inner
36+
* control.
37+
*/
38+
onClick?: React.MouseEventHandler<HTMLDivElement>
39+
} & ObfuscatedClassName
2940

3041
/**
3142
* Private primitive shared by `ControlPresentation` and `BorderedTextField`.
@@ -40,16 +51,12 @@ export type FieldChromeContainerProps = {
4051
*/
4152
export const FieldChromeContainer = forwardRef<HTMLDivElement, FieldChromeContainerProps>(
4253
function FieldChromeContainer(
43-
{ borderRadius = 'small', exceptionallySetClassName, children, ...rest },
54+
{ borderRadius = 'small', exceptionallySetClassName, onClick, children },
4455
ref,
4556
) {
4657
const controlWrapperRef = React.useRef<HTMLDivElement>(null)
4758
const isDispatchedReentryRef = React.useRef(false)
4859

49-
const { onClick, ...restWithoutClick } = rest as typeof rest & {
50-
onClick?: React.MouseEventHandler<HTMLDivElement>
51-
}
52-
5360
function handleWrapperClick(event: React.MouseEvent<HTMLDivElement>) {
5461
if (isDispatchedReentryRef.current) {
5562
isDispatchedReentryRef.current = false
@@ -86,7 +93,6 @@ export const FieldChromeContainer = forwardRef<HTMLDivElement, FieldChromeContai
8693
return (
8794
<Box
8895
ref={ref}
89-
{...restWithoutClick}
9096
className={[
9197
styles.container,
9298
borderRadius === 'large' ? styles.borderRadiusLarge : styles.borderRadiusSmall,

0 commit comments

Comments
 (0)