From 7d2043cc88b8f5a9e3dc5d0f8b7ea022c000ad50 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 28 May 2026 12:57:57 -0400 Subject: [PATCH 01/12] feat: Select theming --- .../src/alpha/select/DefaultSelectControl.tsx | 95 ++++++++++++--- packages/mobile/src/alpha/select/Select.tsx | 21 +++- .../alpha/select/__tests__/Select.test.tsx | 64 ++++++++++ packages/mobile/src/alpha/select/types.ts | 13 ++ .../src/alpha/select/DefaultSelectControl.tsx | 75 ++++++++++-- packages/web/src/alpha/select/Select.tsx | 22 +++- .../alpha/select/__tests__/Select.test.tsx | 114 ++++++++++++++++++ packages/web/src/alpha/select/types.ts | 20 ++- 8 files changed, 388 insertions(+), 36 deletions(-) diff --git a/packages/mobile/src/alpha/select/DefaultSelectControl.tsx b/packages/mobile/src/alpha/select/DefaultSelectControl.tsx index 2e72d35ca6..fef1bf1e7a 100644 --- a/packages/mobile/src/alpha/select/DefaultSelectControl.tsx +++ b/packages/mobile/src/alpha/select/DefaultSelectControl.tsx @@ -1,4 +1,4 @@ -import { forwardRef, memo, useMemo } from 'react'; +import { forwardRef, memo, useCallback, useMemo } from 'react'; import { Pressable, type StyleProp, TouchableOpacity, type ViewStyle } from 'react-native'; import type { ThemeVars } from '@coinbase/cds-common/core/theme'; import { useInputVariant } from '@coinbase/cds-common/hooks/useInputVariant'; @@ -46,6 +46,7 @@ export const DefaultSelectControlComponent = memo( open, placeholder, disabled, + readOnly, setOpen, variant, helperText, @@ -57,10 +58,16 @@ export const DefaultSelectControlComponent = memo( compact, align = 'start', font = 'body', + labelColor = 'fg', + labelFont = 'label1', bordered = true, - borderWidth = bordered ? 100 : 0, - focusedBorderWidth = bordered ? undefined : 200, + borderWidth: borderWidthProp, + focusedBorderWidth: focusedBorderWidthProp, + height, + inputBackground, + borderRadius, maxSelectedOptionsToShow = 3, + accessibilityHint, accessibilityLabel, hiddenSelectedOptionsLabel = 'more', removeSelectedOptionAccessibilityLabel = 'Remove', @@ -76,6 +83,25 @@ export const DefaultSelectControlComponent = memo( ? SelectOptionValue | SelectOptionValue[] | null : SelectOptionValue | null; + const borderWidth = borderWidthProp ?? (bordered ? 100 : 0); + const focusedBorderWidth = focusedBorderWidthProp ?? (bordered ? undefined : 200); + const isInteractionBlocked = disabled || readOnly; + + const readOnlyInputBackground = useMemo(() => { + if (!disabled && readOnly) { + return 'bgSecondary'; + } + return undefined; + }, [disabled, readOnly]); + const resolvedInputBackground = readOnlyInputBackground ?? inputBackground; + + const handleToggleOpen = useCallback(() => { + if (isInteractionBlocked) { + return; + } + setOpen((currentOpen) => !currentOpen); + }, [isInteractionBlocked, setOpen]); + const theme = useTheme(); // When compact, labelVariant is ignored const labelVariant = compact ? undefined : labelVariantProp; @@ -193,28 +219,52 @@ export const DefaultSelectControlComponent = memo( if (typeof label === 'string') { return ( - + {label} ); } return label; - }, [shouldShowInsideLabel, shouldShowCompactLabel, label, styles?.controlLabelNode]); + }, [ + shouldShowInsideLabel, + shouldShowCompactLabel, + label, + labelColor, + labelFont, + styles?.controlLabelNode, + ]); const inlineLabelNode = useMemo(() => { if (!shouldShowInsideLabel && !shouldShowCompactLabel) return null; if (typeof label === 'string') { return ( - + {label} ); } return label; - }, [shouldShowInsideLabel, shouldShowCompactLabel, label, styles?.controlLabelNode]); + }, [ + shouldShowInsideLabel, + shouldShowCompactLabel, + label, + labelColor, + labelFont, + styles?.controlLabelNode, + ]); const valueAlignment = useMemo( () => (align === 'end' ? 'flex-end' : align === 'center' ? 'center' : 'flex-start'), @@ -255,10 +305,14 @@ export const DefaultSelectControlComponent = memo( disabled={disabled || option.disabled} invertColorScheme={false} maxWidth={200} - onPress={(event) => { - event?.stopPropagation(); - onChange?.(option.value as ValueType); - }} + onPress={ + isInteractionBlocked + ? undefined + : (event) => { + event?.stopPropagation(); + onChange?.(option.value as ValueType); + } + } > {option.label ?? option.description ?? option.value ?? ''} @@ -294,6 +348,7 @@ export const DefaultSelectControlComponent = memo( removeSelectedOptionAccessibilityLabel, disabled, onChange, + isInteractionBlocked, ]); // onBlur/onFocus on ViewProps allow null returns but TouchableOpacity's onBlur/onFocus props do not. @@ -302,14 +357,14 @@ export const DefaultSelectControlComponent = memo( () => ( setOpen((s) => !s)} + onPress={handleToggleOpen} style={[{ flexGrow: 1 }, styles?.controlInputNode]} - {...props} > setOpen((s) => !s)} + onPress={handleToggleOpen} > ), - [styles?.controlEndNode, disabled, customEndNode, open, variant, setOpen], + [styles?.controlEndNode, disabled, customEndNode, open, variant, handleToggleOpen], ); const inputStackStyles: StyleProp = useMemo( @@ -426,18 +481,22 @@ export const DefaultSelectControlComponent = memo( return ( { }); }); + describe('readOnly', () => { + it('does not open the tray when readOnly', () => { + render( + + + , + ); + + expect(screen.getByRole('button').props.accessibilityState?.disabled).not.toBe(true); + }); + }); + + describe('ComponentConfig', () => { + it('applies read-only background from component config resolver', () => { + render( + + ({ + inputBackground: readOnly ? 'bgSecondary' : 'bgAlternate', + }), + }} + > + + + , + ); + + expect(screen.getByText('Test Select')).toBeTruthy(); + }); + }); + describe('Ref Forwarding', () => { it('forwards ref correctly', () => { const ref = React.createRef(); diff --git a/packages/mobile/src/alpha/select/types.ts b/packages/mobile/src/alpha/select/types.ts index eb2dac878d..75e240ef14 100644 --- a/packages/mobile/src/alpha/select/types.ts +++ b/packages/mobile/src/alpha/select/types.ts @@ -1,6 +1,7 @@ import type React from 'react'; import type { AccessibilityRole, StyleProp, TouchableOpacity, View, ViewStyle } from 'react-native'; import type { SharedAccessibilityProps } from '@coinbase/cds-common/types'; +import type { SharedInputProps } from '@coinbase/cds-common/types/InputBaseProps'; import type { CellBaseProps } from '../../cells/Cell'; import type { CellAccessoryProps } from '../../cells/CellAccessory'; @@ -245,8 +246,12 @@ export type SelectControlProps< | 'endNode' | 'borderWidth' | 'focusedBorderWidth' + | 'height' + | 'inputBackground' + | 'borderRadius' > & Pick & + Pick & SelectState & { /** * Alignment of the value node. @@ -419,6 +424,14 @@ export type SelectBaseProps< | 'align' | 'font' | 'bordered' + | 'borderWidth' + | 'focusedBorderWidth' + | 'height' + | 'inputBackground' + | 'labelColor' + | 'labelFont' + | 'readOnly' + | 'borderRadius' > & Pick, 'accessory' | 'media' | 'end'> & Pick< diff --git a/packages/web/src/alpha/select/DefaultSelectControl.tsx b/packages/web/src/alpha/select/DefaultSelectControl.tsx index 0e2e5ec3de..ed32e1a281 100644 --- a/packages/web/src/alpha/select/DefaultSelectControl.tsx +++ b/packages/web/src/alpha/select/DefaultSelectControl.tsx @@ -68,6 +68,7 @@ const DefaultSelectControlComponent = memo( open, placeholder, disabled, + readOnly, setOpen, variant, helperText, @@ -80,9 +81,14 @@ const DefaultSelectControlComponent = memo( blendStyles, align = 'start', font = 'body', + labelColor = 'fg', + labelFont = 'label1', bordered = true, - borderWidth = bordered ? 100 : 0, - focusedBorderWidth = bordered ? undefined : 200, + borderWidth: borderWidthProp, + focusedBorderWidth: focusedBorderWidthProp, + height, + inputBackground, + borderRadius, maxSelectedOptionsToShow = 6, hiddenSelectedOptionsLabel = 'more', removeSelectedOptionAccessibilityLabel = 'Remove', @@ -96,6 +102,26 @@ const DefaultSelectControlComponent = memo( }: SelectControlProps, ref: React.Ref, ) => { + const borderWidth = borderWidthProp ?? (bordered ? 100 : 0); + const focusedBorderWidth = focusedBorderWidthProp ?? (bordered ? undefined : 200); + const disableFocusedStyle = !bordered && typeof focusedBorderWidthProp === 'undefined'; + const isInteractionBlocked = disabled || readOnly; + + const readOnlyInputBackground = useMemo(() => { + if (!disabled && readOnly) { + return 'bgSecondary'; + } + return undefined; + }, [disabled, readOnly]); + const resolvedInputBackground = readOnlyInputBackground ?? inputBackground; + + const handleToggleOpen = useCallback(() => { + if (isInteractionBlocked) { + return; + } + setOpen((currentOpen) => !currentOpen); + }, [isInteractionBlocked, setOpen]); + type ValueType = Type extends 'multi' ? SelectOptionValue | SelectOptionValue[] | null : SelectOptionValue | null; @@ -207,7 +233,7 @@ const DefaultSelectControlComponent = memo( if (focusIndex === null) return controlPressableRef.current?.focus(); (valueNodes[focusIndex] as HTMLElement)?.focus(); }, - [onChange, value], + [isInteractionBlocked, onChange, value], ); const interactableBlendStyles = useMemo( @@ -246,7 +272,8 @@ const DefaultSelectControlComponent = memo( return ( @@ -262,6 +289,8 @@ const DefaultSelectControlComponent = memo( classNames?.controlLabelNode, styles?.controlLabelNode, label, + labelColor, + labelFont, ]); const inlineLabelNode = useMemo(() => { @@ -271,7 +300,8 @@ const DefaultSelectControlComponent = memo( return ( @@ -287,6 +317,8 @@ const DefaultSelectControlComponent = memo( classNames?.controlLabelNode, styles?.controlLabelNode, label, + labelColor, + labelFont, ]); const valueNode = useMemo(() => { @@ -318,7 +350,11 @@ const DefaultSelectControlComponent = memo( disabled={option.disabled} invertColorScheme={false} maxWidth={200} - onClick={(event) => handleUnselectValue(event, index)} + onClick={ + isInteractionBlocked + ? undefined + : (event) => handleUnselectValue(event, index) + } > {option.label ?? option.description ?? option.value ?? ''} @@ -362,6 +398,7 @@ const DefaultSelectControlComponent = memo( optionsMap, removeSelectedOptionAccessibilityLabel, handleUnselectValue, + isInteractionBlocked, ]); const inputNode = useMemo( @@ -372,6 +409,7 @@ const DefaultSelectControlComponent = memo( accessibilityLabel={computedControlAccessibilityLabel} aria-expanded={open} aria-haspopup={ariaHaspopup} + aria-readonly={readOnly || undefined} as={role === 'combobox' ? 'div' : 'button'} background="transparent" blendStyles={interactableBlendStyles} @@ -382,7 +420,7 @@ const DefaultSelectControlComponent = memo( flexShrink={1} focusable={false} minWidth={0} - onClick={() => setOpen((s) => !s)} + onClick={handleToggleOpen} onKeyDown={onKeyDown} role={role} style={styles?.controlInputNode} @@ -468,6 +506,7 @@ const DefaultSelectControlComponent = memo( classNames?.controlStartNode, classNames?.controlValueNode, disabled, + readOnly, styles?.controlInputNode, styles?.controlStartNode, styles?.controlValueNode, @@ -480,13 +519,13 @@ const DefaultSelectControlComponent = memo( align, valueNode, contentNode, - setOpen, + handleToggleOpen, ], ); const endNode = useMemo( () => ( - setOpen((s) => !s)} tabIndex={-1}> + )} @@ -513,8 +560,9 @@ const DefaultSelectControlComponent = memo( styles?.controlEndNode, customEndNode, open, + disabled, variant, - setOpen, + handleToggleOpen, ], ); @@ -532,11 +580,16 @@ const DefaultSelectControlComponent = memo( { - if (disabled || open) return; + if (disabled || readOnly || open) return; if (event.ctrlKey || event.metaKey || event.altKey) return; const key = event.key; @@ -158,7 +166,7 @@ const SelectBase = memo( setOpen(true); } }, - [disabled, open, setOpen], + [disabled, readOnly, open, setOpen], ); useEffect(() => { @@ -308,16 +316,24 @@ const SelectBase = memo( ariaHaspopup={accessibilityRoles?.dropdown} blendStyles={styles?.controlBlendStyles} bordered={bordered} + borderRadius={borderRadius} + borderWidth={borderWidth} className={classNames?.control} classNames={controlClassNames} compact={compact} disabled={disabled} endNode={endNode} + focusedBorderWidth={focusedBorderWidth} font={font} + height={height} helperText={helperText} hiddenSelectedOptionsLabel={hiddenSelectedOptionsLabel} + inputBackground={inputBackground} label={label} + labelColor={labelColor} + labelFont={labelFont} labelVariant={labelVariant} + readOnly={readOnly} maxSelectedOptionsToShow={maxSelectedOptionsToShow} onChange={onChange} onKeyDown={handleControlKeyDown} @@ -354,7 +370,7 @@ const SelectBase = memo( label={label} media={media} onChange={onChange} - open={hasMounted && open} + open={hasMounted && open && !readOnly} options={options} selectAllLabel={selectAllLabel} setOpen={setOpen} diff --git a/packages/web/src/alpha/select/__tests__/Select.test.tsx b/packages/web/src/alpha/select/__tests__/Select.test.tsx index 29b82019b6..3885986b8a 100644 --- a/packages/web/src/alpha/select/__tests__/Select.test.tsx +++ b/packages/web/src/alpha/select/__tests__/Select.test.tsx @@ -3,6 +3,7 @@ import { renderA11y } from '@coinbase/cds-web-utils/jest'; import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { ComponentConfigProvider } from '../../../system'; import { DefaultThemeProvider } from '../../../utils/test'; import { Select, type SelectProps } from '../Select'; @@ -670,6 +671,34 @@ describe('Select', () => { expect(screen.queryByRole('listbox')).not.toBeInTheDocument(); }); + it('does not open dropdown when readOnly', async () => { + const user = userEvent.setup(); + render( + + + , + ); + + const button = screen.getByRole('button'); + button.focus(); + await user.keyboard('o'); + + expect(screen.queryByRole('listbox')).not.toBeInTheDocument(); + }); + it('does not open dropdown when modifier key + letter is pressed', async () => { const user = userEvent.setup(); render( @@ -686,6 +715,91 @@ describe('Select', () => { }); }); + describe('readOnly', () => { + it('does not apply disabled opacity styling', () => { + render( + + + + , + ); + + const inputArea = screen.getByTestId('input-interactable-area'); + expect(inputArea).toHaveStyle({ backgroundColor: 'var(--color-bgSecondary)' }); + }); + + it('applies Select defaults from ComponentConfigProvider', () => { + render( + + + + + , + ); + + const inputArea = screen.getByTestId('input-interactable-area'); + expect(inputArea).toHaveStyle({ '--height': '48px' }); + }); + }); + describe('Ref Forwarding', () => { it('forwards ref correctly', () => { const ref = React.createRef(); diff --git a/packages/web/src/alpha/select/types.ts b/packages/web/src/alpha/select/types.ts index 850dd99c84..5607a93265 100644 --- a/packages/web/src/alpha/select/types.ts +++ b/packages/web/src/alpha/select/types.ts @@ -1,5 +1,6 @@ import type React from 'react'; import type { SharedAccessibilityProps } from '@coinbase/cds-common'; +import type { SharedInputProps } from '@coinbase/cds-common/types/InputBaseProps'; import type { CellBaseProps } from '../../cells/Cell'; import type { CellAccessoryProps } from '../../cells/CellAccessory'; @@ -393,8 +394,17 @@ export type SelectControlProps< Omit, 'borderWidth' | 'onChange'> & Pick< InputStackBaseProps, - 'disabled' | 'startNode' | 'variant' | 'labelVariant' | 'testID' | 'endNode' + | 'disabled' + | 'startNode' + | 'variant' + | 'labelVariant' + | 'testID' + | 'endNode' + | 'height' + | 'inputBackground' + | 'borderRadius' > & + Pick & SelectState & { /** * Alignment of the value node. @@ -510,6 +520,14 @@ export type SelectBaseProps< | 'align' | 'font' | 'bordered' + | 'borderWidth' + | 'focusedBorderWidth' + | 'height' + | 'inputBackground' + | 'labelColor' + | 'labelFont' + | 'readOnly' + | 'borderRadius' > & Pick, 'accessory' | 'media' | 'end'> & Pick< From 7c7f7188987cd973a4f006c65b7f48312fefcf6e Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 28 May 2026 13:03:00 -0400 Subject: [PATCH 02/12] Fix height issues --- .../mobile/src/alpha/select/DefaultSelectControl.tsx | 2 -- packages/mobile/src/alpha/select/Select.tsx | 2 -- .../customComponentConfig.tsx | 9 ++++----- .../componentConfigStickerSheet/examples/Select.tsx | 8 ++++++++ .../customComponentConfig.tsx | 6 +++--- .../componentConfigStickerSheet/examples/Select.tsx | 9 +++++++++ 6 files changed, 24 insertions(+), 12 deletions(-) diff --git a/packages/mobile/src/alpha/select/DefaultSelectControl.tsx b/packages/mobile/src/alpha/select/DefaultSelectControl.tsx index fef1bf1e7a..0659f6b48c 100644 --- a/packages/mobile/src/alpha/select/DefaultSelectControl.tsx +++ b/packages/mobile/src/alpha/select/DefaultSelectControl.tsx @@ -63,7 +63,6 @@ export const DefaultSelectControlComponent = memo( bordered = true, borderWidth: borderWidthProp, focusedBorderWidth: focusedBorderWidthProp, - height, inputBackground, borderRadius, maxSelectedOptionsToShow = 3, @@ -488,7 +487,6 @@ export const DefaultSelectControlComponent = memo( endNode={endNode} focused={open && !readOnly} focusedBorderWidth={focusedBorderWidth} - height={height} helperTextNode={helperTextNode} inputBackground={resolvedInputBackground} inputNode={inputNode} diff --git a/packages/mobile/src/alpha/select/Select.tsx b/packages/mobile/src/alpha/select/Select.tsx index 411aa6612a..033820653b 100644 --- a/packages/mobile/src/alpha/select/Select.tsx +++ b/packages/mobile/src/alpha/select/Select.tsx @@ -91,7 +91,6 @@ const SelectBase = memo( bordered = true, borderWidth, focusedBorderWidth, - height, inputBackground, labelColor, labelFont, @@ -194,7 +193,6 @@ const SelectBase = memo( endNode={endNode} focusedBorderWidth={focusedBorderWidth} font={font} - height={height} helperText={helperText} hiddenSelectedOptionsLabel={hiddenSelectedOptionsLabel} inputBackground={inputBackground} diff --git a/packages/mobile/src/system/__stories__/componentConfigStickerSheet/customComponentConfig.tsx b/packages/mobile/src/system/__stories__/componentConfigStickerSheet/customComponentConfig.tsx index cc691087e6..3b1249cdbc 100644 --- a/packages/mobile/src/system/__stories__/componentConfigStickerSheet/customComponentConfig.tsx +++ b/packages/mobile/src/system/__stories__/componentConfigStickerSheet/customComponentConfig.tsx @@ -114,15 +114,14 @@ export const customComponentConfig: ComponentConfig = { height: props.compact ? 24 : 32, }), - Select: (props) => ({ + Select: ({ readOnly, ...props }) => ({ bordered: false, variant: 'foregroundMuted', - inputBackground: 'bgAlternate', + inputBackground: readOnly ? 'bgSecondary' : 'bgAlternate', focusedBorderWidth: 100, - height: props.compact ? 24 : props.labelVariant === 'inside' ? 40 : 32, - font: props.compact ? 'label2' : 'body', + font: props.compact ? (props.align === 'end' ? 'label1' : 'label2') : 'body', labelColor: 'fgMuted', - labelFont: props.compact ? (props.align === 'end' ? 'label1' : 'label2') : 'body', + labelFont: 'label2', }), ListCell: (props) => { diff --git a/packages/mobile/src/system/__stories__/componentConfigStickerSheet/examples/Select.tsx b/packages/mobile/src/system/__stories__/componentConfigStickerSheet/examples/Select.tsx index f5a009132c..07d51d44db 100644 --- a/packages/mobile/src/system/__stories__/componentConfigStickerSheet/examples/Select.tsx +++ b/packages/mobile/src/system/__stories__/componentConfigStickerSheet/examples/Select.tsx @@ -42,6 +42,14 @@ export const SelectExample = memo(() => { placeholder="Compact end align" value={selectValue} /> + ); }); From fb17849b7f1703b2ac281f90902fc45c35f881a0 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 28 May 2026 13:19:06 -0400 Subject: [PATCH 03/12] Fix styling --- .../componentConfigStickerSheet/customComponentConfig.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/web/src/__stories__/componentConfigStickerSheet/customComponentConfig.tsx b/packages/web/src/__stories__/componentConfigStickerSheet/customComponentConfig.tsx index e6406c17e2..72c2be7c21 100644 --- a/packages/web/src/__stories__/componentConfigStickerSheet/customComponentConfig.tsx +++ b/packages/web/src/__stories__/componentConfigStickerSheet/customComponentConfig.tsx @@ -126,10 +126,9 @@ export const customComponentConfig: ComponentConfig = { variant: 'foregroundMuted', inputBackground: readOnly ? 'bgSecondary' : 'bgAlternate', focusedBorderWidth: 100, - height: props.compact ? 24 : props.labelVariant === 'inside' ? 40 : 32, - font: props.compact ? 'label2' : 'body', + font: props.compact ? (props.align === 'end' ? 'label1' : 'label2') : 'body', labelColor: 'fgMuted', - labelFont: props.compact ? (props.align === 'end' ? 'label1' : 'label2') : 'label2', + labelFont: 'label2', }), ListCell: (props) => { From d41080fc189c1ca531f32ee28adf7968e04a729d Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 28 May 2026 13:23:00 -0400 Subject: [PATCH 04/12] Bump version --- packages/common/CHANGELOG.md | 4 ++++ packages/common/package.json | 2 +- packages/mcp-server/CHANGELOG.md | 4 ++++ packages/mcp-server/package.json | 2 +- packages/mobile/CHANGELOG.md | 7 +++++++ packages/mobile/package.json | 2 +- packages/web/CHANGELOG.md | 7 +++++++ packages/web/package.json | 2 +- 8 files changed, 26 insertions(+), 4 deletions(-) diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index 2870a1a2a4..94dfaedcd0 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file. +## 9.2.0 ((5/28/2026, 10:22 AM PST)) + +This is an artificial version bump with no new change. + ## 9.1.2 ((5/28/2026, 06:59 AM PST)) This is an artificial version bump with no new change. diff --git a/packages/common/package.json b/packages/common/package.json index df62fc6f70..068573e0f3 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,6 +1,6 @@ { "name": "@coinbase/cds-common", - "version": "9.1.2", + "version": "9.2.0", "description": "Coinbase Design System - Common", "repository": { "type": "git", diff --git a/packages/mcp-server/CHANGELOG.md b/packages/mcp-server/CHANGELOG.md index fffe0268c8..306cdefe96 100644 --- a/packages/mcp-server/CHANGELOG.md +++ b/packages/mcp-server/CHANGELOG.md @@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file. +## 9.2.0 ((5/28/2026, 10:22 AM PST)) + +This is an artificial version bump with no new change. + ## 9.1.2 ((5/28/2026, 06:59 AM PST)) This is an artificial version bump with no new change. diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json index 517c139055..ea64348c5f 100644 --- a/packages/mcp-server/package.json +++ b/packages/mcp-server/package.json @@ -1,6 +1,6 @@ { "name": "@coinbase/cds-mcp-server", - "version": "9.1.2", + "version": "9.2.0", "description": "Coinbase Design System - MCP Server", "repository": { "type": "git", diff --git a/packages/mobile/CHANGELOG.md b/packages/mobile/CHANGELOG.md index d93b8a0039..faa0ef0ae4 100644 --- a/packages/mobile/CHANGELOG.md +++ b/packages/mobile/CHANGELOG.md @@ -8,6 +8,13 @@ All notable changes to this project will be documented in this file. +## 9.2.0 (5/28/2026 PST) + +#### 🚀 Updates + +- Feat: improve Select theming support. [[#733](https://github.com/coinbase/cds/pull/733)] +- Feat: add readOnly support to Select. [[#733](https://github.com/coinbase/cds/pull/733)] + ## 9.1.2 ((5/28/2026, 06:59 AM PST)) This is an artificial version bump with no new change. diff --git a/packages/mobile/package.json b/packages/mobile/package.json index 05f0485d3e..f430c1a681 100644 --- a/packages/mobile/package.json +++ b/packages/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@coinbase/cds-mobile", - "version": "9.1.2", + "version": "9.2.0", "description": "Coinbase Design System - Mobile", "repository": { "type": "git", diff --git a/packages/web/CHANGELOG.md b/packages/web/CHANGELOG.md index 3659e34dd7..de1939c752 100644 --- a/packages/web/CHANGELOG.md +++ b/packages/web/CHANGELOG.md @@ -8,6 +8,13 @@ All notable changes to this project will be documented in this file. +## 9.2.0 (5/28/2026 PST) + +#### 🚀 Updates + +- Feat: improve Select theming support. [[#733](https://github.com/coinbase/cds/pull/733)] +- Feat: add readOnly support to Select. [[#733](https://github.com/coinbase/cds/pull/733)] + ## 9.1.2 (5/28/2026 PST) #### 🐞 Fixes diff --git a/packages/web/package.json b/packages/web/package.json index 02a2e8ac06..f23c543009 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -1,6 +1,6 @@ { "name": "@coinbase/cds-web", - "version": "9.1.2", + "version": "9.2.0", "description": "Coinbase Design System - Web", "repository": { "type": "git", From f6cc21b73e718434ab7b05ed4d4c16e49ffa2ed7 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 28 May 2026 14:22:24 -0400 Subject: [PATCH 05/12] Fix caretColor --- .../src/alpha/select/DefaultSelectControl.tsx | 31 +++++------- .../src/alpha/select/DefaultSelectControl.tsx | 48 +++++++------------ 2 files changed, 28 insertions(+), 51 deletions(-) diff --git a/packages/mobile/src/alpha/select/DefaultSelectControl.tsx b/packages/mobile/src/alpha/select/DefaultSelectControl.tsx index 0659f6b48c..917fdd1a9d 100644 --- a/packages/mobile/src/alpha/select/DefaultSelectControl.tsx +++ b/packages/mobile/src/alpha/select/DefaultSelectControl.tsx @@ -61,9 +61,9 @@ export const DefaultSelectControlComponent = memo( labelColor = 'fg', labelFont = 'label1', bordered = true, - borderWidth: borderWidthProp, - focusedBorderWidth: focusedBorderWidthProp, - inputBackground, + borderWidth = bordered ? 100 : 0, + focusedBorderWidth = bordered ? undefined : 200, + inputBackground = !disabled && readOnly ? 'bgSecondary' : 'bg', borderRadius, maxSelectedOptionsToShow = 3, accessibilityHint, @@ -82,18 +82,8 @@ export const DefaultSelectControlComponent = memo( ? SelectOptionValue | SelectOptionValue[] | null : SelectOptionValue | null; - const borderWidth = borderWidthProp ?? (bordered ? 100 : 0); - const focusedBorderWidth = focusedBorderWidthProp ?? (bordered ? undefined : 200); const isInteractionBlocked = disabled || readOnly; - const readOnlyInputBackground = useMemo(() => { - if (!disabled && readOnly) { - return 'bgSecondary'; - } - return undefined; - }, [disabled, readOnly]); - const resolvedInputBackground = readOnlyInputBackground ?? inputBackground; - const handleToggleOpen = useCallback(() => { if (isInteractionBlocked) { return; @@ -101,6 +91,12 @@ export const DefaultSelectControlComponent = memo( setOpen((currentOpen) => !currentOpen); }, [isInteractionBlocked, setOpen]); + const caretColor = useMemo((): ThemeVars.Color => { + if (disabled) return 'fgMuted'; + if (!open) return 'fg'; + return variant ? variantColor[variant] : 'fgPrimary'; + }, [disabled, open, variant]); + const theme = useTheme(); // When compact, labelVariant is ignored const labelVariant = compact ? undefined : labelVariantProp; @@ -456,15 +452,12 @@ export const DefaultSelectControlComponent = memo( {customEndNode ? ( customEndNode ) : ( - + )} ), - [styles?.controlEndNode, disabled, customEndNode, open, variant, handleToggleOpen], + [styles?.controlEndNode, disabled, customEndNode, open, caretColor, handleToggleOpen], ); const inputStackStyles: StyleProp = useMemo( @@ -488,7 +481,7 @@ export const DefaultSelectControlComponent = memo( focused={open && !readOnly} focusedBorderWidth={focusedBorderWidth} helperTextNode={helperTextNode} - inputBackground={resolvedInputBackground} + inputBackground={inputBackground} inputNode={inputNode} labelNode={labelNode} labelVariant={labelVariant} diff --git a/packages/web/src/alpha/select/DefaultSelectControl.tsx b/packages/web/src/alpha/select/DefaultSelectControl.tsx index ed32e1a281..fb8a30f382 100644 --- a/packages/web/src/alpha/select/DefaultSelectControl.tsx +++ b/packages/web/src/alpha/select/DefaultSelectControl.tsx @@ -84,10 +84,10 @@ const DefaultSelectControlComponent = memo( labelColor = 'fg', labelFont = 'label1', bordered = true, - borderWidth: borderWidthProp, - focusedBorderWidth: focusedBorderWidthProp, + borderWidth = bordered ? 100 : 0, + focusedBorderWidth = bordered ? undefined : 200, height, - inputBackground, + inputBackground = !disabled && readOnly ? 'bgSecondary' : 'bg', borderRadius, maxSelectedOptionsToShow = 6, hiddenSelectedOptionsLabel = 'more', @@ -102,18 +102,8 @@ const DefaultSelectControlComponent = memo( }: SelectControlProps, ref: React.Ref, ) => { - const borderWidth = borderWidthProp ?? (bordered ? 100 : 0); - const focusedBorderWidth = focusedBorderWidthProp ?? (bordered ? undefined : 200); - const disableFocusedStyle = !bordered && typeof focusedBorderWidthProp === 'undefined'; const isInteractionBlocked = disabled || readOnly; - - const readOnlyInputBackground = useMemo(() => { - if (!disabled && readOnly) { - return 'bgSecondary'; - } - return undefined; - }, [disabled, readOnly]); - const resolvedInputBackground = readOnlyInputBackground ?? inputBackground; + const disableFocusedStyle = !bordered && focusedBorderWidth === 200; const handleToggleOpen = useCallback(() => { if (isInteractionBlocked) { @@ -122,6 +112,12 @@ const DefaultSelectControlComponent = memo( setOpen((currentOpen) => !currentOpen); }, [isInteractionBlocked, setOpen]); + const caretColor = useMemo((): ThemeVars.Color => { + if (disabled) return 'fgMuted'; + if (!open) return 'fg'; + return variant ? variantColor[variant] : 'fgPrimary'; + }, [disabled, open, variant]); + type ValueType = Type extends 'multi' ? SelectOptionValue | SelectOptionValue[] | null : SelectOptionValue | null; @@ -233,7 +229,7 @@ const DefaultSelectControlComponent = memo( if (focusIndex === null) return controlPressableRef.current?.focus(); (valueNodes[focusIndex] as HTMLElement)?.focus(); }, - [isInteractionBlocked, onChange, value], + [onChange, value], ); const interactableBlendStyles = useMemo( @@ -409,7 +405,7 @@ const DefaultSelectControlComponent = memo( accessibilityLabel={computedControlAccessibilityLabel} aria-expanded={open} aria-haspopup={ariaHaspopup} - aria-readonly={readOnly || undefined} + aria-readonly={readOnly} as={role === 'combobox' ? 'div' : 'button'} background="transparent" blendStyles={interactableBlendStyles} @@ -538,18 +534,7 @@ const DefaultSelectControlComponent = memo( {customEndNode ? ( customEndNode ) : ( - + )} @@ -560,8 +545,7 @@ const DefaultSelectControlComponent = memo( styles?.controlEndNode, customEndNode, open, - disabled, - variant, + caretColor, handleToggleOpen, ], ); @@ -582,14 +566,14 @@ const DefaultSelectControlComponent = memo( blendStyles={interactableBlendStyles} borderRadius={borderRadius} borderWidth={borderWidth} - disabled={disabled} disableFocusedStyle={disableFocusedStyle} + disabled={disabled} endNode={endNode} focused={open && !readOnly} focusedBorderWidth={focusedBorderWidth} height={height} helperTextNode={helperTextNode} - inputBackground={resolvedInputBackground} + inputBackground={inputBackground} inputNode={inputNode} labelNode={labelNode} labelVariant={labelVariant} From a4f4ae61462a72acd3f7c022e761ac541f38c5a9 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 28 May 2026 14:30:18 -0400 Subject: [PATCH 06/12] Simplify handleToggleOpen --- packages/mobile/src/alpha/select/DefaultSelectControl.tsx | 4 +--- packages/web/src/alpha/select/DefaultSelectControl.tsx | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/mobile/src/alpha/select/DefaultSelectControl.tsx b/packages/mobile/src/alpha/select/DefaultSelectControl.tsx index 917fdd1a9d..0791b6fc66 100644 --- a/packages/mobile/src/alpha/select/DefaultSelectControl.tsx +++ b/packages/mobile/src/alpha/select/DefaultSelectControl.tsx @@ -85,9 +85,7 @@ export const DefaultSelectControlComponent = memo( const isInteractionBlocked = disabled || readOnly; const handleToggleOpen = useCallback(() => { - if (isInteractionBlocked) { - return; - } + if (isInteractionBlocked) return; setOpen((currentOpen) => !currentOpen); }, [isInteractionBlocked, setOpen]); diff --git a/packages/web/src/alpha/select/DefaultSelectControl.tsx b/packages/web/src/alpha/select/DefaultSelectControl.tsx index fb8a30f382..a6197f1c9c 100644 --- a/packages/web/src/alpha/select/DefaultSelectControl.tsx +++ b/packages/web/src/alpha/select/DefaultSelectControl.tsx @@ -106,9 +106,7 @@ const DefaultSelectControlComponent = memo( const disableFocusedStyle = !bordered && focusedBorderWidth === 200; const handleToggleOpen = useCallback(() => { - if (isInteractionBlocked) { - return; - } + if (isInteractionBlocked) return; setOpen((currentOpen) => !currentOpen); }, [isInteractionBlocked, setOpen]); From 3d927b30c8228fa8f984c5d56b12ce8e20bd14ae Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 28 May 2026 14:38:59 -0400 Subject: [PATCH 07/12] Remove height props --- packages/mobile/src/alpha/select/Select.tsx | 4 ++-- packages/mobile/src/alpha/select/types.ts | 2 -- packages/web/src/alpha/select/DefaultSelectControl.tsx | 2 -- packages/web/src/alpha/select/Select.tsx | 6 ++---- packages/web/src/alpha/select/types.ts | 2 -- 5 files changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/mobile/src/alpha/select/Select.tsx b/packages/mobile/src/alpha/select/Select.tsx index 033820653b..b7a80f787d 100644 --- a/packages/mobile/src/alpha/select/Select.tsx +++ b/packages/mobile/src/alpha/select/Select.tsx @@ -185,9 +185,9 @@ const SelectBase = memo( accessibilityLabel={accessibilityLabel} align={align} blendStyles={styles?.controlBlendStyles} - bordered={bordered} borderRadius={borderRadius} borderWidth={borderWidth} + bordered={bordered} compact={compact} disabled={disabled} endNode={endNode} @@ -200,12 +200,12 @@ const SelectBase = memo( labelColor={labelColor} labelFont={labelFont} labelVariant={labelVariant} - readOnly={readOnly} maxSelectedOptionsToShow={maxSelectedOptionsToShow} onChange={onChange} open={open && !readOnly} options={options} placeholder={placeholder} + readOnly={readOnly} removeSelectedOptionAccessibilityLabel={removeSelectedOptionAccessibilityLabel} setOpen={setOpen} startNode={startNode} diff --git a/packages/mobile/src/alpha/select/types.ts b/packages/mobile/src/alpha/select/types.ts index 75e240ef14..579710e1a4 100644 --- a/packages/mobile/src/alpha/select/types.ts +++ b/packages/mobile/src/alpha/select/types.ts @@ -246,7 +246,6 @@ export type SelectControlProps< | 'endNode' | 'borderWidth' | 'focusedBorderWidth' - | 'height' | 'inputBackground' | 'borderRadius' > & @@ -426,7 +425,6 @@ export type SelectBaseProps< | 'bordered' | 'borderWidth' | 'focusedBorderWidth' - | 'height' | 'inputBackground' | 'labelColor' | 'labelFont' diff --git a/packages/web/src/alpha/select/DefaultSelectControl.tsx b/packages/web/src/alpha/select/DefaultSelectControl.tsx index a6197f1c9c..06663f26f7 100644 --- a/packages/web/src/alpha/select/DefaultSelectControl.tsx +++ b/packages/web/src/alpha/select/DefaultSelectControl.tsx @@ -86,7 +86,6 @@ const DefaultSelectControlComponent = memo( bordered = true, borderWidth = bordered ? 100 : 0, focusedBorderWidth = bordered ? undefined : 200, - height, inputBackground = !disabled && readOnly ? 'bgSecondary' : 'bg', borderRadius, maxSelectedOptionsToShow = 6, @@ -569,7 +568,6 @@ const DefaultSelectControlComponent = memo( endNode={endNode} focused={open && !readOnly} focusedBorderWidth={focusedBorderWidth} - height={height} helperTextNode={helperTextNode} inputBackground={inputBackground} inputNode={inputNode} diff --git a/packages/web/src/alpha/select/Select.tsx b/packages/web/src/alpha/select/Select.tsx index 5a6f8e71e0..b0eefb1c7e 100644 --- a/packages/web/src/alpha/select/Select.tsx +++ b/packages/web/src/alpha/select/Select.tsx @@ -111,7 +111,6 @@ const SelectBase = memo( bordered = true, borderWidth, focusedBorderWidth, - height, inputBackground, labelColor, labelFont, @@ -315,9 +314,9 @@ const SelectBase = memo( align={align} ariaHaspopup={accessibilityRoles?.dropdown} blendStyles={styles?.controlBlendStyles} - bordered={bordered} borderRadius={borderRadius} borderWidth={borderWidth} + bordered={bordered} className={classNames?.control} classNames={controlClassNames} compact={compact} @@ -325,7 +324,6 @@ const SelectBase = memo( endNode={endNode} focusedBorderWidth={focusedBorderWidth} font={font} - height={height} helperText={helperText} hiddenSelectedOptionsLabel={hiddenSelectedOptionsLabel} inputBackground={inputBackground} @@ -333,13 +331,13 @@ const SelectBase = memo( labelColor={labelColor} labelFont={labelFont} labelVariant={labelVariant} - readOnly={readOnly} maxSelectedOptionsToShow={maxSelectedOptionsToShow} onChange={onChange} onKeyDown={handleControlKeyDown} open={open} options={options} placeholder={placeholder} + readOnly={readOnly} removeSelectedOptionAccessibilityLabel={removeSelectedOptionAccessibilityLabel} setOpen={setOpen} startNode={startNode} diff --git a/packages/web/src/alpha/select/types.ts b/packages/web/src/alpha/select/types.ts index 5607a93265..ffbed6ec36 100644 --- a/packages/web/src/alpha/select/types.ts +++ b/packages/web/src/alpha/select/types.ts @@ -400,7 +400,6 @@ export type SelectControlProps< | 'labelVariant' | 'testID' | 'endNode' - | 'height' | 'inputBackground' | 'borderRadius' > & @@ -522,7 +521,6 @@ export type SelectBaseProps< | 'bordered' | 'borderWidth' | 'focusedBorderWidth' - | 'height' | 'inputBackground' | 'labelColor' | 'labelFont' From 89eab96d0693ed40ffce27aa0319d3395daeef0c Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 28 May 2026 15:25:59 -0400 Subject: [PATCH 08/12] Fix lint and tests --- packages/mobile/src/alpha/select/__tests__/Select.test.tsx | 2 +- packages/web/src/alpha/select/__tests__/Select.test.tsx | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/mobile/src/alpha/select/__tests__/Select.test.tsx b/packages/mobile/src/alpha/select/__tests__/Select.test.tsx index 16ff2ac65d..ed2193c552 100644 --- a/packages/mobile/src/alpha/select/__tests__/Select.test.tsx +++ b/packages/mobile/src/alpha/select/__tests__/Select.test.tsx @@ -2,8 +2,8 @@ import React from 'react'; import { View } from 'react-native'; import { fireEvent, render, screen } from '@testing-library/react-native'; -import { Text } from '../../../typography/Text'; import { ComponentConfigProvider } from '../../../system/ComponentConfigProvider'; +import { Text } from '../../../typography/Text'; import { DefaultThemeProvider } from '../../../utils/testHelpers'; import { Select, type SelectOption, type SelectProps } from '../Select'; diff --git a/packages/web/src/alpha/select/__tests__/Select.test.tsx b/packages/web/src/alpha/select/__tests__/Select.test.tsx index 3885986b8a..b993e2d64b 100644 --- a/packages/web/src/alpha/select/__tests__/Select.test.tsx +++ b/packages/web/src/alpha/select/__tests__/Select.test.tsx @@ -786,17 +786,18 @@ describe('Select', () => { value={{ Select: { inputBackground: 'bgAlternate', - height: 32, + labelColor: 'fgMuted', }, }} > - , ); const inputArea = screen.getByTestId('input-interactable-area'); - expect(inputArea).toHaveStyle({ '--height': '48px' }); + expect(inputArea).toHaveStyle({ backgroundColor: 'var(--color-bgPrimary)' }); + expect(screen.getByText(defaultProps.label as string)).toHaveStyle({ color: 'var(--color-fg)' }); }); }); From e5a3d7e09cd61dd3355b9a3e0f9fc3959e7fb3f3 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 28 May 2026 15:28:45 -0400 Subject: [PATCH 09/12] Fix formatting --- packages/web/src/alpha/select/__tests__/Select.test.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/web/src/alpha/select/__tests__/Select.test.tsx b/packages/web/src/alpha/select/__tests__/Select.test.tsx index b993e2d64b..b311555e71 100644 --- a/packages/web/src/alpha/select/__tests__/Select.test.tsx +++ b/packages/web/src/alpha/select/__tests__/Select.test.tsx @@ -797,7 +797,9 @@ describe('Select', () => { const inputArea = screen.getByTestId('input-interactable-area'); expect(inputArea).toHaveStyle({ backgroundColor: 'var(--color-bgPrimary)' }); - expect(screen.getByText(defaultProps.label as string)).toHaveStyle({ color: 'var(--color-fg)' }); + expect(screen.getByText(defaultProps.label as string)).toHaveStyle({ + color: 'var(--color-fg)', + }); }); }); From d21b0d1fd157b9c2e6115e859bb8efbf24e57d90 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Thu, 28 May 2026 16:51:27 -0400 Subject: [PATCH 10/12] Fix caretColor --- .../src/alpha/select/DefaultSelectControl.tsx | 14 ++------------ .../web/src/alpha/select/DefaultSelectControl.tsx | 13 +------------ 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/packages/mobile/src/alpha/select/DefaultSelectControl.tsx b/packages/mobile/src/alpha/select/DefaultSelectControl.tsx index 0791b6fc66..47c6290064 100644 --- a/packages/mobile/src/alpha/select/DefaultSelectControl.tsx +++ b/packages/mobile/src/alpha/select/DefaultSelectControl.tsx @@ -89,12 +89,6 @@ export const DefaultSelectControlComponent = memo( setOpen((currentOpen) => !currentOpen); }, [isInteractionBlocked, setOpen]); - const caretColor = useMemo((): ThemeVars.Color => { - if (disabled) return 'fgMuted'; - if (!open) return 'fg'; - return variant ? variantColor[variant] : 'fgPrimary'; - }, [disabled, open, variant]); - const theme = useTheme(); // When compact, labelVariant is ignored const labelVariant = compact ? undefined : labelVariantProp; @@ -447,15 +441,11 @@ export const DefaultSelectControlComponent = memo( paddingStart={2} style={styles?.controlEndNode} > - {customEndNode ? ( - customEndNode - ) : ( - - )} + {customEndNode ? customEndNode : } ), - [styles?.controlEndNode, disabled, customEndNode, open, caretColor, handleToggleOpen], + [styles?.controlEndNode, disabled, customEndNode, open, handleToggleOpen], ); const inputStackStyles: StyleProp = useMemo( diff --git a/packages/web/src/alpha/select/DefaultSelectControl.tsx b/packages/web/src/alpha/select/DefaultSelectControl.tsx index 06663f26f7..b97becef1f 100644 --- a/packages/web/src/alpha/select/DefaultSelectControl.tsx +++ b/packages/web/src/alpha/select/DefaultSelectControl.tsx @@ -109,12 +109,6 @@ const DefaultSelectControlComponent = memo( setOpen((currentOpen) => !currentOpen); }, [isInteractionBlocked, setOpen]); - const caretColor = useMemo((): ThemeVars.Color => { - if (disabled) return 'fgMuted'; - if (!open) return 'fg'; - return variant ? variantColor[variant] : 'fgPrimary'; - }, [disabled, open, variant]); - type ValueType = Type extends 'multi' ? SelectOptionValue | SelectOptionValue[] | null : SelectOptionValue | null; @@ -528,11 +522,7 @@ const DefaultSelectControlComponent = memo( paddingStart={2} style={styles?.controlEndNode} > - {customEndNode ? ( - customEndNode - ) : ( - - )} + {customEndNode ? customEndNode : } ), @@ -542,7 +532,6 @@ const DefaultSelectControlComponent = memo( styles?.controlEndNode, customEndNode, open, - caretColor, handleToggleOpen, ], ); From 19ff4ac26b7cfbc2e2c8b3aae7889e91ac6cf4d8 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Mon, 8 Jun 2026 10:19:49 -0400 Subject: [PATCH 11/12] Update JSDocs to clarify default values --- packages/mobile/src/alpha/select/types.ts | 18 +++++++++++++++--- packages/web/src/alpha/select/types.ts | 8 ++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/mobile/src/alpha/select/types.ts b/packages/mobile/src/alpha/select/types.ts index 579710e1a4..c40267d6bd 100644 --- a/packages/mobile/src/alpha/select/types.ts +++ b/packages/mobile/src/alpha/select/types.ts @@ -244,9 +244,6 @@ export type SelectControlProps< | 'labelVariant' | 'testID' | 'endNode' - | 'borderWidth' - | 'focusedBorderWidth' - | 'inputBackground' | 'borderRadius' > & Pick & @@ -263,6 +260,21 @@ export type SelectControlProps< * @default true */ bordered?: boolean; + /** + * Width of the border. + * @default 100 when bordered is true, 0 otherwise + */ + borderWidth?: InputStackBaseProps['borderWidth']; + /** + * Additional border width when focused. + * @default 200 when bordered is false, otherwise equals borderWidth + */ + focusedBorderWidth?: InputStackBaseProps['focusedBorderWidth']; + /** + * Background of the input. + * @default 'bgSecondary' when readOnly and not disabled, 'bg' otherwise + */ + inputBackground?: InputStackBaseProps['inputBackground']; /** Array of options to display in the select dropdown. Can be individual options or groups with `label` and `options` */ options: SelectOptionList; /** Label displayed above the control */ diff --git a/packages/web/src/alpha/select/types.ts b/packages/web/src/alpha/select/types.ts index ffbed6ec36..ed885ac442 100644 --- a/packages/web/src/alpha/select/types.ts +++ b/packages/web/src/alpha/select/types.ts @@ -400,7 +400,6 @@ export type SelectControlProps< | 'labelVariant' | 'testID' | 'endNode' - | 'inputBackground' | 'borderRadius' > & Pick & @@ -423,9 +422,14 @@ export type SelectControlProps< borderWidth?: InputStackBaseProps['borderWidth']; /** * Additional border width when focused. - * @default 200 when bordered is false, undefined otherwise + * @default 200 when bordered is false, otherwise equals borderWidth */ focusedBorderWidth?: InputStackBaseProps['focusedBorderWidth']; + /** + * Background of the input. + * @default 'bgSecondary' when readOnly and not disabled, 'bg' otherwise + */ + inputBackground?: InputStackBaseProps['inputBackground']; /** Array of options to display in the select dropdown. Can be individual options or groups with `label` and `options` */ options: SelectOptionList; /** Label displayed above the control */ From fcbdaabbb596e9c3b90628f34f30c0151d2f9c06 Mon Sep 17 00:00:00 2001 From: Hunter Copp Date: Mon, 8 Jun 2026 10:22:20 -0400 Subject: [PATCH 12/12] Fix formatting --- packages/mobile/src/alpha/select/types.ts | 8 +------- packages/web/src/alpha/select/types.ts | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/mobile/src/alpha/select/types.ts b/packages/mobile/src/alpha/select/types.ts index c40267d6bd..c838e99988 100644 --- a/packages/mobile/src/alpha/select/types.ts +++ b/packages/mobile/src/alpha/select/types.ts @@ -238,13 +238,7 @@ export type SelectControlProps< Omit & Pick< InputStackBaseProps, - | 'disabled' - | 'startNode' - | 'variant' - | 'labelVariant' - | 'testID' - | 'endNode' - | 'borderRadius' + 'disabled' | 'startNode' | 'variant' | 'labelVariant' | 'testID' | 'endNode' | 'borderRadius' > & Pick & Pick & diff --git a/packages/web/src/alpha/select/types.ts b/packages/web/src/alpha/select/types.ts index ed885ac442..3f673f3460 100644 --- a/packages/web/src/alpha/select/types.ts +++ b/packages/web/src/alpha/select/types.ts @@ -394,13 +394,7 @@ export type SelectControlProps< Omit, 'borderWidth' | 'onChange'> & Pick< InputStackBaseProps, - | 'disabled' - | 'startNode' - | 'variant' - | 'labelVariant' - | 'testID' - | 'endNode' - | 'borderRadius' + 'disabled' | 'startNode' | 'variant' | 'labelVariant' | 'testID' | 'endNode' | 'borderRadius' > & Pick & SelectState & {