Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the component path system to ensure we are always referencing the correct path. #5914

Merged
merged 1 commit into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@
},
"homepage": "https://github.com/formio/formio.js#readme",
"dependencies": {
"@formio/bootstrap": "3.0.0-dev.98.17ba6ea",
"@formio/bootstrap": "3.0.0-dev.111.ae7f187",
"@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.68cf8c3",
"@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
70 changes: 35 additions & 35 deletions resources/latest.json

Large diffs are not rendered by default.

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 @@ -1275,25 +1275,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
11 changes: 2 additions & 9 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 @@ -831,7 +831,7 @@ export default class Wizard extends Webform {
validateCurrentPage(flags = {}) {
const components = this.currentPage?.components.map((component) => component.component);
// 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(components, this.currentPage.parent.data, flags);
return this.currentPage?.parent.validateComponents(components, this.root.data, flags);
}

emitPrevPage() {
Expand Down Expand Up @@ -1043,13 +1043,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
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
Loading