fix(ui): unify heading help, table sizing and navigation contracts

Verified with the coordinated workspace changes by devkit full run
2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed).
This shared UI pass does not mark the individual module reviews complete.
This commit is contained in:
2026-09-09 02:03:16 +02:00
parent 6591aaa3fd
commit 6d37aa527f
54 changed files with 1728 additions and 117 deletions
+40
View File
@@ -6,6 +6,23 @@ occupies its normal space. `Card bodyLayout="table"` provides an explicit
zero-inset surface, including with loading wrappers and padded notices. The
Organizations/IDM browser fixtures assert row geometry, not just outer shells.
The containing card removes its body padding; tables stay at `width: 100%` and
`max-width: 100%`. Do not cancel padding with negative margins or an expanded
`calc()` width: the table's maximum width correctly clamps that expansion, and
the result is a visible gap. Existing cards with only a DataGrid, admin table
surface, or connection tree inherit the same zero-inset geometry, directly or
through a `LoadingFrame`. Its loading overlay does not count as content and
stays within the table body. Mixed prose/form content keeps the usual insets;
choose `bodyLayout="table"` explicitly when combining a full-width table with
separately padded notices. Ordinary standalone tables retain their border.
Deutsch: Tabellenkarten entfernen den Innenabstand am Karteninhalt. Die Tabelle
bleibt bei 100 Prozent Breite; negative Ränder und verbreiternde `calc()`-Werte
sind nicht erforderlich. Das gilt auch während des Ladens. Karten mit Text
oder Formularen behalten ihren Innenabstand. Für eine randlose Tabelle neben
einem separat gepolsterten Hinweis ist `bodyLayout="table"` ausdrücklich zu
setzen; alleinstehende Tabellen behalten ihren Rahmen.
`DataGrid` turns every declared track into a deterministic pixel layout after
its container has a measurable width. The same contract is used on initial
layout, container resize, persisted-layout restore, and pointer/keyboard resize.
@@ -23,6 +40,21 @@ layout, container resize, persisted-layout restore, and pointer/keyboard resize.
- `maxWidth` bounds direct user growth and free/constrained compensation. In a
cover layout it is a preferred maximum: passive tracks may exceed it when
that is necessary to keep the table flush with its container.
- `preferredMaxWidth` is a presentation-only automatic-fit ceiling. It retains
sensible initial proportions without limiting direct user resizing or
right-side resize compensation. A declared `maxWidth` still wins as the
manual limit. Cover may exceed preferred ceilings after other automatic
sizing targets are exhausted, preserving the no-blank-filler contract.
- Do not give expandable text columns arbitrary `maxWidth` values merely to
control their initial appearance. At that limit, a resize handle can shrink
but cannot grow, regardless of whether the next column is resizable. Prefer
weighted `minmax(..., Nfr)` declarations with `preferredMaxWidth` when needed;
ordinary cover-mode growth may
create horizontal overflow without changing fixed neighbors. Campaign's
Recipient(s), Delivery and configurable recipient-field columns follow this
contract. The existing signature format is unchanged for other columns;
introducing a preferred ceiling invalidates only that grid's previous width
contract, while sort/filter preferences remain intact.
- `columnType: "actions"` marks a custom action/control column. Canonical
`TableActionGroup` content is recognized automatically, even in existing
column declarations. Use `sticky: "end"` for the normal row-action surface.
@@ -71,6 +103,14 @@ den laufenden Ziehvorgang. Am fokussierten Trenner ändern Links/Rechts die Brei
um 10 px, mit Umschalt um 40 px. Eingabe oder Doppelklick setzt die persönliche
Breite dieser Spalte zurück. Schmale Aktionenspalten umbrechen ihre Schaltflächen;
breite Tabellen bleiben horizontal scrollbar.
Eine feste Maximalbreite stoppt das Vergrößern unabhängig von Nachbarspalten.
`preferredMaxWidth` begrenzt dagegen nur die automatische Anfangsaufteilung,
nicht persönliche Breiten oder den Ausgleich beim Ziehen. Empfänger,
Zustellung und konfigurierte Empfängerfelder verwenden diese Darstellungsgrenze.
Bei Bedarf verbreitert sich die Tabelle und bleibt scrollbar; persönliche
Breiten ändern keine Kampagnendaten. Am rechten Scrollrand kann eine breite
linke Spalte wieder verkleinert werden, weil rechte Textspalten über ihre
bevorzugte Anfangsbreite hinaus Platz aufnehmen dürfen.
## Layout Modes
+12
View File
@@ -137,3 +137,15 @@ Browser acceptance is part of the focused workspace gate and can be run alone:
cd /mnt/DATA/git/govoplan-core/webui
npm run test:conformance
```
For shared component regressions, `npm run test:components -- page-layout
documentation-help layout-primitives` compiles the component test configuration
once and executes the selected suites. With no suite names it runs every shared
component suite. Existing single-suite aliases such as `npm run test:page-layout`
remain supported. Every invocation owns an ignored, uniquely named temporary
output directory and removes it on completion, failure, or handled interruption;
parallel invocations cannot delete each other's compiled tests. A forcibly killed
process may leave its own ignored directory behind. The launcher itself is
checked with `npm run test:component-runner` without compiling the application or
starting a server. Browser conformance remains a separate verification layer;
passing component tests does not constitute a complete module review.
+32
View File
@@ -7,6 +7,38 @@ emits a draft only after a real edit. A drag onto the same position, keyboard
pickup/drop without movement, and opening inherited settings do not save or
create an override.
## Standard layout / Standardanordnung
No configuration is required to enable sections. Installed modules contribute
the standard product-area membership, labels, and order: Work; Services and
cases; Records and documents; Communication; Meetings and decisions; Data and
assurance; People and responsibility. The dashboard stays first; unclassified
destinations remain available under More tools. Only nonempty, authorized and
View-visible groups appear. Composed product entries keep their placement
owner's directly declared area. If none exists, the navigation aliases of
other authorized owners preserve area membership. These are live defaults,
not a saved copy that needs replacing whenever optional modules change.
The editor explains whether the current level is inherited, custom grouped, or
explicitly flat. To restore defaults, choose **Use inherited layout** and Save
on the owning page: personal Settings inherits the tenant, tenant settings
inherit the system, and system settings inherit the standard product areas.
An explicitly flat personal, tenant, system, or View configuration is never
silently replaced with standard groups. A View may also explicitly select flat
navigation. Reset the responsible override rather than changing permissions.
Abschnitte sind ohne zusätzliche Konfiguration aktiv. Die Standardanordnung
gliedert verfügbare Module in Arbeit; Leistungen und Vorgänge; Akten und
Dokumente; Kommunikation; Termine und Entscheidungen; Daten und
Qualitätssicherung; Personen und Verantwortung. Das Dashboard steht davor,
nicht zugeordnete Ziele bleiben unter Weitere Werkzeuge erreichbar. Leere oder
nicht zugängliche Gruppen erscheinen nicht. Der gemeinsame Editor zeigt an,
ob eine geerbte, eigene gruppierte oder ausdrücklich ungegliederte Anordnung
vorliegt. **Geerbte Anordnung verwenden** und anschließendes Speichern entfernt
nur die Anpassung dieser Ebene: persönlich → Mandant → System →
Standardanordnung. Bewusst gespeicherte ungegliederte Anordnungen bleiben
erhalten. Diese Darstellung erteilt keine Berechtigungen und ändert keine Daten.
## Stored document and precedence
The version-1 navigation document retains `order`, `hidden` and `locked` and
+49 -6
View File
@@ -4,6 +4,12 @@ This document defines the binding composition grammar for headed GovOPlaN
pages. Core owns the reusable anatomy; each module owns its domain actions,
wording, authorization, consequences, and data state.
The cross-product [UI design principles](UI_DESIGN_PRINCIPLES.md) define the
display-first editing model, heading-adjacent help, and module-review process.
Normal overview/detail surfaces show readable facts; edit coherent settings in
scoped dialogs. Use an explicit editor mode for a justified broad-editing task,
not merely because a page also offers filters or contains an edit dialog (UI-02).
## Required Page Frame
- Use `PageLayout` for every headed standalone, workspace, or embedded page.
@@ -69,11 +75,19 @@ page or pane action bar.
| Page kind | Leading group | Trailing group |
| --- | --- | --- |
| Overview | Context | Help, Reload when refreshable, then ordinary primary actions |
| Collection | Collection context such as export | Help, Reload when refreshable, then Create at the far right |
| Detail | Object context | Help, Reload when refreshable, ordinary primary actions, then a separated destructive group |
| Editor | Context | Dirty state, Help, Reload if distinctly safe, ordinary primary actions, separated destructive actions, Discard, then Save at the far right |
| Workspace | Task context | Help, Reload when refreshable, ordinary primary actions, then a separated destructive group |
| Overview | Context | Reload when refreshable, then ordinary primary actions |
| Collection | Collection context such as export | Reload when refreshable, then Create at the far right |
| Detail | Object context | Reload when refreshable, ordinary primary actions, then a separated destructive group |
| Editor | Context | Dirty state, Reload if distinctly safe, ordinary primary actions, separated destructive actions, Discard, then Save at the far right |
| Workspace | Task context | Reload when refreshable, ordinary primary actions, then a separated destructive group |
Documentation is associated with text, not an action-group slot (UI-01). Put
`DocumentationHelpLink` in the heading component's `titleHelp`, use `TextWithHelp`
for existing contextual words, and retain `FormField`/`FieldLabel` documentation
beside field labels. Full-canvas `WorkspaceActionBar` surfaces can supply `title`
and `titleHelp` together on the leading side. Do not duplicate a heading or use
`helpAction` for a detached documentation icon. See the shared principle for
card/dialog accessibility and examples.
Reload and Create belong to the same right-aligned group, in that order. A
collection-wide toolbar stays above its workspace, not inside the left tree or
@@ -82,12 +96,40 @@ not remove it. Permission-blocked creation remains visible with an explanation.
On narrow screens the trailing group wraps while retaining right alignment and
the same DOM/keyboard order.
An editor's **Cancel/Close** action is not the same as **Reset/Discard changes**.
When it exits an editing mode, set `discardAction.behavior` to `"exit"`: it stays
enabled for a clean draft, while Save remains disabled until there are changes.
The default `"reset"` behavior still requires changes. Both stay blocked during
an in-flight save; the owning page must use the shared discard confirmation before
exiting a dirty draft. Never require a meaningless edit just to leave configuration.
Deutsch: Abbrechen oder Schließen beendet den Bearbeitungsmodus auch ohne
Änderungen (`discardAction.behavior="exit"`). Zurücksetzen setzt dagegen einen
geänderten Entwurf voraus. Beim Abbrechen eines geänderten Entwurfs vor dem
Verwerfen nachfragen; eine laufende Speicherung bleibt geschützt.
Use `Card bodyLayout="table"` for table surfaces, including tables wrapped by
`LoadingFrame`. This removes body padding explicitly, without relying on the
number of children or negative margins. Place any meaningful explanation or
warning in a padded `ContentSection`; do not add a redundant tagline to every
table. Use `ContentGrid` for sibling cards so spacing does not depend on fragments.
The shared stylesheet also recognizes existing table-only card bodies, either
directly or through `LoadingFrame`; loading must not add an inset, expand the
overlay outside the card, or change the table's measured width. Keep the table
at 100% of its container. Do not reintroduce module-local negative margins,
expanded `calc()` widths, or padding overrides to make a table fit. Mixed
content cards retain their normal padding unless they explicitly declare the
table layout. The browser conformance matrix checks all four card edges,
loading/collapse transitions, scrolling, and row actions at desktop and mobile
widths.
Deutsch: Tabellen belegen den Karteninhalt ohne zusätzlichen Innenabstand,
auch während des Ladens. Neue Tabellenkarten verwenden ausdrücklich
`bodyLayout="table"`; ergänzende Hinweise erhalten ihren eigenen Innenabstand.
Negative Ränder oder modulbezogene Breitenkorrekturen sind nicht nötig.
Karten mit gemischtem Text- oder Formularinhalt behalten ihre normalen Abstände.
Use `MultiSelectFilter` for standalone list facets. It and DataGrid share the
same checkbox body and Select all / Deselect all behavior. `null` means no
restriction, `[]` means no matches, and multiple values mean OR within a facet.
@@ -136,7 +178,8 @@ to fill the slot.
Editor bars always keep Discard and Save visible. Their required `state`
projection is one of `clean`, `dirty`, `invalid`, `saving`, `save-failed`, or
`conflict`, and the central component announces it through a live status label.
Clean and saving states disable both persistence actions; invalid disables Save
Clean states disable Save and reset-style Discard, but keep exit-style Cancel
available; saving disables both persistence actions. Invalid disables Save
while retaining Discard. Failed saves and conflicts keep the draft recoverable
and allow an authorized retry after the module has shown the owning error or
conflict evidence. A module may add a more specific validation, policy, or
+150
View File
@@ -0,0 +1,150 @@
# Product UI design principles
These rules govern every GovOPlaN module, including administration, tenant and
user settings, public forms, widgets, dialogs and shared shell surfaces. Core
owns the reusable components; modules own their meaning and authorized behavior.
They complement the [page composition contract](PAGE_LAYOUT_USAGE_GUIDELINES.md).
Initial rule revision: **UI-2026-09-08**. Record this identifier in review evidence;
subsequent rule changes need a dated change reference and a UI-09 propagation check.
## UI-01 — Help belongs to visible text
Place a small documentation book immediately after the heading or label whose
meaning it explains. For example, the Dashboard book belongs to **Dashboard**,
not the Reload, Edit or Save group. The same applies to page, pane, card and
dialog titles, field labels, widget summaries and contextual explanations.
Do not put an isolated documentation icon in an action bar or in an otherwise
empty row. A navigation icon is not a contextual documentation link.
Use `titleHelp={<DocumentationHelpLink reference={...} />}` on `PageLayout`,
`PageHeader`, `PageTitle`, `AdminPageLayout`, `Card` or `Dialog`. Full-canvas
workspaces use the visible `title` and `titleHelp` of `WorkspaceActionBar` (or
`PageActionBar`), separate from its action groups. Use `TextWithHelp` around
existing text for smaller contexts; fields use `FormField`/`FieldLabel` and
their documentation reference. Do not duplicate the heading to add help.
Dashboard widgets provide their owning `documentation` reference in
`DashboardWidgetContribution`; Dashboard renders it beside the existing widget
card title. Do not add a second widget heading or a footer-only documentation icon.
Keep the icon outside the heading's accessible name and outside other buttons
or links. It needs an accessible purpose, keyboard focus and a usable hit area.
Long German headings must wrap without colliding with actions. Collapsing a
card or configuring a dashboard must not change which title the book explains.
The existing optional Docs-module/hosted-documentation fallback remains intact;
placement does not change permissions or require the Docs module.
## UI-02 — Display first; edit deliberately
Ordinary overview and detail pages show compact, readable data. Group related
facts with shared description, summary and card components. Offer a clear Edit
action that opens a scoped dialog for a coherent group of settings. Avoid
presenting every possible input, select and save control simultaneously.
The Campaign dashboard is a priority application: show the effective settings
and their state compactly, then edit related settings in focused dialogs. This
is review work to implement, not a claim that all current screens already comply.
An explicit editing mode is a justified exception for genuinely broad editing,
large tables, graphical designers or other tasks poorly served by a modal.
Record why the exception helps the user. Keep reading and editing distinguishable;
enter editing deliberately and preserve shared Save/Cancel, validation, conflict,
permission and unsaved-draft behavior. Filters, search and selection do not need
an edit dialog: they change the view rather than the underlying data.
Cancel/Close must work even without changes. Never save a draft merely because
a dialog opens, selection changes or navigation happens. Commit only the intended
fields; do not let unrelated incomplete settings block an independent edit.
## UI-03 — Predictable actions and consequences
Follow the shared semantic page/pane action contract: context on the left;
Reload on refreshable surfaces followed by Create on the right. Editable drafts
have the standard state, Cancel/Discard and Save controls. Keep positions stable
across selection and loading changes, and explain unavailable applicable actions.
Separate destructive actions visually and semantically. Reload is a read, not
an implicit synchronization, import, send or other mutation.
## UI-04 — Shared geometry and readable collections
Use central page, card, table, form-grid and dialog primitives. Table cards use
`bodyLayout="table"`, spanning the card body without negative margins or expanded
`calc()` widths. Keep row actions reachable and deliberate column resizing
predictable. Avoid redundant taglines, arbitrary gaps, fixed dialog widths and
horizontal scrolling where content can wrap. Use actual pagination and shared
list filters; every result and every validation detail must remain reachable.
## UI-05 — Stable feedback and loading
Preserve useful loaded content on refresh failure; distinguish initial loading,
empty, unavailable, stale, blocked, failed and successful states. Use the shared
scoped loading overlay. Long operations show truthful processed/total and outcome
counts when known, not simulated progress or whole-page background reloads.
Prevent duplicate submissions without trapping the user in a clean editing mode.
## UI-06 — Predictable navigation and selection
Use shared breadcrumbs, side rail and tree contracts. Tree disclosure controls
expand/collapse; labels select. Track the clicked occurrence when one topic or
record appears in several branches. Preserve context, filters and selection
where appropriate, without carrying data across authorization or tenant scopes.
## UI-07 — Accessibility and German parity
Check keyboard order, visible focus, accessible names, dialog focus restoration,
contrast, zoom and narrow layouts. Status must not rely on color alone. Use
plain, task-oriented wording and complete German UI and module-owned help for
the changed workflow. Long labels must not hide actions or force needless scroll.
## UI-08 — Integrity and permissions are not visual shortcuts
Preserve authorization, versioning, audit evidence, immutable historical data and
safe retries. Do not remove a guard to make an action look available. Keep drafts
recoverable after failures and conflicts. Loading, cancellation and dialog
transitions must not silently write, discard or overwrite data. Never verify UI
changes by sending real mail or changing production records without authorization.
## UI-09 — Review incrementally and propagate lessons
The [Meta UI-review epic](https://git.add-ideas.de/GovOPlaN/govoplan/issues/56)
links one review ledger per module. Each starts pending, including modules
touched by a focused shared-component pass. An implemented help-placement rule
does not constitute a full module review.
Inventory the module's surfaces, inspect them with realistic data and permissions,
record findings and decisions, implement bounded fixes, then attach automated
and manual evidence. Distinguish **to do**, **implemented**, **verified**, and
**accepted exception**. Record the applicable principle IDs and actual screen
coverage; do not mark a module complete from a code search alone.
When a review reveals a reusable lesson, update this document and the central
component/check first where possible. Record the rule change in Meta and create
a follow-up or reopen affected already-reviewed modules. The review ledger must
show which rule revision each module has actually verified. Prefer finishing
the active review slice before expanding functionality, while recording unrelated
bugs and decision-dependent work without losing them.
## Kurzfassung auf Deutsch
- **UI-01:** Das Dokumentationsbuch steht direkt rechts neben der zugehörigen
Überschrift oder Beschriftung, nicht zwischen Aktionsschaltflächen.
- **UI-02:** Daten zunächst kompakt anzeigen; zusammengehörige Einstellungen
gezielt in einem Dialog bearbeiten. Ein ausdrücklich aktivierter
Bearbeitungsmodus, etwa für große Tabellen, ist eine begründete Ausnahme.
Abbrechen funktioniert auch ohne Änderungen; Änderungen werden bewusst gespeichert.
- **UI-03UI-08:** Einheitliche Aktionen und Abstände, erreichbare Tabelleninhalte,
verständlicher Fortschritt, vorhersehbare Navigation, Barrierefreiheit und
deutsche Texte dürfen Berechtigungen oder Datenintegrität nicht schwächen.
- **UI-09:** Alle Module werden einzeln geprüft. Neue Gestaltungsregeln werden
auch in bereits geprüften Modulen nachgezogen und mit Nachweisen dokumentiert.
## Automated coverage and its limits
`tools/checks/check-heading-help.mjs` in Meta checks JSX documentation-link
placement throughout the workspace, including aliases and simple local variables.
Core component and browser tests check heading association, accessible names,
focus, collapse/loading transitions and narrow/wide layouts. Existing layout,
action, dialog and DataGrid checks continue to apply. These tests protect shared
contracts; they do not certify every module's usability or replace the individual
screen reviews and their documented exceptions.