@remoteoss/json-schema-form 0.11.4-beta.0 → 0.11.5-beta.0

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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ #### 0.11.5-beta.0 (2024-08-22)
2
+
3
+ ##### Bug Fixes
4
+
5
+ * **validation:** Validate null field type correctly ([#91](https://github.com/remoteoss/json-schema-form/pull/91)) ([1cb8b61f](https://github.com/remoteoss/json-schema-form/commit/1cb8b61fec06d851832f9e76b3156d6f574b5d41))
6
+ * **files:** accept object value with `name` property ([#91](https://github.com/remoteoss/json-schema-form/pull/91)) ([1cb8b61f](https://github.com/remoteoss/json-schema-form/commit/1cb8b61fec06d851832f9e76b3156d6f574b5d41))
7
+
8
+
1
9
  #### 0.11.4-beta.0 (2024-08-20)
2
10
 
3
11
  ##### Bug Fixes
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
3
  Copyright (c) 2024 Remote Technology, Inc.
4
- NPM Package: @remoteoss/json-schema-form@0.11.4-beta.0
5
- Generated: Tue, 20 Aug 2024 17:17:21 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.11.5-beta.0
5
+ Generated: Thu, 22 Aug 2024 09:27:45 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -553,7 +553,12 @@ var yupSchemas = {
553
553
  multiple: {
554
554
  select: (0, import_yup.array)().nullable(),
555
555
  "group-array": (0, import_yup.array)().nullable()
556
- }
556
+ },
557
+ null: (0, import_yup.mixed)().typeError("The value must be null").test(
558
+ "matchesNullValue",
559
+ ({ value }) => `The value ${JSON.stringify(value)} is not valid.`,
560
+ (value) => value === void 0 || value === null
561
+ )
557
562
  };
558
563
  var yupSchemasToJsonTypes = {
559
564
  string: yupSchemas.text,
@@ -562,7 +567,7 @@ var yupSchemasToJsonTypes = {
562
567
  object: yupSchemas.fieldset,
563
568
  array: yupSchemas.multiple.select,
564
569
  boolean: yupSchemas.checkboxBool,
565
- null: import_noop.default
570
+ null: yupSchemas.null
566
571
  };
567
572
  function getRequiredErrorMessage(inputType, { inlineError, configError }) {
568
573
  if (inlineError)
@@ -668,24 +673,26 @@ function buildYupSchema(field, config, logic) {
668
673
  () => errorMessage.pattern ?? errorMessageFromConfig.pattern ?? `Must have a valid format. E.g. ${randomPlaceholder}`
669
674
  );
670
675
  }
671
- function isFile(files) {
672
- return Array.isArray(files) ? files.every((file) => file instanceof File) : files instanceof File;
676
+ function isValidFileInput(files) {
677
+ return files === void 0 || files === null || files.every(
678
+ (file) => file instanceof File || Object.prototype.hasOwnProperty.call(file, "name")
679
+ );
673
680
  }
674
681
  function withFile(yupSchema) {
675
- return yupSchema.test("isValidFile", "Not a valid file.", isFile);
682
+ return yupSchema.test("isValidFile", "Not a valid file.", isValidFileInput);
676
683
  }
677
684
  function withMaxFileSize(yupSchema) {
678
685
  return yupSchema.test(
679
686
  "isValidFileSize",
680
687
  errorMessage.maxFileSize ?? errorMessageFromConfig.maxFileSize ?? `File size too large. The limit is ${convertKbBytesToMB(propertyFields.maxFileSize)} MB.`,
681
- (files) => isFile(files) && !files?.some((file) => convertBytesToKB(file.size) > propertyFields.maxFileSize)
688
+ (files) => isValidFileInput(files) && !files?.some((file) => convertBytesToKB(file.size) > propertyFields.maxFileSize)
682
689
  );
683
690
  }
684
691
  function withFileFormat(yupSchema) {
685
692
  return yupSchema.test(
686
693
  "isSupportedFormat",
687
694
  errorMessage.accept ?? errorMessageFromConfig.accept ?? `Unsupported file format. The acceptable formats are ${propertyFields.accept}.`,
688
- (files) => isFile(files) && files.length > 0 ? files.some((file) => {
695
+ (files) => isValidFileInput(files) && files?.length > 0 ? files.some((file) => {
689
696
  const fileType = file.name.split(".").pop();
690
697
  return propertyFields.accept.includes(fileType.toLowerCase());
691
698
  }) : true
@@ -740,12 +747,13 @@ function buildYupSchema(field, config, logic) {
740
747
  validators[0] = () => withBaseSchema().of(buildGroupArraySchema());
741
748
  } else if (inputType === supportedTypes.FIELDSET) {
742
749
  validators[0] = () => withBaseSchema().shape(buildFieldSetSchema(propertyFields.fields));
743
- } else if (inputType === supportedTypes.FILE) {
744
- validators.push(withFile);
745
750
  }
746
751
  if (propertyFields.required) {
747
752
  validators.push(withRequired);
748
753
  }
754
+ if (inputType === supportedTypes.FILE) {
755
+ validators.push(withFile);
756
+ }
749
757
  if (typeof propertyFields.minimum !== "undefined") {
750
758
  validators.push(withMin);
751
759
  }
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
3
  Copyright (c) 2024 Remote Technology, Inc.
4
- NPM Package: @remoteoss/json-schema-form@0.11.4-beta.0
5
- Generated: Tue, 20 Aug 2024 17:17:21 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.11.5-beta.0
5
+ Generated: Thu, 22 Aug 2024 09:27:45 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -516,7 +516,12 @@ var yupSchemas = {
516
516
  multiple: {
517
517
  select: array().nullable(),
518
518
  "group-array": array().nullable()
519
- }
519
+ },
520
+ null: mixed().typeError("The value must be null").test(
521
+ "matchesNullValue",
522
+ ({ value }) => `The value ${JSON.stringify(value)} is not valid.`,
523
+ (value) => value === void 0 || value === null
524
+ )
520
525
  };
521
526
  var yupSchemasToJsonTypes = {
522
527
  string: yupSchemas.text,
@@ -525,7 +530,7 @@ var yupSchemasToJsonTypes = {
525
530
  object: yupSchemas.fieldset,
526
531
  array: yupSchemas.multiple.select,
527
532
  boolean: yupSchemas.checkboxBool,
528
- null: noop
533
+ null: yupSchemas.null
529
534
  };
530
535
  function getRequiredErrorMessage(inputType, { inlineError, configError }) {
531
536
  if (inlineError)
@@ -631,24 +636,26 @@ function buildYupSchema(field, config, logic) {
631
636
  () => errorMessage.pattern ?? errorMessageFromConfig.pattern ?? `Must have a valid format. E.g. ${randomPlaceholder}`
632
637
  );
633
638
  }
634
- function isFile(files) {
635
- return Array.isArray(files) ? files.every((file) => file instanceof File) : files instanceof File;
639
+ function isValidFileInput(files) {
640
+ return files === void 0 || files === null || files.every(
641
+ (file) => file instanceof File || Object.prototype.hasOwnProperty.call(file, "name")
642
+ );
636
643
  }
637
644
  function withFile(yupSchema) {
638
- return yupSchema.test("isValidFile", "Not a valid file.", isFile);
645
+ return yupSchema.test("isValidFile", "Not a valid file.", isValidFileInput);
639
646
  }
640
647
  function withMaxFileSize(yupSchema) {
641
648
  return yupSchema.test(
642
649
  "isValidFileSize",
643
650
  errorMessage.maxFileSize ?? errorMessageFromConfig.maxFileSize ?? `File size too large. The limit is ${convertKbBytesToMB(propertyFields.maxFileSize)} MB.`,
644
- (files) => isFile(files) && !files?.some((file) => convertBytesToKB(file.size) > propertyFields.maxFileSize)
651
+ (files) => isValidFileInput(files) && !files?.some((file) => convertBytesToKB(file.size) > propertyFields.maxFileSize)
645
652
  );
646
653
  }
647
654
  function withFileFormat(yupSchema) {
648
655
  return yupSchema.test(
649
656
  "isSupportedFormat",
650
657
  errorMessage.accept ?? errorMessageFromConfig.accept ?? `Unsupported file format. The acceptable formats are ${propertyFields.accept}.`,
651
- (files) => isFile(files) && files.length > 0 ? files.some((file) => {
658
+ (files) => isValidFileInput(files) && files?.length > 0 ? files.some((file) => {
652
659
  const fileType = file.name.split(".").pop();
653
660
  return propertyFields.accept.includes(fileType.toLowerCase());
654
661
  }) : true
@@ -703,12 +710,13 @@ function buildYupSchema(field, config, logic) {
703
710
  validators[0] = () => withBaseSchema().of(buildGroupArraySchema());
704
711
  } else if (inputType === supportedTypes.FIELDSET) {
705
712
  validators[0] = () => withBaseSchema().shape(buildFieldSetSchema(propertyFields.fields));
706
- } else if (inputType === supportedTypes.FILE) {
707
- validators.push(withFile);
708
713
  }
709
714
  if (propertyFields.required) {
710
715
  validators.push(withRequired);
711
716
  }
717
+ if (inputType === supportedTypes.FILE) {
718
+ validators.push(withFile);
719
+ }
712
720
  if (typeof propertyFields.minimum !== "undefined") {
713
721
  validators.push(withMin);
714
722
  }
@@ -1,8 +1,8 @@
1
1
 
2
2
  /*!
3
3
  Copyright (c) 2024 Remote Technology, Inc.
4
- NPM Package: @remoteoss/json-schema-form@0.11.4-beta.0
5
- Generated: Tue, 20 Aug 2024 17:17:21 GMT
4
+ NPM Package: @remoteoss/json-schema-form@0.11.5-beta.0
5
+ Generated: Thu, 22 Aug 2024 09:27:45 GMT
6
6
 
7
7
  MIT License
8
8
 
@@ -12032,7 +12032,12 @@ var yupSchemas = {
12032
12032
  multiple: {
12033
12033
  select: array_default().nullable(),
12034
12034
  "group-array": array_default().nullable()
12035
- }
12035
+ },
12036
+ null: SchemaType().typeError("The value must be null").test(
12037
+ "matchesNullValue",
12038
+ ({ value }) => `The value ${JSON.stringify(value)} is not valid.`,
12039
+ (value) => value === void 0 || value === null
12040
+ )
12036
12041
  };
12037
12042
  var yupSchemasToJsonTypes = {
12038
12043
  string: yupSchemas.text,
@@ -12041,7 +12046,7 @@ var yupSchemasToJsonTypes = {
12041
12046
  object: yupSchemas.fieldset,
12042
12047
  array: yupSchemas.multiple.select,
12043
12048
  boolean: yupSchemas.checkboxBool,
12044
- null: import_noop.default
12049
+ null: yupSchemas.null
12045
12050
  };
12046
12051
  function getRequiredErrorMessage(inputType, { inlineError, configError }) {
12047
12052
  if (inlineError)
@@ -12147,24 +12152,26 @@ function buildYupSchema(field, config, logic) {
12147
12152
  () => errorMessage.pattern ?? errorMessageFromConfig.pattern ?? `Must have a valid format. E.g. ${randomPlaceholder}`
12148
12153
  );
12149
12154
  }
12150
- function isFile(files) {
12151
- return Array.isArray(files) ? files.every((file) => file instanceof File) : files instanceof File;
12155
+ function isValidFileInput(files) {
12156
+ return files === void 0 || files === null || files.every(
12157
+ (file) => file instanceof File || Object.prototype.hasOwnProperty.call(file, "name")
12158
+ );
12152
12159
  }
12153
12160
  function withFile(yupSchema) {
12154
- return yupSchema.test("isValidFile", "Not a valid file.", isFile);
12161
+ return yupSchema.test("isValidFile", "Not a valid file.", isValidFileInput);
12155
12162
  }
12156
12163
  function withMaxFileSize(yupSchema) {
12157
12164
  return yupSchema.test(
12158
12165
  "isValidFileSize",
12159
12166
  errorMessage.maxFileSize ?? errorMessageFromConfig.maxFileSize ?? `File size too large. The limit is ${convertKbBytesToMB(propertyFields.maxFileSize)} MB.`,
12160
- (files) => isFile(files) && !files?.some((file) => convertBytesToKB(file.size) > propertyFields.maxFileSize)
12167
+ (files) => isValidFileInput(files) && !files?.some((file) => convertBytesToKB(file.size) > propertyFields.maxFileSize)
12161
12168
  );
12162
12169
  }
12163
12170
  function withFileFormat(yupSchema) {
12164
12171
  return yupSchema.test(
12165
12172
  "isSupportedFormat",
12166
12173
  errorMessage.accept ?? errorMessageFromConfig.accept ?? `Unsupported file format. The acceptable formats are ${propertyFields.accept}.`,
12167
- (files) => isFile(files) && files.length > 0 ? files.some((file) => {
12174
+ (files) => isValidFileInput(files) && files?.length > 0 ? files.some((file) => {
12168
12175
  const fileType = file.name.split(".").pop();
12169
12176
  return propertyFields.accept.includes(fileType.toLowerCase());
12170
12177
  }) : true
@@ -12219,12 +12226,13 @@ function buildYupSchema(field, config, logic) {
12219
12226
  validators[0] = () => withBaseSchema().of(buildGroupArraySchema());
12220
12227
  } else if (inputType === supportedTypes.FIELDSET) {
12221
12228
  validators[0] = () => withBaseSchema().shape(buildFieldSetSchema(propertyFields.fields));
12222
- } else if (inputType === supportedTypes.FILE) {
12223
- validators.push(withFile);
12224
12229
  }
12225
12230
  if (propertyFields.required) {
12226
12231
  validators.push(withRequired);
12227
12232
  }
12233
+ if (inputType === supportedTypes.FILE) {
12234
+ validators.push(withFile);
12235
+ }
12228
12236
  if (typeof propertyFields.minimum !== "undefined") {
12229
12237
  validators.push(withMin);
12230
12238
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remoteoss/json-schema-form",
3
- "version": "0.11.4-beta.0",
3
+ "version": "0.11.5-beta.0",
4
4
  "description": "Headless UI form powered by JSON Schemas",
5
5
  "author": "Remote.com <engineering@remote.com> (https://remote.com/)",
6
6
  "license": "MIT",
@@ -29,6 +29,7 @@ import {
29
29
  schemaInputWithStatement,
30
30
  schemaInputTypeCheckbox,
31
31
  schemaInputTypeCheckboxBooleans,
32
+ schemaInputTypeNull,
32
33
  schemaWithOrderKeyword,
33
34
  schemaWithPositionDeprecated,
34
35
  schemaDynamicValidationConst,
@@ -60,6 +61,7 @@ import {
60
61
  schemaInputTypeNumberWithPercentage,
61
62
  schemaForErrorMessageSpecificity,
62
63
  jsfConfigForErrorMessageSpecificity,
64
+ schemaInputTypeFile,
63
65
  } from './helpers';
64
66
  import { mockConsole, restoreConsoleAndEnsureItWasNotCalled } from './testUtils';
65
67
 
@@ -1578,6 +1580,48 @@ describe('createHeadlessForm', () => {
1578
1580
  });
1579
1581
  });
1580
1582
 
1583
+ it('supports "null" field type', () => {
1584
+ const { handleValidation, fields } = createHeadlessForm(schemaInputTypeNull, {
1585
+ strictInputType: false,
1586
+ });
1587
+
1588
+ expect(fields).toMatchObject([
1589
+ {
1590
+ name: 'name',
1591
+ label: '(Optional) Name',
1592
+ type: undefined,
1593
+ jsonType: 'null',
1594
+ schema: expect.any(Object),
1595
+ },
1596
+ {
1597
+ name: 'username',
1598
+ label: 'Username',
1599
+ type: 'text',
1600
+ jsonType: 'string',
1601
+ inputType: 'text',
1602
+ maxLength: 4,
1603
+ schema: expect.any(Object),
1604
+ },
1605
+ ]);
1606
+
1607
+ // jsonType `null` fields do not have a corresponding inputType
1608
+ expect(fields[0].inputType).toBeUndefined();
1609
+
1610
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
1611
+
1612
+ expect(validateForm({})).toEqual({
1613
+ username: 'Required field',
1614
+ });
1615
+
1616
+ expect(validateForm({ username: 'hello', name: 'John' })).toEqual({
1617
+ username: 'Please insert up to 4 characters',
1618
+ name: 'The value "John" is not valid.',
1619
+ });
1620
+
1621
+ expect(validateForm({ username: 'john' })).toBeUndefined();
1622
+ expect(validateForm({ username: 'john', name: null })).toBeUndefined();
1623
+ });
1624
+
1581
1625
  it('supports oneOf pattern validation', () => {
1582
1626
  const result = createHeadlessForm(mockTelWithPattern);
1583
1627
 
@@ -2730,6 +2774,35 @@ describe('createHeadlessForm', () => {
2730
2774
  .validate(emptyFile)
2731
2775
  ).resolves.toEqual(emptyFile);
2732
2776
  });
2777
+
2778
+ it('it validates missing file correctly', () => {
2779
+ const { handleValidation } = createHeadlessForm(
2780
+ JSONSchemaBuilder().addInput({ fileInput: mockFileInput }).build()
2781
+ );
2782
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
2783
+
2784
+ expect(validateForm({})).toBeUndefined();
2785
+ expect(validateForm({ fileInput: null })).toBeUndefined();
2786
+ });
2787
+ });
2788
+
2789
+ describe('when a field file is required', () => {
2790
+ it('it validates missing file correctly', () => {
2791
+ const { handleValidation } = createHeadlessForm(schemaInputTypeFile);
2792
+ const validateForm = (vals) => friendlyError(handleValidation(vals));
2793
+
2794
+ expect(validateForm({})).toEqual({
2795
+ a_file: 'Required field',
2796
+ });
2797
+
2798
+ expect(
2799
+ validateForm({
2800
+ a_file: null,
2801
+ })
2802
+ ).toEqual({
2803
+ a_file: 'Required field',
2804
+ });
2805
+ });
2733
2806
  });
2734
2807
 
2735
2808
  describe('when a field has accepted extensions', () => {
@@ -2789,8 +2862,39 @@ describe('createHeadlessForm', () => {
2789
2862
  .validate({ fileInput: [file] })
2790
2863
  ).resolves.toEqual(assertObj));
2791
2864
  });
2865
+
2792
2866
  describe('and file is not instance of a File', () => {
2793
- it('should throw an error', async () =>
2867
+ it('accepts if file object has name property', async () => {
2868
+ expect(
2869
+ object()
2870
+ .shape({
2871
+ fileInput: fields[0].schema,
2872
+ })
2873
+ .validate({ fileInput: [{ name: 'foo.pdf' }] })
2874
+ ).resolves.toEqual({ fileInput: [{ name: 'foo.pdf' }] });
2875
+ });
2876
+
2877
+ it('should validate format', async () =>
2878
+ expect(
2879
+ object()
2880
+ .shape({
2881
+ fileInput: fields[0].schema,
2882
+ })
2883
+ .validate({ fileInput: [{ name: 'foo.txt' }] })
2884
+ ).rejects.toMatchObject({
2885
+ errors: ['Unsupported file format. The acceptable formats are .png,.jpg,.jpeg,.pdf.'],
2886
+ }));
2887
+
2888
+ it('should validate max size', async () =>
2889
+ expect(
2890
+ object()
2891
+ .shape({
2892
+ fileInput: fields[0].schema,
2893
+ })
2894
+ .validate({ fileInput: [{ name: 'foo.txt', size: 1024 * 1024 * 1024 }] })
2895
+ ).rejects.toMatchObject({ errors: ['File size too large. The limit is 20 MB.'] }));
2896
+
2897
+ it('throw an error if invalid file object', async () =>
2794
2898
  expect(
2795
2899
  object()
2796
2900
  .shape({
@@ -782,6 +782,25 @@ export const schemaInputWithStatement = {
782
782
  },
783
783
  };
784
784
 
785
+ export const schemaHiddenInputWithStatement = {
786
+ properties: {
787
+ a_field_statement: {
788
+ type: 'null',
789
+ title: 'Company statement',
790
+ 'x-jsf-presentation': {
791
+ inputType: 'hidden',
792
+ statement: {
793
+ title: 'Important statement',
794
+ description:
795
+ "This statement message will be shown at all times, irrespective of this field's visibility.",
796
+ inputType: 'statement',
797
+ severity: 'warning',
798
+ },
799
+ },
800
+ },
801
+ },
802
+ };
803
+
785
804
  export const schemaInputWithExtra = {
786
805
  properties: {
787
806
  bonus: {
@@ -1141,6 +1160,23 @@ export const schemaInputTypeCheckboxBooleans = JSONSchemaBuilder()
1141
1160
  .setRequiredFields(['boolean_required'])
1142
1161
  .build();
1143
1162
 
1163
+ export const schemaInputTypeNull = {
1164
+ additionalProperties: false,
1165
+ type: 'object',
1166
+ properties: {
1167
+ name: {
1168
+ type: 'null',
1169
+ title: '(Optional) Name',
1170
+ },
1171
+ username: {
1172
+ type: 'string',
1173
+ title: 'Username',
1174
+ maxLength: 4,
1175
+ },
1176
+ },
1177
+ required: ['username'],
1178
+ };
1179
+
1144
1180
  export const schemaCustomErrorMessageByField = {
1145
1181
  properties: {
1146
1182
  tabs: {