@formio/js 5.0.0-rc.37 → 5.0.0-rc.38

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 (61) hide show
  1. package/dist/fonts/bootstrap-icons.woff +0 -0
  2. package/dist/fonts/bootstrap-icons.woff2 +0 -0
  3. package/dist/formio.embed.js +1 -1
  4. package/dist/formio.embed.min.js +1 -1
  5. package/dist/formio.embed.min.js.LICENSE.txt +1 -1
  6. package/dist/formio.form.js +853 -132
  7. package/dist/formio.form.min.js +1 -1
  8. package/dist/formio.form.min.js.LICENSE.txt +5 -3
  9. package/dist/formio.full.css +4 -4
  10. package/dist/formio.full.js +827 -136
  11. package/dist/formio.full.min.css +3 -3
  12. package/dist/formio.full.min.js +1 -1
  13. package/dist/formio.full.min.js.LICENSE.txt +5 -3
  14. package/dist/formio.js +4 -4
  15. package/dist/formio.min.js +1 -1
  16. package/dist/formio.min.js.LICENSE.txt +1 -1
  17. package/dist/formio.utils.js +25 -14
  18. package/dist/formio.utils.min.js +1 -1
  19. package/dist/formio.utils.min.js.LICENSE.txt +5 -3
  20. package/lib/cjs/Element.js +2 -2
  21. package/lib/cjs/components/_classes/component/Component.js +21 -7
  22. package/lib/cjs/components/_classes/component/fixtures/comp5.js +2 -2
  23. package/lib/cjs/components/_classes/list/ListComponent.js +5 -12
  24. package/lib/cjs/components/_classes/nested/NestedComponent.js +17 -9
  25. package/lib/cjs/components/columns/editForm/Columns.edit.display.js +1 -1
  26. package/lib/cjs/components/editgrid/EditGrid.js +11 -4
  27. package/lib/cjs/components/form/Form.js +3 -1
  28. package/lib/cjs/components/html/HTML.js +2 -2
  29. package/lib/cjs/components/html/fixtures/comp3.js +31 -0
  30. package/lib/cjs/components/html/fixtures/index.js +3 -1
  31. package/lib/cjs/components/radio/Radio.js +2 -1
  32. package/lib/cjs/components/radio/fixtures/comp10.js +23 -0
  33. package/lib/cjs/components/radio/fixtures/index.js +3 -1
  34. package/lib/cjs/components/recaptcha/ReCaptcha.js +3 -0
  35. package/lib/cjs/components/select/Select.js +84 -9
  36. package/lib/cjs/components/survey/Survey.js +10 -0
  37. package/lib/cjs/utils/conditionOperators/IsEqualTo.js +19 -1
  38. package/lib/cjs/utils/conditionOperators/IsNotEqualTo.js +4 -5
  39. package/lib/cjs/utils/utils.js +40 -2
  40. package/lib/mjs/Element.js +2 -2
  41. package/lib/mjs/components/_classes/component/Component.js +24 -8
  42. package/lib/mjs/components/_classes/component/fixtures/comp5.js +2 -2
  43. package/lib/mjs/components/_classes/list/ListComponent.js +5 -12
  44. package/lib/mjs/components/_classes/nested/NestedComponent.js +17 -9
  45. package/lib/mjs/components/columns/editForm/Columns.edit.display.js +1 -1
  46. package/lib/mjs/components/editgrid/EditGrid.js +11 -4
  47. package/lib/mjs/components/form/Form.js +3 -1
  48. package/lib/mjs/components/html/HTML.js +2 -2
  49. package/lib/mjs/components/html/fixtures/comp3.js +29 -0
  50. package/lib/mjs/components/html/fixtures/index.js +2 -1
  51. package/lib/mjs/components/radio/Radio.js +2 -1
  52. package/lib/mjs/components/radio/fixtures/comp10.js +21 -0
  53. package/lib/mjs/components/radio/fixtures/index.js +2 -1
  54. package/lib/mjs/components/recaptcha/ReCaptcha.js +3 -0
  55. package/lib/mjs/components/select/Select.js +83 -10
  56. package/lib/mjs/components/survey/Survey.js +10 -0
  57. package/lib/mjs/utils/conditionOperators/IsEqualTo.js +18 -1
  58. package/lib/mjs/utils/conditionOperators/IsNotEqualTo.js +4 -5
  59. package/lib/mjs/utils/utils.js +35 -0
  60. package/package.json +2 -2
  61. package/types/formio.d.ts +4 -0
@@ -30,8 +30,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
30
30
  return (mod && mod.__esModule) ? mod : { "default": mod };
31
31
  };
32
32
  Object.defineProperty(exports, "__esModule", { value: true });
33
- exports.observeOverload = exports.withSwitch = exports.firstNonNil = exports.unfold = exports.bootstrapVersion = exports.uniqueKey = exports.iterateKey = exports.delay = exports.fieldData = exports.getCurrencyAffixes = exports.getNumberDecimalLimit = exports.getNumberSeparators = exports.matchInputMask = exports.unmaskValue = exports.getInputMask = exports.convertFormatToMask = exports.convertFormatToMoment = exports.convertFormatToFlatpickr = exports.getLocaleDateFormatInfo = exports.formatOffset = exports.formatDate = exports.momentDate = exports.loadZones = exports.shouldLoadZones = exports.zonesLoaded = exports.offsetDate = exports.currentTimezone = exports.isValidDate = exports.getDateSetting = exports.guid = exports.uniqueName = exports.convertStringToHTMLElement = exports.unescapeHTML = exports.setActionProperty = exports.checkTrigger = exports.checkCondition = exports.checkJsonConditional = exports.checkCustomConditional = exports.getComponentActualValue = exports.checkSimpleConditional = exports.checkCalculated = exports.isMongoId = exports.boolValue = exports.getElementRect = exports.getPropertyValue = exports.getRandomComponentId = exports.evaluate = exports.moment = exports.ConditionOperators = exports.jsonLogic = void 0;
34
- exports.interpolateErrors = exports.getComponentSavedTypes = exports.componentValueTypes = exports._ = exports.getFocusableElements = exports.isInsideScopingComponent = exports.isPromise = exports.getDataParentComponent = exports.getComponentPath = exports.getComponentPathWithoutIndicies = exports.getBrowserInfo = exports.getIEBrowserVersion = exports.round = exports.getStringFromComponentPath = exports.isChildOf = exports.getArrayFromComponentPath = exports.isInputComponent = exports.interpolate = exports.Evaluator = exports.fastCloneDeep = exports.sanitize = exports.translateHTMLTemplate = exports.getContextButtons = exports.getContextComponents = void 0;
33
+ exports.withSwitch = exports.firstNonNil = exports.unfold = exports.bootstrapVersion = exports.uniqueKey = exports.iterateKey = exports.delay = exports.fieldData = exports.getCurrencyAffixes = exports.getNumberDecimalLimit = exports.getNumberSeparators = exports.matchInputMask = exports.unmaskValue = exports.getInputMask = exports.convertFormatToMask = exports.convertFormatToMoment = exports.convertFormatToFlatpickr = exports.getLocaleDateFormatInfo = exports.formatOffset = exports.formatDate = exports.momentDate = exports.loadZones = exports.shouldLoadZones = exports.zonesLoaded = exports.offsetDate = exports.currentTimezone = exports.isValidDate = exports.getDateSetting = exports.guid = exports.uniqueName = exports.convertStringToHTMLElement = exports.escapeHTML = exports.unescapeHTML = exports.setActionProperty = exports.checkTrigger = exports.checkCondition = exports.checkJsonConditional = exports.checkCustomConditional = exports.getComponentActualValue = exports.checkSimpleConditional = exports.checkCalculated = exports.isMongoId = exports.boolValue = exports.getElementRect = exports.getPropertyValue = exports.getRandomComponentId = exports.evaluate = exports.moment = exports.ConditionOperators = exports.jsonLogic = void 0;
34
+ exports.isSelectResourceWithObjectValue = exports.getItemTemplateKeys = exports.interpolateErrors = exports.getComponentSavedTypes = exports.componentValueTypes = exports._ = exports.getFocusableElements = exports.isInsideScopingComponent = exports.isPromise = exports.getDataParentComponent = exports.getComponentPath = exports.getComponentPathWithoutIndicies = exports.getBrowserInfo = exports.getIEBrowserVersion = exports.round = exports.getStringFromComponentPath = exports.isChildOf = exports.getArrayFromComponentPath = exports.isInputComponent = exports.interpolate = exports.Evaluator = exports.fastCloneDeep = exports.sanitize = exports.translateHTMLTemplate = exports.getContextButtons = exports.getContextComponents = exports.observeOverload = void 0;
35
35
  const lodash_1 = __importDefault(require("lodash"));
36
36
  exports._ = lodash_1.default;
37
37
  const fetch_ponyfill_1 = __importDefault(require("fetch-ponyfill"));
@@ -447,6 +447,22 @@ function unescapeHTML(str) {
447
447
  return doc.documentElement.textContent;
448
448
  }
449
449
  exports.unescapeHTML = unescapeHTML;
450
+ /**
451
+ * Escape HTML characters like <, >, & and etc.
452
+ * @param str
453
+ * @returns {string}
454
+ */
455
+ function escapeHTML(html) {
456
+ if (html) {
457
+ return html.replace(/&/g, '&amp;')
458
+ .replace(/</g, '&lt;')
459
+ .replace(/>/g, '&gt;')
460
+ .replace(/"/g, '&quot;')
461
+ .replace(/'/g, '&#39;');
462
+ }
463
+ return '';
464
+ }
465
+ exports.escapeHTML = escapeHTML;
450
466
  /**
451
467
  * Make HTML element from string
452
468
  * @param str
@@ -1509,3 +1525,25 @@ const interpolateErrors = (component, errors, interpolateFn) => {
1509
1525
  });
1510
1526
  };
1511
1527
  exports.interpolateErrors = interpolateErrors;
1528
+ function getItemTemplateKeys(template) {
1529
+ const templateKeys = [];
1530
+ if (!template) {
1531
+ return templateKeys;
1532
+ }
1533
+ const keys = template.match(/({{\s*(.*?)\s*}})/g);
1534
+ if (keys) {
1535
+ keys.forEach((key) => {
1536
+ const propKey = key.match(/{{\s*item\.(.*?)\s*}}/);
1537
+ if (propKey && propKey.length > 1) {
1538
+ templateKeys.push(propKey[1]);
1539
+ }
1540
+ });
1541
+ }
1542
+ return templateKeys;
1543
+ }
1544
+ exports.getItemTemplateKeys = getItemTemplateKeys;
1545
+ function isSelectResourceWithObjectValue(comp = {}) {
1546
+ const { reference, dataSrc, valueProperty } = comp;
1547
+ return reference || (dataSrc === 'resource' && (!valueProperty || valueProperty === 'data'));
1548
+ }
1549
+ exports.isSelectResourceWithObjectValue = isSelectResourceWithObjectValue;
@@ -179,7 +179,7 @@ export default class Element {
179
179
  * @param persistent
180
180
  * If this listener should persist beyond "destroy" commands.
181
181
  */
182
- addEventListener(obj, type, func, persistent) {
182
+ addEventListener(obj, type, func, persistent, capture) {
183
183
  if (!obj) {
184
184
  return;
185
185
  }
@@ -187,7 +187,7 @@ export default class Element {
187
187
  this.eventHandlers.push({ id: this.id, obj, type, func });
188
188
  }
189
189
  if ('addEventListener' in obj) {
190
- obj.addEventListener(type, func, false);
190
+ obj.addEventListener(type, func, !!capture);
191
191
  }
192
192
  else if ('attachEvent' in obj) {
193
193
  obj.attachEvent(`on${type}`, func);
@@ -3,7 +3,7 @@ import { conformToMask } from '@formio/vanilla-text-mask';
3
3
  import tippy from 'tippy.js';
4
4
  import _ from 'lodash';
5
5
  import isMobile from 'ismobilejs';
6
- import { processOne, processOneSync, validateProcess, validateProcessSync } from '@formio/core/process';
6
+ import { processOne, processOneSync, validateProcessInfo } from '@formio/core/process';
7
7
  import { Formio } from '../../../Formio';
8
8
  import * as FormioUtils from '../../../utils/utils';
9
9
  import { fastCloneDeep, boolValue, getComponentPath, isInsideScopingComponent, currentTimezone } from '../../../utils/utils';
@@ -819,7 +819,13 @@ export default class Component extends Element {
819
819
  renderTemplate(name, data = {}, modeOption) {
820
820
  // Need to make this fall back to form if renderMode is not found similar to how we search templates.
821
821
  const mode = modeOption || this.options.renderMode || 'form';
822
- data.component = this.component;
822
+ data.component = {
823
+ ...this.component,
824
+ };
825
+ // Escape HTML provided in component description and render it as a string instead
826
+ if (this.component.description) {
827
+ data.component.description = FormioUtils.escapeHTML(this.component.description);
828
+ }
823
829
  data.self = this;
824
830
  data.options = this.options;
825
831
  data.readOnly = this.options.readOnly;
@@ -1030,12 +1036,12 @@ export default class Component extends Element {
1030
1036
  const tooltipText = this.interpolate(tooltipDataTitle || tooltipAttribute)
1031
1037
  .replace(/(?:\r\n|\r|\n)/g, '<br />');
1032
1038
  this.tooltips[index] = tippy(tooltip, {
1033
- allowHTML: true,
1039
+ allowHTML: false,
1034
1040
  trigger: 'mouseenter click focus',
1035
1041
  placement: 'right',
1036
1042
  zIndex: 10000,
1037
1043
  interactive: true,
1038
- content: this.t(this.sanitize(tooltipText), { _userInput: true }),
1044
+ content: this.t(tooltipText, { _userInput: true }),
1039
1045
  });
1040
1046
  }
1041
1047
  });
@@ -2574,7 +2580,7 @@ export default class Component extends Element {
2574
2580
  scope: validationScope,
2575
2581
  instance: this,
2576
2582
  processors: [
2577
- validateProcessSync
2583
+ validateProcessInfo
2578
2584
  ]
2579
2585
  });
2580
2586
  const errors = validationScope.errors;
@@ -2655,7 +2661,7 @@ export default class Component extends Element {
2655
2661
  instance: this,
2656
2662
  scope: { errors: [] },
2657
2663
  processors: [
2658
- async ? validateProcess : validateProcessSync
2664
+ validateProcessInfo
2659
2665
  ]
2660
2666
  };
2661
2667
  if (async) {
@@ -2684,7 +2690,12 @@ export default class Component extends Element {
2684
2690
  return this.validateComponent(data, row, flags).then((errors) => {
2685
2691
  allErrors.push(...errors);
2686
2692
  if (this.parent && this.parent.childErrors) {
2687
- this.parent.childErrors.push(...errors);
2693
+ if (errors.length) {
2694
+ this.parent.childErrors.push(...errors);
2695
+ }
2696
+ else {
2697
+ _.remove(this.parent.childErrors, (err) => err.component.key === this.component.key);
2698
+ }
2688
2699
  }
2689
2700
  this.showValidationErrors(errors, data, row, flags);
2690
2701
  return errors.length === 0;
@@ -2695,7 +2706,12 @@ export default class Component extends Element {
2695
2706
  this.showValidationErrors(errors, data, row, flags);
2696
2707
  allErrors.push(...errors);
2697
2708
  if (this.parent && this.parent.childErrors) {
2698
- this.parent.childErrors.push(...errors);
2709
+ if (errors.length) {
2710
+ this.parent.childErrors.push(...errors);
2711
+ }
2712
+ else {
2713
+ _.remove(this.parent.childErrors, (err) => err.component.key === this.component.key);
2714
+ }
2699
2715
  }
2700
2716
  return errors.length === 0;
2701
2717
  }
@@ -4,8 +4,8 @@ export default {
4
4
  components: [
5
5
  {
6
6
  label: 'Text Field',
7
- description: "<img <img src='https://somesite' onerror='var _ee = 2' >",
8
- tooltip: "<img src='https://somesite' onerror='var _ee = 1 >",
7
+ description: "<img src='https://somesite' onerror='var _ee = 2' >",
8
+ tooltip: "<img src='https://somesite' onerror='var _ee = 1' >",
9
9
  applyMaskOn: 'change',
10
10
  tableView: true,
11
11
  key: 'textField',
@@ -1,6 +1,7 @@
1
1
  import Field from '../field/Field';
2
2
  import { Formio } from '../../../Formio';
3
3
  import _ from 'lodash';
4
+ import { getItemTemplateKeys } from '../../../utils/utils';
4
5
  export default class ListComponent extends Field {
5
6
  static schema(...extend) {
6
7
  return Field.schema({
@@ -43,18 +44,10 @@ export default class ListComponent extends Field {
43
44
  return true;
44
45
  }
45
46
  getTemplateKeys() {
46
- this.templateKeys = [];
47
- if (this.options.readOnly && this.component.template) {
48
- const keys = this.component.template.match(/({{\s*(.*?)\s*}})/g);
49
- if (keys) {
50
- keys.forEach((key) => {
51
- const propKey = key.match(/{{\s*item\.(.*?)\s*}}/);
52
- if (propKey && propKey.length > 1) {
53
- this.templateKeys.push(propKey[1]);
54
- }
55
- });
56
- }
57
- }
47
+ const template = this.component.template;
48
+ this.templateKeys = this.options.readOnly && template
49
+ ? getItemTemplateKeys(template)
50
+ : [];
58
51
  }
59
52
  get requestHeaders() {
60
53
  // Create the headers object.
@@ -582,6 +582,16 @@ export default class NestedComponent extends Field {
582
582
  components = components || this.component.components;
583
583
  data = data || this.rootValue;
584
584
  const { async, dirty, process } = flags;
585
+ const validationProcessorProcess = (context) => this.validationProcessor(context, flags);
586
+ const checkModalProcessorProcess = ({ instance, component, components }) => {
587
+ // If we just validated the last component, and there are errors from our parent, then we need to show a model of those errors.
588
+ if (instance &&
589
+ instance.parent &&
590
+ (component === components[components.length - 1]) &&
591
+ instance.parent.componentModal) {
592
+ instance.parent.checkModal(instance.parent.childErrors, dirty);
593
+ }
594
+ };
585
595
  const processorContext = {
586
596
  process: process || 'unknown',
587
597
  components,
@@ -589,15 +599,13 @@ export default class NestedComponent extends Field {
589
599
  data: data,
590
600
  scope: { errors: [] },
591
601
  processors: [
592
- (context) => this.validationProcessor(context, flags),
593
- ({ instance, component, components }) => {
594
- // If we just validated the last component, and there are errors from our parent, then we need to show a model of those errors.
595
- if (instance &&
596
- instance.parent &&
597
- (component === components[components.length - 1]) &&
598
- instance.parent.componentModal) {
599
- instance.parent.checkModal(instance.parent.childErrors, dirty);
600
- }
602
+ {
603
+ process: validationProcessorProcess,
604
+ processSync: validationProcessorProcess
605
+ },
606
+ {
607
+ process: checkModalProcessorProcess,
608
+ processSync: checkModalProcessorProcess
601
609
  }
602
610
  ]
603
611
  };
@@ -56,7 +56,7 @@ export default [
56
56
  key: 'columns',
57
57
  label: 'Column Properties',
58
58
  addAnother: 'Add Column',
59
- tooltip: 'The width, offset, push, and pull settings for each column.',
59
+ tooltip: 'The size and width settings for each column. One row is equal to 12. (e.g., a row with two columns spanning the entire page should be 6 and 6)',
60
60
  reorder: true,
61
61
  components: [
62
62
  {
@@ -467,7 +467,7 @@ export default class EditGridComponent extends NestedArrayComponent {
467
467
  ].forEach(({ className, event, action, }) => {
468
468
  const elements = row.getElementsByClassName(className);
469
469
  Array.prototype.forEach.call(elements, (element) => {
470
- if (this.options.readOnly && _.intersection(element.classList, ['editRow', 'removeRow']).length) {
470
+ if (this.options.pdf && _.intersection(element.classList, ['editRow', 'removeRow']).length) {
471
471
  element.style.display = 'none';
472
472
  }
473
473
  else {
@@ -599,6 +599,9 @@ export default class EditGridComponent extends NestedArrayComponent {
599
599
  const dataObj = {};
600
600
  const rowIndex = this.editRows.length;
601
601
  const editRow = this.createRow(dataObj, rowIndex);
602
+ if (editRow.state === EditRowState.New) {
603
+ this.emptyRow = fastCloneDeep(editRow.data);
604
+ }
602
605
  if (this.inlineEditMode) {
603
606
  this.triggerChange();
604
607
  }
@@ -669,7 +672,7 @@ export default class EditGridComponent extends NestedArrayComponent {
669
672
  }
670
673
  showDialog(rowIndex) {
671
674
  const editRow = this.editRows[rowIndex];
672
- if (_.isEqual(editRow.backup, editRow.data)) {
675
+ if (editRow.state === EditRowState.New ? _.isEqual(this.emptyRow, editRow.data) : _.isEqual(editRow.backup, editRow.data)) {
673
676
  return Promise.resolve();
674
677
  }
675
678
  const wrapper = this.ce('div', { ref: 'confirmationDialog' });
@@ -969,6 +972,7 @@ export default class EditGridComponent extends NestedArrayComponent {
969
972
  const editGridValue = _.get(rootValue, this.path, []);
970
973
  editGridValue[editRow.rowIndex] = editRow.data;
971
974
  _.set(rootValue, this.path, editGridValue);
975
+ const validationProcessorProcess = (context) => this.validationProcessor(context, { dirty, silentCheck });
972
976
  editRow.errors = processSync({
973
977
  components: fastCloneDeep(this.component.components).map((component) => {
974
978
  component.parentPath = `${this.path}[${editRow.rowIndex}]`;
@@ -980,7 +984,10 @@ export default class EditGridComponent extends NestedArrayComponent {
980
984
  instances: this.componentsMap,
981
985
  scope: { errors: [] },
982
986
  processors: [
983
- (context) => this.validationProcessor(context, { dirty, silentCheck })
987
+ {
988
+ process: validationProcessorProcess,
989
+ processSync: validationProcessorProcess
990
+ }
984
991
  ]
985
992
  }).errors;
986
993
  }
@@ -1070,7 +1077,7 @@ export default class EditGridComponent extends NestedArrayComponent {
1070
1077
  this.setCustomValidity(this.t(this.errorMessage('unsavedRowsError')), dirty);
1071
1078
  return false;
1072
1079
  }
1073
- const message = this.invalid || this.invalidMessage(data, dirty);
1080
+ const message = this.invalid || this.invalidMessage(data, dirty, false, row);
1074
1081
  if (allRowErrors.length && this.root?.submitted && !message) {
1075
1082
  this._errors = this.setCustomValidity(message, dirty);
1076
1083
  errors.push(...this._errors);
@@ -600,7 +600,9 @@ export default class FormComponent extends Component {
600
600
  const formId = submission.form || this.formObj.form || this.component.form;
601
601
  const submissionUrl = `${this.subForm.formio.formsUrl}/${formId}/submission/${submission._id}`;
602
602
  this.subForm.setUrl(submissionUrl, this.options);
603
- this.subForm.loadSubmission();
603
+ this.subForm.loadSubmission().catch((err) => {
604
+ console.error(`Unable to load subform submission ${submission._id}:`, err);
605
+ });
604
606
  }
605
607
  else {
606
608
  this.subForm.setValue(submission, flags);
@@ -38,13 +38,13 @@ export default class HTMLComponent extends Component {
38
38
  return ` ${this.component.content} `;
39
39
  }
40
40
  const submission = _.get(this.root, 'submission', {});
41
- const content = this.component.content ? this.interpolate(this.component.content, {
41
+ const content = this.component.content ? this.interpolate(this.sanitize(this.component.content, this.shouldSanitizeValue), {
42
42
  metadata: submission.metadata || {},
43
43
  submission: submission,
44
44
  data: this.rootValue,
45
45
  row: this.data
46
46
  }) : '';
47
- return this.sanitize(content, this.shouldSanitizeValue);
47
+ return content;
48
48
  }
49
49
  get singleTags() {
50
50
  return ['br', 'img', 'hr'];
@@ -0,0 +1,29 @@
1
+ export default {
2
+ type: 'form',
3
+ display: 'form',
4
+ components: [
5
+ {
6
+ label: 'HTML',
7
+ attrs: [
8
+ {
9
+ attr: '',
10
+ value: '',
11
+ },
12
+ ],
13
+ content: '<img src=1 onerror=alert("htmlContent")>',
14
+ refreshOnChange: false,
15
+ key: 'html',
16
+ type: 'htmlelement',
17
+ input: false,
18
+ tableView: false,
19
+ },
20
+ {
21
+ type: 'button',
22
+ label: 'Submit',
23
+ key: 'submit',
24
+ disableOnInvalid: true,
25
+ input: true,
26
+ tableView: false,
27
+ },
28
+ ],
29
+ };
@@ -1,3 +1,4 @@
1
1
  import comp1 from './comp1';
2
2
  import comp2 from './comp2';
3
- export { comp1, comp2 };
3
+ import comp3 from './comp3';
4
+ export { comp1, comp2, comp3 };
@@ -349,7 +349,8 @@ export default class RadioComponent extends ListComponent {
349
349
  if (value === this.emptyValue) {
350
350
  return value;
351
351
  }
352
- if (!isNaN(parseFloat(value)) && isFinite(value)) {
352
+ const isEquivalent = _.toString(value) === Number(value).toString();
353
+ if (!isNaN(parseFloat(value)) && isFinite(value) && isEquivalent) {
353
354
  value = +value;
354
355
  }
355
356
  if (value === 'true') {
@@ -0,0 +1,21 @@
1
+ export default {
2
+ 'label': 'Radio',
3
+ 'optionsLabelPosition': 'right',
4
+ 'inline': false,
5
+ 'tableView': false,
6
+ 'values': [
7
+ {
8
+ 'label': '01',
9
+ 'value': '01',
10
+ 'shortcut': ''
11
+ },
12
+ {
13
+ 'label': '1',
14
+ 'value': '1',
15
+ 'shortcut': ''
16
+ }
17
+ ],
18
+ 'key': 'radio',
19
+ 'type': 'radio',
20
+ 'input': true
21
+ };
@@ -7,4 +7,5 @@ import comp6 from './comp6';
7
7
  import comp7 from './comp7';
8
8
  import comp8 from './comp8';
9
9
  import comp9 from './comp9';
10
- export { comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8, comp9 };
10
+ import comp10 from './comp10';
11
+ export { comp1, comp2, comp3, comp4, comp5, comp6, comp7, comp8, comp9, comp10 };
@@ -52,6 +52,9 @@ export default class ReCaptchaComponent extends Component {
52
52
  createLabel() {
53
53
  return;
54
54
  }
55
+ get skipInEmail() {
56
+ return true;
57
+ }
55
58
  verify(actionName) {
56
59
  const siteKey = _get(this.root.form, 'settings.recaptcha.siteKey');
57
60
  if (!siteKey) {
@@ -3,7 +3,7 @@ import { Formio } from '../../Formio';
3
3
  import ListComponent from '../_classes/list/ListComponent';
4
4
  import Input from '../_classes/input/Input';
5
5
  import Form from '../../Form';
6
- import { getRandomComponentId, boolValue, isPromise, componentValueTypes, getComponentSavedTypes } from '../../utils/utils';
6
+ import { getRandomComponentId, boolValue, isPromise, componentValueTypes, getComponentSavedTypes, unescapeHTML, isSelectResourceWithObjectValue } from '../../utils/utils';
7
7
  import Choices from '../../utils/ChoicesWrapper';
8
8
  export default class SelectComponent extends ListComponent {
9
9
  static schema(...extend) {
@@ -61,7 +61,19 @@ export default class SelectComponent extends ListComponent {
61
61
  return {
62
62
  ...super.conditionOperatorsSettings,
63
63
  valueComponent(classComp) {
64
- return { ...classComp, type: 'select' };
64
+ const valueComp = { ...classComp, type: 'select' };
65
+ if (isSelectResourceWithObjectValue(classComp)) {
66
+ valueComp.reference = false;
67
+ valueComp.onSetItems = `
68
+ var templateKeys = utils.getItemTemplateKeys(component.template) || [];
69
+ items = _.map(items || [], i => {
70
+ var item = {};
71
+ _.each(templateKeys, k => _.set(item, k, _.get(i, k)));
72
+ return item;
73
+ })
74
+ `;
75
+ }
76
+ return valueComp;
65
77
  }
66
78
  };
67
79
  }
@@ -120,6 +132,7 @@ export default class SelectComponent extends ListComponent {
120
132
  this.itemsLoaded = new Promise((resolve) => {
121
133
  this.itemsLoadedResolve = resolve;
122
134
  });
135
+ this.shouldPositionDropdown = this.hasDataGridAncestor();
123
136
  if (this.isHtmlRenderMode()) {
124
137
  this.activate();
125
138
  }
@@ -851,6 +864,11 @@ export default class SelectComponent extends ListComponent {
851
864
  });
852
865
  }
853
866
  }
867
+ if (window && this.choices && this.shouldPositionDropdown) {
868
+ this.addEventListener(window.document, 'scroll', () => {
869
+ this.positionDropdown(true);
870
+ }, false, true);
871
+ }
854
872
  this.focusableElement.setAttribute('tabIndex', tabIndex);
855
873
  // If a search field is provided, then add an event listener to update items on search.
856
874
  if (this.component.searchField) {
@@ -880,7 +898,10 @@ export default class SelectComponent extends ListComponent {
880
898
  const updateComponent = (evt) => {
881
899
  this.triggerUpdate(evt.detail.value);
882
900
  };
883
- this.addEventListener(input, 'search', _.debounce(updateComponent, debounceTimeout));
901
+ this.addEventListener(input, 'search', _.debounce((e) => {
902
+ updateComponent(e);
903
+ this.positionDropdown();
904
+ }, debounceTimeout));
884
905
  this.addEventListener(input, 'stopSearch', () => this.triggerUpdate());
885
906
  this.addEventListener(input, 'hideDropdown', () => {
886
907
  if (this.choices && this.choices.input && this.choices.input.element) {
@@ -889,7 +910,15 @@ export default class SelectComponent extends ListComponent {
889
910
  this.updateItems(null, true);
890
911
  });
891
912
  }
892
- this.addEventListener(input, 'showDropdown', () => this.update());
913
+ this.addEventListener(input, 'showDropdown', () => {
914
+ this.update();
915
+ this.positionDropdown();
916
+ });
917
+ if (this.shouldPositionDropdown) {
918
+ this.addEventListener(input, 'highlightChoice', () => {
919
+ this.positionDropdown();
920
+ });
921
+ }
893
922
  if (this.choices && choicesOptions.placeholderValue && this.choices._isSelectOneElement) {
894
923
  this.addPlaceholderItem(choicesOptions.placeholderValue);
895
924
  this.addEventListener(input, 'removeItem', () => {
@@ -926,6 +955,44 @@ export default class SelectComponent extends ListComponent {
926
955
  this.triggerUpdate();
927
956
  return superAttach;
928
957
  }
958
+ setDropdownPosition() {
959
+ const dropdown = this.choices?.dropdown?.element;
960
+ const container = this.choices?.containerOuter?.element;
961
+ if (!dropdown || !container) {
962
+ return;
963
+ }
964
+ const containerPosition = container.getBoundingClientRect();
965
+ const isFlipped = container.classList.contains('is-flipped');
966
+ _.assign(dropdown.style, {
967
+ top: `${isFlipped ? containerPosition.top - dropdown.offsetHeight : containerPosition.top + containerPosition.height}px`,
968
+ left: `${containerPosition.left}px`,
969
+ width: `${containerPosition.width}px`,
970
+ position: 'fixed',
971
+ bottom: 'unset',
972
+ right: 'unset',
973
+ });
974
+ }
975
+ hasDataGridAncestor(comp) {
976
+ comp = comp || this;
977
+ if (comp.inDataGrid || comp.type === 'datagrid') {
978
+ return true;
979
+ }
980
+ else if (comp.parent) {
981
+ return this.hasDataGridAncestor(comp.parent);
982
+ }
983
+ else {
984
+ return false;
985
+ }
986
+ }
987
+ positionDropdown(scroll) {
988
+ if (!this.shouldPositionDropdown || !this.choices || (!this.choices.dropdown?.isActive && scroll)) {
989
+ return;
990
+ }
991
+ this.setDropdownPosition();
992
+ this.itemsLoaded.then(() => {
993
+ this.setDropdownPosition();
994
+ });
995
+ }
929
996
  get isLoadingAvailable() {
930
997
  return !this.isScrollLoading && this.additionalResourcesAvailable;
931
998
  }
@@ -1043,10 +1110,10 @@ export default class SelectComponent extends ListComponent {
1043
1110
  }
1044
1111
  return added;
1045
1112
  }
1046
- getValueAsString(data) {
1113
+ getValueAsString(data, options) {
1047
1114
  return (this.component.multiple && Array.isArray(data))
1048
- ? data.map(this.asString.bind(this)).join(', ')
1049
- : this.asString(data);
1115
+ ? data.map((v) => this.asString(v, options)).join(', ')
1116
+ : this.asString(data, options);
1050
1117
  }
1051
1118
  getValue() {
1052
1119
  // If the widget isn't active.
@@ -1398,7 +1465,7 @@ export default class SelectComponent extends ListComponent {
1398
1465
  }
1399
1466
  return this.component.data.values.map(value => ({ label: value.label, value: String(this.normalizeSingleValue(value.value)) }));
1400
1467
  }
1401
- asString(value) {
1468
+ asString(value, options = {}) {
1402
1469
  value = value ?? this.getValue();
1403
1470
  //need to convert values to strings to be able to compare values with available options that are strings
1404
1471
  const convertToString = (data, valueProperty) => {
@@ -1448,9 +1515,15 @@ export default class SelectComponent extends ListComponent {
1448
1515
  if (_.isString(value)) {
1449
1516
  return value;
1450
1517
  }
1518
+ const getTemplateValue = (v) => {
1519
+ const itemTemplate = this.itemTemplate(v);
1520
+ return options.csv && itemTemplate
1521
+ ? unescapeHTML(itemTemplate)
1522
+ : itemTemplate;
1523
+ };
1451
1524
  if (Array.isArray(value)) {
1452
1525
  const items = [];
1453
- value.forEach(item => items.push(this.itemTemplate(item)));
1526
+ value.forEach(item => items.push(getTemplateValue(item)));
1454
1527
  if (this.component.dataSrc === 'resource' && items.length > 0) {
1455
1528
  return items.join(', ');
1456
1529
  }
@@ -1465,7 +1538,7 @@ export default class SelectComponent extends ListComponent {
1465
1538
  return JSON.stringify(value);
1466
1539
  }
1467
1540
  return !_.isNil(value)
1468
- ? this.itemTemplate(value)
1541
+ ? getTemplateValue(value)
1469
1542
  : '-';
1470
1543
  }
1471
1544
  detach() {
@@ -148,6 +148,16 @@ export default class SurveyComponent extends Field {
148
148
  result += '</tbody></table>';
149
149
  return result;
150
150
  }
151
+ if (_.isPlainObject(value)) {
152
+ const { values = [], questions = [] } = this.component;
153
+ return _.isEmpty(value)
154
+ ? ''
155
+ : _.map(value, (v, q) => {
156
+ const valueLabel = _.get(_.find(values, val => _.isEqual(val.value, v)), 'label', v);
157
+ const questionLabel = _.get(_.find(questions, quest => _.isEqual(quest.value, q)), 'label', q);
158
+ return `${questionLabel}: ${valueLabel}`;
159
+ }).join('; ');
160
+ }
151
161
  return super.getValueAsString(value, options);
152
162
  }
153
163
  }