feat(17-01): repeater field type and the dependent dropdown save recheck
- cabana: type repeater with nested form.fields (text, textarea, number, checkbox, switch, dropdown, richeditor), prompt, minItems, maxItems, titleFrom; groups, style, nested repeaters and other types fail boot - rows decoded before projection: only nested keys, coerced scalars, per-row required (field.index.nested), row bounds; ProjectWritableFields exempts repeater lists only, nestedValue unchanged - nested richeditor values sanitized with notices under the row path - dependent dropdowns rechecked on save, top level and per row; null when the sibling takes no choice, 422 form.dependent_invalid otherwise - dependent-options accepts repeater.field; schema carries RepeaterForm - backend lang repeater.max_items, repeater.min_items, form.dependent_invalid; TestCompanionsRepeaterSmoke; docs
This commit is contained in:
@@ -1189,6 +1189,14 @@
|
||||
"description": "FirstDay is the first day of the week, 0 (Sunday) to 6.",
|
||||
"type": "integer"
|
||||
},
|
||||
"form": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/cabana.RepeaterForm"
|
||||
}
|
||||
],
|
||||
"description": "Form holds the nested fields of a `type: repeater` field, one set per\nrow (D-07)."
|
||||
},
|
||||
"format": {
|
||||
"description": "Format is a datepicker's WinterCMS (PHP date) display format;\nDisplayFormat is the same format in the SPA's moment-style tokens.",
|
||||
"type": "string"
|
||||
@@ -1218,6 +1226,10 @@
|
||||
"description": "MaxFilesize is the largest accepted file in megabytes.",
|
||||
"type": "number"
|
||||
},
|
||||
"maxItems": {
|
||||
"description": "MaxItems and MinItems bound a repeater's row count, checked on save.",
|
||||
"type": "integer"
|
||||
},
|
||||
"mimeTypes": {
|
||||
"description": "MimeTypes are the allowed MIME patterns (or extensions) of a\nfileupload field.",
|
||||
"items": {
|
||||
@@ -1229,6 +1241,9 @@
|
||||
"description": "MinDate and MaxDate bound a datepicker's calendar date (YYYY-MM-DD),\nchecked again on save.",
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": {
|
||||
"type": "integer"
|
||||
},
|
||||
"mode": {
|
||||
"description": "Mode is the fileupload mode (image or file, default file), the\ndatepicker mode (date, datetime or time, default datetime) or the\npermissioneditor mode (radio or checkbox).",
|
||||
"type": "string"
|
||||
@@ -1301,6 +1316,10 @@
|
||||
],
|
||||
"description": "ThumbOptions carries the preview thumbnail mode."
|
||||
},
|
||||
"titleFrom": {
|
||||
"description": "TitleFrom names the nested field whose value titles a repeater row.",
|
||||
"type": "string"
|
||||
},
|
||||
"translatable": {
|
||||
"description": "Translatable is true when a plugin field type's value is a map of\ncontent locale to string.",
|
||||
"type": "boolean"
|
||||
@@ -2287,6 +2306,20 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"cabana.RepeaterForm": {
|
||||
"properties": {
|
||||
"fields": {
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/cabana.FormField"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"fields"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"cabana.RowAction": {
|
||||
"properties": {
|
||||
"label": {
|
||||
@@ -4104,7 +4137,7 @@
|
||||
},
|
||||
"/{vendor}/{plugin}/{controller}/fields/{field}/dependent-options": {
|
||||
"get": {
|
||||
"description": "Choices of a `type: dropdown` field with `dependsOn`, from the controller's pact.DependentOptionsProvider, for the value of the sibling field the dropdown depends on. The sibling's value is passed as the query parameter `values[\u003csibling\u003e]` (for example `values[type]=acme-post`); a missing or empty value is null. `applies` is false when that value takes no choice at all (the field then stores null) and `options` is then empty; with `applies` true an empty `options` means there is nothing to choose yet. `depth` is the nesting level of a choice taken from a tree (0 at the top); labels are localized. A field that is not a dependsOn dropdown of the controller's form answers 404.",
|
||||
"description": "Choices of a `type: dropdown` field with `dependsOn`, from the controller's pact.DependentOptionsProvider, for the value of the sibling field the dropdown depends on. The sibling's value is passed as the query parameter `values[\u003csibling\u003e]` (for example `values[type]=acme-post`); a missing or empty value is null. `applies` is false when that value takes no choice at all (the field then stores null) and `options` is then empty; with `applies` true an empty `options` means there is nothing to choose yet. `depth` is the nesting level of a choice taken from a tree (0 at the top); labels are localized. For a dropdown inside a repeater row, {field} is `\u003crepeater\u003e.\u003cfield\u003e` and `values[\u003csibling\u003e]` is the row's sibling value. A field that is not a dependsOn dropdown of the controller's form answers 404.",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Vendor",
|
||||
@@ -4134,7 +4167,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"description": "Dropdown field name",
|
||||
"description": "Dropdown field name, or repeater.field for a dropdown in a repeater row",
|
||||
"in": "path",
|
||||
"name": "field",
|
||||
"required": true,
|
||||
@@ -4522,7 +4555,7 @@
|
||||
},
|
||||
"/{vendor}/{plugin}/{controller}/schema/form": {
|
||||
"get": {
|
||||
"description": "The form of a controller, localized. `preview` is present when config_form.yaml declares a preview block: the form then has a read-only preview screen, which shows the fields whose context allows preview, the record actions and, when preview.headerPartial is set, that partial as a status hint. A `type: password` field and every other field the controller lists as virtual is sent in a save body and never has a value in a record response. `preset` on a text or mltext field names the text or mltext field it follows on the create form (type slug or exact; an mltext pair follows per locale) until the administrator edits it. A `type: permissioneditor` field carries `permissionOptions`, the permissions the controller offers the requesting administrator; its value in a record response and in a save body is an object of permission code to integer (radio mode 1 or -1, checkbox mode 1). A `type: relation` field over a protected foreign key is `readOnly` unless its contract declares the key writable. A field whose type a plugin registers carries `element` (the custom-element tag the admin renders), `translatable` (its value is an object of content locale to string) and `typeOptions` (the type's own fields.yaml keys). A `type: dropdown` field with `dependsOn` names the sibling field whose value selects its options and carries no options: they come from the field's dependent-options route.",
|
||||
"description": "The form of a controller, localized. `preview` is present when config_form.yaml declares a preview block: the form then has a read-only preview screen, which shows the fields whose context allows preview, the record actions and, when preview.headerPartial is set, that partial as a status hint. A `type: password` field and every other field the controller lists as virtual is sent in a save body and never has a value in a record response. `preset` on a text or mltext field names the text or mltext field it follows on the create form (type slug or exact; an mltext pair follows per locale) until the administrator edits it. A `type: permissioneditor` field carries `permissionOptions`, the permissions the controller offers the requesting administrator; its value in a record response and in a save body is an object of permission code to integer (radio mode 1 or -1, checkbox mode 1). A `type: relation` field over a protected foreign key is `readOnly` unless its contract declares the key writable. A field whose type a plugin registers carries `element` (the custom-element tag the admin renders), `translatable` (its value is an object of content locale to string) and `typeOptions` (the type's own fields.yaml keys). A `type: dropdown` field with `dependsOn` names the sibling field whose value selects its options and carries no options: they come from the field's dependent-options route. A `type: richeditor` field's value is a string of HTML, sanitized on every save; what the sanitizer removed is reported in the save response's `meta.notices`. A `type: repeater` field carries `form.fields` (the fields of one row), `prompt`, `maxItems`, `minItems` and `titleFrom`; its value in a record response and in a save body is a list of row objects keyed by the nested field names, and a row's errors are keyed `\u003cfield\u003e.\u003cindex\u003e.\u003cnested\u003e`.",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Vendor",
|
||||
|
||||
19
admin/src/api/schema.d.ts
vendored
19
admin/src/api/schema.d.ts
vendored
@@ -1577,7 +1577,7 @@ export interface paths {
|
||||
};
|
||||
/**
|
||||
* Dependent dropdown options
|
||||
* @description Choices of a `type: dropdown` field with `dependsOn`, from the controller's pact.DependentOptionsProvider, for the value of the sibling field the dropdown depends on. The sibling's value is passed as the query parameter `values[<sibling>]` (for example `values[type]=acme-post`); a missing or empty value is null. `applies` is false when that value takes no choice at all (the field then stores null) and `options` is then empty; with `applies` true an empty `options` means there is nothing to choose yet. `depth` is the nesting level of a choice taken from a tree (0 at the top); labels are localized. A field that is not a dependsOn dropdown of the controller's form answers 404.
|
||||
* @description Choices of a `type: dropdown` field with `dependsOn`, from the controller's pact.DependentOptionsProvider, for the value of the sibling field the dropdown depends on. The sibling's value is passed as the query parameter `values[<sibling>]` (for example `values[type]=acme-post`); a missing or empty value is null. `applies` is false when that value takes no choice at all (the field then stores null) and `options` is then empty; with `applies` true an empty `options` means there is nothing to choose yet. `depth` is the nesting level of a choice taken from a tree (0 at the top); labels are localized. For a dropdown inside a repeater row, {field} is `<repeater>.<field>` and `values[<sibling>]` is the row's sibling value. A field that is not a dependsOn dropdown of the controller's form answers 404.
|
||||
*/
|
||||
get: {
|
||||
parameters: {
|
||||
@@ -1590,7 +1590,7 @@ export interface paths {
|
||||
plugin: string;
|
||||
/** @description Controller */
|
||||
controller: string;
|
||||
/** @description Dropdown field name */
|
||||
/** @description Dropdown field name, or repeater.field for a dropdown in a repeater row */
|
||||
field: string;
|
||||
};
|
||||
cookie?: never;
|
||||
@@ -1896,7 +1896,7 @@ export interface paths {
|
||||
};
|
||||
/**
|
||||
* Admin form schema
|
||||
* @description The form of a controller, localized. `preview` is present when config_form.yaml declares a preview block: the form then has a read-only preview screen, which shows the fields whose context allows preview, the record actions and, when preview.headerPartial is set, that partial as a status hint. A `type: password` field and every other field the controller lists as virtual is sent in a save body and never has a value in a record response. `preset` on a text or mltext field names the text or mltext field it follows on the create form (type slug or exact; an mltext pair follows per locale) until the administrator edits it. A `type: permissioneditor` field carries `permissionOptions`, the permissions the controller offers the requesting administrator; its value in a record response and in a save body is an object of permission code to integer (radio mode 1 or -1, checkbox mode 1). A `type: relation` field over a protected foreign key is `readOnly` unless its contract declares the key writable. A field whose type a plugin registers carries `element` (the custom-element tag the admin renders), `translatable` (its value is an object of content locale to string) and `typeOptions` (the type's own fields.yaml keys). A `type: dropdown` field with `dependsOn` names the sibling field whose value selects its options and carries no options: they come from the field's dependent-options route.
|
||||
* @description The form of a controller, localized. `preview` is present when config_form.yaml declares a preview block: the form then has a read-only preview screen, which shows the fields whose context allows preview, the record actions and, when preview.headerPartial is set, that partial as a status hint. A `type: password` field and every other field the controller lists as virtual is sent in a save body and never has a value in a record response. `preset` on a text or mltext field names the text or mltext field it follows on the create form (type slug or exact; an mltext pair follows per locale) until the administrator edits it. A `type: permissioneditor` field carries `permissionOptions`, the permissions the controller offers the requesting administrator; its value in a record response and in a save body is an object of permission code to integer (radio mode 1 or -1, checkbox mode 1). A `type: relation` field over a protected foreign key is `readOnly` unless its contract declares the key writable. A field whose type a plugin registers carries `element` (the custom-element tag the admin renders), `translatable` (its value is an object of content locale to string) and `typeOptions` (the type's own fields.yaml keys). A `type: dropdown` field with `dependsOn` names the sibling field whose value selects its options and carries no options: they come from the field's dependent-options route. A `type: richeditor` field's value is a string of HTML, sanitized on every save; what the sanitizer removed is reported in the save response's `meta.notices`. A `type: repeater` field carries `form.fields` (the fields of one row), `prompt`, `maxItems`, `minItems` and `titleFrom`; its value in a record response and in a save body is a list of row objects keyed by the nested field names, and a row's errors are keyed `<field>.<index>.<nested>`.
|
||||
*/
|
||||
get: {
|
||||
parameters: {
|
||||
@@ -5331,6 +5331,11 @@ export interface components {
|
||||
fill?: string[];
|
||||
/** @description FirstDay is the first day of the week, 0 (Sunday) to 6. */
|
||||
firstDay?: number;
|
||||
/**
|
||||
* @description Form holds the nested fields of a `type: repeater` field, one set per
|
||||
* row (D-07).
|
||||
*/
|
||||
form?: components["schemas"]["cabana.RepeaterForm"];
|
||||
/**
|
||||
* @description Format is a datepicker's WinterCMS (PHP date) display format;
|
||||
* DisplayFormat is the same format in the SPA's moment-style tokens.
|
||||
@@ -5347,6 +5352,8 @@ export interface components {
|
||||
maxFiles?: number;
|
||||
/** @description MaxFilesize is the largest accepted file in megabytes. */
|
||||
maxFilesize?: number;
|
||||
/** @description MaxItems and MinItems bound a repeater's row count, checked on save. */
|
||||
maxItems?: number;
|
||||
/**
|
||||
* @description MimeTypes are the allowed MIME patterns (or extensions) of a
|
||||
* fileupload field.
|
||||
@@ -5357,6 +5364,7 @@ export interface components {
|
||||
* checked again on save.
|
||||
*/
|
||||
minDate?: string;
|
||||
minItems?: number;
|
||||
/**
|
||||
* @description Mode is the fileupload mode (image or file, default file), the
|
||||
* datepicker mode (date, datetime or time, default datetime) or the
|
||||
@@ -5400,6 +5408,8 @@ export interface components {
|
||||
tab?: string;
|
||||
/** @description ThumbOptions carries the preview thumbnail mode. */
|
||||
thumbOptions?: components["schemas"]["cabana.ThumbOptions"];
|
||||
/** @description TitleFrom names the nested field whose value titles a repeater row. */
|
||||
titleFrom?: string;
|
||||
/**
|
||||
* @description Translatable is true when a plugin field type's value is a map of
|
||||
* content locale to string.
|
||||
@@ -5705,6 +5715,9 @@ export interface components {
|
||||
view: components["schemas"]["cabana.RelationPanel"];
|
||||
viewForm?: components["schemas"]["cabana.FormField"][];
|
||||
};
|
||||
"cabana.RepeaterForm": {
|
||||
fields: components["schemas"]["cabana.FormField"][];
|
||||
};
|
||||
"cabana.RowAction": {
|
||||
label?: string;
|
||||
name: string;
|
||||
|
||||
Reference in New Issue
Block a user