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:
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable
+150
@@ -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-03–UI-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.
|
||||
Reference in New Issue
Block a user