Commit 0be44ad
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
- packages/eds-core-react/src/components/next
- Select
- utils
Lines changed: 47 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
0 commit comments