Skip to content

Commit 0be44ad

Browse files
millusclaude
andauthored
feat: Select EDS 2.0 (#4918)
* feat(next): add NativeSelect component * feat(next): rename NativeSelect → Select, align tokens with Figma, add base-select enhancement and Code Connect * fix(next): fix base-select flex layout, remove CSS import from tsx, add data-space-proportions * fix(next): use accent teal colour for Select chevron icon per Figma * fix(next): use neutral grey chevron in Select invalid state per Figma * refactor(next): share dropdown surface styles via menu.css, fix picker connection and icon size * refactor(next): move Select picker CSS out of menu.css into select.css - ::picker(select), ::picker-icon, and option item styles now live entirely in select.css so Select owns all its styling end-to-end - menu.css reverted to its main state — no Select rules remain there - Added --_radius pseudo-private var on .eds-select; ::picker(select) border-radius uses var(--_radius) via CSS custom property inheritance * fix(next): centre picker-icon flex content to fix rotation jump on open * fix(next): smooth Select chevron animation to match Accordion pattern - --_transition-duration: 200ms on .eds-select (inherited by ::picker-icon) - rotate var(--_transition-duration) ease — matches Accordion chevron - outline-color / box-shadow transitions get ease-in-out — matches Button/Chip - prefers-reduced-motion: reduce resets --_transition-duration to 0s * fix(next): fix Select readOnly form submission, remove aria-readonly, add placeholder prop - Add hidden input to preserve form value when readOnly (native <select> has no readonly attr; disabled excludes value from FormData) - Remove aria-readonly — setting it on a disabled element is contradictory; screen readers would announce both states simultaneously. The visual read-only distinction is carried by data-readonly on the container. - Add placeholder prop: renders as a disabled <option value=""> at the top of the list so the trigger shows instructional text before selection * fix(next): fix Select read-only tokens, chevron height, and selected-option checkmark Read-only state: - Background: --eds-color-bg-input (same as filled, not --eds-color-bg-disabled) - Border: --eds-color-bg-disabled (subtle #e1e1e1, not --eds-color-border-strong) Chevron (::picker-icon in base-select mode): - Suppress native UA glyph with color: transparent — it fills the full box and looks much taller than the compact SVG fallback - Replace with CSS mask using the same path (M7 9.5l5 5 5-5H7z) so base-select and fallback modes are visually identical - Color via background-color so CSS custom properties work correctly - Add align-self: center to counter UA default of flex-start Selected option: - display: flex + ::checkmark with order:1 / margin-inline-start:auto puts the check indicator at the trailing edge per Figma spec - Only renders for :checked options — no empty space on unselected items Picker surface: - Add overflow: clip before overflow-y: auto to prevent horizontal bleed while keeping vertical scrollability * fix(next): fix Select invalid state styling, add indicator prop, improve readOnly accessibility - Invalid state: isolate picker surface with data-color-appearance="neutral", use static danger tokens for trigger, pin Field label/description/helper to neutral colors, fix error icon size (data-font-size="xs") and padding calc to use static spacing token - Add indicator prop for label annotations such as "(Required)" or "(Optional)" - readOnly: remove disabled attribute so field stays in tab order and is readable by screen readers; add aria-readonly="true" and onKeyDown guard to prevent value changes * docs(next): improve Select Storybook controls and story descriptions - Add argTypes with categories (Core, Label, Content, States, Advanced, Styling, Other) - Add component description with beta warning, import snippet, and when-to-use guidance - Add story descriptions explaining behaviour and non-obvious constraints - Reorder stories: states (Invalid, Disabled, ReadOnly) directly after Default - Replace ComfortableDensity with DensityModes showing both densities side by side - Add Stack decorator (maxWidth 320px) and default args for interactive controls * docs(next): add description and placeholder to Select Figma Code Connect * docs(next): make Object options story show label vs form value split * refactor(next): tighten Select CSS nesting and remove redundant rules * fix(next): address Select code review findings - Add hidden attribute to placeholder option so it doesn't appear in the open dropdown list (correct HTML pattern for placeholder semantics) - Update stale readOnly comment to reflect pointer-events/onKeyDown approach - Update placeholder story description and test to match hidden behaviour * fix(next): address remaining Select re-review findings - Suppress React controlled-field warning when readOnly: provide no-op onChange if consumer passes value without onChange (field is non-editable anyway) - Replace hardcoded 1.5rem chevron width with --eds-sizing-icon-lg token in both the fallback padding-inline-end calc and the ::picker-icon dimensions * refactor(next): simplify Select CSS with nesting and dynamic tokens * fix(next): fix Select lint error and simplify CSS icon overlay rules * fix(next): replace hardcoded values with sizing tokens in Select CSS * feat(next): add optgroup support to Select via SelectOptionGroup type * feat(next): add GroupedOptions story and style optgroup labels in Select picker - Add GroupedOptions Storybook story demonstrating SelectOptionGroup usage - Style optgroup labels in base-select picker: xs body font, subtle colour, surface background, border-top separator, and correct padding per Figma - Restore explicit font/colour properties on option to prevent Chrome UA overrides for options inside optgroups - Apply --_color pseudo-private variable pattern to select trigger colour (disabled and placeholder states override the variable, not the property) * fix(next): remove hardcoded 300px picker height and replace checkmark size with token - Remove max-block-size: 300px from ::picker(select) — browser default is sufficient - Replace ::checkmark width/height 1.25rem with var(--eds-sizing-icon-md) * fix(next): align readOnly+invalid danger appearance with error icon suppression data-color-appearance was set to danger for invalid+readOnly but the error icon was already suppressed for readOnly — inconsistent visual state. Both now exclude readOnly so a read-only field is always rendered as neutral. * fix(next): enforce getOptionLabel at compile time for object options, fix readOnly+invalid state - Add SelectOptionAccessors<T> conditional type: getOptionLabel is required when T is not string, preventing silent "[object Object]" labels at runtime - Align data-color-appearance with displayErrorIcon: readOnly+invalid now renders as neutral (no danger border, no error icon) for consistency * fix(next): gate aria-invalid on !readOnly to match visual state Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(next): address review comments — discriminant on SelectOptionGroup, --_color on option, readOnly keyboard test * fix(next): apply --_color pattern to chevron, error-icon and optgroup * fix(next): fix Select height to 36px spacious and 24px comfortable Use xs-squished vertical padding and data-selectable-space="sm" + data-space-proportions="squished" to match Input/Autocomplete heights. Comfortable override uses --eds-spacing-vertical-3xs (2px) to compensate for native <select> UA intrinsic height. * fix(next): remove redundant color property from option :disabled state * fix(next): align optgroup label with options in base-select picker --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ec174a2 commit 0be44ad

10 files changed

Lines changed: 1338 additions & 13 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import figma from '@figma/code-connect'
2+
import { Select } from '.'
3+
4+
figma.connect(
5+
Select,
6+
'https://www.figma.com/design/dz0XQdc5j7AAtjXr1gTfVR/%F0%9F%94%B9-EDS-Core-Components?node-id=2010-3074',
7+
{
8+
props: {
9+
disabled: figma.enum('State', { Disabled: true }),
10+
readOnly: figma.enum('State', { 'Read-only': true }),
11+
invalid: figma.enum('Validation', { Error: true }),
12+
label: figma.boolean('Title + Description', {
13+
true: 'Label',
14+
false: undefined,
15+
}),
16+
description: figma.boolean('Title + Description', {
17+
true: 'Help with more details',
18+
false: undefined,
19+
}),
20+
placeholder: figma.string('Placeholder'),
21+
helperMessage: figma.boolean('Helper Message', {
22+
true: 'Helper message',
23+
false: undefined,
24+
}),
25+
},
26+
example: ({
27+
disabled,
28+
readOnly,
29+
invalid,
30+
label,
31+
description,
32+
placeholder,
33+
helperMessage,
34+
}) => (
35+
<Select
36+
label={label}
37+
description={description}
38+
placeholder={placeholder}
39+
options={['Option 1', 'Option 2', 'Option 3']}
40+
disabled={disabled}
41+
readOnly={readOnly}
42+
invalid={invalid}
43+
helperMessage={helperMessage}
44+
/>
45+
),
46+
},
47+
)

0 commit comments

Comments
 (0)