Fix settings navigation routing
Replace hash links in the settings side navigation with button-driven in-page scrolling so the hash router does not leave the settings page.
This commit is contained in:
@@ -37,7 +37,9 @@ describe('SettingsPanel', () => {
|
||||
expect(html).toContain('MoA 참조 모델');
|
||||
expect(html).toContain('패스트 모드');
|
||||
expect(html).toContain('Codex 실험 기능');
|
||||
expect(html).toContain('href="#settings-codex"');
|
||||
expect(html).toContain('data-settings-target="settings-codex"');
|
||||
expect(html).toContain('aria-controls="settings-codex"');
|
||||
expect(html).not.toContain('href="#settings-codex"');
|
||||
expect(html).toContain('/goal');
|
||||
expect(html).toContain('변경 적용');
|
||||
expect(html).toContain('불러오는 중');
|
||||
|
||||
@@ -32,31 +32,15 @@ interface AccountData {
|
||||
}
|
||||
|
||||
const SETTINGS_NAV_ITEMS = [
|
||||
{ targetId: 'settings-general', title: '일반', detail: '표시 · 언어' },
|
||||
{
|
||||
href: '#settings-general',
|
||||
title: '일반',
|
||||
detail: '표시 · 언어',
|
||||
},
|
||||
{
|
||||
href: '#settings-models',
|
||||
targetId: 'settings-models',
|
||||
title: '모델',
|
||||
detail: 'owner · reviewer · arbiter',
|
||||
},
|
||||
{
|
||||
href: '#settings-moa',
|
||||
title: 'MoA',
|
||||
detail: '참조 모델 · 연결 테스트',
|
||||
},
|
||||
{
|
||||
href: '#settings-codex',
|
||||
title: 'Codex',
|
||||
detail: 'fast mode · /goal',
|
||||
},
|
||||
{
|
||||
href: '#settings-accounts',
|
||||
title: '계정',
|
||||
detail: 'Claude · Codex',
|
||||
},
|
||||
{ targetId: 'settings-moa', title: 'MoA', detail: '참조 모델 · 연결 테스트' },
|
||||
{ targetId: 'settings-codex', title: 'Codex', detail: 'fast mode · /goal' },
|
||||
{ targetId: 'settings-accounts', title: '계정', detail: 'Claude · Codex' },
|
||||
] as const;
|
||||
|
||||
export interface SettingsPanelProps {
|
||||
@@ -171,13 +155,25 @@ function SettingsHero() {
|
||||
}
|
||||
|
||||
function SettingsNav() {
|
||||
const scrollToSection = (targetId: string) => {
|
||||
document
|
||||
.getElementById(targetId)
|
||||
?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
};
|
||||
|
||||
return (
|
||||
<aside className="settings-nav" aria-label="설정 섹션">
|
||||
{SETTINGS_NAV_ITEMS.map((item) => (
|
||||
<a href={item.href} key={item.href}>
|
||||
<button
|
||||
aria-controls={item.targetId}
|
||||
data-settings-target={item.targetId}
|
||||
key={item.targetId}
|
||||
onClick={() => scrollToSection(item.targetId)}
|
||||
type="button"
|
||||
>
|
||||
<strong>{item.title}</strong>
|
||||
<small>{item.detail}</small>
|
||||
</a>
|
||||
</button>
|
||||
))}
|
||||
</aside>
|
||||
);
|
||||
|
||||
@@ -734,14 +734,17 @@ button:disabled {
|
||||
box-shadow: var(--card-shadow);
|
||||
}
|
||||
|
||||
.settings-nav a {
|
||||
.settings-nav button {
|
||||
display: grid;
|
||||
gap: 2px;
|
||||
padding: 10px 12px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 12px;
|
||||
background: transparent;
|
||||
color: var(--muted);
|
||||
text-decoration: none;
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition:
|
||||
background 140ms ease,
|
||||
border-color 140ms ease,
|
||||
@@ -749,13 +752,19 @@ button:disabled {
|
||||
transform 140ms ease;
|
||||
}
|
||||
|
||||
.settings-nav a:hover {
|
||||
.settings-nav button:hover,
|
||||
.settings-nav button:focus-visible {
|
||||
transform: translateX(2px);
|
||||
border-color: rgba(214, 130, 88, 0.24);
|
||||
background: rgba(214, 130, 88, 0.08);
|
||||
color: var(--bg-ink);
|
||||
}
|
||||
|
||||
.settings-nav button:focus-visible {
|
||||
outline: 2px solid rgba(214, 130, 88, 0.34);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.settings-nav strong {
|
||||
color: currentColor;
|
||||
font-size: 13px;
|
||||
|
||||
Reference in New Issue
Block a user