enhancement
major
minor
major
minor
major
#29487
React view: <form> exposes neither column count nor label position, and there is no <group> element
Found in 8.0.0-alpha7 while building the Consulting application's React view UI (Consulting issue #94).
Summary
Two related gaps make a form in a view non-tunable:
- FormElement.Config has no attribute for the column count or the label position, so every <form> in every view renders with the React defaults maxColumns = 3 and labelPosition = "auto".
- TLFormGroup / ReactFormGroupControl exists in tl-layout-react — header, border (none/subtle/outlined), collapsible, fullLine — but tl-layout-view registers no @TagName for it. The tag list is field, inset, grid, text. A view author cannot group fields at all.
Effect
A form in a pane narrower than 960px gets two columns with labels above the fields, because auto only switches to side labels from 960px form width. The result reads as a ragged two-column block of stacked label/field pairs rather than a form. In the Consulting application this affects every detail view (client, project, stakeholder, task, document): the detail pane is 60% of the window, ~840px on a 1440px screen, so it never reaches the threshold. There is no way to say "one column here" or "labels beside the fields here", and no way to separate Name from Contact from Address.
Root cause
- com.top_logic.layout.view.form.FormControl is the TLFormLayout (super(context, initialObject, "TLFormLayout")) and sets neither maxColumns nor labelPosition on it.
- com.top_logic.layout.view.form.FormElement.Config declares no corresponding property, so there is nothing to set from XML either.
- Client side, the component computes its columns as repeat(auto-fit, minmax(min(21rem, 100%), 1fr)) — 21rem being max(16, 64 / maxColumns) — and picks side labels only when width / maxColumns >= 320px.
- com.top_logic.layout.react.control.form.ReactFormGroupControl and its TLFormGroup component are complete and used internally, but no UIElement in tl-layout-view creates one.
Request
- max-columns and label-position on FormElement.Config, passed through to the TLFormLayout properties that already exist.
- A <group> element in tl-layout-view wrapping ReactFormGroupControl, with the properties the component already has (label, border, collapsible, full-line) and children like any other container.
Workaround
Overriding the framework's own internal class names (.tlFormLayout, .tlFormField) from the application stylesheet. It reaches the column count, not the grouping, and it breaks on every engine upgrade.
Lösung
Column count and label position of a form. A <form> takes the same two layout attributes a <fields> grid already takes: max-columns (the greatest number of columns the fields are laid out in, 3 by default) and label-position (auto by default: labels beside the inputs where the column is wide enough, above them where it is not; side and top fix the position whatever the width). A field-level position such as after or hidden is rejected with a configuration error. Both elements share the options through one configuration interface (FormLayoutOptions), so they are documented and validated once.
{{{#!xml <form input="selectedMilestone" max-columns="1" label-position="side">
<field attribute="name"/>
...
</form> }}}
Sections of a form: <group>. A new container element <group> (GroupElement) places its content in a section of the enclosing form grid. Its attributes are the ones the section already offers: a <label> shown as the section heading, border (none, subtle or outlined; none by default), collapsible (a chevron in the heading folds the section away), collapsed (the initial state of a collapsible section) and full-line. A section spans the whole form by default (full-line="true"); with full-line="false" it takes one column of the grid and stacks its content in it. Its content is arbitrary view content — fields, texts, insets, nested groups — and it works inside <fields> as well as inside <form>, both being the same grid.
{{{#!xml <form input="model">
<group border="subtle" collapsible="true">
<label>
<en>Address</en>
<de>Adresse</de>
</label>
<field attribute="street"/>
<field attribute="city"/>
</group>
</form> }}}
Framework: the border of ReactFormGroupControl is a typed enumeration (ReactFormGroupControl.GroupBorder) instead of a plain string, in the Java constructor and at every caller. The value sent to the client is unchanged. In the configuration editor, the section holding a collection property used to pass the value default, which no stylesheet rule knows and which therefore drew no frame; it now takes the subtle frame of the sibling section holding a nested configuration.
Demo and documentation: in tl-demo-react, the "Edit demo object" dialog of the Attributes page sections its fifty fields into eight collapsible groups with a subtle frame (the "Source code" group starts folded), and the milestone form of the responsive master-detail demo is laid out in one column with side labels. The FAQ docs/faq/react-view-layer.md describes both the <form> options and <group>.