Skip to content

Commit e783b0b

Browse files
refactor(frontend/settings): add Storybook stories for 6 settings components (#6853) (#7843)
Co-authored-by: Paperclip <noreply@paperclip.ing>
1 parent cc52645 commit e783b0b

6 files changed

Lines changed: 429 additions & 0 deletions
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import type { Meta } from '@storybook/vue3';
2+
import ApiKeySetupWizard from './ApiKeySetupWizard.vue';
3+
// #7273: relaxed to StoryObj<any> for render-only stories that don't match component props
4+
import type { StoryObj } from '@storybook/vue3';
5+
type Story = StoryObj<any>;
6+
7+
const meta = {
8+
title: 'Components/Settings/ApiKeySetupWizard',
9+
component: ApiKeySetupWizard,
10+
tags: ['autodocs'],
11+
argTypes: {
12+
modelValue: {
13+
control: 'boolean',
14+
description: 'Controls the visibility of the wizard modal (v-model)',
15+
},
16+
},
17+
} as Meta<typeof ApiKeySetupWizard>;
18+
19+
export default meta;
20+
21+
export const Open: Story = {
22+
args: {
23+
modelValue: true,
24+
},
25+
};
26+
27+
export const Closed: Story = {
28+
args: {
29+
modelValue: false,
30+
},
31+
};
32+
33+
export const StepOne: Story = {
34+
name: 'Step 1 – Role Selection',
35+
args: {
36+
modelValue: true,
37+
},
38+
};
39+
40+
export const WithRolesSelected: Story = {
41+
name: 'With Roles Pre-selected (render)',
42+
render: () => ({
43+
components: { ApiKeySetupWizard },
44+
data() {
45+
return { open: true };
46+
},
47+
template: `<ApiKeySetupWizard v-model="open" />`,
48+
}),
49+
};
50+
51+
export const ClosedState: Story = {
52+
name: 'Modal Hidden',
53+
render: () => ({
54+
components: { ApiKeySetupWizard },
55+
data() {
56+
return { open: false };
57+
},
58+
template: `
59+
<div>
60+
<p style="color:#aaa">Modal is closed — modelValue=false</p>
61+
<ApiKeySetupWizard v-model="open" />
62+
</div>
63+
`,
64+
}),
65+
};
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import type { Meta } from '@storybook/vue3';
2+
import FeatureFlagsSettingsPanel from './FeatureFlagsSettingsPanel.vue';
3+
// #7273: relaxed to StoryObj<any> for render-only stories that don't match component props
4+
import type { StoryObj } from '@storybook/vue3';
5+
type Story = StoryObj<any>;
6+
7+
const meta = {
8+
title: 'Components/Settings/FeatureFlagsSettingsPanel',
9+
component: FeatureFlagsSettingsPanel,
10+
tags: ['autodocs'],
11+
argTypes: {},
12+
} as Meta<typeof FeatureFlagsSettingsPanel>;
13+
14+
export default meta;
15+
16+
// The panel fetches its own data on mount; stories render the full
17+
// lifecycle with the network calls hitting the real backend (or MSW mocks).
18+
19+
export const Default: Story = {
20+
name: 'Default (fetches on mount)',
21+
render: () => ({
22+
components: { FeatureFlagsSettingsPanel },
23+
template: `<FeatureFlagsSettingsPanel />`,
24+
}),
25+
};
26+
27+
export const LoadingState: Story = {
28+
name: 'Loading State (render)',
29+
render: () => ({
30+
components: { FeatureFlagsSettingsPanel },
31+
template: `
32+
<div style="max-width:800px">
33+
<FeatureFlagsSettingsPanel />
34+
</div>
35+
`,
36+
}),
37+
};
38+
39+
export const InContainer: Story = {
40+
name: 'Inside Settings Container',
41+
render: () => ({
42+
components: { FeatureFlagsSettingsPanel },
43+
template: `
44+
<div style="padding:24px;background:var(--bg-primary,#0f0f23);min-height:400px">
45+
<FeatureFlagsSettingsPanel />
46+
</div>
47+
`,
48+
}),
49+
};
50+
51+
export const NarrowViewport: Story = {
52+
name: 'Narrow Viewport',
53+
render: () => ({
54+
components: { FeatureFlagsSettingsPanel },
55+
template: `
56+
<div style="max-width:480px;padding:16px">
57+
<FeatureFlagsSettingsPanel />
58+
</div>
59+
`,
60+
}),
61+
};
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type { Meta } from '@storybook/vue3';
2+
import LanguageSettingsPanel from './LanguageSettingsPanel.vue';
3+
// #7273: relaxed to StoryObj<any> for render-only stories that don't match component props
4+
import type { StoryObj } from '@storybook/vue3';
5+
type Story = StoryObj<any>;
6+
7+
const meta = {
8+
title: 'Components/Settings/LanguageSettingsPanel',
9+
component: LanguageSettingsPanel,
10+
tags: ['autodocs'],
11+
argTypes: {},
12+
} as Meta<typeof LanguageSettingsPanel>;
13+
14+
export default meta;
15+
16+
// LanguageSettingsPanel has no external props; it reads from usePreferences /
17+
// useAvailableLanguages composables on mount.
18+
19+
export const Default: Story = {
20+
name: 'Default',
21+
render: () => ({
22+
components: { LanguageSettingsPanel },
23+
template: `<LanguageSettingsPanel />`,
24+
}),
25+
};
26+
27+
export const InCard: Story = {
28+
name: 'In Settings Card',
29+
render: () => ({
30+
components: { LanguageSettingsPanel },
31+
template: `
32+
<div style="max-width:560px;padding:24px;background:var(--bg-primary,#0f0f23)">
33+
<LanguageSettingsPanel />
34+
</div>
35+
`,
36+
}),
37+
};
38+
39+
export const SuccessStatus: Story = {
40+
name: 'After Successful Change (render)',
41+
render: () => ({
42+
components: { LanguageSettingsPanel },
43+
template: `
44+
<div style="max-width:560px">
45+
<p style="color:#94a3b8;margin-bottom:12px;font-size:14px">
46+
Select a language from the dropdown to see the success status message.
47+
</p>
48+
<LanguageSettingsPanel />
49+
</div>
50+
`,
51+
}),
52+
};
53+
54+
export const NarrowViewport: Story = {
55+
name: 'Narrow Viewport (mobile)',
56+
render: () => ({
57+
components: { LanguageSettingsPanel },
58+
template: `
59+
<div style="max-width:360px">
60+
<LanguageSettingsPanel />
61+
</div>
62+
`,
63+
}),
64+
};
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import type { Meta } from '@storybook/vue3';
2+
import SettingsPanel from './SettingsPanel.vue';
3+
// #7273: relaxed to StoryObj<any> for render-only stories that don't match component props
4+
import type { StoryObj } from '@storybook/vue3';
5+
type Story = StoryObj<any>;
6+
7+
const meta = {
8+
title: 'Components/Settings/SettingsPanel',
9+
component: SettingsPanel,
10+
tags: ['autodocs'],
11+
argTypes: {},
12+
} as Meta<typeof SettingsPanel>;
13+
14+
export default meta;
15+
16+
// SettingsPanel is a data-container that fetches settings from the backend
17+
// on mount and uses router-view for sub-routes. Stories render the shell
18+
// with the default empty state visible until the async load resolves.
19+
20+
export const Default: Story = {
21+
name: 'Default (loading then offline)',
22+
render: () => ({
23+
components: { SettingsPanel },
24+
template: `<SettingsPanel style="height:600px" />`,
25+
}),
26+
};
27+
28+
export const InFullWidthLayout: Story = {
29+
name: 'Full-Width Layout',
30+
render: () => ({
31+
components: { SettingsPanel },
32+
template: `
33+
<div style="width:100%;height:700px;background:var(--bg-primary,#0f0f23)">
34+
<SettingsPanel />
35+
</div>
36+
`,
37+
}),
38+
};
39+
40+
export const OfflineState: Story = {
41+
name: 'Offline / Backend Unreachable',
42+
render: () => ({
43+
components: { SettingsPanel },
44+
template: `
45+
<div style="max-width:1024px;height:600px">
46+
<p style="color:#94a3b8;font-size:13px;margin-bottom:8px">
47+
When the backend is unreachable, the panel shows an offline banner.
48+
</p>
49+
<SettingsPanel />
50+
</div>
51+
`,
52+
}),
53+
};
54+
55+
export const NarrowSidebar: Story = {
56+
name: 'Narrow (480px) Sidebar Mode',
57+
render: () => ({
58+
components: { SettingsPanel },
59+
template: `
60+
<div style="max-width:480px;height:600px;overflow:hidden">
61+
<SettingsPanel />
62+
</div>
63+
`,
64+
}),
65+
};
66+
67+
export const WithUnsavedChangesContext: Story = {
68+
name: 'With Unsaved Changes Banner (context)',
69+
render: () => ({
70+
components: { SettingsPanel },
71+
template: `
72+
<div style="height:600px">
73+
<p style="color:#f59e0b;font-size:13px;margin-bottom:8px">
74+
Change a setting to trigger the save/discard action bar at the bottom.
75+
</p>
76+
<SettingsPanel />
77+
</div>
78+
`,
79+
}),
80+
};
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import type { Meta } from '@storybook/vue3';
2+
import VoiceSettingsPanel from './VoiceSettingsPanel.vue';
3+
// #7273: relaxed to StoryObj<any> for render-only stories that don't match component props
4+
import type { StoryObj } from '@storybook/vue3';
5+
type Story = StoryObj<any>;
6+
7+
const meta = {
8+
title: 'Components/Settings/VoiceSettingsPanel',
9+
component: VoiceSettingsPanel,
10+
tags: ['autodocs'],
11+
argTypes: {},
12+
} as Meta<typeof VoiceSettingsPanel>;
13+
14+
export default meta;
15+
16+
// VoiceSettingsPanel has no external props; it fetches voice profiles via
17+
// useVoiceProfiles composable on mount.
18+
19+
export const Default: Story = {
20+
name: 'Default (fetches voices on mount)',
21+
render: () => ({
22+
components: { VoiceSettingsPanel },
23+
template: `<VoiceSettingsPanel />`,
24+
}),
25+
};
26+
27+
export const InCard: Story = {
28+
name: 'In Settings Card',
29+
render: () => ({
30+
components: { VoiceSettingsPanel },
31+
template: `
32+
<div style="max-width:520px;padding:20px;background:var(--bg-secondary,#1a1a2e);border-radius:8px">
33+
<VoiceSettingsPanel />
34+
</div>
35+
`,
36+
}),
37+
};
38+
39+
export const WithAddDialogHint: Story = {
40+
name: 'Add Voice Dialog Hint',
41+
render: () => ({
42+
components: { VoiceSettingsPanel },
43+
template: `
44+
<div style="max-width:520px;padding:20px">
45+
<p style="color:#94a3b8;font-size:13px;margin-bottom:12px">
46+
Click "Add Voice Profile" to open the voice creation dialog.
47+
</p>
48+
<VoiceSettingsPanel />
49+
</div>
50+
`,
51+
}),
52+
};
53+
54+
export const NarrowViewport: Story = {
55+
name: 'Narrow Viewport (mobile)',
56+
render: () => ({
57+
components: { VoiceSettingsPanel },
58+
template: `
59+
<div style="max-width:360px;padding:12px">
60+
<VoiceSettingsPanel />
61+
</div>
62+
`,
63+
}),
64+
};
65+
66+
export const LoadingState: Story = {
67+
name: 'Loading State (render)',
68+
render: () => ({
69+
components: { VoiceSettingsPanel },
70+
template: `
71+
<div style="max-width:520px">
72+
<p style="color:#94a3b8;font-size:13px;margin-bottom:8px">
73+
Loading spinner visible before voices are fetched.
74+
</p>
75+
<VoiceSettingsPanel />
76+
</div>
77+
`,
78+
}),
79+
};

0 commit comments

Comments
 (0)