feat(fp): WP-15 — missing stories: shell + brief components
Add the 7 stories CLAUDE.md's testing rule ("UI is exercised via Storybook
stories") was missing: shared/layout/shell (Design System/Templates/Shell)
and all six previously-unstoried brief components (passage-picker,
rejection-comments, diagnostics-panel, letter-block, letter-preview,
letter-section — Domein/Brief/*), each with a default state plus the
meaningful variants (locked/editable, findings/clean, show/entry, etc).
Every *.component.ts in the repo now has a co-located story; *.page.ts
files stay unstoried, matching the existing norm.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { Diagnostic } from '@brief/domain/placeholders';
|
||||
import { DiagnosticsPanelComponent } from './diagnostics-panel.component';
|
||||
|
||||
const location = { blockId: 'local-2', paragraphIndex: 0, nodeIndex: 1 };
|
||||
|
||||
const errorAndWarning: Diagnostic[] = [
|
||||
{
|
||||
severity: 'error',
|
||||
code: 'unknown-placeholder',
|
||||
placeholderKey: 'onbekend_veld',
|
||||
location,
|
||||
message: 'Onbekend veld "onbekend_veld" — controleer de spelling.',
|
||||
},
|
||||
{
|
||||
severity: 'warning',
|
||||
code: 'unresolved-at-send',
|
||||
placeholderKey: 'reden_besluit',
|
||||
location,
|
||||
message: '"Reden besluit" is nog niet ingevuld.',
|
||||
},
|
||||
];
|
||||
|
||||
const meta: Meta<DiagnosticsPanelComponent> = {
|
||||
title: 'Domein/Brief/Diagnostics Panel',
|
||||
component: DiagnosticsPanelComponent,
|
||||
args: { locate: () => {} },
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<DiagnosticsPanelComponent>;
|
||||
|
||||
export const Findings: Story = { args: { diagnostics: errorAndWarning } };
|
||||
export const Clean: Story = { args: { diagnostics: [] } };
|
||||
47
src/app/brief/ui/letter-block/letter-block.stories.ts
Normal file
47
src/app/brief/ui/letter-block/letter-block.stories.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { LetterBlock } from '@brief/domain/brief';
|
||||
import { LetterBlockComponent } from './letter-block.component';
|
||||
|
||||
const passageBlock: LetterBlock = {
|
||||
type: 'passage',
|
||||
blockId: 'local-1',
|
||||
sourcePassageId: 'p1',
|
||||
sourceVersion: 1,
|
||||
edited: false,
|
||||
content: { paragraphs: [{ nodes: [{ type: 'text', text: 'Geachte heer/mevrouw,' }] }] },
|
||||
};
|
||||
|
||||
const freeTextBlock: LetterBlock = {
|
||||
type: 'freeText',
|
||||
blockId: 'local-2',
|
||||
content: {
|
||||
paragraphs: [
|
||||
{
|
||||
nodes: [
|
||||
{ type: 'text', text: 'Wij hebben besloten om reden ' },
|
||||
{ type: 'placeholder', key: 'reden_besluit' },
|
||||
{ type: 'text', text: '.' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const placeholders = [{ key: 'reden_besluit', label: 'Reden besluit' }];
|
||||
|
||||
const meta: Meta<LetterBlockComponent> = {
|
||||
title: 'Domein/Brief/Letter Block',
|
||||
component: LetterBlockComponent,
|
||||
args: {
|
||||
block: passageBlock,
|
||||
placeholders,
|
||||
contentChanged: () => {},
|
||||
removed: () => {},
|
||||
moved: () => {},
|
||||
},
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<LetterBlockComponent>;
|
||||
|
||||
export const ReadOnlyPassage: Story = { args: { editable: false } };
|
||||
export const EditableFreeText: Story = { args: { block: freeTextBlock, editable: true } };
|
||||
88
src/app/brief/ui/letter-preview/letter-preview.stories.ts
Normal file
88
src/app/brief/ui/letter-preview/letter-preview.stories.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { Brief } from '@brief/domain/brief';
|
||||
import { Diagnostic } from '@brief/domain/placeholders';
|
||||
import { LetterPreviewComponent } from './letter-preview.component';
|
||||
|
||||
const brief: Brief = {
|
||||
briefId: 'b1',
|
||||
beroep: 'arts',
|
||||
templateId: 't1',
|
||||
drafterId: 'demo-drafter',
|
||||
status: { tag: 'draft' },
|
||||
placeholders: [
|
||||
{ key: 'naam_zorgverlener', label: 'Naam zorgverlener', autoResolvable: true },
|
||||
{ key: 'reden_besluit', label: 'Reden besluit', autoResolvable: false },
|
||||
],
|
||||
sections: [
|
||||
{
|
||||
sectionKey: 'aanhef',
|
||||
title: 'Aanhef',
|
||||
required: true,
|
||||
locked: true,
|
||||
blocks: [
|
||||
{
|
||||
type: 'passage',
|
||||
blockId: 'local-1',
|
||||
sourcePassageId: 'p1',
|
||||
sourceVersion: 1,
|
||||
edited: false,
|
||||
content: {
|
||||
paragraphs: [
|
||||
{
|
||||
nodes: [
|
||||
{ type: 'text', text: 'Geachte heer/mevrouw ' },
|
||||
{ type: 'placeholder', key: 'naam_zorgverlener' },
|
||||
{ type: 'text', text: ',' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
sectionKey: 'kern',
|
||||
title: 'Kern van het besluit',
|
||||
required: true,
|
||||
locked: false,
|
||||
blocks: [
|
||||
{
|
||||
type: 'freeText',
|
||||
blockId: 'local-2',
|
||||
content: {
|
||||
paragraphs: [
|
||||
{
|
||||
nodes: [
|
||||
{ type: 'text', text: 'Wij hebben besloten om reden ' },
|
||||
{ type: 'placeholder', key: 'reden_besluit' },
|
||||
{ type: 'text', text: '.' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const diagnostics: Diagnostic[] = [
|
||||
{
|
||||
severity: 'warning',
|
||||
code: 'unresolved-at-send',
|
||||
placeholderKey: 'reden_besluit',
|
||||
location: { blockId: 'local-2', paragraphIndex: 0, nodeIndex: 1 },
|
||||
message: '"Reden besluit" is nog niet ingevuld.',
|
||||
},
|
||||
];
|
||||
|
||||
const meta: Meta<LetterPreviewComponent> = {
|
||||
title: 'Domein/Brief/Letter Preview',
|
||||
component: LetterPreviewComponent,
|
||||
args: { brief, diagnostics },
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<LetterPreviewComponent>;
|
||||
|
||||
export const Default: Story = {};
|
||||
export const ZonderBevindingen: Story = { args: { diagnostics: [] } };
|
||||
55
src/app/brief/ui/letter-section/letter-section.stories.ts
Normal file
55
src/app/brief/ui/letter-section/letter-section.stories.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { LetterSection, LibraryPassage } from '@brief/domain/brief';
|
||||
import { LetterSectionComponent } from './letter-section.component';
|
||||
|
||||
const section: LetterSection = {
|
||||
sectionKey: 'kern',
|
||||
title: 'Kern van het besluit',
|
||||
required: true,
|
||||
locked: false,
|
||||
blocks: [
|
||||
{
|
||||
type: 'freeText',
|
||||
blockId: 'local-2',
|
||||
content: {
|
||||
paragraphs: [
|
||||
{
|
||||
nodes: [
|
||||
{ type: 'text', text: 'Wij hebben besloten om reden ' },
|
||||
{ type: 'placeholder', key: 'reden_besluit' },
|
||||
{ type: 'text', text: '.' },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const emptySection: LetterSection = { ...section, blocks: [] };
|
||||
|
||||
const passages: LibraryPassage[] = [
|
||||
{
|
||||
passageId: 'p2',
|
||||
scope: 'beroep',
|
||||
beroep: 'arts',
|
||||
sectionKey: 'kern',
|
||||
label: 'Toelichting arts',
|
||||
version: 1,
|
||||
content: { paragraphs: [{ nodes: [{ type: 'text', text: 'Als arts ...' }] }] },
|
||||
},
|
||||
];
|
||||
|
||||
const placeholders = [{ key: 'reden_besluit', label: 'Reden besluit' }];
|
||||
|
||||
const meta: Meta<LetterSectionComponent> = {
|
||||
title: 'Domein/Brief/Letter Section',
|
||||
component: LetterSectionComponent,
|
||||
args: { section, availablePassages: passages, placeholders, edit: () => {} },
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<LetterSectionComponent>;
|
||||
|
||||
export const ReadOnly: Story = { args: { editable: false } };
|
||||
export const Editable: Story = { args: { editable: true } };
|
||||
export const EditableEmpty: Story = { args: { section: emptySection, editable: true } };
|
||||
37
src/app/brief/ui/passage-picker/passage-picker.stories.ts
Normal file
37
src/app/brief/ui/passage-picker/passage-picker.stories.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { LibraryPassage } from '@brief/domain/brief';
|
||||
import { PassagePickerComponent } from './passage-picker.component';
|
||||
|
||||
const passages: LibraryPassage[] = [
|
||||
{
|
||||
passageId: 'p1',
|
||||
scope: 'global',
|
||||
sectionKey: 'aanhef',
|
||||
label: 'Standaard aanhef',
|
||||
version: 1,
|
||||
content: { paragraphs: [{ nodes: [{ type: 'text', text: 'Geachte heer/mevrouw,' }] }] },
|
||||
},
|
||||
{
|
||||
passageId: 'p2',
|
||||
scope: 'beroep',
|
||||
beroep: 'arts',
|
||||
sectionKey: 'aanhef',
|
||||
label: 'Aanhef, arts-specifiek',
|
||||
version: 1,
|
||||
content: { paragraphs: [{ nodes: [{ type: 'text', text: 'Geachte collega,' }] }] },
|
||||
},
|
||||
];
|
||||
|
||||
const meta: Meta<PassagePickerComponent> = {
|
||||
title: 'Domein/Brief/Passage Picker',
|
||||
component: PassagePickerComponent,
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
template: `<app-passage-picker [passages]="passages" (insert)="insert($event)" />`,
|
||||
}),
|
||||
args: { passages, insert: () => {} },
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<PassagePickerComponent>;
|
||||
|
||||
export const Default: Story = {};
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { RejectionCommentsComponent } from './rejection-comments.component';
|
||||
|
||||
const meta: Meta<RejectionCommentsComponent> = {
|
||||
title: 'Domein/Brief/Rejection Comments',
|
||||
component: RejectionCommentsComponent,
|
||||
args: { reject: () => {} },
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<RejectionCommentsComponent>;
|
||||
|
||||
export const Show: Story = {
|
||||
args: { mode: 'show', comments: 'Graag de aanhef formeler.' },
|
||||
};
|
||||
export const Entry: Story = { args: { mode: 'entry' } };
|
||||
export const EntryBusy: Story = { args: { mode: 'entry', busy: true } };
|
||||
16
src/app/shared/layout/shell/shell.stories.ts
Normal file
16
src/app/shared/layout/shell/shell.stories.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { applicationConfig } from '@storybook/angular';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { ShellComponent } from './shell.component';
|
||||
|
||||
const meta: Meta<ShellComponent> = {
|
||||
title: 'Design System/Templates/Shell',
|
||||
component: ShellComponent,
|
||||
decorators: [applicationConfig({ providers: [provideRouter([])] })],
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<ShellComponent>;
|
||||
|
||||
// No route matches, so <router-outlet> renders nothing — this story is about the
|
||||
// persistent chrome (skip-link, header, footer), not routed page content.
|
||||
export const Default: Story = {};
|
||||
Reference in New Issue
Block a user