@cosmicdrift/kumiko-renderer 0.215.2 → 0.215.4

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-renderer",
3
- "version": "0.215.2",
3
+ "version": "0.215.4",
4
4
  "description": "Platform-agnostic React renderer for Kumiko screens. Contains the shared logic — primitives-contract, hooks, KumikoScreen, navigation & SSE abstractions — that any platform-specific renderer (web, native) composes. No DOM, no EventSource, no react-dom.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -15,8 +15,8 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
- "@cosmicdrift/kumiko-framework": "0.215.2",
19
- "@cosmicdrift/kumiko-headless": "0.215.2",
18
+ "@cosmicdrift/kumiko-framework": "0.215.4",
19
+ "@cosmicdrift/kumiko-headless": "0.215.4",
20
20
  "react": "^19.2.6",
21
21
  "temporal-polyfill": "^0.3.2",
22
22
  "zod": "^4.4.3"
@@ -25,7 +25,7 @@
25
25
  "@testing-library/react": "^16.3.2",
26
26
  "@types/react": "^19.2.14",
27
27
  "jsdom": "^29.1.1",
28
- "@cosmicdrift/kumiko-locale-de": "0.215.2"
28
+ "@cosmicdrift/kumiko-locale-de": "0.215.4"
29
29
  },
30
30
  "repository": {
31
31
  "type": "git",
@@ -88,6 +88,11 @@ const extensionSection: EditSectionViewModel = {
88
88
  kind: "extension",
89
89
  title: "Custom",
90
90
  component: {},
91
+ contributesToFormSubmit: false,
92
+ };
93
+ const composedExtensionSection: EditSectionViewModel = {
94
+ ...extensionSection,
95
+ contributesToFormSubmit: true,
91
96
  };
92
97
 
93
98
  describe("hasEditableSection", () => {
@@ -106,8 +111,12 @@ describe("hasEditableSection", () => {
106
111
  expect(hasEditableSection([fieldsSection(true), fieldsSection(false)])).toBe(true);
107
112
  });
108
113
 
109
- test("extension section counts as editable (carries its own save)", () => {
110
- expect(hasEditableSection([extensionSection])).toBe(true);
114
+ test("extension section without form-submit opt-in false", () => {
115
+ expect(hasEditableSection([extensionSection])).toBe(false);
116
+ });
117
+
118
+ test("extension section with contributesToFormSubmit → true", () => {
119
+ expect(hasEditableSection([composedExtensionSection])).toBe(true);
111
120
  });
112
121
 
113
122
  test("no sections → false", () => {
@@ -1,14 +1,15 @@
1
1
  import type { EditSectionViewModel, SubmitResult } from "@cosmicdrift/kumiko-headless";
2
2
 
3
3
  // Hides the Save button when no field is editable and no extension section
4
- // exists (extension carries its own dirty/save; a purely read-only form has nothing to submit).
4
+ // contributes to the composed form submit. Extensions that persist via their
5
+ // own dispatcher writes must not opt in (fw#2359).
5
6
  // Explicit-positive per kind (653/1), not `s.kind !== "fields"` — a future
6
7
  // third EditSectionViewModel member would otherwise default to "editable"
7
8
  // without anyone deciding that on purpose.
8
9
  export function hasEditableSection(sections: readonly EditSectionViewModel[]): boolean {
9
10
  return sections.some(
10
11
  (s) =>
11
- s.kind === "extension" ||
12
+ (s.kind === "extension" && s.contributesToFormSubmit) ||
12
13
  (s.kind === "fields" && s.visible && s.fields.some((f) => !f.readOnly && f.visible)),
13
14
  );
14
15
  }
@@ -813,7 +813,7 @@ export function RenderEdit<TValues extends FormValues, TCtx = unknown>(
813
813
  [vm.sections, fieldsFilter],
814
814
  );
815
815
 
816
- // true for an extension section with no fields of its own too (it carries its own dirty/save).
816
+ // true when editable fields exist or an extension opted into composed submit (fw#2359).
817
817
  const isFormEditable = hasEditableSection(filteredSections);
818
818
 
819
819
  // Persistiert alle composed Extension-Sections mit der aufgelösten entityId.