diff --git a/addons/website/static/src/snippets/s_website_form/000.js b/addons/website/static/src/snippets/s_website_form/000.js
index fe9c5021404..207328f82b8 100644
--- a/addons/website/static/src/snippets/s_website_form/000.js
+++ b/addons/website/static/src/snippets/s_website_form/000.js
@@ -48,6 +48,8 @@ odoo.define('website.s_website_form', function (require) {
init: function () {
this._super(...arguments);
this._recaptcha = new ReCaptcha();
+ this._visibilityFunctionByFieldName = new Map();
+ this._visibilityFunctionByFieldEl = new Map();
this.__started = new Promise(resolve => this.__startResolve = resolve);
},
willStart: async function () {
@@ -71,9 +73,28 @@ odoo.define('website.s_website_form', function (require) {
start: function () {
var self = this;
+ // Prepare visibility data and update field visibilities
+ const visibilityFunctionsByFieldName = new Map();
+ for (const fieldEl of this.$target[0].querySelectorAll('[data-visibility-dependency]')) {
+ const inputName = fieldEl.querySelector('.s_website_form_input').name;
+ if (!visibilityFunctionsByFieldName.has(inputName)) {
+ visibilityFunctionsByFieldName.set(inputName, []);
+ }
+ const func = this._buildVisibilityFunction(fieldEl);
+ visibilityFunctionsByFieldName.get(inputName).push(func);
+ this._visibilityFunctionByFieldEl.set(fieldEl, func);
+ }
+ for (const [name, funcs] of visibilityFunctionsByFieldName.entries()) {
+ this._visibilityFunctionByFieldName.set(name, () => funcs.some(func => func()));
+ }
+ this._updateFieldsVisibility();
+
+ this._onFieldInputDebounced = _.debounce(this._onFieldInput.bind(this), 400);
+ this.$el.on('input.s_website_form', '.s_website_form_field', this._onFieldInputDebounced);
+
// Initialize datetimepickers
var datepickers_options = {
- minDate: moment({ y: 1000 }),
+ minDate: moment({y: 1000}),
maxDate: moment({y: 9999, M: 11, d: 31}),
calendarWeeks: true,
icons: {
@@ -119,6 +140,13 @@ odoo.define('website.s_website_form', function (require) {
});
}
+ // Check disabled states
+ this.inputEls = this.$target[0].querySelectorAll('.s_website_form_field.s_website_form_field_hidden_if .s_website_form_input');
+ this._disabledStates = new Map();
+ for (const inputEl of this.inputEls) {
+ this._disabledStates[inputEl] = inputEl.disabled;
+ }
+
return this._super(...arguments).then(() => this.__startResolve());
},
@@ -156,6 +184,16 @@ odoo.define('website.s_website_form', function (require) {
// Reinitialize dates
this.$allDates.removeClass('s_website_form_datepicker_initialized');
+
+ // Restore disabled attribute
+ for (const inputEl of this.inputEls) {
+ inputEl.disabled = !!this._disabledStates.get(inputEl);
+ }
+
+ // All 'hidden if' fields start with d-none
+ this.$target[0].querySelectorAll('.s_website_form_field_hidden_if:not(.d-none)').forEach(el => el.classList.add('d-none'));
+
+ this.$el.off('.s_website_form');
},
send: async function (e) {
@@ -395,6 +433,11 @@ odoo.define('website.s_website_form', function (require) {
message: message,
})));
},
+
+ //----------------------------------------------------------------------
+ // Private
+ //----------------------------------------------------------------------
+
/**
* Gets the user's field needed to be fetched to pre-fill the form.
*
@@ -403,5 +446,132 @@ odoo.define('website.s_website_form', function (require) {
_getUserPreFillFields() {
return ['name', 'phone', 'email', 'commercial_company_name'];
},
+ /**
+ * Compares the value with the comparable (and the between) with
+ * comparator as a means to compare
+ *
+ * @private
+ * @param {string} comparator The way that $value and $comparable have
+ * to be compared
+ * @param {string} [value] The value of the field
+ * @param {string} [comparable] The value to compare
+ * @param {string} [between] The maximum date value in case comparator
+ * is between or !between
+ * @returns {boolean}
+ */
+ _compareTo(comparator, value = '', comparable, between) {
+ switch (comparator) {
+ case 'contains':
+ return value.includes(comparable);
+ case '!contains':
+ return !value.includes(comparable);
+ case 'equal':
+ case 'selected':
+ return value === comparable;
+ case '!equal':
+ case '!selected':
+ return value !== comparable;
+ case 'set':
+ return value;
+ case '!set':
+ return !value;
+ case 'greater':
+ return value > comparable;
+ case 'less':
+ return value < comparable;
+ case 'greater or equal':
+ return value >= comparable;
+ case 'less or equal':
+ return value <= comparable;
+ }
+ // Date & Date Time comparison requires formatting the value
+ if (value.includes(':')) {
+ const datetimeFormat = time.getLangDatetimeFormat();
+ value = moment(value, datetimeFormat)._d.getTime() / 1000;
+ } else {
+ const dateFormat = time.getLangDateFormat();
+ value = moment(value, dateFormat)._d.getTime() / 1000;
+ }
+ comparable = parseInt(comparable);
+ between = parseInt(between) || '';
+ switch (comparator) {
+ case 'dateEqual':
+ return value === comparable;
+ case 'date!equal':
+ return value !== comparable;
+ case 'before':
+ return value < comparable;
+ case 'after':
+ return value > comparable;
+ case 'equal or before':
+ return value <= comparable;
+ case 'between':
+ return value >= comparable && value <= between;
+ case '!between':
+ return !(value >= comparable && value <= between);
+ case 'equal or after':
+ return value >= comparable;
+ }
+ },
+ /**
+ * @private
+ * @param {HTMLElement} fieldEl the field we want to have a function
+ * that calculates its visibility
+ * @returns {function} the function to be executed when we want to
+ * recalculate the visibility of fieldEl
+ */
+ _buildVisibilityFunction(fieldEl) {
+ const visibilityCondition = fieldEl.dataset.visibilityCondition;
+ const dependencyName = fieldEl.dataset.visibilityDependency;
+ const comparator = fieldEl.dataset.visibilityComparator;
+ const between = fieldEl.dataset.visibilityBetween;
+ return () => {
+ // To be visible, at least one field with the dependency name must be visible.
+ const dependencyVisibilityFunction = this._visibilityFunctionByFieldName.get(dependencyName);
+ const dependencyIsVisible = !dependencyVisibilityFunction || dependencyVisibilityFunction();
+ if (!dependencyIsVisible) {
+ return false;
+ }
+
+ const formData = new FormData(this.$target[0]);
+ const currentValueOfDependency = formData.get(dependencyName);
+ return this._compareTo(comparator, currentValueOfDependency, visibilityCondition, between);
+ };
+ },
+ /**
+ * Calculates the visibility for each field with conditional visibility
+ */
+ _updateFieldsVisibility() {
+ for (const [fieldEl, visibilityFunction] of this._visibilityFunctionByFieldEl.entries()) {
+ this._updateFieldVisibility(fieldEl, visibilityFunction());
+ }
+ },
+ /**
+ * Changes the visibility of a field.
+ *
+ * @param {HTMLElement} fieldEl
+ * @param {boolean} haveToBeVisible
+ */
+ _updateFieldVisibility(fieldEl, haveToBeVisible) {
+ const fieldContainerEl = fieldEl.closest('.s_website_form_field');
+ fieldContainerEl.classList.toggle('d-none', !haveToBeVisible);
+ for (const inputEl of fieldContainerEl.querySelectorAll('.s_website_form_input')) {
+ // Hidden inputs should also be disabled so that their data are
+ // not sent on form submit.
+ inputEl.disabled = !haveToBeVisible;
+ }
+ },
+
+ //----------------------------------------------------------------------
+ // Handlers
+ //----------------------------------------------------------------------
+
+ /**
+ * Calculates the visibility of the fields at each input event on the
+ * form (this method should be debounced in the start).
+ */
+ _onFieldInput() {
+ this._updateFieldsVisibility();
+ },
});
});
diff --git a/addons/website/static/src/snippets/s_website_form/001.scss b/addons/website/static/src/snippets/s_website_form/001.scss
index c4f5654053e..445c7e9cd54 100644
--- a/addons/website/static/src/snippets/s_website_form/001.scss
+++ b/addons/website/static/src/snippets/s_website_form/001.scss
@@ -4,6 +4,11 @@
display: block;
opacity: 0.5;
}
+ // Fields with conditional visibility are visible and identifiable in the editor
+ .s_website_form_field_hidden_if {
+ display: block !important;
+ background-color: $o-we-fg-light;
+ }
// Select inputs do not trigger the default browser behavior
// Since we use a custom editable element
.s_website_form_field select {
diff --git a/addons/website/static/src/snippets/s_website_form/options.js b/addons/website/static/src/snippets/s_website_form/options.js
index ab578fea3e7..d2711fc3a9b 100644
--- a/addons/website/static/src/snippets/s_website_form/options.js
+++ b/addons/website/static/src/snippets/s_website_form/options.js
@@ -148,6 +148,8 @@ const FormEditor = options.Class.extend({
});
const FieldEditor = FormEditor.extend({
+ VISIBILITY_DATASET: ['visibilityDependency', 'visibilityCondition', 'visibilityComparator', 'visibilityBetween'],
+
/**
* @override
*/
@@ -908,6 +910,22 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
const fieldEl = this._renderField(field);
this._replaceFieldElement(fieldEl);
},
+ /**
+ * Removes the visibility conditions concerned by the deleted field
+ *
+ * @override
+ */
+ onRemove() {
+ const fieldName = this.$target[0].querySelector('.s_website_form_input').name;
+ const isMultipleField = this.formEl.querySelectorAll(`.s_website_form_input[name="${fieldName}"]`).length > 1;
+ if (isMultipleField) {
+ return;
+ }
+ const dependentFieldContainerEl = this.formEl.querySelectorAll(`[data-visibility-dependency="${fieldName}"]`);
+ for (const fieldContainerEl of dependentFieldContainerEl) {
+ this._deleteConditionalVisibility(fieldContainerEl);
+ }
+ },
//----------------------------------------------------------------------
// Options
@@ -959,7 +977,15 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
if (multiple) {
multiple.dataset.name = value;
}
- this.$target[0].querySelectorAll('.s_website_form_input').forEach(el => el.name = value);
+ const inputEls = this.$target[0].querySelectorAll('.s_website_form_input');
+ const previousInputName = inputEls[0].name;
+ inputEls.forEach(el => el.name = value);
+
+ // Synchronize the fields whose visibility depends on this field
+ const dependentEls = this.formEl.querySelectorAll(`.s_website_form_field[data-visibility-dependency="${previousInputName}"]`);
+ for (const dependentEl of dependentEls) {
+ dependentEl.dataset.visibilityDependency = value;
+ }
}
},
/**
@@ -1019,10 +1045,47 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
*/
renderListItems: async function (previewMode, value, params) {
const valueList = JSON.parse(value);
+
+ // Synchronize the possible values with the fields whose visibility
+ // depends on the current field
+ const newValuesText = valueList.map(value => value.name);
+ const inputEls = this.$target[0].querySelectorAll('.s_website_form_input, option');
+ const inputName = this.$target[0].querySelector('.s_website_form_input').name;
+ for (let i = 0; i < inputEls.length; i++) {
+ const input = inputEls[i];
+ if (newValuesText[i] && input.value && !newValuesText.includes(input.value)) {
+ for (const dependentEl of this.formEl.querySelectorAll(
+ `[data-visibility-condition="${input.value}"][data-visibility-dependency="${inputName}"]`)) {
+ dependentEl.dataset.visibilityCondition = newValuesText[i];
+ }
+ break;
+ }
+ }
+
const field = this._getActiveField(true);
field.records = valueList;
await this._replaceField(field);
},
+ /**
+ * Sets the visibility of the field.
+ *
+ * @see this.selectClass for parameters
+ */
+ setVisibility(previewMode, widgetValue, params) {
+ if (widgetValue === 'conditional') {
+ // Set a default visibility dependency
+ const firstInputOfForm = this.formEl.querySelector('.s_website_form_field:not(.s_website_form_dnone) input');
+ this._setVisibilityDependency(firstInputOfForm.name);
+ return;
+ }
+ this._deleteConditionalVisibility(this.$target[0]);
+ },
+ /**
+ * @see this.selectClass for parameters
+ */
+ setVisibilityDependency(previewMode, widgetValue, params) {
+ this._setVisibilityDependency(widgetValue);
+ },
//----------------------------------------------------------------------
// Private
@@ -1059,6 +1122,8 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
return this.$target[0].classList.contains(params.activeValue) ? params.activeValue : 'false';
case 'renderListItems':
return JSON.stringify(this._getListItems());
+ case 'setVisibilityDependency':
+ return this.$target[0].dataset.visibilityDependency || '';
}
return this._super(...arguments);
},
@@ -1066,7 +1131,52 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
* @override
*/
_computeWidgetVisibility: function (widgetName, params) {
+ const dependencyEl = this._getDependencyEl();
switch (widgetName) {
+ case 'hidden_condition_time_comparators_opt':
+ return dependencyEl && dependencyEl.dataset.target;
+ case 'hidden_condition_date_between':
+ return dependencyEl && dependencyEl.dataset.target && dependencyEl.dataset.target.includes('#datepicker')
+ && ['between', '!between'].includes(this.$target[0].getAttribute('data-visibility-comparator'));
+ case 'hidden_condition_datetime_between':
+ return dependencyEl && dependencyEl.dataset.target && dependencyEl.dataset.target.includes('#datetimepicker')
+ && ['between', '!between'].includes(this.$target[0].dataset.visibilityComparator);
+ case 'hidden_condition_additional_datetime':
+ return dependencyEl && dependencyEl.dataset.target && dependencyEl.dataset.target.includes('#datetimepicker')
+ && !['set', '!set'].includes(this.$target[0].dataset.visibilityComparator);
+ case 'hidden_condition_additional_date':
+ return dependencyEl && dependencyEl.dataset.target && dependencyEl.dataset.target.includes('#datepicker')
+ && !['set', '!set'].includes(this.$target[0].dataset.visibilityComparator);
+ case 'hidden_condition_additional_text':
+ if (!this.$target[0].classList.contains('s_website_form_field_hidden_if') ||
+ (dependencyEl && (['checkbox', 'radio'].includes(dependencyEl.type) || dependencyEl.nodeName === 'SELECT'))) {
+ return false;
+ }
+ if (!dependencyEl) {
+ return true;
+ }
+ if (dependencyEl.dataset.target && dependencyEl.dataset.target.includes('#date')) {
+ return false;
+ }
+ return (['text', 'email', 'tel', 'url', 'search', 'password', 'number'].includes(dependencyEl.type)
+ || dependencyEl.nodeName === 'TEXTAREA') && !['set', '!set'].includes(this.$target[0].dataset.visibilityComparator);
+ case 'hidden_condition_no_text_opt':
+ return dependencyEl && (dependencyEl.type === 'checkbox' || dependencyEl.type === 'radio' || dependencyEl.nodeName === 'SELECT');
+ case 'hidden_condition_num_opt':
+ return dependencyEl && dependencyEl.type === 'number';
+ case 'hidden_condition_text_opt':
+ if (!this.$target[0].classList.contains('s_website_form_field_hidden_if') || (dependencyEl &&
+ dependencyEl.dataset.target && dependencyEl.dataset.target.includes('#date'))) {
+ return false;
+ }
+ return !dependencyEl || (['text', 'email', 'tel', 'url', 'search', 'password'].includes(dependencyEl.type) ||
+ dependencyEl.nodeName === 'TEXTAREA');
+ case 'hidden_condition_date_opt':
+ return dependencyEl && dependencyEl.dataset.target && dependencyEl.dataset.target.includes('#datepicker');
+ case 'hidden_condition_datetime_opt':
+ return dependencyEl && dependencyEl.dataset.target && dependencyEl.dataset.target.includes('#datetimepicker');
+ case 'hidden_condition_opt':
+ return this.$target[0].classList.contains('s_website_form_field_hidden_if');
case 'char_input_type_opt':
return !this.$target[0].classList.contains('s_website_form_custom') && ['char', 'email', 'tel', 'url'].includes(this.$target[0].dataset.type);
case 'multi_check_display_opt':
@@ -1078,10 +1188,93 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
}
return this._super(...arguments);
},
+ /**
+ * Deletes all attributes related to conditional visibility.
+ *
+ * @param {HTMLElement} fieldEl
+ */
+ _deleteConditionalVisibility(fieldEl) {
+ for (const name of this.VISIBILITY_DATASET) {
+ delete fieldEl.dataset[name];
+ }
+ fieldEl.classList.remove('s_website_form_field_hidden_if', 'd-none');
+ },
+ /**
+ * @returns {HTMLElement} The visibility dependency of the field
+ */
+ _getDependencyEl() {
+ const dependencyName = this.$target[0].dataset.visibilityDependency;
+ return this.formEl.querySelector(`.s_website_form_input[name="${dependencyName}"]`);
+ },
/**
* @override
*/
_renderCustomXML: async function (uiFragment) {
+ // Update available visibility dependencies
+ const selectDependencyEl = uiFragment.querySelector('we-select[data-name="hidden_condition_opt"]');
+ if (selectDependencyEl) {
+ const existingDependencyNames = [];
+ for (const el of this.formEl.querySelectorAll('.s_website_form_field:not(.s_website_form_dnone)')) {
+ const inputEl = el.querySelector('.s_website_form_input');
+ if (el.querySelector('.s_website_form_label_content') && inputEl && inputEl.name
+ && inputEl.name !== this.$target[0].querySelector('.s_website_form_input').name
+ && !existingDependencyNames.includes(inputEl.name)) {
+ const button = document.createElement('we-button');
+ button.textContent = el.querySelector('.s_website_form_label_content').textContent;
+ button.dataset.setVisibilityDependency = inputEl.name;
+ selectDependencyEl.append(button);
+ existingDependencyNames.push(inputEl.name);
+ }
+ }
+
+ const comparator = this.$target[0].dataset.visibilityComparator;
+ const dependencyEl = this._getDependencyEl();
+ if (dependencyEl) {
+ if ((['radio', 'checkbox'].includes(dependencyEl.type) || dependencyEl.nodeName === 'SELECT')) {
+ // Update available visibility options
+ const selectOptEl = uiFragment.querySelectorAll('we-select[data-name="hidden_condition_no_text_opt"]')[1];
+ const inputContainerEl = this.$target[0];
+ const dependencyEl = this._getDependencyEl();
+ if (dependencyEl.nodeName === 'SELECT') {
+ for (const option of dependencyEl.querySelectorAll('option')) {
+ const button = document.createElement('we-button');
+ button.textContent = option.value;
+ button.dataset.selectDataAttribute = option.value;
+ selectOptEl.append(button);
+ }
+ if (!inputContainerEl.dataset.visibilityCondition) {
+ inputContainerEl.dataset.visibilityCondition = dependencyEl.querySelector('option').value;
+ }
+ } else { // DependecyEl is a radio or a checkbox
+ const dependencyContainerEl = dependencyEl.closest('.s_website_form_field');
+ const inputsInDependencyContainer = dependencyContainerEl.querySelectorAll('.s_website_form_input');
+ for (const el of inputsInDependencyContainer) {
+ const button = document.createElement('we-button');
+ button.textContent = el.value;
+ button.dataset.selectDataAttribute = el.value;
+ selectOptEl.append(button);
+ }
+ if (!inputContainerEl.dataset.visibilityCondition) {
+ inputContainerEl.dataset.visibilityCondition = inputsInDependencyContainer[0].value;
+ }
+ }
+ if (!inputContainerEl.dataset.visibilityComparator) {
+ inputContainerEl.dataset.visibilityComparator = 'selected';
+ }
+ this.rerender = comparator ? this.rerender : true;
+ }
+ if (!comparator) {
+ // Set a default comparator according to the type of dependency
+ if (dependencyEl.dataset.target) {
+ this.$target[0].dataset.visibilityComparator = 'after';
+ } else if (['text', 'email', 'tel', 'url', 'search', 'password', 'number'].includes(dependencyEl.type)
+ || dependencyEl.nodeName === 'TEXTAREA') {
+ this.$target[0].dataset.visibilityComparator = 'equal';
+ }
+ }
+ }
+ }
+
const selectEl = uiFragment.querySelector('we-select[data-name="type_opt"]');
const currentFieldName = this._getFieldName();
const fieldsInForm = Array.from(this.formEl.querySelectorAll('.s_website_form_field:not(.s_website_form_custom) .s_website_form_input')).map(el => el.name).filter(el => el !== currentFieldName);
@@ -1119,7 +1312,11 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
await this._fetchFieldRecords(field);
list.dataset.availableRecords = JSON.stringify(field.records);
}
- uiFragment.appendChild(list);
+ if (selectDependencyEl) {
+ uiFragment.insertBefore(list, uiFragment.querySelector('we-select[string="Visibility"]'));
+ } else {
+ uiFragment.appendChild(list);
+ }
},
/**
* Replaces the target content with the field provided.
@@ -1144,10 +1341,40 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
* @param {HTMLElement} fieldEl
*/
_replaceFieldElement(fieldEl) {
+ const hasConditionalVisibility = this.$target[0].classList.contains('s_website_form_field_hidden_if');
+ const previousName = this.$target[0].querySelector('.s_website_form_input').name;
[...this.$target[0].childNodes].forEach(node => node.remove());
[...fieldEl.childNodes].forEach(node => this.$target[0].appendChild(node));
[...fieldEl.attributes].forEach(el => this.$target[0].removeAttribute(el.nodeName));
[...fieldEl.attributes].forEach(el => this.$target[0].setAttribute(el.nodeName, el.nodeValue));
+ if (hasConditionalVisibility) {
+ this.$target[0].classList.add('s_website_form_field_hidden_if', 'd-none');
+ }
+ const dependentFieldEls = this.formEl.querySelectorAll(`.s_website_form_field[data-visibility-dependency="${previousName}"]`);
+ const newName = this.$target[0].querySelector('.s_website_form_input').name;
+ if (previousName !== newName && dependentFieldEls) {
+ // In order to keep the visibility conditions consistent,
+ // when the name has changed, it means that the type has changed so
+ // all fields whose visibility depends on this field must be updated so that
+ // they no longer have conditional visibility
+ for (const fieldEl of dependentFieldEls) {
+ this._deleteConditionalVisibility(fieldEl);
+ }
+ }
+ },
+ /**
+ * Sets the visibility dependency of the field.
+ *
+ * @param {string} value name of the dependency input
+ */
+ _setVisibilityDependency(value) {
+ delete this.$target[0].dataset.visibilityCondition;
+ delete this.$target[0].dataset.visibilityComparator;
+ const previousDependency = this._getDependencyEl();
+ if (this.formEl.querySelector(`.s_website_form_input[name="${value}"]`).type !== (previousDependency && previousDependency.type)) {
+ this.rerender = true;
+ }
+ this.$target[0].dataset.visibilityDependency = value;
},
/**
* @private
diff --git a/addons/website/static/tests/tours/website_form_editor.js b/addons/website/static/tests/tours/website_form_editor.js
index dacc63ccb7d..8149ee5c94f 100644
--- a/addons/website/static/tests/tours/website_form_editor.js
+++ b/addons/website/static/tests/tours/website_form_editor.js
@@ -4,6 +4,11 @@ odoo.define('website.tour.form_editor', function (require) {
const rpc = require('web.rpc');
const tour = require("web_tour.tour");
+ // Visibility possible values:
+ const VISIBLE = 'Always Visible';
+ const HIDDEN = 'Hidden';
+ const CONDITIONALVISIBILITY = 'Visible only if';
+
const selectButtonByText = function (text) {
return [{
content: "Open the select",
@@ -23,7 +28,7 @@ odoo.define('website.tour.form_editor', function (require) {
trigger: `we-select we-button[${data}]`,
}];
};
- const addField = function (data, name, type, label, required, hidden) {
+ const addField = function (data, name, type, label, required, display = {visibility: VISIBLE, condition: ''}) {
const ret = [{
content: "Select form",
extra_trigger: '.s_website_form_field',
@@ -37,8 +42,17 @@ odoo.define('website.tour.form_editor', function (require) {
content: "Wait for field to load",
trigger: `.s_website_form_field[data-type="${name}"], .s_website_form_input[name="${name}"]`, //custom or existing field
run: function () {},
- }];
+ },
+ ...selectButtonByText(display.visibility),
+ ];
let testText = '.s_website_form_field';
+ if (display.condition) {
+ ret.push({
+ content: "Set the visibility condition",
+ trigger: 'we-input[data-attribute-name="visibilityCondition"] input',
+ run: `text ${display.condition}`,
+ });
+ }
if (required) {
testText += '.s_website_form_required';
ret.push({
@@ -46,13 +60,6 @@ odoo.define('website.tour.form_editor', function (require) {
trigger: 'we-button[data-name="required_opt"] we-checkbox',
});
}
- if (hidden) {
- testText += '.s_website_form_field_hidden';
- ret.push({
- content: "Mark the field as hidden",
- trigger: 'we-button[data-name="hidden_opt"] we-checkbox',
- });
- }
if (label) {
testText += `:has(label:contains("${label}"))`;
ret.push({
@@ -72,11 +79,11 @@ odoo.define('website.tour.form_editor', function (require) {
});
return ret;
};
- const addCustomField = function (name, type, label, required, hidden) {
- return addField(`data-custom-field="${name}"`, name, type, label, required, hidden);
+ const addCustomField = function (name, type, label, required, display) {
+ return addField(`data-custom-field="${name}"`, name, type, label, required, display);
};
- const addExistingField = function (name, type, label, required, hidden) {
- return addField(`data-existing-field="${name}"`, name, type, label, required, hidden);
+ const addExistingField = function (name, type, label, required, display) {
+ return addField(`data-existing-field="${name}"`, name, type, label, required, display);
};
tour.register("website_form_editor_tour", {
@@ -104,9 +111,11 @@ odoo.define('website.tour.form_editor', function (require) {
trigger: '[data-field-name="email_to"] input',
run: 'text_blur test@test.test',
},
+ ...addExistingField('email_cc', 'text', 'Test conditional visibility', false, {visibility: CONDITIONALVISIBILITY, condition: 'odoo'}),
+
...addExistingField('date', 'text', 'Test Date', true),
- ...addExistingField('record_name', 'text', 'Awesome Label', false, true),
+ ...addExistingField('record_name', 'text', 'Awesome Label', false, {visibility: HIDDEN}),
...addExistingField('body_html', 'textarea', 'Your Message', true),
@@ -394,6 +403,25 @@ odoo.define('website.tour.form_editor', function (require) {
trigger: "textarea[name='description']",
run: "text magan"
},
+ {
+ content: "Check if conditional field is visible, it shouldn't.",
+ trigger: "body",
+ run: function () {
+ const style = window.getComputedStyle(this.$anchor[0].getElementsByClassName('s_website_form_field_hidden_if')[0]);
+ if (style.display !== 'none') {
+ console.error('error This field should be invisible when the name is not odoo');
+ }
+ }
+ },
+ {
+ content: "Change name input",
+ trigger: "input[name='name']",
+ run: "text odoo",
+ },
+ {
+ content: "Check if conditional field is visible, it should.",
+ trigger: "input[name='email_cc']",
+ },
{
content: "Send the form",
trigger: ".s_website_form_send"
diff --git a/addons/website/views/snippets/s_website_form.xml b/addons/website/views/snippets/s_website_form.xml
index 1f05efe502a..da436168824 100644
--- a/addons/website/views/snippets/s_website_form.xml
+++ b/addons/website/views/snippets/s_website_form.xml
@@ -133,8 +133,61 @@
data-apply-to=".s_website_form_date input"/>