feat(brief): WP-28 demo script, e2e spec, and story gap-fill
Closes phase 6 (Brief v2): a demo script mapping shipped scenarios to URL+click paths (no Brief v2 PRD ever existed to translate one from — written directly against the code instead), one e2e spec covering compose→approve→send and admin republish→drafter-sees-it, and Storybook state gaps (rejection diff, read-only viewer, org logo, upload rejection) that prior WPs left uncovered. Flags passage-picker as dead code, superseded by besluit-panel. npm run e2e is not verified green in this sandbox — see WP-28's Deviations section; the pre-existing, unmodified smoke.spec.ts fails identically here, pointing at a sandbox rendering issue rather than a regression. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -149,3 +149,15 @@ export const WithDiff: Story = {
|
||||
export const PageBreak: Story = {
|
||||
args: { editableRegions: 'none', brief: longBrief, diagnostics: [] },
|
||||
};
|
||||
|
||||
// Inline SVG so the story needs no backend/upload round-trip (WP-26 logo upload).
|
||||
const sampleLogo =
|
||||
'data:image/svg+xml;utf8,' +
|
||||
encodeURIComponent(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="120" height="40"><rect width="120" height="40" fill="#003366"/><text x="60" y="25" font-size="14" fill="white" text-anchor="middle">CIBG</text></svg>',
|
||||
);
|
||||
|
||||
/** Published org logo (WP-26 AC2): the letterhead shows it above the org name. */
|
||||
export const MetLogo: Story = {
|
||||
args: { editableRegions: 'none', diagnostics: [], logoUrl: sampleLogo },
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import { LetterComposerComponent } from './letter-composer.component';
|
||||
import { Brief, BriefDecisions, BriefStatus, LibraryPassage } from '@brief/domain/brief';
|
||||
import { allDiagnostics } from '@brief/domain/brief';
|
||||
import { OrgTemplate } from '@brief/domain/org-template';
|
||||
import { BlockDiffKind } from '@brief/domain/brief-diff';
|
||||
|
||||
const orgTemplate: OrgTemplate = {
|
||||
subOrgId: 'cibg-registers',
|
||||
@@ -117,17 +118,29 @@ function brief(status: BriefStatus): Brief {
|
||||
};
|
||||
}
|
||||
|
||||
const render = (b: Brief, decisions: BriefDecisions) => ({
|
||||
const render = (
|
||||
b: Brief,
|
||||
decisions: BriefDecisions,
|
||||
extra: {
|
||||
blockDiffs?: ReadonlyMap<string, BlockDiffKind>;
|
||||
removedCount?: number;
|
||||
logoUrl?: string | null;
|
||||
} = {},
|
||||
) => ({
|
||||
props: {
|
||||
brief: b,
|
||||
orgTemplate,
|
||||
diagnostics: allDiagnostics(b),
|
||||
...decisions,
|
||||
busy: false,
|
||||
blockDiffs: extra.blockDiffs ?? new Map<string, BlockDiffKind>(),
|
||||
removedCount: extra.removedCount ?? 0,
|
||||
logoUrl: extra.logoUrl ?? null,
|
||||
},
|
||||
template: `<app-letter-composer [brief]="brief" [orgTemplate]="orgTemplate"
|
||||
template: `<app-letter-composer [brief]="brief" [orgTemplate]="orgTemplate" [logoUrl]="logoUrl"
|
||||
[diagnostics]="diagnostics" [canApprove]="canApprove" [canReject]="canReject"
|
||||
[canSend]="canSend" [busy]="busy"></app-letter-composer>`,
|
||||
[canSend]="canSend" [busy]="busy" [blockDiffs]="blockDiffs"
|
||||
[removedCount]="removedCount"></app-letter-composer>`,
|
||||
});
|
||||
|
||||
const meta: Meta<LetterComposerComponent> = {
|
||||
@@ -167,3 +180,43 @@ export const Sent: Story = {
|
||||
canRevealBigNummer: false,
|
||||
}),
|
||||
};
|
||||
|
||||
/** Approver's "Toon wijzigingen" (WP-27): a resubmitted letter with blocks changed,
|
||||
added and removed since the last rejection. */
|
||||
export const RejectionDiff: Story = {
|
||||
render: () =>
|
||||
render(
|
||||
brief({
|
||||
tag: 'submitted',
|
||||
submittedBy: 'demo-drafter',
|
||||
submittedAt: '2026-07-02',
|
||||
}),
|
||||
{
|
||||
canEdit: false,
|
||||
canApprove: true,
|
||||
canReject: true,
|
||||
canSend: false,
|
||||
canRevealBigNummer: false,
|
||||
},
|
||||
{
|
||||
blockDiffs: new Map<string, BlockDiffKind>([
|
||||
['local-2', 'changed'],
|
||||
['local-3', 'added'],
|
||||
]),
|
||||
removedCount: 1,
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
/** A pure viewer (e.g. admin) has no approve/reject/send capability on this letter —
|
||||
the read-only notice, not a broken-looking editor. */
|
||||
export const AlleenLezen: Story = {
|
||||
render: () =>
|
||||
render(brief({ tag: 'submitted', submittedBy: 'demo-drafter', submittedAt: '2026-07-01' }), {
|
||||
canEdit: false,
|
||||
canApprove: false,
|
||||
canReject: false,
|
||||
canSend: false,
|
||||
canRevealBigNummer: false,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -79,3 +79,26 @@ export const Invalid: Story = {
|
||||
export const NoHistory: Story = {
|
||||
args: { history: [], publishedVersion: 0 },
|
||||
};
|
||||
|
||||
// Inline SVG so the story needs no backend/upload round-trip.
|
||||
const sampleLogo =
|
||||
'data:image/svg+xml;utf8,' +
|
||||
encodeURIComponent(
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" width="120" height="40"><rect width="120" height="40" fill="#003366"/><text x="60" y="25" font-size="14" fill="white" text-anchor="middle">CIBG</text></svg>',
|
||||
);
|
||||
|
||||
/** Published logo (WP-26 AC2): the letterhead canvas shows it above the org name. */
|
||||
export const MetLogo: Story = {
|
||||
args: { logoUrl: sampleLogo },
|
||||
};
|
||||
|
||||
/** Client-side upload rejection (existing `rejectReason`, WP-26 AC5) — type/size caught
|
||||
before the file ever reaches the backend. */
|
||||
export const LogoUploadFout: Story = {
|
||||
args: {
|
||||
uploadState: {
|
||||
...uploadWithCategory,
|
||||
rejections: { 'org-logo': 'Alleen PNG of JPEG, maximaal 2 MB.' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -8,7 +8,11 @@ import { LibraryPassage } from '@brief/domain/brief';
|
||||
|
||||
/** Molecule: multi-select list of the section's library passages. One "Voeg toe"
|
||||
inserts ALL checked passages at once (a single message upstream) — there is no
|
||||
single-insert path. Presentational: emits the chosen passages in list order. */
|
||||
single-insert path. Presentational: emits the chosen passages in list order.
|
||||
|
||||
Superseded by `besluit-panel` (WP-27's guided drafting): no consumer left in
|
||||
`src/app` outside its own story (WP-28 audit). Kept for now rather than deleted
|
||||
in-flight of an unrelated WP; a future cleanup can remove it. */
|
||||
@Component({
|
||||
selector: 'app-passage-picker',
|
||||
imports: [FormsModule, CheckboxComponent, ButtonComponent, TextInputComponent],
|
||||
|
||||
Reference in New Issue
Block a user