Skip to content

Commit d21e085

Browse files
committed
fix: enforce mobile settings group consistency
1 parent 43b6a6e commit d21e085

2 files changed

Lines changed: 48 additions & 29 deletions

File tree

packages/web/src/features/settings/components/settings-page.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ describe("SettingsPage", () => {
286286
expect(within(mobileRoot).getByText("界面与交互")).toBeInTheDocument();
287287
expect(document.querySelector(".settings-mobile-root-hero")).toBeNull();
288288

289-
const buttons = screen.getAllByRole("button");
289+
const buttons = within(mobileRoot).getAllByRole("button");
290290
const labels = buttons.map((button) => button.getAttribute("aria-label")).filter(Boolean);
291291

292292
expect(labels).toEqual(expect.arrayContaining(["通用", "Agents", "外观", "快捷键"]));

packages/web/src/features/settings/components/settings-page.tsx

Lines changed: 47 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,32 @@ const MOBILE_SETTINGS_GROUPS = [
152152
sections: readonly SettingsSection[];
153153
}[];
154154

155+
function resolveMobileSettingsGroups(
156+
availableSections: readonly {
157+
id: SettingsSection;
158+
labelKey: string;
159+
iconSemantic: Parameters<typeof ThemedIcon>[0]["semantic"];
160+
}[]
161+
) {
162+
const sectionsById = new Map(availableSections.map((section) => [section.id, section]));
163+
const groupedSectionIds = MOBILE_SETTINGS_GROUPS.flatMap((group) => group.sections);
164+
165+
if (groupedSectionIds.length !== availableSections.length) {
166+
throw new Error("Mobile settings groups are out of sync with available sections.");
167+
}
168+
169+
for (const sectionId of groupedSectionIds) {
170+
if (!sectionsById.has(sectionId)) {
171+
throw new Error(`Missing mobile settings section mapping for "${sectionId}".`);
172+
}
173+
}
174+
175+
return MOBILE_SETTINGS_GROUPS.map((group) => ({
176+
titleKey: group.titleKey,
177+
sections: group.sections.map((sectionId) => sectionsById.get(sectionId)!),
178+
}));
179+
}
180+
155181
/**
156182
* Settings Page
157183
*
@@ -464,39 +490,32 @@ export function SettingsPage() {
464490
const renderMobileRoot = () => (
465491
<main className="settings-content settings-content--mobile-root">
466492
<div className="settings-mobile-root" data-testid="settings-mobile-root">
467-
{MOBILE_SETTINGS_GROUPS.map((group) => (
493+
{resolveMobileSettingsGroups(availableSections).map((group) => (
468494
<section key={group.titleKey} className="settings-mobile-group">
469495
<h2 className="settings-mobile-group__title">{t(group.titleKey)}</h2>
470496
<div className="settings-mobile-group__list">
471-
{group.sections.map((sectionId) => {
472-
const section = availableSections.find((entry) => entry.id === sectionId);
473-
if (!section) {
474-
return null;
475-
}
476-
477-
return (
478-
<button
479-
key={section.id}
480-
type="button"
481-
className="settings-mobile-item"
482-
aria-label={t(section.labelKey)}
483-
onClick={() => setNavigationState({ kind: "detail", section: section.id })}
484-
>
485-
<span className="settings-mobile-item__icon-shell" aria-hidden="true">
486-
<span className="settings-mobile-item__icon">
487-
<ThemedIcon semantic={section.iconSemantic} size={18} />
488-
</span>
497+
{group.sections.map((section) => (
498+
<button
499+
key={section.id}
500+
type="button"
501+
className="settings-mobile-item"
502+
aria-label={t(section.labelKey)}
503+
onClick={() => setNavigationState({ kind: "detail", section: section.id })}
504+
>
505+
<span className="settings-mobile-item__icon-shell" aria-hidden="true">
506+
<span className="settings-mobile-item__icon">
507+
<ThemedIcon semantic={section.iconSemantic} size={18} />
489508
</span>
490-
<span className="settings-mobile-item__copy">
491-
<span className="settings-mobile-item__label">{t(section.labelKey)}</span>
492-
<span className="settings-mobile-item__hint">
493-
{t(getMobileSectionHintKey(section.id))}
494-
</span>
509+
</span>
510+
<span className="settings-mobile-item__copy">
511+
<span className="settings-mobile-item__label">{t(section.labelKey)}</span>
512+
<span className="settings-mobile-item__hint">
513+
{t(getMobileSectionHintKey(section.id))}
495514
</span>
496-
<ChevronRight size={16} className="settings-mobile-item__arrow" />
497-
</button>
498-
);
499-
})}
515+
</span>
516+
<ChevronRight size={16} className="settings-mobile-item__arrow" />
517+
</button>
518+
))}
500519
</div>
501520
</section>
502521
))}

0 commit comments

Comments
 (0)