@nolwenture/formik-jsonschema-form 0.26.8 → 0.27.1

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 (33) hide show
  1. package/README.md +19 -0
  2. package/dist/cjs/Form.stories.js +8 -1
  3. package/dist/cjs/fields/ObjectField.js +18 -7
  4. package/dist/cjs/pdfFields/ObjectField.js +18 -7
  5. package/dist/cjs/pdfTemplates/ObjectSectionTemplate.js +11 -0
  6. package/dist/cjs/pdfTemplates/index.js +1 -0
  7. package/dist/cjs/sampleData/groupedData.js +20 -0
  8. package/dist/cjs/styledComponents.js +4 -2
  9. package/dist/cjs/templates/ObjectSectionTemplate.js +16 -0
  10. package/dist/cjs/templates/index.js +1 -0
  11. package/dist/cjs/utils/__tests/orderByUiSchema.test.js +22 -3
  12. package/dist/cjs/utils/orderByUiSchema.js +3 -2
  13. package/dist/esm/Form.stories.js +7 -0
  14. package/dist/esm/fields/ObjectField.js +18 -7
  15. package/dist/esm/pdfFields/ObjectField.js +18 -7
  16. package/dist/esm/pdfTemplates/ObjectSectionTemplate.js +7 -0
  17. package/dist/esm/pdfTemplates/index.js +1 -0
  18. package/dist/esm/sampleData/groupedData.js +17 -0
  19. package/dist/esm/styledComponents.js +2 -0
  20. package/dist/esm/templates/ObjectSectionTemplate.js +12 -0
  21. package/dist/esm/templates/index.js +1 -0
  22. package/dist/esm/utils/__tests/orderByUiSchema.test.js +22 -3
  23. package/dist/esm/utils/orderByUiSchema.js +3 -2
  24. package/dist/types/Form.stories.d.ts +1 -0
  25. package/dist/types/pdfTemplates/ObjectSectionTemplate.d.ts +3 -0
  26. package/dist/types/pdfTemplates/index.d.ts +1 -0
  27. package/dist/types/sampleData/groupedData.d.ts +7 -0
  28. package/dist/types/styledComponents.d.ts +2 -0
  29. package/dist/types/templates/ObjectSectionTemplate.d.ts +3 -0
  30. package/dist/types/templates/index.d.ts +1 -0
  31. package/dist/types/types.d.ts +13 -0
  32. package/dist/types/utils/orderByUiSchema.d.ts +6 -1
  33. package/package.json +1 -1
package/README.md CHANGED
@@ -989,6 +989,25 @@ const uiSchema = {
989
989
  };
990
990
  ```
991
991
 
992
+ Order can be used to group items inside object to sections.
993
+
994
+ ```tsx
995
+ const schema = {
996
+ properties: {
997
+ firstField: { type: "string" },
998
+ secondField: { type: "string" },
999
+ thirdField: { type: "string" },
1000
+ },
1001
+ };
1002
+
1003
+ const uiSchema = {
1004
+ "ui:order": [
1005
+ "firstField",
1006
+ {title: "More fields", fields: ["secondField", "thirdField"] collapsible: true}
1007
+ ],
1008
+ };
1009
+ ```
1010
+
992
1011
  In this example, the resulting order would be ["secondField", "firstField", "thirdField"].
993
1012
 
994
1013
  Wildcard `"*"` and `hidden fields` cannot be combined.
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.HideSubmitButton = exports.FileUploadForm = exports.proExample = exports.Collapsible = exports.MultipleChoice = exports.WithRefAndDefinitions = exports.WithConditions = exports.WithRequiredInput = exports.WithGeneratedSchema = exports.StylesCustomized = exports.StrictlyTyped = exports.CompleteSchema = exports.WithUiSchema = exports.Simple = void 0;
6
+ exports.GroupedForm = exports.HideSubmitButton = exports.FileUploadForm = exports.proExample = exports.Collapsible = exports.MultipleChoice = exports.WithRefAndDefinitions = exports.WithConditions = exports.WithRequiredInput = exports.WithGeneratedSchema = exports.StylesCustomized = exports.StrictlyTyped = exports.CompleteSchema = exports.WithUiSchema = exports.Simple = void 0;
7
7
  const jsx_runtime_1 = require("react/jsx-runtime");
8
8
  const addon_actions_1 = require("@storybook/addon-actions");
9
9
  const styled_components_1 = __importDefault(require("styled-components"));
@@ -15,6 +15,7 @@ const pdfProExampleSchema_1 = require("./sampleData/pdfProExampleSchema");
15
15
  const fileUpload_1 = require("./sampleData/fileUpload");
16
16
  const FileUploadContext_1 = require("./FileUploadContext");
17
17
  const FileUploadField_stories_1 = require("./fields/stories/FileUploadField.stories");
18
+ const groupedData_1 = require("./sampleData/groupedData");
18
19
  const MockFileUploadProvider = ({ children, }) => {
19
20
  return ((0, jsx_runtime_1.jsx)(FileUploadContext_1.FileUploadContext.Provider, { value: {
20
21
  uploadFile: FileUploadField_stories_1.mockUploadFile,
@@ -228,3 +229,9 @@ exports.HideSubmitButton = {
228
229
  hideSubmit: true,
229
230
  },
230
231
  };
232
+ exports.GroupedForm = {
233
+ args: {
234
+ schema: groupedData_1.groupedSchema,
235
+ uiSchema: groupedData_1.groupedUiSchema,
236
+ },
237
+ };
@@ -19,7 +19,7 @@ const useObjectField = (name) => {
19
19
  return { value, setValue };
20
20
  };
21
21
  const ObjectField = (props) => {
22
- const { fields: { SchemaField }, templates: { ObjectFieldTemplate, CollapsibleObjectFieldTemplate }, components: { ObjectFieldItem, ObjectFieldItemButtons }, hideUnmarkedAdditionalProperties, } = (0, react_1.useContext)(Form_1.FormRegistry);
22
+ const { fields: { SchemaField }, templates: { ObjectFieldTemplate, CollapsibleObjectFieldTemplate, ObjectSectionTemplate, }, components: { ObjectFieldItem, ObjectFieldItemButtons }, hideUnmarkedAdditionalProperties, } = (0, react_1.useContext)(Form_1.FormRegistry);
23
23
  (0, hooks_1.useSetDefault)(props);
24
24
  const { name, schema, uiSchema } = props;
25
25
  const { value, setValue } = useObjectField(name);
@@ -41,12 +41,23 @@ const ObjectField = (props) => {
41
41
  ? CollapsibleObjectFieldTemplate
42
42
  : ObjectFieldTemplate;
43
43
  const orderedProperties = (0, utils_1.orderByUiSchema)(uiSchema?.["ui:order"], Object.keys(properties || {}));
44
- return ((0, jsx_runtime_1.jsxs)(FieldTemplateComponent, { ...props, "data-fjsf-object": name, children: [(orderedProperties ?? Object.keys(properties || {}))
45
- .map((childName) => {
46
- const childSchema = properties?.[childName];
47
- if (!childSchema)
48
- return undefined;
49
- return ((0, jsx_runtime_1.jsx)(SchemaField, { required: required?.includes(childName), name: name ? [name, childName].join(".") : childName, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[childName] }, childName));
44
+ return ((0, jsx_runtime_1.jsxs)(FieldTemplateComponent, { ...props, "data-fjsf-object": name, children: [orderedProperties
45
+ .map((child) => {
46
+ if (typeof child === "string") {
47
+ const childSchema = properties?.[child];
48
+ if (!childSchema)
49
+ return undefined;
50
+ return ((0, jsx_runtime_1.jsx)(SchemaField, { required: required?.includes(child), name: name ? [name, child].join(".") : child, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[child] }, child));
51
+ }
52
+ else {
53
+ const { title, fields, collapsible } = child;
54
+ return ((0, jsx_runtime_1.jsx)(ObjectSectionTemplate, { title: title, collapsible: collapsible, children: fields.map((child2) => {
55
+ const childSchema = properties?.[child2];
56
+ if (!childSchema)
57
+ return undefined;
58
+ return ((0, jsx_runtime_1.jsx)(SchemaField, { name: name ? [name, child2].join(".") : child2, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[child2] }, child2));
59
+ }) }, fields.join("_")));
60
+ }
50
61
  })
51
62
  .filter((x) => x), (!hideUnmarkedAdditionalProperties || additionalProperties) &&
52
63
  Object.entries(additionalValues || {}).map(([childName]) => ((0, jsx_runtime_1.jsxs)(ObjectFieldItem, { children: [(0, jsx_runtime_1.jsx)(SchemaField, { required: required?.includes(childName), name: name ? [name, childName].join(".") : childName, schema: typeof schema.additionalProperties !== "boolean"
@@ -17,7 +17,7 @@ const useObjectField = (name) => {
17
17
  return { value, setValue };
18
18
  };
19
19
  const ObjectField = (props) => {
20
- const { fields: { SchemaField }, templates: { ObjectFieldTemplate, CollapsibleObjectFieldTemplate }, } = (0, react_1.useContext)(FormPdf_1.FormPdfRegistry);
20
+ const { fields: { SchemaField }, templates: { ObjectFieldTemplate, CollapsibleObjectFieldTemplate, ObjectSectionTemplate, }, } = (0, react_1.useContext)(FormPdf_1.FormPdfRegistry);
21
21
  (0, hooks_1.useSetDefault)(props);
22
22
  const { name, schema, uiSchema } = props;
23
23
  const { value, setValue } = useObjectField(name);
@@ -37,12 +37,23 @@ const ObjectField = (props) => {
37
37
  ? CollapsibleObjectFieldTemplate
38
38
  : ObjectFieldTemplate;
39
39
  const orderedProperties = (0, utils_1.orderByUiSchema)(uiSchema?.["ui:order"], Object.keys(properties || {}));
40
- return ((0, jsx_runtime_1.jsx)(FieldTemplateComponent, { ...props, children: (orderedProperties ?? Object.keys(properties || {}))
41
- .map((childName) => {
42
- const childSchema = properties?.[childName];
43
- if (!childSchema)
44
- return undefined;
45
- return ((0, jsx_runtime_1.jsx)(SchemaField, { name: name ? [name, childName].join(".") : childName, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[childName] }, childName));
40
+ return ((0, jsx_runtime_1.jsx)(FieldTemplateComponent, { ...props, children: orderedProperties
41
+ .map((child, index) => {
42
+ if (typeof child === "string") {
43
+ const childSchema = properties?.[child];
44
+ if (!childSchema)
45
+ return undefined;
46
+ return ((0, jsx_runtime_1.jsx)(SchemaField, { name: name ? [name, child].join(".") : child, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[child] }, child));
47
+ }
48
+ else {
49
+ const { title, fields } = child;
50
+ return ((0, jsx_runtime_1.jsx)(ObjectSectionTemplate, { title: title, children: fields.map((child2) => {
51
+ const childSchema = properties?.[child2];
52
+ if (!childSchema)
53
+ return undefined;
54
+ return ((0, jsx_runtime_1.jsx)(SchemaField, { name: name ? [name, child2].join(".") : child2, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[child2] }, child2));
55
+ }) }, fields.join("_")));
56
+ }
46
57
  })
47
58
  .filter((x) => x) }));
48
59
  };
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ObjectSectionTemplate = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const Form_1 = require("../Form");
7
+ const ObjectSectionTemplate = ({ title, children, }) => {
8
+ const { components: { ObjectFieldSection, ObjectFieldSectionTitle }, } = (0, react_1.useContext)(Form_1.FormRegistry);
9
+ return ((0, jsx_runtime_1.jsxs)(ObjectFieldSection, { children: [(0, jsx_runtime_1.jsx)(ObjectFieldSectionTitle, { children: title }), children] }));
10
+ };
11
+ exports.ObjectSectionTemplate = ObjectSectionTemplate;
@@ -18,3 +18,4 @@ __exportStar(require("./ArrayFieldTemplate"), exports);
18
18
  __exportStar(require("./FieldTemplate"), exports);
19
19
  __exportStar(require("./ObjectFieldTemplate"), exports);
20
20
  __exportStar(require("./CollapsibleObjectFieldTemplate"), exports);
21
+ __exportStar(require("./ObjectSectionTemplate"), exports);
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.groupedUiSchema = exports.groupedSchema = void 0;
4
+ exports.groupedSchema = {
5
+ title: "TITLE",
6
+ required: ["firstName"],
7
+ properties: {
8
+ firstName: { type: "string", title: "First name" },
9
+ lastName: { type: "string", title: "Last name" },
10
+ age: { type: "number", title: "Age" },
11
+ hoppy: { type: "string", title: "Hoppy" },
12
+ },
13
+ };
14
+ exports.groupedUiSchema = {
15
+ "ui:order": [
16
+ { title: "Names", fields: ["firstName", "lastName"], collapsible: true },
17
+ "*",
18
+ "hoppy",
19
+ ],
20
+ };
@@ -26,8 +26,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
26
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.RadioButtonField = exports.ButtonLabelFalse = exports.ButtonLabelTrue = exports.ButtonLabel = exports.RadioButtonFieldInput = exports.RadioButtonFieldElement = exports.RadioButtonFieldContainer = exports.ToggleSwitchField = exports.ToggleFieldInput = exports.ToggleLabel = exports.Toggle = exports.RequiredIndicator = exports.FieldInput = exports.ErrorMessage = exports.InfoFieldButton = exports.InfoFieldLabel = exports.InfoFieldContainerElement = exports.InfoFieldBoxInnerElement = exports.InfoFieldBoxArrowElement = exports.InfoFieldBoxElement = exports.FieldHelp = exports.FieldDescription = exports.FieldTitle = exports.FieldWrapper = exports.ArrayFieldAddButton = exports.ArrayFieldRemoveButton = exports.ArrayFieldDownButton = exports.ArrayFieldUpButton = exports.ArrayFieldLineButton = exports.ArrayFieldButton = exports.ArrayFieldItemButtons = exports.ArrayFieldItem = exports.ArrayFieldList = exports.ArrayFieldTitle = exports.ArrayFieldWrapper = exports.getObjectFieldButton = exports.ObjectFieldAddButton = exports.ObjectFieldRemoveButton = exports.ObjectFieldDownButton = exports.ObjectFieldUpButton = exports.ObjectFieldLineButton = exports.ObjectFieldButton = exports.ObjectFieldItemButtons = exports.ObjectFieldItem = exports.ObjectFieldList = exports.ObjectFieldTitle = exports.ObjectFieldWrapper = exports.FormButton = exports.FormButtonWrapper = exports.Form = void 0;
30
- exports.FileUploadField = exports.FileUploadFieldContainer = exports.FileInfoText = exports.RemoveButton = exports.FileItemContainer = exports.ProgressText = exports.ProgressBar = exports.ProgressBarContainer = exports.HiddenFileInput = exports.FileUploadButton = exports.MultipleChoiceFieldPdf = exports.BooleanFieldPdf = exports.EJSField = exports.TextAreaPdf = exports.StringFieldPdf = exports.SearchInputContainer = exports.SearchClearButton = exports.SearchInput = exports.SearchNotFound = exports.SearchListExpand = exports.SearchListOption = exports.SearchDropdownContainer = exports.SearchableSelectContainer = exports.CollapsibleObjectFieldTitle = exports.CollapsibleObjectFieldWrapper = exports.CollapsibleChildWrapper = exports.Icon = exports.CollapsibleToggle = exports.Clickable = exports.BooleanFieldWrapper = exports.TextAreaInputField = exports.TextAreaFieldInput = exports.SearchableMultiTagSelectField = exports.MultiSelectOptionListWithoutItem = exports.MultiSelectOptionListItem = exports.MultiSelectOptionList = exports.MultiSelectBadgeCloseElement = exports.MultiSelectBadge = exports.MultiSelectBadgeContainer = exports.MultiSelectInput = exports.MultiSelectSearchContainer = exports.MultiSelectContainer = exports.MultipleChoiceField = exports.MultipleChoiceFieldLabel = exports.MultipleChoiceFieldInput = exports.MultipleChoiceFieldElement = exports.MultipleChoiceFieldContainer = void 0;
29
+ exports.ButtonLabelTrue = exports.ButtonLabel = exports.RadioButtonFieldInput = exports.RadioButtonFieldElement = exports.RadioButtonFieldContainer = exports.ToggleSwitchField = exports.ToggleFieldInput = exports.ToggleLabel = exports.Toggle = exports.RequiredIndicator = exports.FieldInput = exports.ErrorMessage = exports.InfoFieldButton = exports.InfoFieldLabel = exports.InfoFieldContainerElement = exports.InfoFieldBoxInnerElement = exports.InfoFieldBoxArrowElement = exports.InfoFieldBoxElement = exports.FieldHelp = exports.FieldDescription = exports.FieldTitle = exports.FieldWrapper = exports.ArrayFieldAddButton = exports.ArrayFieldRemoveButton = exports.ArrayFieldDownButton = exports.ArrayFieldUpButton = exports.ArrayFieldLineButton = exports.ArrayFieldButton = exports.ArrayFieldItemButtons = exports.ArrayFieldItem = exports.ArrayFieldList = exports.ArrayFieldTitle = exports.ArrayFieldWrapper = exports.ObjectFieldSectionTitle = exports.ObjectFieldSection = exports.getObjectFieldButton = exports.ObjectFieldAddButton = exports.ObjectFieldRemoveButton = exports.ObjectFieldDownButton = exports.ObjectFieldUpButton = exports.ObjectFieldLineButton = exports.ObjectFieldButton = exports.ObjectFieldItemButtons = exports.ObjectFieldItem = exports.ObjectFieldList = exports.ObjectFieldTitle = exports.ObjectFieldWrapper = exports.FormButton = exports.FormButtonWrapper = exports.Form = void 0;
30
+ exports.FileUploadField = exports.FileUploadFieldContainer = exports.FileInfoText = exports.RemoveButton = exports.FileItemContainer = exports.ProgressText = exports.ProgressBar = exports.ProgressBarContainer = exports.HiddenFileInput = exports.FileUploadButton = exports.MultipleChoiceFieldPdf = exports.BooleanFieldPdf = exports.EJSField = exports.TextAreaPdf = exports.StringFieldPdf = exports.SearchInputContainer = exports.SearchClearButton = exports.SearchInput = exports.SearchNotFound = exports.SearchListExpand = exports.SearchListOption = exports.SearchDropdownContainer = exports.SearchableSelectContainer = exports.CollapsibleObjectFieldTitle = exports.CollapsibleObjectFieldWrapper = exports.CollapsibleChildWrapper = exports.Icon = exports.CollapsibleToggle = exports.Clickable = exports.BooleanFieldWrapper = exports.TextAreaInputField = exports.TextAreaFieldInput = exports.SearchableMultiTagSelectField = exports.MultiSelectOptionListWithoutItem = exports.MultiSelectOptionListItem = exports.MultiSelectOptionList = exports.MultiSelectBadgeCloseElement = exports.MultiSelectBadge = exports.MultiSelectBadgeContainer = exports.MultiSelectInput = exports.MultiSelectSearchContainer = exports.MultiSelectContainer = exports.MultipleChoiceField = exports.MultipleChoiceFieldLabel = exports.MultipleChoiceFieldInput = exports.MultipleChoiceFieldElement = exports.MultipleChoiceFieldContainer = exports.RadioButtonField = exports.ButtonLabelFalse = void 0;
31
31
  const jsx_runtime_1 = require("react/jsx-runtime");
32
32
  const formik = __importStar(require("formik"));
33
33
  const react_1 = require("react");
@@ -77,6 +77,8 @@ const getObjectFieldButton = ({ purpose, onClick, uiSchema, }) => {
77
77
  }
78
78
  };
79
79
  exports.getObjectFieldButton = getObjectFieldButton;
80
+ exports.ObjectFieldSection = styled_components_1.default.fieldset ``;
81
+ exports.ObjectFieldSectionTitle = styled_components_1.default.legend ``;
80
82
  exports.ArrayFieldWrapper = exports.ObjectFieldWrapper;
81
83
  exports.ArrayFieldTitle = exports.ObjectFieldTitle;
82
84
  exports.ArrayFieldList = exports.ObjectFieldList;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ObjectSectionTemplate = void 0;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const Form_1 = require("../Form");
7
+ const ObjectSectionTemplate = ({ title, collapsible, children, }) => {
8
+ const [collapsed, setCollapsed] = (0, react_1.useState)(collapsible);
9
+ const { components: { ObjectFieldSection, ObjectFieldSectionTitle }, } = (0, react_1.useContext)(Form_1.FormRegistry);
10
+ return ((0, jsx_runtime_1.jsxs)(ObjectFieldSection, { children: [(0, jsx_runtime_1.jsxs)(ObjectFieldSectionTitle, { onClick: () => {
11
+ if (collapsible) {
12
+ setCollapsed((currentState) => !currentState);
13
+ }
14
+ }, children: [collapsible && (collapsed ? "▸" : "▾"), title] }), !collapsed && children] }));
15
+ };
16
+ exports.ObjectSectionTemplate = ObjectSectionTemplate;
@@ -18,3 +18,4 @@ __exportStar(require("./ArrayFieldTemplate"), exports);
18
18
  __exportStar(require("./FieldTemplate"), exports);
19
19
  __exportStar(require("./ObjectFieldTemplate"), exports);
20
20
  __exportStar(require("./CollapsibleObjectFieldTemplate"), exports);
21
+ __exportStar(require("./ObjectSectionTemplate"), exports);
@@ -7,11 +7,22 @@ const noAsteriskOrder = ["firstName", "age"];
7
7
  const asteriskAsFirstElement = ["*", "firstName"];
8
8
  const asteriskAsLastElement = ["age", "*"];
9
9
  const asteriskAsMiddleElement = ["hobbies", "*", "age"];
10
- const orderWithDuplicates = ["age", "*", "age", "*", "lastName"];
10
+ const orderWithDuplicates = [
11
+ "age",
12
+ "*",
13
+ "age",
14
+ "*",
15
+ "lastName",
16
+ ];
17
+ const orderWithGroups = [
18
+ { title: "names", fields: ["firstName", "lastName"] },
19
+ "*",
20
+ "hobbies",
21
+ ];
11
22
  describe("orderByUiSchema", () => {
12
- it("returns empty array when ui:order is not given", () => {
23
+ it("returns allProperties when ui:order is not given", () => {
13
24
  const orderedProperties = (0, orderByUiSchema_1.orderByUiSchema)(emptyOrder, allProperties);
14
- expect(orderedProperties).toEqual(undefined);
25
+ expect(orderedProperties).toEqual(allProperties);
15
26
  });
16
27
  it("returns only given properties when order includes no asterisk", () => {
17
28
  const orderedProperties = (0, orderByUiSchema_1.orderByUiSchema)(noAsteriskOrder, allProperties);
@@ -62,4 +73,12 @@ describe("orderByUiSchema", () => {
62
73
  "lastName",
63
74
  ]);
64
75
  });
76
+ it("solves groups properly", () => {
77
+ const orderedProperties = (0, orderByUiSchema_1.orderByUiSchema)(orderWithGroups, allProperties);
78
+ expect(orderedProperties).toMatchObject([
79
+ { title: "names", fields: ["firstName", "lastName"] },
80
+ "age",
81
+ "hobbies",
82
+ ]);
83
+ });
65
84
  });
@@ -10,14 +10,15 @@ function orderByUiSchema(order, allProperties) {
10
10
  return order;
11
11
  }
12
12
  }
13
- return undefined;
13
+ return allProperties;
14
14
  }
15
15
  function orderWithAsterisk(order, allProperties) {
16
16
  const orderWithoutDuplicates = [...new Set(order)];
17
+ const fieldsInOrder = orderWithoutDuplicates.flatMap((entry) => typeof entry === "string" ? entry : entry.fields);
17
18
  const finalOrderedArray = [];
18
19
  orderWithoutDuplicates.forEach((element) => {
19
20
  if (element === "*") {
20
- const propertiesWithoutOrder = allProperties.filter((prop) => !orderWithoutDuplicates.includes(prop));
21
+ const propertiesWithoutOrder = allProperties.filter((prop) => !fieldsInOrder.includes(prop));
21
22
  finalOrderedArray.push(...propertiesWithoutOrder);
22
23
  }
23
24
  else {
@@ -9,6 +9,7 @@ import { pdfProExampleSchema, pdfProExampleUiSchema, } from "./sampleData/pdfPro
9
9
  import { fileUploadSchema, fileUploadUiSchema } from "./sampleData/fileUpload";
10
10
  import { FileUploadContext } from "./FileUploadContext";
11
11
  import { mockFetchFile, mockRemoveFile, mockUploadFile, } from "./fields/stories/FileUploadField.stories";
12
+ import { groupedSchema, groupedUiSchema } from "./sampleData/groupedData";
12
13
  const MockFileUploadProvider = ({ children, }) => {
13
14
  return (_jsx(FileUploadContext.Provider, { value: {
14
15
  uploadFile: mockUploadFile,
@@ -220,3 +221,9 @@ export const HideSubmitButton = {
220
221
  hideSubmit: true,
221
222
  },
222
223
  };
224
+ export const GroupedForm = {
225
+ args: {
226
+ schema: groupedSchema,
227
+ uiSchema: groupedUiSchema,
228
+ },
229
+ };
@@ -16,7 +16,7 @@ const useObjectField = (name) => {
16
16
  return { value, setValue };
17
17
  };
18
18
  export const ObjectField = (props) => {
19
- const { fields: { SchemaField }, templates: { ObjectFieldTemplate, CollapsibleObjectFieldTemplate }, components: { ObjectFieldItem, ObjectFieldItemButtons }, hideUnmarkedAdditionalProperties, } = useContext(FormRegistry);
19
+ const { fields: { SchemaField }, templates: { ObjectFieldTemplate, CollapsibleObjectFieldTemplate, ObjectSectionTemplate, }, components: { ObjectFieldItem, ObjectFieldItemButtons }, hideUnmarkedAdditionalProperties, } = useContext(FormRegistry);
20
20
  useSetDefault(props);
21
21
  const { name, schema, uiSchema } = props;
22
22
  const { value, setValue } = useObjectField(name);
@@ -38,12 +38,23 @@ export const ObjectField = (props) => {
38
38
  ? CollapsibleObjectFieldTemplate
39
39
  : ObjectFieldTemplate;
40
40
  const orderedProperties = orderByUiSchema(uiSchema?.["ui:order"], Object.keys(properties || {}));
41
- return (_jsxs(FieldTemplateComponent, { ...props, "data-fjsf-object": name, children: [(orderedProperties ?? Object.keys(properties || {}))
42
- .map((childName) => {
43
- const childSchema = properties?.[childName];
44
- if (!childSchema)
45
- return undefined;
46
- return (_jsx(SchemaField, { required: required?.includes(childName), name: name ? [name, childName].join(".") : childName, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[childName] }, childName));
41
+ return (_jsxs(FieldTemplateComponent, { ...props, "data-fjsf-object": name, children: [orderedProperties
42
+ .map((child) => {
43
+ if (typeof child === "string") {
44
+ const childSchema = properties?.[child];
45
+ if (!childSchema)
46
+ return undefined;
47
+ return (_jsx(SchemaField, { required: required?.includes(child), name: name ? [name, child].join(".") : child, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[child] }, child));
48
+ }
49
+ else {
50
+ const { title, fields, collapsible } = child;
51
+ return (_jsx(ObjectSectionTemplate, { title: title, collapsible: collapsible, children: fields.map((child2) => {
52
+ const childSchema = properties?.[child2];
53
+ if (!childSchema)
54
+ return undefined;
55
+ return (_jsx(SchemaField, { name: name ? [name, child2].join(".") : child2, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[child2] }, child2));
56
+ }) }, fields.join("_")));
57
+ }
47
58
  })
48
59
  .filter((x) => x), (!hideUnmarkedAdditionalProperties || additionalProperties) &&
49
60
  Object.entries(additionalValues || {}).map(([childName]) => (_jsxs(ObjectFieldItem, { children: [_jsx(SchemaField, { required: required?.includes(childName), name: name ? [name, childName].join(".") : childName, schema: typeof schema.additionalProperties !== "boolean"
@@ -14,7 +14,7 @@ const useObjectField = (name) => {
14
14
  return { value, setValue };
15
15
  };
16
16
  export const ObjectField = (props) => {
17
- const { fields: { SchemaField }, templates: { ObjectFieldTemplate, CollapsibleObjectFieldTemplate }, } = useContext(FormPdfRegistry);
17
+ const { fields: { SchemaField }, templates: { ObjectFieldTemplate, CollapsibleObjectFieldTemplate, ObjectSectionTemplate, }, } = useContext(FormPdfRegistry);
18
18
  useSetDefault(props);
19
19
  const { name, schema, uiSchema } = props;
20
20
  const { value, setValue } = useObjectField(name);
@@ -34,12 +34,23 @@ export const ObjectField = (props) => {
34
34
  ? CollapsibleObjectFieldTemplate
35
35
  : ObjectFieldTemplate;
36
36
  const orderedProperties = orderByUiSchema(uiSchema?.["ui:order"], Object.keys(properties || {}));
37
- return (_jsx(FieldTemplateComponent, { ...props, children: (orderedProperties ?? Object.keys(properties || {}))
38
- .map((childName) => {
39
- const childSchema = properties?.[childName];
40
- if (!childSchema)
41
- return undefined;
42
- return (_jsx(SchemaField, { name: name ? [name, childName].join(".") : childName, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[childName] }, childName));
37
+ return (_jsx(FieldTemplateComponent, { ...props, children: orderedProperties
38
+ .map((child, index) => {
39
+ if (typeof child === "string") {
40
+ const childSchema = properties?.[child];
41
+ if (!childSchema)
42
+ return undefined;
43
+ return (_jsx(SchemaField, { name: name ? [name, child].join(".") : child, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[child] }, child));
44
+ }
45
+ else {
46
+ const { title, fields } = child;
47
+ return (_jsx(ObjectSectionTemplate, { title: title, children: fields.map((child2) => {
48
+ const childSchema = properties?.[child2];
49
+ if (!childSchema)
50
+ return undefined;
51
+ return (_jsx(SchemaField, { name: name ? [name, child2].join(".") : child2, schema: typeof childSchema !== "boolean" ? childSchema : {}, uiSchema: uiSchema?.[child2] }, child2));
52
+ }) }, fields.join("_")));
53
+ }
43
54
  })
44
55
  .filter((x) => x) }));
45
56
  };
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useContext } from "react";
3
+ import { FormRegistry } from "../Form";
4
+ export const ObjectSectionTemplate = ({ title, children, }) => {
5
+ const { components: { ObjectFieldSection, ObjectFieldSectionTitle }, } = useContext(FormRegistry);
6
+ return (_jsxs(ObjectFieldSection, { children: [_jsx(ObjectFieldSectionTitle, { children: title }), children] }));
7
+ };
@@ -2,3 +2,4 @@ export * from "./ArrayFieldTemplate";
2
2
  export * from "./FieldTemplate";
3
3
  export * from "./ObjectFieldTemplate";
4
4
  export * from "./CollapsibleObjectFieldTemplate";
5
+ export * from "./ObjectSectionTemplate";
@@ -0,0 +1,17 @@
1
+ export const groupedSchema = {
2
+ title: "TITLE",
3
+ required: ["firstName"],
4
+ properties: {
5
+ firstName: { type: "string", title: "First name" },
6
+ lastName: { type: "string", title: "Last name" },
7
+ age: { type: "number", title: "Age" },
8
+ hoppy: { type: "string", title: "Hoppy" },
9
+ },
10
+ };
11
+ export const groupedUiSchema = {
12
+ "ui:order": [
13
+ { title: "Names", fields: ["firstName", "lastName"], collapsible: true },
14
+ "*",
15
+ "hoppy",
16
+ ],
17
+ };
@@ -46,6 +46,8 @@ export const getObjectFieldButton = ({ purpose, onClick, uiSchema, }) => {
46
46
  return (_jsx(ObjectFieldButton, { ...(uiSchema?.["ui:customButtonOptions"]?.props ?? {}), onClick: onClick, children: uiSchema?.["ui:customButtonOptions"]?.content ?? "" }));
47
47
  }
48
48
  };
49
+ export const ObjectFieldSection = styled.fieldset ``;
50
+ export const ObjectFieldSectionTitle = styled.legend ``;
49
51
  export const ArrayFieldWrapper = ObjectFieldWrapper;
50
52
  export const ArrayFieldTitle = ObjectFieldTitle;
51
53
  export const ArrayFieldList = ObjectFieldList;
@@ -0,0 +1,12 @@
1
+ import { jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useContext, useState } from "react";
3
+ import { FormRegistry } from "../Form";
4
+ export const ObjectSectionTemplate = ({ title, collapsible, children, }) => {
5
+ const [collapsed, setCollapsed] = useState(collapsible);
6
+ const { components: { ObjectFieldSection, ObjectFieldSectionTitle }, } = useContext(FormRegistry);
7
+ return (_jsxs(ObjectFieldSection, { children: [_jsxs(ObjectFieldSectionTitle, { onClick: () => {
8
+ if (collapsible) {
9
+ setCollapsed((currentState) => !currentState);
10
+ }
11
+ }, children: [collapsible && (collapsed ? "▸" : "▾"), title] }), !collapsed && children] }));
12
+ };
@@ -2,3 +2,4 @@ export * from "./ArrayFieldTemplate";
2
2
  export * from "./FieldTemplate";
3
3
  export * from "./ObjectFieldTemplate";
4
4
  export * from "./CollapsibleObjectFieldTemplate";
5
+ export * from "./ObjectSectionTemplate";
@@ -5,11 +5,22 @@ const noAsteriskOrder = ["firstName", "age"];
5
5
  const asteriskAsFirstElement = ["*", "firstName"];
6
6
  const asteriskAsLastElement = ["age", "*"];
7
7
  const asteriskAsMiddleElement = ["hobbies", "*", "age"];
8
- const orderWithDuplicates = ["age", "*", "age", "*", "lastName"];
8
+ const orderWithDuplicates = [
9
+ "age",
10
+ "*",
11
+ "age",
12
+ "*",
13
+ "lastName",
14
+ ];
15
+ const orderWithGroups = [
16
+ { title: "names", fields: ["firstName", "lastName"] },
17
+ "*",
18
+ "hobbies",
19
+ ];
9
20
  describe("orderByUiSchema", () => {
10
- it("returns empty array when ui:order is not given", () => {
21
+ it("returns allProperties when ui:order is not given", () => {
11
22
  const orderedProperties = orderByUiSchema(emptyOrder, allProperties);
12
- expect(orderedProperties).toEqual(undefined);
23
+ expect(orderedProperties).toEqual(allProperties);
13
24
  });
14
25
  it("returns only given properties when order includes no asterisk", () => {
15
26
  const orderedProperties = orderByUiSchema(noAsteriskOrder, allProperties);
@@ -60,4 +71,12 @@ describe("orderByUiSchema", () => {
60
71
  "lastName",
61
72
  ]);
62
73
  });
74
+ it("solves groups properly", () => {
75
+ const orderedProperties = orderByUiSchema(orderWithGroups, allProperties);
76
+ expect(orderedProperties).toMatchObject([
77
+ { title: "names", fields: ["firstName", "lastName"] },
78
+ "age",
79
+ "hobbies",
80
+ ]);
81
+ });
63
82
  });
@@ -7,14 +7,15 @@ export function orderByUiSchema(order, allProperties) {
7
7
  return order;
8
8
  }
9
9
  }
10
- return undefined;
10
+ return allProperties;
11
11
  }
12
12
  function orderWithAsterisk(order, allProperties) {
13
13
  const orderWithoutDuplicates = [...new Set(order)];
14
+ const fieldsInOrder = orderWithoutDuplicates.flatMap((entry) => typeof entry === "string" ? entry : entry.fields);
14
15
  const finalOrderedArray = [];
15
16
  orderWithoutDuplicates.forEach((element) => {
16
17
  if (element === "*") {
17
- const propertiesWithoutOrder = allProperties.filter((prop) => !orderWithoutDuplicates.includes(prop));
18
+ const propertiesWithoutOrder = allProperties.filter((prop) => !fieldsInOrder.includes(prop));
18
19
  finalOrderedArray.push(...propertiesWithoutOrder);
19
20
  }
20
21
  else {
@@ -17,3 +17,4 @@ export declare const Collapsible: Story;
17
17
  export declare const proExample: Story;
18
18
  export declare const FileUploadForm: Story;
19
19
  export declare const HideSubmitButton: Story;
20
+ export declare const GroupedForm: Story;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { ObjectSectionTemplateProps } from "../types";
3
+ export declare const ObjectSectionTemplate: React.FC<ObjectSectionTemplateProps>;
@@ -2,3 +2,4 @@ export * from "./ArrayFieldTemplate";
2
2
  export * from "./FieldTemplate";
3
3
  export * from "./ObjectFieldTemplate";
4
4
  export * from "./CollapsibleObjectFieldTemplate";
5
+ export * from "./ObjectSectionTemplate";
@@ -0,0 +1,7 @@
1
+ import type { Schema, UiSchema } from "../types";
2
+ export type SimpleFormDataType = {
3
+ firstName: string;
4
+ lastName: string;
5
+ };
6
+ export declare const groupedSchema: Schema;
7
+ export declare const groupedUiSchema: UiSchema;
@@ -28,6 +28,8 @@ export declare const ObjectFieldAddButton: import("styled-components/dist/types"
28
28
  ref?: ((instance: HTMLAnchorElement | null) => void | React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES[keyof React.DO_NOT_USE_OR_YOU_WILL_BE_FIRED_CALLBACK_REF_RETURN_VALUES]) | React.RefObject<HTMLAnchorElement> | null | undefined;
29
29
  }, never>> & string;
30
30
  export declare const getObjectFieldButton: ({ purpose, onClick, uiSchema, }: ObjectFieldButtonProps) => import("react/jsx-runtime").JSX.Element;
31
+ export declare const ObjectFieldSection: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<React.DetailedHTMLProps<React.FieldsetHTMLAttributes<HTMLFieldSetElement>, HTMLFieldSetElement>, never>> & string;
32
+ export declare const ObjectFieldSectionTitle: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLLegendElement>, HTMLLegendElement>, never>> & string;
31
33
  export declare const ArrayFieldWrapper: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<React.DetailedHTMLProps<React.FieldsetHTMLAttributes<HTMLFieldSetElement>, HTMLFieldSetElement>, never>> & string;
32
34
  export declare const ArrayFieldTitle: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLLegendElement>, HTMLLegendElement>, never>> & string;
33
35
  export declare const ArrayFieldList: import("styled-components/dist/types").IStyledComponentBase<"web", import("styled-components").FastOmit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLUListElement>, HTMLUListElement>, never>> & string;
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import { ObjectSectionTemplateProps } from "../types";
3
+ export declare const ObjectSectionTemplate: React.FC<ObjectSectionTemplateProps>;
@@ -2,3 +2,4 @@ export * from "./ArrayFieldTemplate";
2
2
  export * from "./FieldTemplate";
3
3
  export * from "./ObjectFieldTemplate";
4
4
  export * from "./CollapsibleObjectFieldTemplate";
5
+ export * from "./ObjectSectionTemplate";
@@ -45,6 +45,11 @@ export type UiSchema = {
45
45
  [key: string]: any;
46
46
  };
47
47
  };
48
+ "ui:order"?: (string | {
49
+ title: string;
50
+ fields: string[];
51
+ collapsible?: boolean;
52
+ })[];
48
53
  [name: string]: any;
49
54
  };
50
55
  export type FormRegistryContextValue = {
@@ -107,11 +112,17 @@ export type CollapsibleObjectFieldTemplateProps = {
107
112
  children: React.ReactNode;
108
113
  name?: string;
109
114
  };
115
+ export type ObjectSectionTemplateProps = {
116
+ title: string;
117
+ collapsible?: boolean;
118
+ children: React.ReactNode;
119
+ };
110
120
  export type TemplatesType = {
111
121
  FieldTemplate: React.FunctionComponent<FieldTemplateProps>;
112
122
  ObjectFieldTemplate: React.FunctionComponent<ObjectFieldTemplateProps>;
113
123
  ArrayFieldTemplate: React.FunctionComponent<ArrayFieldTemplateProps>;
114
124
  CollapsibleObjectFieldTemplate: React.FunctionComponent<ObjectFieldTemplateProps>;
125
+ ObjectSectionTemplate: React.FunctionComponent<ObjectSectionTemplateProps>;
115
126
  };
116
127
  /*** COMPONENTS ***/
117
128
  export type ComponentsType = {
@@ -128,6 +139,8 @@ export type ComponentsType = {
128
139
  ObjectFieldRemoveButton: React.FunctionComponent<any>;
129
140
  ObjectFieldUpButton: React.FunctionComponent<any>;
130
141
  ObjectFieldDownButton: React.FunctionComponent<any>;
142
+ ObjectFieldSection: React.FunctionComponent<any>;
143
+ ObjectFieldSectionTitle: React.FunctionComponent<any>;
131
144
  ArrayFieldWrapper: React.FunctionComponent<any>;
132
145
  ArrayFieldTitle: React.FunctionComponent<any>;
133
146
  ArrayFieldItem: React.FunctionComponent<any>;
@@ -1 +1,6 @@
1
- export declare function orderByUiSchema(order: string[] | undefined, allProperties: string[]): string[] | undefined;
1
+ import { UiSchema } from "../types";
2
+ export declare function orderByUiSchema(order: UiSchema["ui:order"], allProperties: string[]): (string | {
3
+ title: string;
4
+ fields: string[];
5
+ collapsible?: boolean;
6
+ })[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nolwenture/formik-jsonschema-form",
3
- "version": "0.26.8",
3
+ "version": "0.27.1",
4
4
  "description": "Formik form generation from json schema",
5
5
  "author": "Antti Vikman <antti.vikman@nolwenture.com>",
6
6
  "contributors": [