MishkaCmsCore. Runtime. PageBuilder. PageForms. FormChecks
(Mishka CMS Core v0.0.1)
Copy Markdown
View Source
The Forms group of the audit rail: what is wrong with the page's forms, and the tree edit that puts each one right.
Every finding carries the shape the rail already renders — node_id, group, severity, rule,
message, detail — so a form check reads beside an accessibility one and its Fix button is the
same button. What is different is that most of these have a mechanical answer: repair/3 hands
back the ops that fix the document, and the few with no edit behind them say why rather than
appearing to work.
What is asked of each node
Not asked here at all: MishkaCmsCore.Runtime.PageBuilder.Fields.form_node?/2 and
MishkaCmsCore.Runtime.PageBuilder.Fields.field_node?/2 are the editor's one answer to both, so
what the insert funnel treats as a form, what MishkaCmsCore.Runtime.PageBuilder.Containment
refuses to nest, and what the audit reports on cannot disagree. This module used to carry its own
copy of each, and so did the other two.
Two questions about a submit button, two answers
form-no-submit asks, per node and at any depth inside the form, whether anything sends it
(Fields.submit_node?/3): a bare <button>, a kit button left to its default, the CMS's own
<.button>. A button whose type is computed (Fields.type_unread?/3) may send it, so the rule
stays quiet rather than offer a second Send button. submit-no-form asks the reverse about a button outside every form, and there only a
written type="submit" or "image" counts (Fields.explicit_submit?/3), so a hero or dialog
button stays quiet.
Why a wrap is an insert and a delete
MishkaCmsCore.Runtime.PageBuilder.Op's :wrap builds a bare tag with no attributes, and a form
is nothing without its five. There is also no id to write them onto: the wrapper is minted inside
the combinator. So the field's own source is re-parsed, wrapped and bound in one go by
FormPlacement.wrap/3, inserted where the field stood, and the field deleted — two ops in one
MishkaCmsCore.Runtime.PageBuilder.EditorSession.apply_ops/4 run, which is one undo step.
A Fix that cannot act says so
Two of these have no edit behind them, and each says why in a sentence the author reads instead
of appearing to work: there is no form on the page to move a button into (submit-no-form);
unwrapping this form would strand the fields inside it (form-in-form). repair/3 answers
{:say, sentence} for those, which is not the same as :none — :none is a stale click, and the
assistant is the right place for a document that has moved on. Everything else here has exactly
one right answer and takes it.
A note for developers
form-no-destination is a :note, not counted against the page: a form nobody wrote code for
still checks its fields and thanks the visitor. A form that names no submit event and posts to its
own action has somewhere to go, so it raises nothing (receiver/2).
Its Fix finishes the form first, one edit per click: its name (as), then its source (for),
then its submit event. A form with all three answers {:code, event, sentence}: the form's submit
event, for the editor to open in the Code window, and the sentence to show someone who cannot
write code.
See MishkaCmsCore.Runtime.PageBuilder.PageForms for what declares a form in the first place, and
MishkaCmsCore.Runtime.PageBuilder.PageForms.Naming for the five names a seed derives.
Summary
Functions
Every Forms finding this document raises, in document order, with the duplicate-name blockers last.
Whether these nodes hold a form at all.
What receives this form's submissions, the check form-no-destination makes
The ops that fix rule on node_id, the sentence to show when there is nothing this can edit, or
:none.
The ops one of a finding's fixes makes, when it offers more than one: "typing" on
form-no-submit makes the form work as they type (FormPlacement.how_ops/3). Any other fix is
repair/3's.
Whether rule names one of this module's findings.
Functions
@spec findings(MishkaCmsCore.Runtime.PageBuilder.Tree.t(), map() | nil, map()) :: [ map() ]
Every Forms finding this document raises, in document order, with the duplicate-name blockers last.
record is the page being edited, read for the event handlers it declares; session is the
editing session, read for the palette and for the section — a section whose compiler emits no form
keeps only the rules HTML itself is wrong about.
@spec forms?([MishkaCmsCore.Runtime.PageBuilder.Tree.Node.t()], map()) :: boolean()
Whether these nodes hold a form at all.
The rail's passing rows are conditional on it: "every form reaches a handler" is not a tick a page with no form has earned, it is a check that never ran — and on a section that compiles no form it is not a check at all.
@spec receiver(MishkaCmsCore.Runtime.PageBuilder.Tree.Node.t(), map() | nil) :: :code | :plugin | :action | :none
What receives this form's submissions, the check form-no-destination makes:
:codewhen a handlerrecordstores answers the form's submit event (PageForms.answered?/2) with code that is not the untouched default (LiveViewCompiler.untouched_handler?/3);:pluginwhen no such handler does and a plugin listens onpage:form:submitted;:actionwhen the form posts to its ownaction: with no submit event, or once its answers are checked (PageForms.sends_elsewhere?/1);:noneotherwise.
@spec repair(map(), String.t(), String.t() | nil) :: {:op, map()} | {:ops, [map()]} | {:say, String.t()} | {:code, String.t(), String.t()} | :none
The ops that fix rule on node_id, the sentence to show when there is nothing this can edit, or
:none.
{:ops, ops} is a run applied as one history entry; {:op, op} is a single edit; {:say, sentence} is a refusal the author reads, for the fixes whose answer is not in this document at
all. A rule this module does not answer for, and a node that is no longer in the tree, both give
:none — the caller falls back to the assistant, which is the right answer for a stale click.
@spec repair(map(), String.t(), String.t() | nil, String.t() | nil) :: {:op, map()} | {:ops, [map()]} | {:say, String.t()} | {:code, String.t(), String.t()} | :none
The ops one of a finding's fixes makes, when it offers more than one: "typing" on
form-no-submit makes the form work as they type (FormPlacement.how_ops/3). Any other fix is
repair/3's.
Whether rule names one of this module's findings.
Fix on such a finding is answered by repair/3; Fix on any other is handed to the assistant.