feat(cibg): WP-12 — CIBG Datablock for application data
Adopt the vendored CIBG Datablock (.data-block / .block-wrapper) as the way to show application data: - New app-data-block molecule (grey surface + white panel + projected rows, optional heading, stacked variant, aria-label) + stories. - data-row switches to a `div[app-data-row]` attribute selector so the <dl>'s direct child is a native <div> (HTML5.1 dl > div > dt+dd). This makes the definition list axe-clean — a bare custom element between <dl> and its dt/dd trips axe's definition-list rule regardless of display:contents, a defect the dashboard shipped live. Re-enables a11y on the data-row / review-section / registration-summary stories (previously disabled pending this rework). - review-section folds onto app-data-block (drops its hand-carried classes). - registration-summary + dashboard "Persoonsgegevens (BRP)" drop app-card and render as datablocks; both wizards' review rows + the beroep row convert to the div selector. GREEN: lint, check:tokens, 178 tests, build, build-storybook, 136 axe stories. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
33
src/app/shared/ui/data-block/data-block.component.ts
Normal file
33
src/app/shared/ui/data-block/data-block.component.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Component, input } from '@angular/core';
|
||||
import { HeadingComponent } from '@shared/ui/heading/heading.component';
|
||||
|
||||
/** Molecule: CIBG Huisstijl **Datablock** (designsystem.cibg.nl/componenten/datablock)
|
||||
— THE way to show user/application data. A grey `.data-block` surface holds a white
|
||||
`.block-wrapper` panel with a `<dl>` of projected `<app-data-row>`s. Use `stacked`
|
||||
(`.data-block--stacked`) when labels/values are long and should stack. Replaces the
|
||||
`app-card + dl` idiom for data views (the datablock carries its own surface, so it is
|
||||
NOT wrapped in an `app-card` — see WP-12). When there is no visible `heading`, pass an
|
||||
`ariaLabel` so the definition list is announced. */
|
||||
@Component({
|
||||
selector: 'app-data-block',
|
||||
imports: [HeadingComponent],
|
||||
template: `
|
||||
@if (heading()) { <app-heading [level]="level()">{{ heading() }}</app-heading> }
|
||||
<div class="data-block" [class.data-block--stacked]="stacked()">
|
||||
<div class="block-wrapper">
|
||||
<dl class="mb-0" [attr.aria-label]="ariaLabel() || null">
|
||||
<ng-content />
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
})
|
||||
export class DataBlockComponent {
|
||||
heading = input('');
|
||||
/** Heading level when `heading` is set (default h3, matching `app-card`). */
|
||||
level = input<1 | 2 | 3 | 4 | 5>(3);
|
||||
/** Stacks label above value (`.data-block--stacked`) for long content. */
|
||||
stacked = input(false);
|
||||
/** Accessible name for the `<dl>` when there is no visible heading. */
|
||||
ariaLabel = input('');
|
||||
}
|
||||
26
src/app/shared/ui/data-block/data-block.stories.ts
Normal file
26
src/app/shared/ui/data-block/data-block.stories.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { moduleMetadata } from '@storybook/angular';
|
||||
import { DataBlockComponent } from './data-block.component';
|
||||
import { DataRowComponent } from '@shared/ui/data-row/data-row.component';
|
||||
|
||||
const meta: Meta<DataBlockComponent> = {
|
||||
title: 'Molecules/Data Block',
|
||||
component: DataBlockComponent,
|
||||
decorators: [moduleMetadata({ imports: [DataRowComponent] })],
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
template: `
|
||||
<app-data-block [heading]="heading" [stacked]="stacked" [ariaLabel]="ariaLabel">
|
||||
<div app-data-row key="BIG-nummer" value="19012345601"></div>
|
||||
<div app-data-row key="Naam" value="J. de Vries"></div>
|
||||
<div app-data-row key="Beroep" value="Verpleegkundige"></div>
|
||||
<div app-data-row key="Registratiedatum" value="1 maart 2018"></div>
|
||||
</app-data-block>`,
|
||||
}),
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<DataBlockComponent>;
|
||||
|
||||
export const Default: Story = { args: { heading: 'Persoonsgegevens (BRP)' } };
|
||||
export const ZonderKop: Story = { args: { ariaLabel: 'Registratiegegevens' } };
|
||||
export const Stacked: Story = { args: { heading: 'Toelichting', stacked: true } };
|
||||
@@ -1,12 +1,17 @@
|
||||
import { Component, input } from '@angular/core';
|
||||
|
||||
/** Molecule: one key/value row in a CIBG Huisstijl "controlestap" data summary
|
||||
(`dt.col-md-4` / `dd.col-md-8`). `display:contents` so the dt/dd become direct
|
||||
flex children of the parent `<dl class="row">` — the Bootstrap grid classes
|
||||
need that to lay out correctly. Wrap several in a `<dl class="row mb-0">`. */
|
||||
/** Molecule: one key/value row inside a CIBG Huisstijl **Datablock** (see
|
||||
`data-block.component.ts`) — the row primitive of the datablock/`controlestap`
|
||||
data summary. Used on a `<div>` so the `<dl>`'s direct child is a native element
|
||||
(the HTML5.1 `dl > div > dt + dd` grouping), which keeps the definition list
|
||||
axe-clean — a bare custom element between `<dl>` and its `<dt>/<dd>` trips axe's
|
||||
definition-list rule regardless of `display:contents`. The host is the Bootstrap
|
||||
`.row`; `dt.col-md-4`/`dd.col-md-8` give the label/value widths. Wrap several in a
|
||||
`<dl class="mb-0">` (a `<app-data-block>`). Project custom content (e.g. a badge)
|
||||
into the `<dd>` via `<ng-content>`. */
|
||||
@Component({
|
||||
selector: 'app-data-row',
|
||||
styles: [`:host{display:contents}`],
|
||||
selector: 'div[app-data-row]',
|
||||
host: { class: 'row' },
|
||||
template: `
|
||||
<dt class="col-md-4">{{ key() }}</dt>
|
||||
<dd class="col-md-8"><ng-content>{{ value() }}</ng-content></dd>
|
||||
|
||||
@@ -6,16 +6,11 @@ const meta: Meta<DataRowComponent> = {
|
||||
component: DataRowComponent,
|
||||
render: (args) => ({
|
||||
props: args,
|
||||
// Rows are dt.col-md-4/dd.col-md-8 flex children of a Bootstrap .row dl (CIBG controlestap).
|
||||
template: `<dl class="row mb-0"><app-data-row [key]="key" [value]="value" /></dl>`,
|
||||
// A row is a `.row` <div> grouping dt.col-md-4/dd.col-md-8 inside the datablock <dl>
|
||||
// (HTML5.1 dl > div > dt+dd — a native div child keeps the definition list axe-clean).
|
||||
template: `<dl class="mb-0"><div app-data-row [key]="key" [value]="value"></div></dl>`,
|
||||
}),
|
||||
args: { key: 'BIG-nummer', value: '19012345601' },
|
||||
parameters: {
|
||||
// Structural: app-data-row's host sits between the <dl> and its <dt>/<dd> —
|
||||
// axe's definition-list rule needs them adjacent regardless of `display:contents`.
|
||||
// WP-12 (CIBG Datablock) reworks this markup; see docs/backlog/WP-12-datablock.md.
|
||||
a11y: { disable: true },
|
||||
},
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<DataRowComponent>;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Component, input, output } from '@angular/core';
|
||||
import { DataBlockComponent } from '@shared/ui/data-block/data-block.component';
|
||||
|
||||
/** Molecule: one section of a CIBG Huisstijl "controlestap" (wizard review step) —
|
||||
a heading with a "Wijzigen" link, and the section's `<app-data-row>`s in a
|
||||
`.data-block > .block-wrapper > dl.row`. Domain-free; the caller supplies the
|
||||
a heading with a "Wijzigen" link, and the section's `<app-data-row>`s in a CIBG
|
||||
Datablock (composes `<app-data-block>`). Domain-free; the caller supplies the
|
||||
heading and decides what "Wijzigen" does (typically jump back to a step). */
|
||||
@Component({
|
||||
selector: 'app-review-section',
|
||||
imports: [DataBlockComponent],
|
||||
template: `
|
||||
<div class="d-flex">
|
||||
<h2>{{ heading() }}</h2>
|
||||
@@ -15,13 +17,7 @@ import { Component, input, output } from '@angular/core';
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="data-block">
|
||||
<div class="block-wrapper">
|
||||
<dl class="row mb-0">
|
||||
<ng-content />
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
<app-data-block><ng-content /></app-data-block>
|
||||
`,
|
||||
})
|
||||
export class ReviewSectionComponent {
|
||||
|
||||
@@ -11,16 +11,11 @@ const meta: Meta<ReviewSectionComponent> = {
|
||||
props: args,
|
||||
template: `
|
||||
<app-review-section [heading]="heading" [editLabel]="editLabel" [editAriaLabel]="editAriaLabel" [showEdit]="showEdit">
|
||||
<app-data-row key="Straat en huisnummer" value="Dorpsstraat 1" />
|
||||
<app-data-row key="Postcode" value="1234 AB" />
|
||||
<app-data-row key="Woonplaats" value="Utrecht" />
|
||||
<div app-data-row key="Straat en huisnummer" value="Dorpsstraat 1"></div>
|
||||
<div app-data-row key="Postcode" value="1234 AB"></div>
|
||||
<div app-data-row key="Woonplaats" value="Utrecht"></div>
|
||||
</app-review-section>`,
|
||||
}),
|
||||
parameters: {
|
||||
// Structural: app-data-row's host sits between the <dl> and its <dt>/<dd> —
|
||||
// fixed by the WP-12 Datablock rework. See docs/backlog/WP-12-datablock.md.
|
||||
a11y: { disable: true },
|
||||
},
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<ReviewSectionComponent>;
|
||||
|
||||
Reference in New Issue
Block a user