Skip to content

docs: document CRUD customization API and editor behavior - #5894

Merged
peholmst merged 4 commits into
mainfrom
worktree-crud-docs-issue-103
Aug 27, 2026
Merged

docs: document CRUD customization API and editor behavior#5894
peholmst merged 4 commits into
mainfrom
worktree-crud-docs-issue-103

Conversation

@peholmst

Copy link
Copy Markdown
Member

Addresses vaadin/agentic-dx-improvement#103, which reported that the Flow CRUD documentation presents the component as a mostly turn-key "grid + generated editor" and leaves several decision-critical facts discoverable only by inspecting the jar.

Changes

articles/components/crud/index.adoc:

  • Editor Actions promoted from ==== to ===, and expanded with two new subsections:
    • Save Button State — the exact enablement rules (dirty, not validity), the role of setDirty(), and how to keep Save enabled at all times.
    • Editor Button Access — a table of getSaveButton() / getCancelButton() / getDeleteButton() / getNewButton() / setNewButton(), with an example of hiding Delete for datasets that archive rather than remove records, plus a note that setText() on these buttons is overwritten by localization.
  • Custom Editor — documents CrudEditor<E> as a public extension point, with a table of when CRUD calls each method and a live non-BinderCrudEditor example that validates itself and renders a top-of-form role="alert" error summary.
  • Controlling the Editor Programmatically — collects edit()/EditMode, setOpened(), setEditorPosition(), and setEditOnClick() into one table.
  • No Manual Reordering note under Sorting & Filtering, cross-linked to Grid Replacement.
  • Lazy Backend Loading — how CrudFilter's constraint and sort-order maps translate into WHERE/ORDER BY, the DataProvider<E, CrudFilter> requirement enforced by CrudGrid, and the existing PersonDataProvider as the reference implementation.
  • Styling — surfaces the no-border variant and CrudVariant on the main page and links to the styling page, which the main page didn't link at all.

New examples: PersonCrudEditor.java, CrudCustomEditor.java, CrudEditorButtons.java, and crud-imports.ts (preimport for the Flow-only demos).

Two corrections to the ticket

Everything was checked against the sources for vaadin-crud-flow 25.2.1 / 25.3.0-alpha8, @vaadin/crud, and Flow's element binding. Two of the ticket's claims don't hold:

  • Gap 2 claimed there's no public API for an always-enabled Save. There is: crud.getSaveButton().setEnabled(true). Crud's internal SaveButton overrides onEnabledStateChanged to permanently override the web component's __isSaveBtnDisabled, and re-applies it on attach. Flow also overrides the client-side __validate to always pass, so the server-side CrudEditor.validate() is the only gate on a save. Documented as supported, which makes the ADR-0020 pattern from the ticket achievable.
  • Gap 3 proposed getDeleteButton().setVisible(false). That isn't reliable: setVisible(false) sets Flow's hidden attribute, but __deleteButtonPropsChanged runs toggleAttribute('hidden', isNew) each time the editor opens, so Delete reappears when editing an existing item. Vaadin's own integration tests only use setEnabled on these buttons. The docs recommend getStyle().set("display", "none") instead, with a short note on why.

CrudVariant.AURA_NO_BORDER is deprecated in favour of NO_BORDER as of 25.2.1, so the docs point at NO_BORDER.

Verification

  • mvn compile passes.
  • spotless:apply produces no changes to the new Java files.
  • Prettier and ESLint are clean on crud-imports.ts.
  • Vale couldn't run locally (no Ruby/asciidoctor on this machine), so the page was linted as a .txt copy against .vale-pr.ini and diffed against the pre-change baseline. The only new hits are artifacts of that workaround — [methodname]/[classname] roles, _italics_, and source-block attributes, all in ignored classes and scopes for the real run. The two genuine ones ("deletable" spelling, a Chicago title-case heading) are fixed.
  • The rendered demos have not been visually verified — the docs server wasn't started. Worth a look in the preview build.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ

Addresses vaadin/agentic-dx-improvement#103, which reported that the Flow
CRUD documentation under-documents the component's extension points, forcing
readers to inspect the jar to make an adoption decision.

- Document the CrudEditor interface as a public extension point, with a
  non-Binder editor example that validates itself and renders a top-of-form
  error summary.
- Specify the Save button enablement rules, the role of setDirty(), and how
  to keep Save enabled at all times via getSaveButton().setEnabled(true).
- Document the getSaveButton()/getCancelButton()/getDeleteButton()/
  getNewButton() accessors, with an example of hiding Delete for datasets
  that archive rather than remove records.
- Collect the programmatic editor controls (edit, setOpened,
  setEditorPosition, setEditOnClick) into one table.
- Note that CRUD has no manual row reordering, and point at grid replacement.
- Explain how CrudFilter maps sorting and filtering onto a lazy backend.
- Surface the no-border variant and CrudVariant on the main page, and link
  to the styling page.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

Preview Deployment

This PR has been deployed for preview.

URL: https://docs-preview-pr-5894.fly.dev

Changed pages

Added content is highlighted in green; removed content is marked in red on each page.

Built from c6aa164

Vaadin.ProductName flagged "= Crud Styling". The sibling styling pages all
spell out the component name in their titles, as does the CRUD index page.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ
Comment thread articles/components/crud/index.adoc Outdated
Comment thread articles/components/crud/index.adoc Outdated
Comment thread frontend/demo/component/crud/crud-imports.ts Outdated

An editor that doesn't propagate its field changes -- one built from a composite component that wraps its fields, for example -- can leave Save permanently disabled. Call [methodname]`setDirty(true)` on the CRUD to enable it explicitly in such a case.

Some applications need Save to be enabled at all times. A common accessibility pattern is to let the user submit at any point and then show a summary of what needs fixing. Set the enabled state of the Save Button directly to get this: CRUD stops managing that Button's state from then on, leaving it enabled regardless of whether the editor is dirty. The editor's [methodname]`validate()` method still decides whether a save goes through.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this behavior documented in the CRUD Javadoc? If not, this might actually be a bug in the CRUD component.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this part is documented. Crud.getSaveButton():

NOTE: State of the button set with HasEnabled#setEnabled(boolean) will remain even if dirty state of the crud changes

and Crud.setDirty(boolean):

A dirty Crud has its editor Save button enabled. […] NOTE: editor Save button will not be automatically enabled in case its enabled state was changed with Crud#getSaveButton()

The two @see each other, so the override is a deliberate, documented escape hatch. SaveButton.onEnabledStateChanged implements it by overriding the web component's __isSaveBtnDisabled, and Crud.onAttach re-applies it, so it survives detach/attach.

One caveat on the paragraph above this one, which is not in the Javadoc: "Validity doesn't factor into this: Save is enabled for invalid input, too." That comes from the implementation — vaadin-crud-mixin.js has __isSaveBtnDisabled(isDirty) { return !isDirty; }, and Flow's Crud.onAttach sets this.__validate = function () { return true; } so client-side validation never gates the save. Server-side CrudEditor.validate() is the only gate. Happy to drop that sentence if you would rather not pin down undocumented behavior.

- Add explicit [#save-button-state] and [#editor-button-access] anchors.
  The [badge-flow]#Flow# suffix leaks into the generated heading ids
  ("save-button-state-flow"), so the two cross-references pointed at
  anchors that don't exist. Verified in a browser: the page now has no
  broken in-page links.
- Include PersonCrudEditor.java without `render`. It isn't a Component,
  so there's nothing to render; the convention for a supporting class is
  a plain listing, as in articles/flow/binding-data/index.adoc.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ
Comment thread articles/components/crud/index.adoc Outdated

Set the Button labels through <<#localization,Localization>>, not [methodname]`setText()`: CRUD writes the localized labels onto its default Buttons, overwriting anything set that way.

Some datasets shouldn't allow deletion at all -- for example, records that are archived or deactivated instead of removed, so that history is preserved. Hide the Delete Button with CSS in that case.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a hack or a workaround. Unless it is advocated by the CRUD Javadoc, it should be stated as such, pending a better Java API for achieving the same thing.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — it is a workaround, and the Javadoc does not advocate it. Reworded to say so, and added a note:

There's no API for removing the Delete Button, so the only way to do this at present is to hide it with CSS.

Hiding Delete Is a Workaround — Setting display: none isn't advocated by the CRUD API; it's the only thing that works today. Don't reach for setVisible(false): CRUD manages the Delete Button's hidden attribute itself, and it clears the attribute each time the editor opens for an existing item, which makes the Button reappear.

That last part is arguably a component bug worth its own ticket: Flow's setVisible(false) sets the hidden attribute (SimpleElementBindingStrategy.setElementInvisible), while __deleteButtonPropsChanged in vaadin-crud-mixin.js runs deleteButton.toggleAttribute('hidden', isNew) on every __isNew change, clearing it. So the one obvious API for this silently does not work. A setDeleteButtonVisible(boolean), or having CRUD respect the server-side visibility, would remove the need for the CSS. Want me to file that against flow-components?

- Remove the Styling section; it duplicated styling.adoc.
- Drop frontend/demo/component/crud/crud-imports.ts and point the two
  Flow-only examples at the existing crud-editor-content.ts instead. It
  already imports crud, email-field, form-layout, and text-field, so no
  new file is needed.
- Frame hiding the Delete Button as a workaround rather than a technique,
  and warn against setVisible(false), which CRUD undoes when the editor
  opens for an existing item.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ
@peholmst
peholmst merged commit 14521d8 into main Aug 27, 2026
8 checks passed
@peholmst
peholmst deleted the worktree-crud-docs-issue-103 branch August 27, 2026 11:39
@peholmst peholmst added the target/v25.2 Automatically cherry-pick to the v25.2 branch label Aug 27, 2026
peholmst added a commit that referenced this pull request Aug 28, 2026
)

* docs: document CRUD customization API and editor behavior

Addresses vaadin/agentic-dx-improvement#103, which reported that the Flow
CRUD documentation under-documents the component's extension points, forcing
readers to inspect the jar to make an adoption decision.

- Document the CrudEditor interface as a public extension point, with a
  non-Binder editor example that validates itself and renders a top-of-form
  error summary.
- Specify the Save button enablement rules, the role of setDirty(), and how
  to keep Save enabled at all times via getSaveButton().setEnabled(true).
- Document the getSaveButton()/getCancelButton()/getDeleteButton()/
  getNewButton() accessors, with an example of hiding Delete for datasets
  that archive rather than remove records.
- Collect the programmatic editor controls (edit, setOpened,
  setEditorPosition, setEditOnClick) into one table.
- Note that CRUD has no manual row reordering, and point at grid replacement.
- Explain how CrudFilter maps sorting and filtering onto a lazy backend.
- Surface the no-border variant and CrudVariant on the main page, and link
  to the styling page.


Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ

* docs: use the CRUD product name in the styling page title

Vaadin.ProductName flagged "= Crud Styling". The sibling styling pages all
spell out the component name in their titles, as does the CRUD index page.


Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ

* docs: fix CRUD anchors and drop a stray render attribute

- Add explicit [#save-button-state] and [#editor-button-access] anchors.
  The [badge-flow]#Flow# suffix leaks into the generated heading ids
  ("save-button-state-flow"), so the two cross-references pointed at
  anchors that don't exist. Verified in a browser: the page now has no
  broken in-page links.
- Include PersonCrudEditor.java without `render`. It isn't a Component,
  so there's nothing to render; the convention for a supporting class is
  a plain listing, as in articles/flow/binding-data/index.adoc.


Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ

* docs: address review feedback on the CRUD page

- Remove the Styling section; it duplicated styling.adoc.
- Drop frontend/demo/component/crud/crud-imports.ts and point the two
  Flow-only examples at the existing crud-editor-content.ts instead. It
  already imports crud, email-field, form-layout, and text-field, so no
  new file is needed.
- Frame hiding the Delete Button as a workaround rather than a technique,
  and warn against setVisible(false), which CRUD undoes when the editor
  opens for an existing item.


Claude-Session: https://claude.ai/code/session_01CvBbsmxH7A3F8WyMz2hfZZ

---------

Co-authored-by: Petter Holmström <petter@vaadin.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cherry-picked-v25.2 target/v25.2 Automatically cherry-pick to the v25.2 branch

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants