@tsed/schema-formio 8.0.1 → 8.0.2

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 (77) hide show
  1. package/package.json +11 -10
  2. package/src/components/anyToComponent.ts +28 -0
  3. package/src/components/arrayToComponent.ts +43 -0
  4. package/src/components/booleanToComponent.ts +15 -0
  5. package/src/components/dateToComponent.ts +66 -0
  6. package/src/components/defaultToComponent.ts +17 -0
  7. package/src/components/enumToComponent.ts +33 -0
  8. package/src/components/mapToComponent.ts +17 -0
  9. package/src/components/nestedToComponent.ts +23 -0
  10. package/src/components/numberToComponent.ts +20 -0
  11. package/src/components/objectToEditGrid.ts +13 -0
  12. package/src/components/panelToComponent.ts +13 -0
  13. package/src/components/propertiesToComponents.ts +106 -0
  14. package/src/components/stringToComponent.ts +44 -0
  15. package/src/decorators/component.ts +34 -0
  16. package/src/decorators/conditional.spec.ts +73 -0
  17. package/src/decorators/conditional.ts +49 -0
  18. package/src/decorators/currency.spec.ts +40 -0
  19. package/src/decorators/currency.ts +19 -0
  20. package/src/decorators/customClass.spec.ts +35 -0
  21. package/src/decorators/customClass.ts +14 -0
  22. package/src/decorators/customConditional.spec.ts +95 -0
  23. package/src/decorators/customConditional.ts +85 -0
  24. package/src/decorators/dataSourceJson.spec.ts +88 -0
  25. package/src/decorators/dataSourceJson.ts +26 -0
  26. package/src/decorators/dataSourceUrl.spec.ts +111 -0
  27. package/src/decorators/dataSourceUrl.ts +19 -0
  28. package/src/decorators/errorLabel.spec.ts +35 -0
  29. package/src/decorators/errorLabel.ts +14 -0
  30. package/src/decorators/errorMessage.spec.ts +35 -0
  31. package/src/decorators/errorMessage.ts +13 -0
  32. package/src/decorators/form.ts +25 -0
  33. package/src/decorators/hidden.spec.ts +35 -0
  34. package/src/decorators/hidden.ts +18 -0
  35. package/src/decorators/inputTags.spec.ts +36 -0
  36. package/src/decorators/inputTags.ts +22 -0
  37. package/src/decorators/label.spec.ts +34 -0
  38. package/src/decorators/label.ts +21 -0
  39. package/src/decorators/mask.spec.ts +105 -0
  40. package/src/decorators/mask.ts +46 -0
  41. package/src/decorators/maxWords.spec.ts +35 -0
  42. package/src/decorators/maxWords.ts +13 -0
  43. package/src/decorators/minWords.spec.ts +35 -0
  44. package/src/decorators/minWords.ts +13 -0
  45. package/src/decorators/modalEdit.spec.ts +35 -0
  46. package/src/decorators/modalEdit.ts +14 -0
  47. package/src/decorators/multiple.spec.ts +35 -0
  48. package/src/decorators/multiple.ts +14 -0
  49. package/src/decorators/openWhenEmpty.ts +14 -0
  50. package/src/decorators/password.spec.ts +35 -0
  51. package/src/decorators/password.ts +20 -0
  52. package/src/decorators/placeholder.spec.ts +35 -0
  53. package/src/decorators/placeholder.ts +15 -0
  54. package/src/decorators/prefix.spec.ts +35 -0
  55. package/src/decorators/prefix.ts +14 -0
  56. package/src/decorators/select.spec.ts +82 -0
  57. package/src/decorators/select.ts +25 -0
  58. package/src/decorators/suffix.spec.ts +35 -0
  59. package/src/decorators/suffix.ts +14 -0
  60. package/src/decorators/tableView.spec.ts +35 -0
  61. package/src/decorators/tableView.ts +15 -0
  62. package/src/decorators/tabs.ts +18 -0
  63. package/src/decorators/textCase.spec.ts +35 -0
  64. package/src/decorators/textCase.ts +15 -0
  65. package/src/decorators/textarea.spec.ts +35 -0
  66. package/src/decorators/textarea.ts +21 -0
  67. package/src/decorators/tooltip.spec.ts +35 -0
  68. package/src/decorators/tooltip.ts +15 -0
  69. package/src/decorators/validate.ts +18 -0
  70. package/src/domain/FormioDataResolver.ts +5 -0
  71. package/src/index.ts +52 -0
  72. package/src/registries/FormioMappersContainer.ts +21 -0
  73. package/src/registries/FormsContainer.ts +4 -0
  74. package/src/utils/getFormioProps.ts +12 -0
  75. package/src/utils/getFormioSchema.spec.ts +84 -0
  76. package/src/utils/getFormioSchema.ts +62 -0
  77. package/src/utils/getRef.ts +8 -0
@@ -0,0 +1,40 @@
1
+ import {getFormioSchema} from "../utils/getFormioSchema.js";
2
+ import {Currency} from "./currency.js";
3
+
4
+ describe("Currency", () => {
5
+ it("should declare currency", async () => {
6
+ class Model {
7
+ @Currency()
8
+ test: number;
9
+ }
10
+
11
+ expect(await getFormioSchema(Model)).toEqual({
12
+ components: [
13
+ {
14
+ currency: "USD",
15
+ delimiter: true,
16
+ disabled: false,
17
+ input: true,
18
+ inputFormat: "plain",
19
+ label: "Test",
20
+ key: "test",
21
+ mask: false,
22
+ requireDecimal: false,
23
+ spellcheck: true,
24
+ type: "number",
25
+ validate: {
26
+ required: false
27
+ }
28
+ }
29
+ ],
30
+ display: "form",
31
+ machineName: "model",
32
+ name: "model",
33
+ title: "Model",
34
+ type: "form",
35
+ submissionAccess: [],
36
+ access: [],
37
+ tags: []
38
+ });
39
+ });
40
+ });
@@ -0,0 +1,19 @@
1
+ import {Component} from "./component.js";
2
+ /**
3
+ * Configure the property as Currency component.
4
+ * @decorator
5
+ * @formio
6
+ * @property
7
+ * @schema
8
+ */
9
+ export function Currency(props: Record<string, any> = {}): PropertyDecorator {
10
+ return Component({
11
+ currency: "USD",
12
+ inputFormat: "plain",
13
+ mask: false,
14
+ spellcheck: true,
15
+ delimiter: true,
16
+ ...props,
17
+ type: "currency"
18
+ });
19
+ }
@@ -0,0 +1,35 @@
1
+ import {getFormioSchema} from "../utils/getFormioSchema.js";
2
+ import {CustomClass} from "./customClass.js";
3
+
4
+ describe("@CustomClass", () => {
5
+ it("should add a custom css class on field", async () => {
6
+ class Model {
7
+ @CustomClass("css-class")
8
+ test: string;
9
+ }
10
+
11
+ expect(await getFormioSchema(Model)).toEqual({
12
+ access: [],
13
+ components: [
14
+ {
15
+ disabled: false,
16
+ input: true,
17
+ key: "test",
18
+ label: "Test",
19
+ customClass: "css-class",
20
+ type: "textfield",
21
+ validate: {
22
+ required: false
23
+ }
24
+ }
25
+ ],
26
+ display: "form",
27
+ machineName: "model",
28
+ name: "model",
29
+ submissionAccess: [],
30
+ tags: [],
31
+ title: "Model",
32
+ type: "form"
33
+ });
34
+ });
35
+ });
@@ -0,0 +1,14 @@
1
+ import {Component} from "./component.js";
2
+
3
+ /**
4
+ * Add a custom css class.
5
+ * @decorator
6
+ * @formio
7
+ * @property
8
+ * @schema
9
+ */
10
+ export function CustomClass(customClass: string) {
11
+ return Component({
12
+ customClass
13
+ });
14
+ }
@@ -0,0 +1,95 @@
1
+ import {getFormioSchema} from "../utils/getFormioSchema.js";
2
+ import {CustomConditional} from "./customConditional.js";
3
+
4
+ describe("@CustomConditional", () => {
5
+ it("should add a custom conditional rule on field (1)", async () => {
6
+ class Model {
7
+ @CustomConditional(({value}) => value === 1)
8
+ test: string;
9
+ }
10
+
11
+ expect(await getFormioSchema(Model)).toEqual({
12
+ access: [],
13
+ components: [
14
+ {
15
+ disabled: false,
16
+ input: true,
17
+ key: "test",
18
+ label: "Test",
19
+ type: "textfield",
20
+ customConditional: "show = value === 1",
21
+ validate: {
22
+ required: false
23
+ }
24
+ }
25
+ ],
26
+ display: "form",
27
+ machineName: "model",
28
+ name: "model",
29
+ submissionAccess: [],
30
+ tags: [],
31
+ title: "Model",
32
+ type: "form"
33
+ });
34
+ });
35
+ it("should add a custom conditional rule on field (2)", async () => {
36
+ class Model {
37
+ @CustomConditional((ctx) => ctx.value === 1)
38
+ test: string;
39
+ }
40
+
41
+ expect(await getFormioSchema(Model)).toEqual({
42
+ access: [],
43
+ components: [
44
+ {
45
+ disabled: false,
46
+ input: true,
47
+ key: "test",
48
+ label: "Test",
49
+ type: "textfield",
50
+ customConditional: "show = value === 1",
51
+ validate: {
52
+ required: false
53
+ }
54
+ }
55
+ ],
56
+ display: "form",
57
+ machineName: "model",
58
+ name: "model",
59
+ submissionAccess: [],
60
+ tags: [],
61
+ title: "Model",
62
+ type: "form"
63
+ });
64
+ });
65
+ it("should add a custom conditional rule on field (3)", async () => {
66
+ class Model {
67
+ @CustomConditional("show = value === 1")
68
+ test: string;
69
+ }
70
+
71
+ expect(await getFormioSchema(Model)).toEqual({
72
+ access: [],
73
+ components: [
74
+ {
75
+ disabled: false,
76
+ input: true,
77
+ key: "test",
78
+ label: "Test",
79
+ type: "textfield",
80
+ customConditional: "show = value === 1",
81
+ validate: {
82
+ required: false
83
+ }
84
+ }
85
+ ],
86
+ display: "form",
87
+ machineName: "model",
88
+ name: "model",
89
+ submissionAccess: [],
90
+ tags: [],
91
+ title: "Model",
92
+ type: "form"
93
+ });
94
+ });
95
+ });
@@ -0,0 +1,85 @@
1
+ import {isArrowFn} from "@tsed/core";
2
+ import {FormioComponent, FormioForm, FormioSubmission} from "@tsed/formio-types";
3
+ import type {Utils} from "formiojs";
4
+ import type {LoDashStatic} from "lodash";
5
+ import type {Moment} from "moment";
6
+
7
+ import {Component} from "./component.js";
8
+
9
+ export interface CustomConditionalCtx<Value = any, Data = any, Row = any> {
10
+ /**
11
+ * The complete submission object.
12
+ */
13
+ form: FormioForm;
14
+ /**
15
+ * The complete submission object.
16
+ */
17
+ submission: FormioSubmission<Data>;
18
+ /**
19
+ * The complete submission data object.
20
+ */
21
+ data: Data;
22
+ /**
23
+ * Contextual "row" data, used within DataGrid, EditGrid, and Container components
24
+ */
25
+ row: Row;
26
+ /**
27
+ * The current component JSON
28
+ */
29
+ component: FormioComponent;
30
+ /**
31
+ * The current component instance.
32
+ */
33
+ instance: any;
34
+ /**
35
+ * The current value of the component.
36
+ */
37
+ value: Value;
38
+ /**
39
+ * The moment.js library for date manipulation.
40
+ */
41
+ moment: Moment;
42
+ /**
43
+ * An instance of Lodash.
44
+ */
45
+ _: LoDashStatic;
46
+ /**
47
+ * An instance of the FormioUtils object.
48
+ */
49
+ utils: typeof Utils;
50
+ /**
51
+ * An alias for "utils".
52
+ */
53
+ util: typeof Utils;
54
+ }
55
+
56
+ function sanitize(customConditional: Function) {
57
+ const str = customConditional.toString();
58
+
59
+ const [signature, ...fn] = str.split("=>");
60
+ let content = fn.join("=>");
61
+
62
+ if (!signature.includes("{")) {
63
+ content = content.replace(new RegExp(`${signature.trim()}.`, "gi"), "");
64
+ }
65
+
66
+ return `show = ${content.trim()}`;
67
+ }
68
+
69
+ /**
70
+ * Adds custom conditional rule to display or not the input form.
71
+ * @decorator
72
+ * @formio
73
+ * @property
74
+ * @schema
75
+ * @param customConditional
76
+ */
77
+ export function CustomConditional(customConditional: string | ((ctx: CustomConditionalCtx) => any)) {
78
+ if (isArrowFn(customConditional)) {
79
+ customConditional = sanitize(customConditional);
80
+ }
81
+
82
+ return Component({
83
+ customConditional
84
+ });
85
+ }
@@ -0,0 +1,88 @@
1
+ import {getFormioSchema} from "../utils/getFormioSchema.js";
2
+ import {DataSourceJson} from "./dataSourceJson.js";
3
+ import {Select} from "./select.js";
4
+
5
+ describe("DataSourceJson", () => {
6
+ it("should declare datasource", async () => {
7
+ class Model {
8
+ @Select()
9
+ @DataSourceJson([
10
+ {
11
+ id: "id",
12
+ value: "value"
13
+ }
14
+ ])
15
+ test: string;
16
+ }
17
+
18
+ expect(await getFormioSchema(Model)).toEqual({
19
+ components: [
20
+ {
21
+ data: {
22
+ json: '[{"id":"id","value":"value"}]'
23
+ },
24
+ dataSrc: "json",
25
+ disabled: false,
26
+ input: true,
27
+ label: "Test",
28
+ key: "test",
29
+ selectThreshold: 0.3,
30
+ type: "select",
31
+ validate: {
32
+ required: false
33
+ },
34
+ widget: "choicesjs"
35
+ }
36
+ ],
37
+ display: "form",
38
+ machineName: "model",
39
+ name: "model",
40
+ title: "Model",
41
+ type: "form",
42
+ submissionAccess: [],
43
+ access: [],
44
+ tags: []
45
+ });
46
+ });
47
+ it("should declare datasource from resolver", async () => {
48
+ class Model {
49
+ @Select()
50
+ @DataSourceJson(() => [
51
+ {
52
+ id: "id",
53
+ value: "value"
54
+ }
55
+ ])
56
+ test: string;
57
+ }
58
+
59
+ expect(await getFormioSchema(Model)).toEqual({
60
+ components: [
61
+ {
62
+ data: {
63
+ json: '[{"id":"id","value":"value"}]'
64
+ },
65
+ dataSrc: "json",
66
+ disabled: false,
67
+ input: true,
68
+ label: "Test",
69
+ key: "test",
70
+ selectThreshold: 0.3,
71
+ type: "select",
72
+ validate: {
73
+ required: false
74
+ },
75
+ widget: "choicesjs"
76
+ }
77
+ ],
78
+ display: "form",
79
+ machineName: "model",
80
+ name: "model",
81
+ title: "Model",
82
+ type: "form",
83
+ submissionAccess: [],
84
+ access: [],
85
+ tags: []
86
+ });
87
+ });
88
+ });
@@ -0,0 +1,26 @@
1
+ import {isFunction} from "@tsed/core";
2
+
3
+ import {FormioDataResolver, FormioDataResolverCtx} from "../domain/FormioDataResolver.js";
4
+ import {Component} from "./component.js";
5
+
6
+ const wrap = (resolver: FormioDataResolver) => async (opts: FormioDataResolverCtx) => {
7
+ const result = await resolver(opts);
8
+ return JSON.stringify(result);
9
+ };
10
+
11
+ /**
12
+ * Set custom json data source
13
+ * @param data
14
+ * @param props
15
+ * @constructor
16
+ */
17
+ export function DataSourceJson(data: any | FormioDataResolver, props: Record<string, any> = {}) {
18
+ return Component({
19
+ ...props,
20
+ dataSrc: "json",
21
+ data: {
22
+ ...props.data,
23
+ json: isFunction(data) ? wrap(data) : JSON.stringify(data)
24
+ }
25
+ });
26
+ }
@@ -0,0 +1,111 @@
1
+ import {getFormioSchema} from "../utils/getFormioSchema.js";
2
+ import {DataSourceUrl} from "./dataSourceUrl.js";
3
+ import {Select} from "./select.js";
4
+
5
+ describe("@DataSourceUrl", () => {
6
+ it("should declare datasource url", async () => {
7
+ class Model {
8
+ @Select()
9
+ @DataSourceUrl("https://domain.com/api/datasource")
10
+ test: string;
11
+ }
12
+
13
+ expect(await getFormioSchema(Model)).toEqual({
14
+ components: [
15
+ {
16
+ data: {
17
+ url: "https://domain.com/api/datasource"
18
+ },
19
+ dataSrc: "url",
20
+ disabled: false,
21
+ input: true,
22
+ label: "Test",
23
+ key: "test",
24
+ selectThreshold: 0.3,
25
+ type: "select",
26
+ validate: {
27
+ required: false
28
+ },
29
+ widget: "choicesjs"
30
+ }
31
+ ],
32
+ display: "form",
33
+ machineName: "model",
34
+ name: "model",
35
+ title: "Model",
36
+ type: "form",
37
+ submissionAccess: [],
38
+ access: [],
39
+ tags: []
40
+ });
41
+ });
42
+ it("should declare datasource url with a resolver", async () => {
43
+ class Model {
44
+ @Select()
45
+ @DataSourceUrl(({env}) => `/datasource?env=${env}`)
46
+ test: string;
47
+ }
48
+
49
+ const schema1 = await getFormioSchema(Model, {env: "integ"});
50
+
51
+ expect(schema1).toEqual({
52
+ components: [
53
+ {
54
+ data: {
55
+ url: "/datasource?env=integ"
56
+ },
57
+ dataSrc: "url",
58
+ disabled: false,
59
+ input: true,
60
+ label: "Test",
61
+ key: "test",
62
+ selectThreshold: 0.3,
63
+ type: "select",
64
+ validate: {
65
+ required: false
66
+ },
67
+ widget: "choicesjs"
68
+ }
69
+ ],
70
+ display: "form",
71
+ machineName: "model",
72
+ name: "model",
73
+ title: "Model",
74
+ type: "form",
75
+ submissionAccess: [],
76
+ access: [],
77
+ tags: []
78
+ });
79
+
80
+ const schema2 = await getFormioSchema(Model, {env: "staging"});
81
+
82
+ expect(schema2).toEqual({
83
+ components: [
84
+ {
85
+ data: {
86
+ url: "/datasource?env=staging"
87
+ },
88
+ dataSrc: "url",
89
+ disabled: false,
90
+ input: true,
91
+ label: "Test",
92
+ key: "test",
93
+ selectThreshold: 0.3,
94
+ type: "select",
95
+ validate: {
96
+ required: false
97
+ },
98
+ widget: "choicesjs"
99
+ }
100
+ ],
101
+ display: "form",
102
+ machineName: "model",
103
+ name: "model",
104
+ title: "Model",
105
+ type: "form",
106
+ submissionAccess: [],
107
+ access: [],
108
+ tags: []
109
+ });
110
+ });
111
+ });
@@ -0,0 +1,19 @@
1
+ import {FormioDataResolver} from "../domain/FormioDataResolver.js";
2
+ import {Component} from "./component.js";
3
+
4
+ /**
5
+ * Set URL data on the current component.
6
+ * @param url
7
+ * @param props
8
+ * @constructor
9
+ */
10
+ export function DataSourceUrl(url: string | FormioDataResolver, props: Record<string, any> = {}) {
11
+ return Component({
12
+ ...props,
13
+ dataSrc: "url",
14
+ data: {
15
+ ...props.data,
16
+ url
17
+ }
18
+ });
19
+ }
@@ -0,0 +1,35 @@
1
+ import {getFormioSchema} from "../utils/getFormioSchema.js";
2
+ import {ErrorLabel} from "./errorLabel.js";
3
+
4
+ describe("@ErrorLabel", () => {
5
+ it("should set the error label", async () => {
6
+ class Model {
7
+ @ErrorLabel("Error label")
8
+ test: string;
9
+ }
10
+
11
+ expect(await getFormioSchema(Model)).toEqual({
12
+ components: [
13
+ {
14
+ disabled: false,
15
+ errorLabel: "Error label",
16
+ input: true,
17
+ key: "test",
18
+ label: "Test",
19
+ type: "textfield",
20
+ validate: {
21
+ required: false
22
+ }
23
+ }
24
+ ],
25
+ display: "form",
26
+ machineName: "model",
27
+ name: "model",
28
+ title: "Model",
29
+ type: "form",
30
+ submissionAccess: [],
31
+ access: [],
32
+ tags: []
33
+ });
34
+ });
35
+ });
@@ -0,0 +1,14 @@
1
+ import {Component} from "./component.js";
2
+
3
+ /**
4
+ * Set the error label when an error occur.
5
+ * @decorator
6
+ * @formio
7
+ * @property
8
+ * @schema
9
+ */
10
+ export function ErrorLabel(message: string) {
11
+ return Component({
12
+ errorLabel: message
13
+ });
14
+ }
@@ -0,0 +1,35 @@
1
+ import {getFormioSchema} from "../utils/getFormioSchema.js";
2
+ import {ErrorMessage} from "./errorMessage.js";
3
+
4
+ describe("@ErrorMessage", () => {
5
+ it("should set a custom error message", async () => {
6
+ class Model {
7
+ @ErrorMessage("My message")
8
+ test: string;
9
+ }
10
+
11
+ expect(await getFormioSchema(Model)).toEqual({
12
+ components: [
13
+ {
14
+ disabled: false,
15
+ input: true,
16
+ key: "test",
17
+ label: "Test",
18
+ type: "textfield",
19
+ validate: {
20
+ required: false,
21
+ customMessage: "My message"
22
+ }
23
+ }
24
+ ],
25
+ display: "form",
26
+ machineName: "model",
27
+ name: "model",
28
+ title: "Model",
29
+ type: "form",
30
+ submissionAccess: [],
31
+ access: [],
32
+ tags: []
33
+ });
34
+ });
35
+ });
@@ -0,0 +1,13 @@
1
+ import {Validate} from "./validate.js";
2
+
3
+ /**
4
+ * Add error message when an error occur.
5
+ * @decorator
6
+ * @formio
7
+ * @property
8
+ * @schema
9
+ * @param message
10
+ */
11
+ export function ErrorMessage(message: string) {
12
+ return Validate("customMessage", message);
13
+ }
@@ -0,0 +1,25 @@
1
+ import {nameOf, StoreSet, useDecorators} from "@tsed/core";
2
+ import {FormioForm} from "@tsed/formio-types";
3
+ import {Name} from "@tsed/schema";
4
+ import {kebabCase} from "change-case";
5
+
6
+ import {FormsContainer} from "../registries/FormsContainer.js";
7
+ import {Label} from "./label.js";
8
+
9
+ /**
10
+ * Expose the model as Formio Form.
11
+ * @param form
12
+ */
13
+ export function Form(form: Partial<Omit<FormioForm, "components" | "_id">> = {}): ClassDecorator {
14
+ return useDecorators(
15
+ (target: any) => {
16
+ const name = form.name || nameOf(target);
17
+ const machineName = kebabCase(name);
18
+ FormsContainer.set(name, target as any);
19
+ FormsContainer.set(machineName, target as any);
20
+ },
21
+ form.name && Name(form.name),
22
+ form.label && Label(form.label),
23
+ form && StoreSet("formio:form", form)
24
+ );
25
+ }
@@ -0,0 +1,35 @@
1
+ import {getFormioSchema} from "../utils/getFormioSchema.js";
2
+ import {Hidden} from "./hidden.js";
3
+
4
+ describe("Hidden", () => {
5
+ it("should declare a model with Hidden field", async () => {
6
+ class Model {
7
+ @Hidden()
8
+ test: string;
9
+ }
10
+
11
+ expect(await getFormioSchema(Model)).toEqual({
12
+ components: [
13
+ {
14
+ disabled: false,
15
+ hidden: true,
16
+ input: true,
17
+ key: "test",
18
+ label: "Test",
19
+ type: "textfield",
20
+ validate: {
21
+ required: false
22
+ }
23
+ }
24
+ ],
25
+ display: "form",
26
+ machineName: "model",
27
+ name: "model",
28
+ title: "Model",
29
+ type: "form",
30
+ submissionAccess: [],
31
+ access: [],
32
+ tags: []
33
+ });
34
+ });
35
+ });