summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLauri Eskola2018-11-06 15:38:46 (GMT)
committerLauri Eskola2018-11-06 15:41:25 (GMT)
commitf3e0022173ee8cb8efb016eee7533a23ca6fe090 (patch)
tree300da66e3d282e6cf327e208e162d2c323917c92
parentc0c662c427e041217ab0bb015880cd6c064fa0a3 (diff)
Issue #2995570 by lauriii, jrockowitz, drpal, aaronbauman: #states breaks when OR is used
(cherry picked from commit 26624808f2e5a16d7996acf171e13963ef535ba7)
-rw-r--r--core/misc/states.es6.js50
-rw-r--r--core/misc/states.js29
-rw-r--r--core/modules/system/system.post_update.php9
-rw-r--r--core/modules/system/tests/modules/form_test/form_test.routing.yml7
-rw-r--r--core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php55
-rw-r--r--core/tests/Drupal/Nightwatch/Tests/statesTest.js23
6 files changed, 153 insertions, 20 deletions
diff --git a/core/misc/states.es6.js b/core/misc/states.es6.js
index dd2c315..811a511 100644
--- a/core/misc/states.es6.js
+++ b/core/misc/states.es6.js
@@ -60,6 +60,30 @@
}
/**
+ * 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.
*
* @type {Drupal~behavior}
@@ -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;
},
diff --git a/core/misc/states.js b/core/misc/states.js
index fcdc37a..7b451b0 100644
--- a/core/misc/states.js
+++ b/core/misc/states.js
@@ -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]);
});
}
diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php
index 0662853..ba8e798 100644
--- a/core/modules/system/system.post_update.php
+++ b/core/modules/system/system.post_update.php
@@ -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.
+}
diff --git a/core/modules/system/tests/modules/form_test/form_test.routing.yml b/core/modules/system/tests/modules/form_test/form_test.routing.yml
index f2de126..ce4dfa9 100644
--- a/core/modules/system/tests/modules/form_test/form_test.routing.yml
+++ b/core/modules/system/tests/modules/form_test/form_test.routing.yml
@@ -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'
diff --git a/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php b/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php
new file mode 100644
index 0000000..5debb5b
--- /dev/null
+++ b/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php
@@ -0,0 +1,55 @@
+<?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) {
+ }
+
+}
diff --git a/core/tests/Drupal/Nightwatch/Tests/statesTest.js b/core/tests/Drupal/Nightwatch/Tests/statesTest.js
new file mode 100644
index 0000000..f7ee1ba
--- /dev/null
+++ b/core/tests/Drupal/Nightwatch/Tests/statesTest.js
@@ -0,0 +1,23 @@
+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);
+ },
+};