@simai-studio/ui 5.20261023.16 → 5.20261023.214

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +4 -4
  2. package/distr/component/accordion/css/accordion.min.css +1 -1
  3. package/distr/component/accordion/js/accordion.min.js +1 -1
  4. package/distr/component/modal/css/modal.min.css +1 -1
  5. package/distr/component/modal/js/modal.min.js +1 -1
  6. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/a-field-hidden-by-its-condition-is-not-saved.json +45 -0
  7. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/a-field-says-what-it-falls-back-to.json +12 -0
  8. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/a-field-taken-out-of-an-area-is-unplaced.json +40 -0
  9. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/a-reader-is-offered-nothing-to-change.json +23 -0
  10. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/a-related-group-is-a-list-not-fields.json +23 -0
  11. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/a-stale-confirmation-is-asked-again.json +39 -0
  12. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/conditions-arrive-declared.json +82 -0
  13. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/create-applied-answers-with-the-id.json +24 -0
  14. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/create-field-errors-are-refused.json +22 -0
  15. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/field-presentation-keys-are-declared.json +12 -0
  16. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/group-save-applied-is-one-revision.json +16 -0
  17. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/group-save-confirmed-with-the-digest-is-applied.json +35 -0
  18. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/group-save-field-errors-are-refused.json +16 -0
  19. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/group-save-may-ask-for-a-confirmation.json +38 -0
  20. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/group-save-stale-revision-is-a-conflict.json +16 -0
  21. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/load-applied-carries-the-object.json +12 -0
  22. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/load-for-create-is-an-empty-object.json +24 -0
  23. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/load-late-answer-is-dropped.json +15 -0
  24. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/load-refused-is-explained.json +12 -0
  25. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/load-unavailable-is-honest.json +12 -0
  26. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/lookup-arrives-with-its-label.json +70 -0
  27. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/opening-a-reference-is-the-hosts-decision.json +12 -0
  28. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/opening-the-record-as-a-page-is-offered-only-where-it-leads-somewhere.json +28 -0
  29. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/options-carry-the-source-and-a-limit.json +33 -0
  30. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/options-page-carries-a-cursor.json +15 -0
  31. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/options-refused-is-explained.json +14 -0
  32. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/options-search-by-source-reaches-the-host.json +30 -0
  33. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/options-search-reaches-the-host.json +14 -0
  34. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/options-without-the-capability.json +14 -0
  35. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/rare-fields-arrive-declared.json +12 -0
  36. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/removing-a-file-is-a-save.json +13 -0
  37. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/save-applied-is-one-revision.json +26 -0
  38. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/save-may-ask-for-a-confirmation.json +36 -0
  39. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/save-stale-revision-is-a-conflict.json +23 -0
  40. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/the-address-is-the-hosts.json +12 -0
  41. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/upload-answers-with-the-stored-file.json +14 -0
  42. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/upload-refused-is-explained.json +14 -0
  43. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-create-answers-with-the-record-through-the-new-view.json +32 -0
  44. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-create-shared-may-be-refused.json +27 -0
  45. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-delete-applied-answers-with-what-is-left.json +25 -0
  46. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-delete-the-last-shared-one-is-refused.json +25 -0
  47. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-profile-applied-carries-the-record-through-it.json +25 -0
  48. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-profile-refused-is-explained.json +25 -0
  49. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-profile-unavailable-without-the-capability.json +23 -0
  50. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-rename-changes-the-label-only.json +26 -0
  51. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-reset-answers-with-what-now-resolves.json +30 -0
  52. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-save-against-a-stale-revision-is-a-conflict.json +36 -0
  53. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-save-is-one-layer-one-revision.json +35 -0
  54. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-save-may-name-an-area-the-record-does-not-carry.json +49 -0
  55. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-save-rejects-entries-by-kind.json +36 -0
  56. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/cases/view-settings-without-the-capability.json +30 -0
  57. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/kit/manifest.json +66 -0
  58. package/distr/core/contracts/ports/simai.property-view-port/1.14.0/port.json +2484 -0
  59. package/distr/core/contracts/public-variables.v1.json +7 -0
  60. package/distr/core/css/core.css +14 -0
  61. package/distr/core/css/core.min.css +1 -1
  62. package/distr/smart/drawer/css/drawer.min.css +1 -1
  63. package/distr/smart/drawer/js/drawer.min.js +9 -8
  64. package/distr/smart/drawer/smart.manifest.json +12 -6
  65. package/distr/smart/field-renderers/js/field-renderers.min.js +128 -87
  66. package/distr/smart/modal/js/modal.min.js +1 -1
  67. package/distr/smart/modal/smart.manifest.json +3 -2
  68. package/distr/smart/modal/template/default.js +1 -1
  69. package/package-manifest.json +70 -17
  70. package/package.json +4 -4
@@ -0,0 +1,26 @@
1
+ {
2
+ "id": "save-applied-is-one-revision",
3
+ "intent": "record.save",
4
+ "summary": "A whole-object save is one revision carrying only the changed fields.",
5
+ "given": {
6
+ "capabilities": []
7
+ },
8
+ "when": {
9
+ "intent": "record.save",
10
+ "payload": {
11
+ "record_id": "record-1",
12
+ "collection": "pages",
13
+ "values": {
14
+ "title": "Другое название",
15
+ "slug": "other"
16
+ },
17
+ "revision": "7"
18
+ }
19
+ },
20
+ "then": {
21
+ "answer": "applied",
22
+ "answer_must_include": [
23
+ "record"
24
+ ]
25
+ }
26
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "id": "save-may-ask-for-a-confirmation",
3
+ "intent": "record.save",
4
+ "summary": "A whole-object save may be confirmed first, the same way as a group save.",
5
+ "given": {
6
+ "capabilities": [],
7
+ "confirms": [
8
+ "record.save"
9
+ ]
10
+ },
11
+ "when": {
12
+ "intent": "record.save",
13
+ "payload": {
14
+ "record_id": "record-1",
15
+ "collection": "contracts",
16
+ "values": {
17
+ "type": {
18
+ "value": "framework",
19
+ "label": "Рамочный"
20
+ }
21
+ },
22
+ "revision": "7"
23
+ }
24
+ },
25
+ "then": {
26
+ "answer": "refused",
27
+ "answer_must_include": [
28
+ "reason",
29
+ "confirm"
30
+ ],
31
+ "data_must_include": [
32
+ "confirm"
33
+ ],
34
+ "note": "reason is confirm_required. Raised again with confirm set to the digest, the same payload is applied."
35
+ }
36
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "id": "save-stale-revision-is-a-conflict",
3
+ "intent": "record.save",
4
+ "summary": "A whole-object save against a moved revision is a conflict, never an overwrite.",
5
+ "given": {
6
+ "capabilities": []
7
+ },
8
+ "when": {
9
+ "intent": "record.save",
10
+ "payload": {
11
+ "record_id": "record-1",
12
+ "collection": "pages",
13
+ "values": {
14
+ "title": "Другое название"
15
+ },
16
+ "revision": "6"
17
+ }
18
+ },
19
+ "then": {
20
+ "answer": "conflict",
21
+ "note": "The person's values stay on screen with the conflict said."
22
+ }
23
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "id": "the-address-is-the-hosts",
3
+ "intent": "record.load",
4
+ "summary": "A window's address comes with the object, and a host that sends none gets no copy or open controls rather than an invented link.",
5
+ "given": {"capabilities": []},
6
+ "when": {"intent": "record.load", "payload": {"record_id": "record-1", "collection": "pages", "reason": "connected"}},
7
+ "then": {
8
+ "answer": "applied",
9
+ "answer_must_include": ["record"],
10
+ "note": "record.address is optional. The component copies it and opens it and constructs nothing: a composite that guessed at a URL would be guessing at the consumer's routing, and would be wrong first in whichever consumer did not match the guess."
11
+ }
12
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "upload-answers-with-the-stored-file",
3
+ "intent": "field.upload",
4
+ "capability": "field-upload",
5
+ "summary": "A file the person chose is stored by the host, which answers with it in the shape the field reads.",
6
+ "given": {"capabilities": ["field-upload"]},
7
+ "when": {"intent": "field.upload", "payload": {"record_id": "record-1", "collection": "pages",
8
+ "field": "document", "file": "<the File the person chose>"}},
9
+ "then": {
10
+ "answer": "applied",
11
+ "answer_must_include": ["file"],
12
+ "note": "The answer carries {value, label, mime?, href?} -- the same shape the field already reads -- so the view shows the stored file without a second read. The File is passed as it is: a component that encoded it to base64 would double the bytes of every upload for nothing."
13
+ }
14
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "id": "upload-refused-is-explained",
3
+ "intent": "field.upload",
4
+ "capability": "field-upload",
5
+ "summary": "A file the host will not take is refused with a reason the field shows, and nothing is added to the value.",
6
+ "given": {"capabilities": ["field-upload"]},
7
+ "when": {"intent": "field.upload", "payload": {"record_id": "record-1", "collection": "pages",
8
+ "field": "document", "file": "<a file too large, or of a kind the host rejects>"}},
9
+ "then": {
10
+ "answer": "refused",
11
+ "answer_must_include": ["reason"],
12
+ "note": "The reason is shown under the field. A silent failure leaves a person looking at a field that did not change and no way to know why."
13
+ }
14
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "intent": "view.create",
3
+ "summary": "Making a view answers with the record through it: a person who has just made one is looking at it.",
4
+ "given": {
5
+ "capabilities": [
6
+ "view-settings"
7
+ ]
8
+ },
9
+ "when": {
10
+ "intent": "view.create",
11
+ "payload": {
12
+ "record_id": "record-1",
13
+ "collection": "pages",
14
+ "label": "Для продаж",
15
+ "shared": true,
16
+ "view": {
17
+ "schema_version": 1,
18
+ "sections": {},
19
+ "groups": {},
20
+ "fields": {}
21
+ }
22
+ }
23
+ },
24
+ "then": {
25
+ "answer": "applied",
26
+ "answer_must_include": [
27
+ "record"
28
+ ],
29
+ "note": "The answer carries the record and its view, so the new view's id and its place in the list come from the host rather than being guessed at here."
30
+ },
31
+ "id": "view-create-answers-with-the-record-through-the-new-view"
32
+ }
@@ -0,0 +1,27 @@
1
+ {
2
+ "intent": "view.create",
3
+ "summary": "A person who may not write the shared layer may not make a shared view either.",
4
+ "given": {
5
+ "capabilities": [
6
+ "view-settings"
7
+ ]
8
+ },
9
+ "when": {
10
+ "intent": "view.create",
11
+ "payload": {
12
+ "record_id": "record-1",
13
+ "collection": "pages",
14
+ "label": "Для всех",
15
+ "shared": true,
16
+ "view": {}
17
+ }
18
+ },
19
+ "then": {
20
+ "answer": "refused",
21
+ "answer_must_include": [
22
+ "reason"
23
+ ],
24
+ "note": "The same right that governs the shared layer governs a shared view; saying so is what stops a person making one and losing it on the next read."
25
+ },
26
+ "id": "view-create-shared-may-be-refused"
27
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "intent": "view.delete",
3
+ "summary": "Removing a view answers with the record through whichever view is now current.",
4
+ "given": {
5
+ "capabilities": [
6
+ "view-settings"
7
+ ]
8
+ },
9
+ "when": {
10
+ "intent": "view.delete",
11
+ "payload": {
12
+ "record_id": "record-1",
13
+ "collection": "pages",
14
+ "profile": "mine"
15
+ }
16
+ },
17
+ "then": {
18
+ "answer": "applied",
19
+ "answer_must_include": [
20
+ "record"
21
+ ],
22
+ "note": "Deleting the view a person was looking at leaves them looking at something; which one is the host's to decide and is said in the answer."
23
+ },
24
+ "id": "view-delete-applied-answers-with-what-is-left"
25
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "intent": "view.delete",
3
+ "summary": "The last shared view cannot be removed: a scope with none has nothing for a person who has made none of their own.",
4
+ "given": {
5
+ "capabilities": [
6
+ "view-settings"
7
+ ]
8
+ },
9
+ "when": {
10
+ "intent": "view.delete",
11
+ "payload": {
12
+ "record_id": "record-1",
13
+ "collection": "pages",
14
+ "profile": "default"
15
+ }
16
+ },
17
+ "then": {
18
+ "answer": "refused",
19
+ "answer_must_include": [
20
+ "reason"
21
+ ],
22
+ "note": "The component does not offer the control in that case either, so this refusal is a guard rather than the way a person finds out."
23
+ },
24
+ "id": "view-delete-the-last-shared-one-is-refused"
25
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "id": "view-profile-applied-carries-the-record-through-it",
3
+ "intent": "view.profile",
4
+ "summary": "Choosing a view answers with the record read through it, because a view is a whole arrangement rather than a filter over the one already on screen.",
5
+ "given": {
6
+ "capabilities": [
7
+ "view-settings"
8
+ ]
9
+ },
10
+ "when": {
11
+ "intent": "view.profile",
12
+ "payload": {
13
+ "record_id": "record-1",
14
+ "collection": "pages",
15
+ "profile": "expert"
16
+ }
17
+ },
18
+ "then": {
19
+ "answer": "applied",
20
+ "answer_must_include": [
21
+ "record"
22
+ ],
23
+ "note": "Answering applied with nothing else would leave the component holding the old arrangement under a new name."
24
+ }
25
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "id": "view-profile-refused-is-explained",
3
+ "intent": "view.profile",
4
+ "summary": "A view this person may not open is refused with a reason, not silently ignored.",
5
+ "given": {
6
+ "capabilities": [
7
+ "view-settings"
8
+ ]
9
+ },
10
+ "when": {
11
+ "intent": "view.profile",
12
+ "payload": {
13
+ "record_id": "record-1",
14
+ "collection": "pages",
15
+ "profile": "profile-forbidden"
16
+ }
17
+ },
18
+ "then": {
19
+ "answer": "refused",
20
+ "answer_must_include": [
21
+ "reason"
22
+ ],
23
+ "note": "Without the reason a chooser that snaps back looks broken rather than guarded."
24
+ }
25
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "id": "view-profile-unavailable-without-the-capability",
3
+ "intent": "view.profile",
4
+ "summary": "A host that does not configure views cannot switch between them either.",
5
+ "given": {
6
+ "capabilities": []
7
+ },
8
+ "when": {
9
+ "intent": "view.profile",
10
+ "payload": {
11
+ "record_id": "record-1",
12
+ "collection": "pages",
13
+ "profile": "expert"
14
+ }
15
+ },
16
+ "then": {
17
+ "answer": "unavailable",
18
+ "answer_must_include": [
19
+ "reason"
20
+ ],
21
+ "note": "The component draws no chooser without the capability; this is what a host answers if one is asked for anyway."
22
+ }
23
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "intent": "view.rename",
3
+ "summary": "A rename changes the label. The id is permanent, so anything pointing at the view keeps pointing at it.",
4
+ "given": {
5
+ "capabilities": [
6
+ "view-settings"
7
+ ]
8
+ },
9
+ "when": {
10
+ "intent": "view.rename",
11
+ "payload": {
12
+ "record_id": "record-1",
13
+ "collection": "pages",
14
+ "profile": "expert",
15
+ "label": "Экспертный режим"
16
+ }
17
+ },
18
+ "then": {
19
+ "answer": "applied",
20
+ "answer_must_include": [
21
+ "record"
22
+ ],
23
+ "note": "Answering with the record is how the list of views comes back renamed without a second read."
24
+ },
25
+ "id": "view-rename-changes-the-label-only"
26
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "id": "view-reset-answers-with-what-now-resolves",
3
+ "intent": "view.reset",
4
+ "capability": "view-settings",
5
+ "summary": "Clearing a layer answers with the arrangement that resolves after it is gone, so the panel redraws without reading the record again.",
6
+ "given": {
7
+ "capabilities": [
8
+ "view-settings"
9
+ ]
10
+ },
11
+ "when": {
12
+ "intent": "view.reset",
13
+ "payload": {
14
+ "record_id": null,
15
+ "collection": "pages",
16
+ "target": {
17
+ "kind": "personal",
18
+ "revision": "v7"
19
+ }
20
+ }
21
+ },
22
+ "then": {
23
+ "answer": "applied",
24
+ "answer_must_include": [
25
+ "revision",
26
+ "view"
27
+ ],
28
+ "note": "A reset is a write against the revision, not a deletion, and the component never sends its own idea of the defaults -- the host owns those, and writing them would overwrite them with a guess. The answer carries what now resolves because a second read would show the person a reload for a change they already made."
29
+ }
30
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "id": "view-save-against-a-stale-revision-is-a-conflict",
3
+ "intent": "view.save",
4
+ "capability": "view-settings",
5
+ "summary": "A save against a revision someone else has moved past is a conflict carrying the layer as it now stands.",
6
+ "given": {
7
+ "capabilities": [
8
+ "view-settings"
9
+ ]
10
+ },
11
+ "when": {
12
+ "intent": "view.save",
13
+ "payload": {
14
+ "record_id": null,
15
+ "collection": "pages",
16
+ "target": {
17
+ "kind": "shared",
18
+ "revision": "v2"
19
+ },
20
+ "view": {
21
+ "schema_version": 1,
22
+ "sections": {},
23
+ "groups": {},
24
+ "fields": {}
25
+ }
26
+ }
27
+ },
28
+ "then": {
29
+ "answer": "conflict",
30
+ "answer_must_include": [
31
+ "revision",
32
+ "view"
33
+ ],
34
+ "note": "The conflict carries the stored layer, not only the revision. Without it the component would be left drawing an arrangement nobody stored, and the person would go on dragging fields in a view that no longer exists."
35
+ }
36
+ }
@@ -0,0 +1,35 @@
1
+ {
2
+ "id": "view-save-is-one-layer-one-revision",
3
+ "intent": "view.save",
4
+ "capability": "view-settings",
5
+ "summary": "A change to the arrangement writes the whole layer against its revision and answers with the revision the layer now has.",
6
+ "given": {
7
+ "capabilities": [
8
+ "view-settings"
9
+ ]
10
+ },
11
+ "when": {
12
+ "intent": "view.save",
13
+ "payload": {
14
+ "record_id": null,
15
+ "collection": "pages",
16
+ "target": {
17
+ "kind": "personal",
18
+ "revision": "v7"
19
+ },
20
+ "view": {
21
+ "schema_version": 1,
22
+ "sections": {},
23
+ "groups": {},
24
+ "fields": {}
25
+ }
26
+ }
27
+ },
28
+ "then": {
29
+ "answer": "applied",
30
+ "answer_must_include": [
31
+ "revision"
32
+ ],
33
+ "note": "The layer is sent whole, never as a patch of the entry that changed. Two people reordering the same view with per-entry writes would interleave into an arrangement neither of them chose; whole-layer compare-and-set makes them collide, which is the same reason a group saves as one revision."
34
+ }
35
+ }
@@ -0,0 +1,49 @@
1
+ {
2
+ "id": "view-save-may-name-an-area-the-record-does-not-carry",
3
+ "intent": "view.save",
4
+ "capability": "view-settings",
5
+ "summary": "A layer may name an area or a section the record does not carry. The host keeps it and returns it in the next read, or refuses with its reason.",
6
+ "given": {
7
+ "capabilities": [
8
+ "view-settings"
9
+ ]
10
+ },
11
+ "when": {
12
+ "intent": "view.save",
13
+ "payload": {
14
+ "record_id": null,
15
+ "collection": "pages",
16
+ "target": {
17
+ "kind": "personal",
18
+ "revision": "v7"
19
+ },
20
+ "view": {
21
+ "schema_version": 1,
22
+ "sections": {
23
+ "view-section-m1": {
24
+ "label": "Договоры",
25
+ "order": 2,
26
+ "hidden": false
27
+ }
28
+ },
29
+ "groups": {
30
+ "view-group-m1": {
31
+ "label": "Договор",
32
+ "section": "view-section-m1",
33
+ "order": 1,
34
+ "columns": 1,
35
+ "hidden": false
36
+ }
37
+ },
38
+ "fields": {}
39
+ }
40
+ }
41
+ },
42
+ "then": {
43
+ "answer": "applied",
44
+ "answer_must_include": [
45
+ "revision"
46
+ ],
47
+ "note": "This is the one act of arranging that does not rearrange what the host sent: it names something new. The component draws it at once from the layer, so a person sees what they made, but it survives the next record.load only if the host returns it. A host that will not keep a new area answers refused with a reason rather than applied, and the component reads the record again."
48
+ }
49
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "id": "view-save-rejects-entries-by-kind",
3
+ "intent": "view.save",
4
+ "capability": "view-settings",
5
+ "summary": "A layer naming an entry the host does not know is refused, with the reason under the entry that caused it, keyed by kind and key.",
6
+ "given": {
7
+ "capabilities": [
8
+ "view-settings"
9
+ ]
10
+ },
11
+ "when": {
12
+ "intent": "view.save",
13
+ "payload": {
14
+ "record_id": null,
15
+ "collection": "pages",
16
+ "target": {
17
+ "kind": "personal",
18
+ "revision": "v7"
19
+ },
20
+ "view": {
21
+ "schema_version": 1,
22
+ "sections": {
23
+ "ghost": {
24
+ "order": 1
25
+ }
26
+ },
27
+ "groups": {},
28
+ "fields": {}
29
+ }
30
+ }
31
+ },
32
+ "then": {
33
+ "answer": "refused",
34
+ "note": "Keyed by kind -- section:ghost -- because a section and a field may share a key. A bare key could name two different entries, and the error would be shown under the wrong one, which is worse than no error at all: it accuses something that is fine."
35
+ }
36
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "id": "view-settings-without-the-capability",
3
+ "intent": "view.save",
4
+ "capability": "view-settings",
5
+ "summary": "A host that does not declare view-settings is never asked to save a view, and the component shows nothing that would lead there.",
6
+ "given": {
7
+ "capabilities": []
8
+ },
9
+ "when": {
10
+ "intent": "view.save",
11
+ "payload": {
12
+ "record_id": null,
13
+ "collection": "pages",
14
+ "target": {
15
+ "kind": "personal",
16
+ "revision": null
17
+ },
18
+ "view": {
19
+ "schema_version": 1,
20
+ "sections": {},
21
+ "groups": {},
22
+ "fields": {}
23
+ }
24
+ }
25
+ },
26
+ "then": {
27
+ "answer": "unavailable",
28
+ "note": "The control is absent rather than present and failing. A person offered a way to rearrange a view that cannot be stored would lose the arrangement on the next load and have no way to know why."
29
+ }
30
+ }
@@ -0,0 +1,66 @@
1
+ {
2
+ "schema": "simai.composite.host-port.conformance.v1",
3
+ "port": "simai.property-view-port",
4
+ "version": "1.14.0",
5
+ "transport": "neutral",
6
+ "summary": "The cases of 1.13.0's kit, unchanged, and nine of 1.14.0: a lookup field and its candidates by source and limit, a save confirmed with a digest, and fields that arrive with their conditions.",
7
+ "answers": [
8
+ "applied",
9
+ "conflict",
10
+ "refused",
11
+ "unavailable"
12
+ ],
13
+ "cases": [
14
+ "load-applied-carries-the-object",
15
+ "load-refused-is-explained",
16
+ "load-unavailable-is-honest",
17
+ "load-late-answer-is-dropped",
18
+ "rare-fields-arrive-declared",
19
+ "group-save-applied-is-one-revision",
20
+ "group-save-stale-revision-is-a-conflict",
21
+ "group-save-field-errors-are-refused",
22
+ "options-page-carries-a-cursor",
23
+ "options-search-reaches-the-host",
24
+ "options-without-the-capability",
25
+ "options-refused-is-explained",
26
+ "the-address-is-the-hosts",
27
+ "upload-answers-with-the-stored-file",
28
+ "upload-refused-is-explained",
29
+ "removing-a-file-is-a-save",
30
+ "opening-a-reference-is-the-hosts-decision",
31
+ "field-presentation-keys-are-declared",
32
+ "view-save-is-one-layer-one-revision",
33
+ "view-save-against-a-stale-revision-is-a-conflict",
34
+ "view-save-rejects-entries-by-kind",
35
+ "view-reset-answers-with-what-now-resolves",
36
+ "view-settings-without-the-capability",
37
+ "a-related-group-is-a-list-not-fields",
38
+ "a-field-says-what-it-falls-back-to",
39
+ "view-save-may-name-an-area-the-record-does-not-carry",
40
+ "a-field-taken-out-of-an-area-is-unplaced",
41
+ "opening-the-record-as-a-page-is-offered-only-where-it-leads-somewhere",
42
+ "view-profile-applied-carries-the-record-through-it",
43
+ "view-profile-refused-is-explained",
44
+ "view-profile-unavailable-without-the-capability",
45
+ "view-create-answers-with-the-record-through-the-new-view",
46
+ "view-create-shared-may-be-refused",
47
+ "view-rename-changes-the-label-only",
48
+ "view-delete-the-last-shared-one-is-refused",
49
+ "view-delete-applied-answers-with-what-is-left",
50
+ "load-for-create-is-an-empty-object",
51
+ "a-reader-is-offered-nothing-to-change",
52
+ "create-applied-answers-with-the-id",
53
+ "create-field-errors-are-refused",
54
+ "save-applied-is-one-revision",
55
+ "save-stale-revision-is-a-conflict",
56
+ "lookup-arrives-with-its-label",
57
+ "options-carry-the-source-and-a-limit",
58
+ "options-search-by-source-reaches-the-host",
59
+ "group-save-may-ask-for-a-confirmation",
60
+ "group-save-confirmed-with-the-digest-is-applied",
61
+ "a-stale-confirmation-is-asked-again",
62
+ "save-may-ask-for-a-confirmation",
63
+ "conditions-arrive-declared",
64
+ "a-field-hidden-by-its-condition-is-not-saved"
65
+ ]
66
+ }