feat(17-02): repeater field and dependent dropdown states
- RepeaterField: rows with drag handle, index, titleFrom title, error chip, move up/down (buttons, ArrowUp/ArrowDown on the handle, native drag and drop), expand/collapse, remove (asks only for a row holding a value), add with nested defaults, maxItems counter, empty state, collapse all; every change edits form state only - 422 row paths (content.3.answer) open the row, mark it and focus the nested control; the banner no longer repeats row messages - DropdownField dependsOn: waiting, loading, not used, empty, loaded with depth indent, failed with retry; cached per sibling value, last request wins, a stale value is cleared after a sibling change and announced - FormView provides form feedback (errors and notices by row path, the polite live region, its confirm dialog) and the options cache; FormGrid provides the sibling scope for repeater rows - repeater.* and form.dependent_* phrase keys (en, pl), smoke specs, docs
This commit is contained in:
142
admin/tests/fixtures/companions.form-schema.json
vendored
142
admin/tests/fixtures/companions.form-schema.json
vendored
@@ -3,25 +3,139 @@
|
||||
"name": "Link",
|
||||
"modelClass": "Link",
|
||||
"fields": [
|
||||
{ "name": "title", "type": "text", "label": "Title", "span": "left" },
|
||||
{ "name": "description", "type": "richeditor", "label": "Description", "comment": "Shown under the title." }
|
||||
{
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"label": "Title",
|
||||
"span": "left"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"type": "dropdown",
|
||||
"label": "Type",
|
||||
"span": "right",
|
||||
"options": [
|
||||
{
|
||||
"value": "url",
|
||||
"label": "Link"
|
||||
},
|
||||
{
|
||||
"value": "post",
|
||||
"label": "Post"
|
||||
},
|
||||
{
|
||||
"value": "category",
|
||||
"label": "Category"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "reference",
|
||||
"type": "dropdown",
|
||||
"label": "Reference",
|
||||
"span": "left",
|
||||
"dependsOn": "type"
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"type": "richeditor",
|
||||
"label": "Description",
|
||||
"comment": "Shown under the title."
|
||||
},
|
||||
{
|
||||
"name": "rows",
|
||||
"type": "repeater",
|
||||
"label": "Questions",
|
||||
"span": "full",
|
||||
"prompt": "Add question",
|
||||
"titleFrom": "question",
|
||||
"maxItems": 3,
|
||||
"form": {
|
||||
"fields": [
|
||||
{
|
||||
"name": "question",
|
||||
"type": "text",
|
||||
"label": "Question",
|
||||
"span": "left",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "kind",
|
||||
"type": "dropdown",
|
||||
"label": "Kind",
|
||||
"span": "right",
|
||||
"options": [
|
||||
{
|
||||
"value": "post",
|
||||
"label": "Post"
|
||||
},
|
||||
{
|
||||
"value": "url",
|
||||
"label": "Link"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "target",
|
||||
"type": "dropdown",
|
||||
"label": "Target",
|
||||
"span": "left",
|
||||
"dependsOn": "kind"
|
||||
},
|
||||
{
|
||||
"name": "visible",
|
||||
"type": "checkbox",
|
||||
"label": "Visible",
|
||||
"span": "right",
|
||||
"default": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"messages": {
|
||||
"create": { "other": "New link" },
|
||||
"update": { "other": "Edit link" },
|
||||
"saved": { "other": "Saved" },
|
||||
"deleteConfirm": { "other": "Delete this record?" },
|
||||
"deleted": { "other": "Record deleted" },
|
||||
"preview": { "other": "Link details" },
|
||||
"edit": { "other": "Edit link" }
|
||||
"create": {
|
||||
"other": "New link"
|
||||
},
|
||||
"update": {
|
||||
"other": "Edit link"
|
||||
},
|
||||
"saved": {
|
||||
"other": "Saved"
|
||||
},
|
||||
"deleteConfirm": {
|
||||
"other": "Delete this record?"
|
||||
},
|
||||
"deleted": {
|
||||
"other": "Record deleted"
|
||||
},
|
||||
"preview": {
|
||||
"other": "Link details"
|
||||
},
|
||||
"edit": {
|
||||
"other": "Edit link"
|
||||
}
|
||||
},
|
||||
"redirects": {
|
||||
"default": "acme/companions/links",
|
||||
"create": { "redirect": "acme/companions/links/:id", "redirectClose": "acme/companions/links" },
|
||||
"update": { "redirect": "acme/companions/links", "redirectClose": "acme/companions/links" }
|
||||
"create": {
|
||||
"redirect": "acme/companions/links/:id",
|
||||
"redirectClose": "acme/companions/links"
|
||||
},
|
||||
"update": {
|
||||
"redirect": "acme/companions/links",
|
||||
"redirectClose": "acme/companions/links"
|
||||
}
|
||||
},
|
||||
"meta": { "locale": "en" },
|
||||
"assets": { "scripts": [], "styles": [] }
|
||||
"meta": {
|
||||
"locale": "en"
|
||||
},
|
||||
"assets": {
|
||||
"scripts": [],
|
||||
"styles": []
|
||||
}
|
||||
},
|
||||
"meta": { "locale": "en" }
|
||||
"meta": {
|
||||
"locale": "en"
|
||||
}
|
||||
}
|
||||
|
||||
75
admin/tests/fixtures/lang.json
vendored
75
admin/tests/fixtures/lang.json
vendored
@@ -252,6 +252,27 @@
|
||||
"backend::lang.form.deleting": {
|
||||
"other": "Usuwanie…"
|
||||
},
|
||||
"backend::lang.form.dependent_cleared": {
|
||||
"other": "Wyczyszczono pole „:field”, bo zmieniły się jego opcje."
|
||||
},
|
||||
"backend::lang.form.dependent_empty": {
|
||||
"other": "Brak rekordów typu „:type”. Najpierw dodaj rekord albo wybierz inny typ."
|
||||
},
|
||||
"backend::lang.form.dependent_failed": {
|
||||
"other": "Nie udało się wczytać opcji."
|
||||
},
|
||||
"backend::lang.form.dependent_invalid": {
|
||||
"other": "Wybierz jedną z dostępnych opcji."
|
||||
},
|
||||
"backend::lang.form.dependent_not_used": {
|
||||
"other": "Nie dotyczy tego wyboru"
|
||||
},
|
||||
"backend::lang.form.dependent_retry": {
|
||||
"other": "Ponów wczytywanie opcji"
|
||||
},
|
||||
"backend::lang.form.dependent_waiting": {
|
||||
"other": "Najpierw wybierz pole „:field”"
|
||||
},
|
||||
"backend::lang.form.discard": {
|
||||
"other": "Porzuć zmiany"
|
||||
},
|
||||
@@ -609,6 +630,60 @@
|
||||
"backend::lang.relation.unlink": {
|
||||
"other": "Odłącz"
|
||||
},
|
||||
"backend::lang.repeater.add": {
|
||||
"other": "Dodaj pozycję"
|
||||
},
|
||||
"backend::lang.repeater.collapse": {
|
||||
"other": "Zwiń: :title"
|
||||
},
|
||||
"backend::lang.repeater.collapse_all": {
|
||||
"other": "Zwiń wszystkie"
|
||||
},
|
||||
"backend::lang.repeater.count": {
|
||||
"other": "Pozycje: :current z :max"
|
||||
},
|
||||
"backend::lang.repeater.empty": {
|
||||
"other": "Brak pozycji"
|
||||
},
|
||||
"backend::lang.repeater.empty_hint": {
|
||||
"other": "Użyj przycisku „:prompt” poniżej, aby dodać pierwszą."
|
||||
},
|
||||
"backend::lang.repeater.expand": {
|
||||
"other": "Rozwiń: :title"
|
||||
},
|
||||
"backend::lang.repeater.expand_all": {
|
||||
"other": "Rozwiń wszystkie"
|
||||
},
|
||||
"backend::lang.repeater.max_items": {
|
||||
"other": "Usuń pozycje, aby zostało ich najwyżej :max."
|
||||
},
|
||||
"backend::lang.repeater.min_items": {
|
||||
"other": "Dodaj pozycje, aby było ich co najmniej :min."
|
||||
},
|
||||
"backend::lang.repeater.move": {
|
||||
"other": "Przenieś: :title"
|
||||
},
|
||||
"backend::lang.repeater.move_down": {
|
||||
"other": "Przenieś niżej: :title"
|
||||
},
|
||||
"backend::lang.repeater.move_up": {
|
||||
"other": "Przenieś wyżej: :title"
|
||||
},
|
||||
"backend::lang.repeater.moved": {
|
||||
"other": "Przeniesiono :title na pozycję :position z :total."
|
||||
},
|
||||
"backend::lang.repeater.remove": {
|
||||
"other": "Usuń pozycję"
|
||||
},
|
||||
"backend::lang.repeater.remove_confirm": {
|
||||
"other": "Usunąć „:title”? Zmiana zostanie zapisana razem z rekordem."
|
||||
},
|
||||
"backend::lang.repeater.remove_named": {
|
||||
"other": "Usuń: :title"
|
||||
},
|
||||
"backend::lang.repeater.untitled": {
|
||||
"other": "Pozycja :n"
|
||||
},
|
||||
"backend::lang.richeditor.bold": {
|
||||
"other": "Pogrubienie"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user