feat: add <ep-select> form select (Etherpad nice-select look)#5
feat: add <ep-select> form select (Etherpad nice-select look)#5JohnMcLear wants to merge 1 commit into
Conversation
The library had no native-<select>-style component — the closest were the
toolbar picker (ep-toolbar-select) and the menu (ep-dropdown). This adds a
proper form select styled to match Etherpad colibris's nice-select look
(the plugin Etherpad uses to skin native <select>s):
- nice-select DOM/visuals: .nice-select box + rotating chevron, .list popup
with the colibris palette (bg-soft box, bg-color option hover/selected,
bold selected, greyed disabled).
- API: options[{value,label,disabled?}], value (reflected), placeholder,
disabled, name; fires ep-select:change {value,label}.
- Keyboard: Enter/Space toggle+select, Arrow up/down (skips disabled),
Escape; closes on outside click.
Built test-first; 6 Storybook play-function tests cover render, open,
select+event, preselected value, disabled box, disabled option. Suite is
now 88 tests across 16 files. Carries the dlx→exec Playwright CI fix.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Qodo reviews are paused for this user.Troubleshooting steps vary by plan Learn more → On a Teams plan? Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center? |
Review Summary by QodoAdd form select component with nice-select styling
WalkthroughsDescription• Add `` form select component matching Etherpad nice-select styling • Implements full keyboard navigation (Enter/Space/Arrow keys/Escape) • Includes 6 comprehensive Storybook play-function tests covering all features • Fix Playwright CI by replacing pnpm dlx with pnpm exec Diagramflowchart LR
A["EpSelect Component"] --> B["DOM & Styling"]
A --> C["API & Events"]
A --> D["Keyboard Interaction"]
B --> E["nice-select box + chevron"]
B --> F["Dropdown list with colibris palette"]
C --> G["options, value, placeholder, disabled"]
C --> H["ep-select:change event"]
D --> I["Enter/Space/Arrow/Escape keys"]
D --> J["Outside click closes"]
File Changes1. src/EpSelect.ts
|
Code Review by Qodo
1. Stale focusIndex crash
|
| private _onKeydown(e: KeyboardEvent) { | ||
| if (this.disabled) return; | ||
| switch (e.key) { | ||
| case 'Enter': | ||
| case ' ': | ||
| e.preventDefault(); | ||
| if (this.open && this._focusIndex >= 0) this._select(this.options[this._focusIndex]); | ||
| else this._toggle(); | ||
| break; |
There was a problem hiding this comment.
1. Stale focusindex crash 🐞 Bug ≡ Correctness
EpSelect._onKeydown calls _select(this.options[this._focusIndex]) without checking the upper bound, so if options changes while open, Enter/Space can pass undefined into _select() and throw when reading opt.disabled. This can crash the component during keyboard interaction in dynamic UIs.
Agent Prompt
## Issue description
`EpSelect._onKeydown()` can call `_select()` with an out-of-bounds option (undefined) if `options` changes while the menu is open, leading to a runtime exception when `_select()` dereferences `opt.disabled`.
## Issue Context
`options` is a public reactive property and can be updated by consumers at any time. The repo's `EpDropdown` guards focus index bounds before dereferencing.
## Fix Focus Areas
- src/EpSelect.ts[171-181]
- src/EpSelect.ts[192-200]
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
Why
The library had no native-
<select>-style component. The closest were<ep-toolbar-select>(the editbar font/heading picker) and<ep-dropdown>(a menu) — neither is a form select with a bound value. The demo's own theme switcher had to fall back to a bare, unstyled native<select>.Etherpad skins its
<select>s with nice-select (jquery-nice-select), so this component reproduces that look against the colibris palette.What
<ep-select>— a form select matching nice-select:.nice-selectbox with rotating chevron +.listpopup;bg-softbox,bg-coloroption hover/selected, bold selected option, greyed disabled option — ported fromcss/pad/form.css(base geometry) +skins/colibris/src/components/form.css(colours).options([{value,label,disabled?}]),value(reflected),placeholder,disabled,name; firesep-select:changewith{ value, label }.Tests (built test-first)
6 Storybook play-function tests: render+placeholder+option count, opens on click, selects option (value + event + closes), preselected value, disabled box doesn't open, disabled option not selectable. Full suite now 88 tests / 16 files, typecheck clean.
Verified the rendered look matches nice-select in light + dark (bold selected row, normal others, greyed disabled, rotating chevron) with no console errors.
(Carries the
pnpm dlx→pnpm execPlaywright CI fix so this branch's browser tests pass independently.)🤖 Generated with Claude Code