Skip to content

refactor: remove theme section from Settings page#120

Merged
ryota-murakami merged 1 commit intomainfrom
refactor/remove-settings-theme-section
Feb 15, 2026
Merged

refactor: remove theme section from Settings page#120
ryota-murakami merged 1 commit intomainfrom
refactor/remove-settings-theme-section

Conversation

@ryota-murakami
Copy link
Contributor

@ryota-murakami ryota-murakami commented Feb 15, 2026

Summary

  • Remove theme selection grid from Settings page (14 themes + System button) — duplicated sidebar ThemeToggle functionality
  • Remove "Change Theme" command from CommandPalette
  • Update Settings loading skeleton to match Display-only layout
  • Rewrite theme E2E tests (persistence + visual application) to use sidebar ThemeToggle dropdown instead of Settings page
  • Update CLAUDE.md to prefer pnpm e2e:parallel over pnpm e2e

Aligns Settings page with SPEC.md definition (Display-only, theme via Sidebar).

Files Changed (9)

File Change
src/app/settings/SettingsClient.tsx Remove ThemeButton, theme Card, theme imports (-125 lines)
src/app/settings/loading.tsx Replace theme grid skeleton with Display Settings skeleton
src/app/settings/SettingsClient.stories.tsx Update JSDoc
src/components/CommandPalette/CommandPalette.tsx Remove "Change Theme" command + Palette icon
src/tests/unit/.../CommandPalette.test.tsx Remove "Change Theme" assertion
e2e/logged-in/settings.spec.ts Replace theme tests with display settings tests
e2e/logged-in/theme-persistence.spec.ts Rewrite: sidebar ThemeToggle dropdown
e2e/logged-in/theme-visual-application.spec.ts Rewrite: sidebar ThemeToggle dropdown
CLAUDE.md Prefer pnpm e2e:parallel for E2E

Test plan

  • pnpm typecheck — clean
  • pnpm lint — 0 warnings
  • pnpm test — 1282 unit tests pass
  • pnpm build — success
  • pnpm e2e — 276 pass, 0 fail
  • Browser verify: Settings shows Display-only (no theme grid)
  • Browser verify: Sidebar ThemeToggle dropdown works (all 14 themes)
  • Browser verify: Command Palette has no "Change Theme" entry

Summary by CodeRabbit

  • New Features

    • Added display preferences toggles for compact mode and card metadata visibility in settings.
  • Bug Fixes/Changes

    • Relocated theme selection from settings page to sidebar for improved accessibility.
    • Removed "Change Theme" from the command palette.
  • Tests

    • Updated E2E tests to reflect new theme selection workflow and display settings verification.

Theme selection was duplicated between the Settings page grid and the
sidebar ThemeToggle dropdown. Remove the Settings page theme UI to
align with SPEC.md which defines Settings as Display-only. Theme is
now exclusively accessed via the sidebar ThemeToggle.

- Remove ThemeButton component and theme card grid from SettingsClient
- Remove "Change Theme" command from CommandPalette
- Rewrite theme E2E tests to use sidebar dropdown instead of Settings
- Update loading skeleton to match Display-only layout
- Update CLAUDE.md to prefer pnpm e2e:parallel
@vercel
Copy link
Contributor

vercel bot commented Feb 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
gitbox Ready Ready Preview, Comment Feb 15, 2026 0:59am

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Feb 15, 2026

📝 Walkthrough

Walkthrough

Theme management is migrated from the Settings page to the sidebar ThemeToggle component. The Settings page now focuses exclusively on display preferences (compact mode, card metadata). E2E tests are refactored to interact with the new sidebar-based theme selection, and the "Change Theme" command is removed from the Command Palette.

Changes

Cohort / File(s) Summary
Documentation & Configuration
CLAUDE.md
Updated E2E test guidelines to promote parallel execution with sequential fallback.
Settings UI Components
src/app/settings/SettingsClient.tsx, src/app/settings/SettingsClient.stories.tsx, src/app/settings/loading.tsx
Removed ThemeButton and inline theme management from SettingsClient; refactored to Redux-driven display settings (compact mode, card metadata). Updated skeleton structure and component documentation accordingly.
Command Palette
src/components/CommandPalette/CommandPalette.tsx, src/tests/unit/components/CommandPalette/CommandPalette.test.tsx
Removed "Change Theme" command and its corresponding test assertion; simplifies action list.
E2E Theme Tests
e2e/logged-in/settings.spec.ts, e2e/logged-in/theme-persistence.spec.ts, e2e/logged-in/theme-visual-application.spec.ts
Refactored theme selection flow: introduced selectThemeFromSidebar() helper; replaced /settings navigation with /boards; adjusted test assertions to use sidebar-based theme interaction instead of settings page controls.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • PR #47: Removes per-board theme handling and shifts theme control to global/sidebar mechanism—mirrors the architectural change in this PR.
  • PR #113: Modifies CommandPalette.tsx with test coverage updates—overlaps in the same file modified here.
  • PR #116: Updates the same E2E spec files (settings.spec.ts, theme-persistence.spec.ts, theme-visual-application.spec.ts)—direct test overlap.

Poem

🎨 Theme now lives in the sidebar's embrace,
Settings shed their chromatic face,
E2E tests dance the new way,
Display toggles lead the day,
Simpler palettes make the case! 🌈

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: removing the theme section from the Settings page. It is clear, specific, and directly reflects the main objective of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/remove-settings-theme-section

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@morph-subagents
Copy link

🤖 Morph Preview Test

Looks like you hit your rate limits!

Please upgrade your limits here, or wait a few minutes and try again.

If you need help, reach out to support@morphllm.com.


Automated testing by Morph

@codecov-commenter
Copy link

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 72.21%. Comparing base (83f1682) to head (245d655).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #120      +/-   ##
==========================================
- Coverage   72.28%   72.21%   -0.08%     
==========================================
  Files         143      143              
  Lines        4236     4214      -22     
  Branches     1135     1097      -38     
==========================================
- Hits         3062     3043      -19     
+ Misses       1153     1150       -3     
  Partials       21       21              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/settings/SettingsClient.stories.tsx (1)

26-28: ⚠️ Potential issue | 🟡 Minor

Stale JSDoc — still references "theme selection".

Line 27 says "theme selection and display preferences" but theme selection was removed from this component.

Proposed fix
-/**
- * Default settings page showing theme selection and display preferences
- */
+/**
+ * Default settings page showing display preferences
+ */
🤖 Fix all issues with AI agents
In `@e2e/logged-in/settings.spec.ts`:
- Around line 25-41: In the "should display display settings" test, add a
networkidle wait after navigation so the page is fully loaded before assertions;
replace or augment the existing page.goto('/settings') call in that test with a
networkidle-aware navigation (e.g., call page.goto('/settings', { waitUntil:
'networkidle' }) or call await page.waitForLoadState('networkidle') immediately
after page.goto) so the subsequent checks on displaySection, compactToggle, and
metadataToggle run against a stable loaded page.

In `@e2e/logged-in/theme-visual-application.spec.ts`:
- Around line 51-55: The current check for an open menu (existingMenu via
page.locator('[role="menu"]')) passively waits for it to disappear which can
cause flakiness; change the logic so that if existingMenu.isVisible() resolves
true, actively close it (for example by sending an Escape key via
page.keyboard.press('Escape') or clicking outside via page.click('body')) and
then await expect(existingMenu).not.toBeVisible({ timeout: 3000 }); ensure this
flow is executed only when existingMenu.isVisible() returns true to avoid
unnecessary actions.
🧹 Nitpick comments (1)
e2e/logged-in/theme-visual-application.spec.ts (1)

44-67: Duplicated helper — extract to a shared utility.

selectThemeFromSidebar is duplicated in theme-persistence.spec.ts (lines 16-29), and the two versions diverge: this one has pre-close logic (lines 51-55) that the other lacks. This inconsistency will lead to maintenance drift.

Extract to a shared file (e.g., e2e/helpers/theme.ts) and import in both specs.

Comment on lines +25 to 41
test('should display display settings', async ({ page }) => {
await page.goto('/settings')

// Look for theme section
const themeSection = page.getByText(/theme|appearance/i)
await expect(themeSection.first()).toBeVisible()
// Look for display section
const displaySection = page.getByText(/display/i)
await expect(displaySection.first()).toBeVisible()

// Should have theme selection options
const themeOptions = page.locator(
'[data-testid="theme-option"], button:has-text("midnight"), button:has-text("sunrise")',
)
await expect(themeOptions.first()).toBeVisible()
})

test('should change theme when selecting option', async ({ page }) => {
await page.goto('/settings')

// Get initial body classes
const initialClasses = await page.locator('html').getAttribute('class')
// Should have compact mode toggle
const compactToggle = page.getByRole('switch', { name: /compact mode/i })
await expect(compactToggle).toBeVisible()

// Click a different theme option
const themeButton = page.locator(
'button:has-text("midnight"), button:has-text("graphite"), [data-testid="theme-midnight"]',
)

if (await themeButton.first().isVisible()) {
await themeButton.first().click()

// Wait for theme change by verifying class attribute changes
await expect(async () => {
const newClasses = await page.locator('html').getAttribute('class')
expect(newClasses).not.toBe(initialClasses)
}).toPass({ timeout: 5000 })
}
// Should have show card metadata toggle
const metadataToggle = page.getByRole('switch', {
name: /show card metadata/i,
})
await expect(metadataToggle).toBeVisible()
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing networkidle wait after navigation.

Per coding guidelines, E2E tests should use networkidle wait strategy for page loads. The other theme test files follow this consistently.

Proposed fix
   test('should display display settings', async ({ page }) => {
     await page.goto('/settings')
+    await page.waitForLoadState('networkidle')

As per coding guidelines, "Use networkidle wait strategy for page loads."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('should display display settings', async ({ page }) => {
await page.goto('/settings')
// Look for theme section
const themeSection = page.getByText(/theme|appearance/i)
await expect(themeSection.first()).toBeVisible()
// Look for display section
const displaySection = page.getByText(/display/i)
await expect(displaySection.first()).toBeVisible()
// Should have theme selection options
const themeOptions = page.locator(
'[data-testid="theme-option"], button:has-text("midnight"), button:has-text("sunrise")',
)
await expect(themeOptions.first()).toBeVisible()
})
test('should change theme when selecting option', async ({ page }) => {
await page.goto('/settings')
// Get initial body classes
const initialClasses = await page.locator('html').getAttribute('class')
// Should have compact mode toggle
const compactToggle = page.getByRole('switch', { name: /compact mode/i })
await expect(compactToggle).toBeVisible()
// Click a different theme option
const themeButton = page.locator(
'button:has-text("midnight"), button:has-text("graphite"), [data-testid="theme-midnight"]',
)
if (await themeButton.first().isVisible()) {
await themeButton.first().click()
// Wait for theme change by verifying class attribute changes
await expect(async () => {
const newClasses = await page.locator('html').getAttribute('class')
expect(newClasses).not.toBe(initialClasses)
}).toPass({ timeout: 5000 })
}
// Should have show card metadata toggle
const metadataToggle = page.getByRole('switch', {
name: /show card metadata/i,
})
await expect(metadataToggle).toBeVisible()
})
test('should display display settings', async ({ page }) => {
await page.goto('/settings')
await page.waitForLoadState('networkidle')
// Look for display section
const displaySection = page.getByText(/display/i)
await expect(displaySection.first()).toBeVisible()
// Should have compact mode toggle
const compactToggle = page.getByRole('switch', { name: /compact mode/i })
await expect(compactToggle).toBeVisible()
// Should have show card metadata toggle
const metadataToggle = page.getByRole('switch', {
name: /show card metadata/i,
})
await expect(metadataToggle).toBeVisible()
})
🤖 Prompt for AI Agents
In `@e2e/logged-in/settings.spec.ts` around lines 25 - 41, In the "should display
display settings" test, add a networkidle wait after navigation so the page is
fully loaded before assertions; replace or augment the existing
page.goto('/settings') call in that test with a networkidle-aware navigation
(e.g., call page.goto('/settings', { waitUntil: 'networkidle' }) or call await
page.waitForLoadState('networkidle') immediately after page.goto) so the
subsequent checks on displaySection, compactToggle, and metadataToggle run
against a stable loaded page.

Comment on lines +51 to +55
// Ensure any previously open dropdown is closed before opening a new one
const existingMenu = page.locator('[role="menu"]')
if (await existingMenu.isVisible().catch(() => false)) {
await expect(existingMenu).not.toBeVisible({ timeout: 3000 })
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Passive wait won't close an already-open dropdown — potential flakiness.

If a dropdown is already open, this block waits up to 3s for it to disappear on its own but never actively closes it (e.g., via Escape or clicking outside). If the menu stays open, the assertion fails.

Proposed fix
   // Ensure any previously open dropdown is closed before opening a new one
   const existingMenu = page.locator('[role="menu"]')
   if (await existingMenu.isVisible().catch(() => false)) {
-    await expect(existingMenu).not.toBeVisible({ timeout: 3000 })
+    await page.keyboard.press('Escape')
+    await expect(existingMenu).not.toBeVisible({ timeout: 3000 })
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Ensure any previously open dropdown is closed before opening a new one
const existingMenu = page.locator('[role="menu"]')
if (await existingMenu.isVisible().catch(() => false)) {
await expect(existingMenu).not.toBeVisible({ timeout: 3000 })
}
// Ensure any previously open dropdown is closed before opening a new one
const existingMenu = page.locator('[role="menu"]')
if (await existingMenu.isVisible().catch(() => false)) {
await page.keyboard.press('Escape')
await expect(existingMenu).not.toBeVisible({ timeout: 3000 })
}
🤖 Prompt for AI Agents
In `@e2e/logged-in/theme-visual-application.spec.ts` around lines 51 - 55, The
current check for an open menu (existingMenu via page.locator('[role="menu"]'))
passively waits for it to disappear which can cause flakiness; change the logic
so that if existingMenu.isVisible() resolves true, actively close it (for
example by sending an Escape key via page.keyboard.press('Escape') or clicking
outside via page.click('body')) and then await
expect(existingMenu).not.toBeVisible({ timeout: 3000 }); ensure this flow is
executed only when existingMenu.isVisible() returns true to avoid unnecessary
actions.

@github-actions
Copy link

🧪 E2E Coverage Report (Sharded: 12 parallel jobs)

Metric Coverage
Lines 94.39%
Functions 17.77%
Branches 16.98%
Statements 30.56%

📊 Full report available in workflow artifacts

@ryota-murakami ryota-murakami merged commit 245d655 into main Feb 15, 2026
18 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants