fix(onboarding): replace internal boot native selects#1942
fix(onboarding): replace internal boot native selects#1942Ajit-Mehrotra merged 4 commits intomainfrom
Conversation
- Purpose: replace the internal boot step's native dropdowns with the shared Select component used elsewhere in onboarding. - Before: the step rendered browser-native select popups for slot count, device selection, and boot size presets. - Problem: native dropdown rendering can pick OS/browser popup colors that mismatch the black theme, which made option text effectively invisible for some users while remaining hard to reproduce locally. - Change: swap those controls to @unraid/ui Select, build typed item lists for each control, and preserve the step's existing selection and validation flow through explicit update handlers. - Tests: update the internal boot step test to stub the shared Select component so the existing step behavior stays covered without depending on portal rendering. - Verification: automated test execution is still blocked in this new worktree because dependencies are not installed yet.
WalkthroughReplaced native selects with Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1942 +/- ##
==========================================
+ Coverage 51.51% 51.53% +0.01%
==========================================
Files 1025 1025
Lines 70460 70506 +46
Branches 7793 7803 +10
==========================================
+ Hits 36297 36332 +35
- Misses 34040 34051 +11
Partials 123 123 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts (1)
64-85: Mock emits string values while component may expect numbers.The native
<select>always emits string values via$event.target.value. ForslotCountItemswhich uses numeric values (e.g.,value: 1), the mock will emit"1"instead of1. ThehandleSlotCountChangehandler already handles this conversion, so tests will pass, but be aware of this type coercion behavior.Additionally, the mock assumes all items are objects with
item.value. PerSelectItemTypedefinition, items can be primitives. Consider adding robustness for future-proofing:♻️ Optional: Handle primitive items in mock
Select: { props: ['modelValue', 'items', 'disabled', 'placeholder'], emits: ['update:modelValue'], template: ` <select data-testid="select" :disabled="disabled" :value="modelValue ?? ''" `@change`="$emit('update:modelValue', $event.target.value)" > <option v-if="placeholder" value="">{{ placeholder }}</option> <option v-for="item in items" - :key="item.value" - :value="item.value" - :disabled="item.disabled" + :key="typeof item === 'object' ? item.value : item" + :value="typeof item === 'object' ? item.value : item" + :disabled="typeof item === 'object' ? item.disabled : false" > - {{ item.label }} + {{ typeof item === 'object' ? item.label : item }} </option> </select> `, },Based on learnings: "Stub complex child components that aren't the focus of the test" - the current mock is sufficient for the component's usage patterns.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts` around lines 64 - 85, The Select mock emits string values from $event.target.value which can misrepresent numeric item values used by slotCountItems and consumed by handleSlotCountChange; update the Select mock (component name Select) to emit a parsed value (e.g., coerce numeric-looking strings back to numbers before emitting to 'update:modelValue') and make the item iteration resilient to primitive items by using item.value ?? item when reading the option value/label so the mock supports both object and primitive SelectItemType shapes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue`:
- Around line 526-531: The handlers handleSlotCountChange,
handleDeviceSelection, and handleBootSizePresetChange have too-narrow parameter
types (currently string | number) and must accept the Select component's
AcceptableValue union; update each handler signature to accept AcceptableValue
(import the type from Radix Vue) or a safe unknown/any, then perform type-guards
inside the function: detect and coerce number/strings via
Number.parseInt/Number.isFinite before assigning to slotCount.value, handle
boolean/object cases appropriately (ignore or map to a valid value), and ensure
device/boot-preset handlers validate the value type before setting the related
reactive state. Ensure imports/reference to AcceptableValue and use explicit
guards so the handlers match the `@update`:model-value event shape.
---
Nitpick comments:
In `@web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts`:
- Around line 64-85: The Select mock emits string values from
$event.target.value which can misrepresent numeric item values used by
slotCountItems and consumed by handleSlotCountChange; update the Select mock
(component name Select) to emit a parsed value (e.g., coerce numeric-looking
strings back to numbers before emitting to 'update:modelValue') and make the
item iteration resilient to primitive items by using item.value ?? item when
reading the option value/label so the mock supports both object and primitive
SelectItemType shapes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 420b9a36-a8f8-4a6a-a8bb-a01e4fcdf5ab
📒 Files selected for processing (2)
web/__test__/components/Onboarding/OnboardingInternalBootStep.test.tsweb/src/components/Onboarding/steps/OnboardingInternalBootStep.vue
web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue
Outdated
Show resolved
Hide resolved
- Purpose: make the internal boot step's shared Select handlers compatible with the value shape emitted by the component. - Before: the handlers assumed narrower string/number inputs than the Select component's emitted event type. - Problem: web type-check failed on the new onboarding select wiring because the handler signatures were not assignable to the component event contract. - Change: accept unknown values at the handler boundary, normalize them through a single typed helper, and keep the slot-count and preset logic unchanged after conversion. - Verification: pnpm --dir web type-check, pnpm --dir api type-check, pnpm --dir web lint, pnpm --filter ./api lint, pnpm --dir web test, and pnpm --filter ./api test all pass in this worktree.
- Purpose: make the internal boot step's shared Select controls visually match the rest of the onboarding form in dark mode. - Before: the internal boot dropdown triggers used the shared Select defaults, which rendered darker than the neighboring onboarding inputs. - Problem: the controls looked visually out of place and made the onboarding dark theme feel inconsistent even after moving off native browser selects. - Change: add onboarding-specific Select trigger classes for border, background, text, placeholder, and focus-ring treatment within the internal boot step. - Scope: applies to slot count, device selection, and boot reserved size only. - Verification: pnpm --dir web type-check and pnpm --dir web test OnboardingInternalBootStep.test.ts passed before commit.
- Purpose: make the remaining onboarding Select controls match the form surfaces in dark mode. - Before: core settings dropdowns still used the shared Select defaults, so timezone, language, and theme looked darker than adjacent onboarding inputs. - Problem: the onboarding flow still felt visually inconsistent after fixing the internal boot step alone. - Change: apply the same onboarding-specific Select trigger classes in the core settings step for timezone, language, and theme selectors. - Scope: styling only; no behavior or data-flow changes. - Verification: pnpm --dir web type-check and pnpm --dir web test OnboardingCoreSettingsStep.test.ts passed before commit.
|
This plugin has been deployed to Cloudflare R2 and is available for testing. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
web/src/components/Onboarding/steps/OnboardingCoreSettingsStep.vue (1)
359-360: Consider extracting duplicated styling constant to a shared location.This
onboardingSelectClassesconstant is identically defined inOnboardingInternalBootStep.vue(lines 558-560). Extracting it to a shared module (e.g.,onboarding/constants.tsor a composable) would prevent future divergence if the styling needs adjustment.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/Onboarding/steps/OnboardingCoreSettingsStep.vue` around lines 359 - 360, The duplicated CSS class string assigned to onboardingSelectClasses (found in OnboardingCoreSettingsStep.vue and identically in OnboardingInternalBootStep.vue) should be extracted to a shared constant/export so both components import the same value; create a new module (e.g., onboarding/constants.ts or a composable) that exports a named constant like ONBOARDING_SELECT_CLASSES, replace the inline onboardingSelectClasses declarations in both components with an import of that constant, and update usages to reference the imported symbol to prevent future drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@web/src/components/Onboarding/steps/OnboardingCoreSettingsStep.vue`:
- Around line 359-360: The duplicated CSS class string assigned to
onboardingSelectClasses (found in OnboardingCoreSettingsStep.vue and identically
in OnboardingInternalBootStep.vue) should be extracted to a shared
constant/export so both components import the same value; create a new module
(e.g., onboarding/constants.ts or a composable) that exports a named constant
like ONBOARDING_SELECT_CLASSES, replace the inline onboardingSelectClasses
declarations in both components with an import of that constant, and update
usages to reference the imported symbol to prevent future drift.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 51ac4834-6896-4c59-9985-c6c13b25d049
📒 Files selected for processing (2)
web/src/components/Onboarding/steps/OnboardingCoreSettingsStep.vueweb/src/components/Onboarding/steps/OnboardingInternalBootStep.vue
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2912373271
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const getDeviceSelectItems = (index: number): SelectItemType[] => | ||
| deviceOptions.value.map((option) => ({ | ||
| value: option.value, | ||
| label: option.label, | ||
| disabled: isDeviceDisabled(option.value, index), |
There was a problem hiding this comment.
Restore an explicit empty device option
Mapping deviceOptions directly into items removes the previously selectable empty value, and placeholder only affects display state. Because duplicate device choices are disabled via isDeviceDisabled, users with two selected slots can no longer swap assignments (for example A/B to B/A) since there is no way to temporarily clear one slot. Please add an explicit empty item (equivalent to the old <option value="">) so a slot can be unselected and reassigned.
Useful? React with 👍 / 👎.
🤖 I have created a release *beep* *boop* --- ## [4.31.0](v4.30.1...v4.31.0) (2026-03-23) ### Features * **api:** support encrypted array start inputs ([#1944](#1944)) ([018a8d5](018a8d5)) * **onboarding:** add shared loading states ([#1945](#1945)) ([776c8cc](776c8cc)) * Serverside state for onboarding display ([#1936](#1936)) ([682d51c](682d51c)) ### Bug Fixes * **api:** reconcile emhttp state without spinning disks ([#1946](#1946)) ([d3e0b95](d3e0b95)) * **onboarding:** auto-open incomplete onboarding on 7.3+ ([#1940](#1940)) ([f0241a8](f0241a8)) * **onboarding:** replace internal boot native selects ([#1942](#1942)) ([d6ea032](d6ea032)) * preserve onboarding resume state on reload ([#1941](#1941)) ([91f7fe9](91f7fe9)) * recover VM availability after reconnect ([#1947](#1947)) ([e064de7](e064de7)) * Unify callback server payloads ([#1938](#1938)) ([f58fcc0](f58fcc0)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Summary
This PR replaces the onboarding flow's remaining native dropdowns with the shared
Selectcomponent from@unraid/ui, fixes the resulting select handler typing, and aligns the onboarding select trigger styling with the rest of the form in dark mode.The work is targeted at the black-theme report where dropdowns in onboarding were either unreadable for some users or visually inconsistent with adjacent inputs.
Work Intent
Onboarding color fixes.
Problem
The onboarding flow still had a few browser-native
<select>elements in the internal boot step. That left the open dropdown menu styling up to the browser and operating system, which can produce theme mismatches that are hard to reproduce locally.After moving those controls to the shared
Selectcomponent, another issue became obvious: the shared trigger defaults looked darker than the surrounding onboarding inputs in dark mode, so the controls still felt visually off even though the popup rendering problem was addressed.What Changed
OnboardingInternalBootStepwith@unraid/uiSelectSelectItemType[]item listsSelectcomponent without depending on portal renderingSelecttrigger styling inOnboardingInternalBootStepso internal boot dropdowns match nearby inputs in dark modeSelecttrigger styling inOnboardingCoreSettingsStepso timezone, language, and theme selectors match the rest of the onboarding formWhy This Approach
Using the same shared
Selectcomponent already used elsewhere in onboarding gives us control over menu rendering and theme tokens instead of relying on browser-native popup behavior.Keeping the dark-mode styling override local to onboarding lets us make these selects visually match the onboarding form without changing the shared
Selectcomponent globally.Verification
Passed in this worktree:
pnpm --dir web type-checkpnpm --dir api type-checkpnpm --dir web lintpnpm --filter ./api lintpnpm --dir web testpnpm --filter ./api testpnpm --dir web test OnboardingInternalBootStep.test.tspnpm --dir web test OnboardingCoreSettingsStep.test.tsNotes
--no-verifybecauselint-stagedwas unavailable in the fresh worktree at that pointSummary by CodeRabbit
Refactor
Tests