Skip to content

Commit

Permalink
Refactor the component path system to ensure we are always referencin…
Browse files Browse the repository at this point in the history
…g the correct path.
  • Loading branch information
travist committed Nov 25, 2024
1 parent 0b7cea4 commit 5124dbf
Show file tree
Hide file tree
Showing 31 changed files with 1,294 additions and 1,510 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@
"dependencies": {
"@formio/bootstrap": "3.0.0-dev.98.17ba6ea",
"@formio/choices.js": "^10.2.1",
"@formio/core": "2.1.0-dev.191.8c609ab",
"@formio/text-mask-addons": "^3.8.0-formio.3",
"@formio/core": "2.1.0-dev.193.dc60547",
"@formio/text-mask-addons": "3.8.0-formio.4",
"@formio/vanilla-text-mask": "^5.1.1-formio.1",
"abortcontroller-polyfill": "^1.7.5",
"autocompleter": "^8.0.4",
Expand Down
54 changes: 24 additions & 30 deletions src/Webform.js
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ export default class Webform extends NestedDataComponent {
try {
// Do not set the form again if it has been already set
if (isFormAlreadySet && JSON.stringify(this._form) === JSON.stringify(form)) {
this.formReadyResolve();
return Promise.resolve();
}

Expand All @@ -669,13 +670,10 @@ export default class Webform extends NestedDataComponent {
if (this.onSetForm) {
this.onSetForm(_.cloneDeep(this._form), form);
}

if (this.parent?.component?.modalEdit) {
return Promise.resolve();
}
} catch (err) {
console.warn(err);
// If provided form is not a valid JSON object, do not set it too
this.formReadyReject(err);
return Promise.resolve();
}

Expand Down Expand Up @@ -959,6 +957,9 @@ export default class Webform extends NestedDataComponent {
}

getValue() {
if (!this._submission) {
this._submission = {};
}
if (!this._submission.data) {
this._submission.data = {};
}
Expand Down Expand Up @@ -1271,32 +1272,24 @@ export default class Webform extends NestedDataComponent {
}

// Mark any components as invalid if in a custom message.
const componentErrors = {};
errors.forEach((err) => {
const { components = [] } = err;
if (err.component) {
components.push(err.component);
const path = err.path || err.context?.path || err.component?.key;
if (!componentErrors[path]) {
componentErrors[path] = [];
}
componentErrors[path].push(err);
});

if (err.path) {
components.push(err.path);
// Iterate through all of our component errors and apply them to the components.
for (let path in componentErrors) {
const component = this.getComponent(path);
const errors = componentErrors[path];
if (component) {
component.serverErrors = errors.filter((err) => err.fromServer);
component.setCustomValidity(errors, true)
}

components.forEach((path) => {
const originalPath = getStringFromComponentPath(path);
const component = this.getComponent(path, _.identity, originalPath);

if (err.fromServer) {
if (component.serverErrors) {
component.serverErrors.push(err);
} else {
component.serverErrors = [err];
}
}
const components = _.compact(Array.isArray(component) ? component : [component]);

components.forEach((component) => component.setCustomValidity(err.message, true));
});
});
}

const displayedErrors = [];
if (errors.length) {
Expand Down Expand Up @@ -1521,7 +1514,7 @@ export default class Webform extends NestedDataComponent {
});
}

submitForm(options = {}) {
submitForm(options = {}, local = false) {
this.clearServerErrors();

return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -1556,6 +1549,7 @@ export default class Webform extends NestedDataComponent {
return reject("Invalid Submission");
}
const errors = this.validate(submission.data, {
local,
dirty: true,
silentCheck: false,
process: "submit",
Expand All @@ -1575,11 +1569,11 @@ export default class Webform extends NestedDataComponent {

this.everyComponent((comp) => {
if (submission._vnote && comp.type === "form" && comp.component.reference) {
_.get(submission.data, comp.path, {})._vnote = submission._vnote;
_.get(submission.data, local ? comp.paths?.localDataPath : comp.path, {})._vnote = submission._vnote;
}
const { persistent } = comp.component;
if (persistent === "client-only") {
_.unset(submission.data, comp.path);
_.unset(submission.data, local ? comp.paths?.localDataPath : comp.path);
}
});

Expand Down Expand Up @@ -1777,7 +1771,7 @@ export default class Webform extends NestedDataComponent {
return;
}
const captchaComponent = [];
eachComponent(this.components, (component) => {
this.eachComponent((component) => {
if (/^(re)?captcha$/.test(component.type) && component.component.eventType === 'formLoad') {
captchaComponent.push(component);
}
Expand Down
19 changes: 4 additions & 15 deletions src/WebformBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -1263,25 +1263,14 @@ export default class WebformBuilder extends Component {
findRepeatablePaths() {
const repeatablePaths = [];
const keys = new Map();

eachComponent(this.form.components, (comp, path) => {
if (!comp.key) {
return;
}

if (keys.has(comp.key)) {
if (keys.get(comp.key).includes(path)) {
repeatablePaths.push(path);
}
else {
keys.set(comp.key, [...keys.get(comp.key), path]);
}
eachComponent(this.form.components, (comp, path, components, parent, paths) => {
if (keys.has(paths.dataPath)) {
repeatablePaths.push(paths.dataPath);
}
else {
keys.set(comp.key, [path]);
keys.set(paths.dataPath, true);
}
}, true);

return repeatablePaths;
}

Expand Down
17 changes: 6 additions & 11 deletions src/Wizard.js
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ export default class Wizard extends Webform {

attachHeader() {
const isAllowPrevious = this.isAllowPrevious();
this.attachTooltips(this.refs[`${this.wizardKey}-tooltip`], this.currentPanel.tooltip);
this.attachTooltips(this.refs[`${this.wizardKey}-tooltip`], this.currentPanel?.tooltip);

if (this.isBreadcrumbClickable() || isAllowPrevious) {
this.refs[`${this.wizardKey}-link`]?.forEach((link, index) => {
Expand Down Expand Up @@ -690,7 +690,9 @@ export default class Wizard extends Webform {
}
this.redraw().then(() => {
this.checkData(this.submission.data);
this.validateCurrentPage();
this.validateCurrentPage({
dirty: this.submitted
});
});
return Promise.resolve();
}
Expand Down Expand Up @@ -825,7 +827,7 @@ export default class Wizard extends Webform {

validateCurrentPage(flags = {}) {
// Accessing the parent ensures the right instance (whether it's the parent Wizard or a nested Wizard) performs its validation
return this.currentPage?.parent.validateComponents(this.currentPage.component.components, this.currentPage.parent.data, flags);
return this.currentPage?.parent.validateComponents(this.currentPage.component.components, this.root.data, flags);
}

emitPrevPage() {
Expand Down Expand Up @@ -1036,13 +1038,6 @@ export default class Wizard extends Webform {
}
}

redraw() {
if (this.parent?.component?.modalEdit) {
return this.parent.redraw();
}
return super.redraw();
}

rebuild() {
const currentPage = this.page;
const setCurrentPage = () => this.setPage(currentPage);
Expand Down Expand Up @@ -1080,7 +1075,7 @@ export default class Wizard extends Webform {
errors = [...errors, ...subWizard.errors]
}
})
};
}
return super.showErrors(errors, triggerEvent)
}

Expand Down
34 changes: 1 addition & 33 deletions src/components/Components.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Component from './_classes/component/Component';
import EditFormUtils from './_classes/component/editForm/utils';
import BaseEditForm from './_classes/component/Component.form';
import { getComponentKey, getModelType } from '../utils/utils';
import _ from 'lodash';
export default class Components {
static _editFormUtils = EditFormUtils;
Expand Down Expand Up @@ -57,35 +56,6 @@ export default class Components {
Components.components[name] = comp;
}

/**
* Return a path of component's value.
* @param {Component} component - The component instance.
* @returns {string} - The component's value path.
*/
static getComponentPath(component) {
let path = '';
const componentKey = getComponentKey(component.component);
if (componentKey) {
let thisPath = component.options?.parent || component;
while (thisPath && !thisPath.allowData && thisPath.parent) {
thisPath = thisPath.parent;
}
// TODO: any component that is nested in e.g. a Data Grid or an Edit Grid is going to receive a row prop; the problem
// is that options.row is passed to each further nested component, which results in erroneous paths like
// `editGrid[0].container[0].textField` rather than `editGrid[0].container.textField`. This should be adapted for other
// components with a tree-like data model
const rowIndex = component.row;
const rowIndexPath = rowIndex && !['container'].includes(thisPath.component.type) ? `[${Number.parseInt(rowIndex)}]` : '';
path = `${thisPath.path}${rowIndexPath}.`;
if (rowIndexPath && getModelType(thisPath) === 'nestedDataArray') {
path = `${path}data.`;
}
path += componentKey;
return _.trim(path, '.');
}
return path;
}

static create(component, options, data) {
let comp = null;
if (component.type && Components.components.hasOwnProperty(component.type)) {
Expand All @@ -110,9 +80,7 @@ export default class Components {
else {
comp = new Component(component, options, data);
}
const path = Components.getComponentPath(comp);
if (path) {
comp.path = path;
if (comp.path) {
comp.componentsMap[comp.path] = comp;
}
return comp;
Expand Down
Loading

0 comments on commit 5124dbf

Please sign in to comment.