Skip to content
Snippets Groups Projects
Unverified Commit 26624808 authored by Lauri Timmanee's avatar Lauri Timmanee
Browse files

Issue #2995570 by lauriii, jrockowitz, drpal, aaronbauman: #states breaks when OR is used

parent 39c3361d
No related branches found
No related tags found
No related merge requests found
......@@ -59,6 +59,30 @@
return typeof a === 'undefined' || typeof b === 'undefined';
}
/**
* Bitwise AND with a third undefined state.
*
* @function Drupal.states~ternary
*
* @param {*} a
* Value a.
* @param {*} b
* Value b
*
* @return {bool}
* The result.
*/
function ternary(a, b) {
if (typeof a === 'undefined') {
return b;
}
if (typeof b === 'undefined') {
return a;
}
return a && b;
}
/**
* Attaches the states.
*
......@@ -305,18 +329,20 @@
// bogus, we don't want to end up with an infinite loop.
else if ($.isPlainObject(constraints)) {
// This constraint is an object (AND).
result = Object.keys(constraints).every(constraint => {
const check = this.checkConstraints(
constraints[constraint],
selector,
constraint,
);
/**
* The checkConstraints() function's return value can be undefined. If
* this so, consider it to have returned true.
*/
return typeof check === 'undefined' ? true : check;
});
// eslint-disable-next-line no-restricted-syntax
for (const n in constraints) {
if (constraints.hasOwnProperty(n)) {
result = ternary(
result,
this.checkConstraints(constraints[n], selector, n),
);
// False and anything else will evaluate to false, so return when
// any false condition is found.
if (result === false) {
return false;
}
}
}
}
return result;
},
......
......@@ -24,6 +24,17 @@
return typeof a === 'undefined' || typeof b === 'undefined';
}
function ternary(a, b) {
if (typeof a === 'undefined') {
return b;
}
if (typeof b === 'undefined') {
return a;
}
return a && b;
}
Drupal.behaviors.states = {
attach: function attach(context, settings) {
var $states = $(context).find('[data-drupal-states]');
......@@ -127,8 +138,6 @@
}
},
verifyConstraints: function verifyConstraints(constraints, selector) {
var _this3 = this;
var result = void 0;
if ($.isArray(constraints)) {
var hasXor = $.inArray('xor', constraints) === -1;
......@@ -144,11 +153,15 @@
}
}
} else if ($.isPlainObject(constraints)) {
result = Object.keys(constraints).every(function (constraint) {
var check = _this3.checkConstraints(constraints[constraint], selector, constraint);
for (var n in constraints) {
if (constraints.hasOwnProperty(n)) {
result = ternary(result, this.checkConstraints(constraints[n], selector, n));
return typeof check === 'undefined' ? true : check;
});
if (result === false) {
return false;
}
}
}
}
return result;
},
......@@ -197,7 +210,7 @@
states.Trigger.prototype = {
initialize: function initialize() {
var _this4 = this;
var _this3 = this;
var trigger = states.Trigger.states[this.state];
......@@ -205,7 +218,7 @@
trigger.call(window, this.element);
} else {
Object.keys(trigger || {}).forEach(function (event) {
_this4.defaultTrigger(event, trigger[event]);
_this3.defaultTrigger(event, trigger[event]);
});
}
......
......@@ -169,3 +169,12 @@ function system_post_update_extra_fields(&$sandbox = NULL) {
$config_entity_updater->update($sandbox, 'entity_form_display', $callback);
$config_entity_updater->update($sandbox, 'entity_view_display', $callback);
}
/**
* Force cache clear to ensure aggregated JavaScript files are regenerated.
*
* @see https://www.drupal.org/project/drupal/issues/2995570
*/
function system_post_update_states_clear_cache() {
// Empty post-update hook.
}
......@@ -513,3 +513,10 @@ form_test.optional_container:
_title: 'Optional container testing'
requirements:
_access: 'TRUE'
form_test.javascript_states_form:
path: '/form-test/javascript-states-form'
defaults:
_form: '\Drupal\form_test\Form\JavascriptStatesForm'
requirements:
_access: 'TRUE'
<?php
namespace Drupal\form_test\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Builds a simple form to test states.
*
* @see \Drupal\FunctionalJavascriptTests\Core\Form\JavascriptStatesTest
*/
class JavascriptStatesForm extends FormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'javascript_states_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['select'] = [
'#type' => 'select',
'#title' => 'select 1',
'#options' => [0 => 0, 1 => 1, 2 => 2],
];
$form['number'] = [
'#type' => 'number',
'#title' => 'enter 1',
];
$form['textfield'] = [
'#type' => 'textfield',
'#title' => 'textfield',
'#states' => [
'visible' => [
[':input[name="select"]' => ['value' => '1']],
'or',
[':input[name="number"]' => ['value' => '1']],
],
],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
}
}
module.exports = {
'@tags': ['core'],
before(browser) {
browser.drupalInstall().drupalLoginAsAdmin(() => {
browser
.drupalRelativeURL('/admin/modules')
.setValue('input[type="search"]', 'FormAPI')
.waitForElementVisible('input[name="modules[form_test][enable]"]', 1000)
.click('input[name="modules[form_test][enable]"]')
.click('input[type="submit"]') // Submit module form.
.click('input[type="submit"]'); // Confirm installation of dependencies.
});
},
after(browser) {
browser.drupalUninstall();
},
'Test form with state API': browser => {
browser
.drupalRelativeURL('/form-test/javascript-states-form')
.waitForElementVisible('body', 1000)
.waitForElementNotVisible('input[name="textfield"]', 1000);
},
};
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment