diff --git a/core/includes/common.inc b/core/includes/common.inc index 03af03b..7bb70a2 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -568,7 +568,7 @@ function drupal_js_defaults($data = NULL) { * @param $elements * A renderable array element having a #states property as described above. * - * @see form_example_states_form() + * @see \Drupal\form_test\Form\JavascriptStatesForm */ function drupal_process_states(&$elements) { $elements['#attached']['library'][] = 'core/drupal.states'; 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 d8c5833..22b1221 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 @@ -480,3 +480,10 @@ form_test.get_form: _form: '\Drupal\form_test\Form\FormTestGetForm' 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..5bb45c8 --- /dev/null +++ b/core/modules/system/tests/modules/form_test/src/Form/JavascriptStatesForm.php @@ -0,0 +1,273 @@ + 'textfield', + '#title' => $this->t('Name'), + '#states' => array( + 'invisible' => array( + ':input[name="anonymous"]' => array('checked' => TRUE), + ), + ), + ); + + // Uncheck anonymous field when the name field is filled. + $form['anonymous'] = array( + '#type' => 'checkbox', + '#title' => $this->t('I prefer to remain anonymous'), + '#default_value' => '1', + '#states' => array( + 'unchecked' => array( + ':input[name="name"]' => array('filled' => TRUE), + ), + ), + ); + + $form['student_type'] = array( + '#type' => 'radios', + '#options' => array( + 'high_school' => $this->t('High School'), + 'undergraduate' => $this->t('Undergraduate'), + 'graduate' => $this->t('Graduate'), + ), + '#title' => $this->t('What type of student are you?'), + ); + $form['high_school'] = array( + '#type' => 'fieldset', + '#title' => $this->t('High School Information'), + // This #states rule says that the "high school" fieldset should only + // be shown if the "student_type" form element is set to "High School". + '#states' => array( + 'visible' => array( + ':input[name="student_type"]' => array('value' => 'high_school'), + ), + ), + ); + + // High school information. + $form['high_school']['tests_taken'] = array( + '#type' => 'checkboxes', + '#options' => array_combine(array('SAT', 'ACT'), array(t('SAT'), t('ACT'))), + '#title' => $this->t('What standardized tests did you take?'), + // This #states rule says that this checkboxes array will be visible only + // when $form['student_type'] is set to t('High School'). + // It uses the jQuery selector :input[name=student_type] to choose the + // element which triggers the behavior, and then defines the "High School" + // value as the one that triggers visibility. + '#states' => array( + // Action to take. + 'visible' => array( + ':input[name="student_type"]' => array('value' => 'high_school'), + ), + ), + ); + + $form['high_school']['sat_score'] = array( + '#type' => 'textfield', + '#title' => $this->t('Your SAT score:'), + '#size' => 4, + + // This #states rule limits visibility to when the $form['tests_taken'] + // 'SAT' checkbox is checked." + '#states' => array( + // Action to take. + 'visible' => array( + ':input[name="tests_taken[SAT]"]' => array('checked' => TRUE), + ), + ), + ); + $form['high_school']['act_score'] = array( + '#type' => 'textfield', + '#title' => $this->t('Your ACT score:'), + '#size' => 4, + + // Set this element visible if the ACT checkbox above is checked. + '#states' => array( + // Action to take. + 'visible' => array( + ':input[name="tests_taken[ACT]"]' => array('checked' => TRUE), + ), + ), + ); + + // Undergrad information. + $form['undergraduate'] = array( + '#type' => 'fieldset', + '#title' => $this->t('Undergraduate Information'), + // This #states rule says that the "undergraduate" fieldset should only + // be shown if the "student_type" form element is set to "Undergraduate". + '#states' => array( + 'visible' => array( + ':input[name="student_type"]' => array('value' => 'undergraduate'), + ), + ), + ); + + $form['undergraduate']['how_many_years'] = array( + '#type' => 'select', + '#title' => $this->t('How many years have you completed?'), + // The options here are integers, but since all the action here happens + // using the DOM on the client, we will have to use strings to work with + // them. + '#options' => array( + 1 => $this->t('One'), + 2 => $this->t('Two'), + 3 => $this->t('Three'), + 4 => $this->t('Four'), + 5 => $this->t('Lots'), + ), + ); + + $form['undergraduate']['comment'] = array( + '#type' => 'item', + '#description' => $this->t("Wow, that's a long time."), + '#states' => array( + 'visible' => array( + // Note that '5' must be used here instead of the integer 5. + // The information is coming from the DOM as a string. + ':input[name="how_many_years"]' => array('value' => '5'), + ), + ), + ); + $form['undergraduate']['school_name'] = array( + '#type' => 'textfield', + '#title' => $this->t('Your college or university:'), + ); + $form['undergraduate']['school_country'] = array( + '#type' => 'select', + '#options' => array_combine(array('UK', 'Other'), array(t('UK'), t('Other'))), + '#title' => $this->t('In what country is your college or university located?'), + ); + $form['undergraduate']['country_writein'] = array( + '#type' => 'textfield', + '#size' => 20, + '#title' => $this->t('Please enter the name of the country where your college or university is located.'), + + // Only show this field if school_country is set to 'Other'. + '#states' => array( + // Action to take: Make visible. + 'visible' => array( + ':input[name="school_country"]' => array('value' => $this->t('Other')), + ), + ), + ); + + $form['undergraduate']['thanks'] = array( + '#type' => 'item', + '#description' => $this->t('Thanks for providing both your school and your country.'), + '#states' => array( + // Here visibility requires that two separate conditions be true. + 'visible' => array( + ':input[name="school_country"]' => array('value' => $this->t('Other')), + ':input[name="country_writein"]' => array('filled' => TRUE), + ), + ), + ); + $form['undergraduate']['go_away'] = array( + '#type' => 'submit', + '#value' => $this->t('Done with form'), + '#states' => array( + // Here visibility requires that two separate conditions be true. + 'visible' => array( + ':input[name="school_country"]' => array('value' => $this->t('Other')), + ':input[name="country_writein"]' => array('filled' => TRUE), + ), + ), + ); + + // Graduate student information. + $form['graduate'] = array( + '#type' => 'fieldset', + '#title' => $this->t('Graduate School Information'), + // This #states rule says that the "graduate" fieldset should only + // be shown if the "student_type" form element is set to "Graduate". + '#states' => array( + 'visible' => array( + ':input[name="student_type"]' => array('value' => 'graduate'), + ), + ), + ); + $form['graduate']['more_info'] = array( + '#type' => 'textarea', + '#title' => $this->t('Please describe your graduate studies'), + ); + + $form['graduate']['info_provide'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Check here if you have provided information above'), + '#disabled' => TRUE, + '#states' => array( + // Mark this checkbox checked if the "more_info" textarea has something + // in it, if it's 'filled'. + 'checked' => array( + ':input[name="more_info"]' => array('filled' => TRUE), + ), + ), + ); + + $form['average'] = array( + '#type' => 'textfield', + '#title' => $this->t('Enter your average'), + // To trigger a state when the same controlling element can have more than + // one possible value, put all values in a higher-level array. + '#states' => array( + 'visible' => array( + ':input[name="student_type"]' => array( + array('value' => 'high_school'), + array('value' => 'undergraduate'), + ), + ), + ), + ); + + $form['expand_more_info'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Check here if you want to add more information.'), + ); + $form['more_info'] = array( + '#type' => 'details', + '#title' => $this->t('Additional Information'), + // Expand the expand_more_info fieldset if the box is checked. + '#states' => array( + 'expanded' => array( + ':input[name="expand_more_info"]' => array('checked' => TRUE), + ), + ), + ); + $form['more_info']['feedback'] = array( + '#type' => 'textarea', + '#title' => $this->t('What do you have to say?'), + ); + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + } + +} diff --git a/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php new file mode 100644 index 0000000..beee43b --- /dev/null +++ b/core/tests/Drupal/FunctionalJavascriptTests/Core/Form/JavascriptStatesTest.php @@ -0,0 +1,105 @@ +drupalGet('form-test/javascript-states-form'); + $page = $this->getSession()->getPage(); + + $this->assertElementNotVisible('#edit-name'); + + // This should make the name element visible. + $page->uncheckField('I prefer to remain anonymous'); + $this->assertElementVisible('#edit-name'); + + // This should make the name element invisible. + $page->checkField('I prefer to remain anonymous'); + $this->assertElementNotVisible('#edit-name'); + + // This should make the name element visible. + $page->uncheckField('I prefer to remain anonymous'); + $this->assertElementVisible('#edit-name'); + + // Ensure all the details elements are invisible. + $this->assertElementNotVisible('#edit-high-school'); + $this->assertElementNotVisible('#edit-undergraduate'); + $this->assertElementNotVisible('#edit-graduate'); + $this->assertElementNotVisible('#edit-average'); + + // High school form testing. + $this->click('#edit-student-type-high-school'); + $this->assertElementVisible('#edit-high-school'); + $this->assertElementNotVisible('#edit-undergraduate'); + $this->assertElementNotVisible('#edit-graduate'); + $this->assertElementVisible('#edit-average'); + + $this->assertElementNotVisible('#edit-sat-score'); + $this->assertElementNotVisible('#edit-act-score'); + $this->click('#edit-tests-taken-sat'); + $this->assertElementVisible('#edit-sat-score'); + $this->assertElementNotVisible('#edit-act-score'); + $this->click('#edit-tests-taken-act'); + $this->assertElementVisible('#edit-act-score'); + $this->assertElementVisible('#edit-sat-score'); + $this->click('#edit-tests-taken-sat'); + $this->click('#edit-tests-taken-act'); + $this->assertElementNotVisible('#edit-sat-score'); + $this->assertElementNotVisible('#edit-act-score'); + + // Undergraduate form testing. + $this->click('#edit-student-type-undergraduate'); + $this->assertElementNotVisible('#edit-high-school'); + $this->assertElementVisible('#edit-undergraduate'); + $this->assertElementNotVisible('#edit-graduate'); + $this->assertElementVisible('#edit-average'); + + $this->assertElementNotVisible('#edit-comment--description'); + $page->selectFieldOption('How many years have you completed?', '5'); + $this->assertElementVisible('#edit-comment--description'); + $this->assertElementNotVisible('#edit-country-writein'); + $page->selectFieldOption('In what country is your college or university located?', 'Other'); + $this->assertElementVisible('#edit-country-writein'); + $page->selectFieldOption('In what country is your college or university located?', 'UK'); + $this->assertElementNotVisible('#edit-country-writein'); + + // Graduate form testing. + $this->click('#edit-student-type-graduate'); + $this->assertElementNotVisible('#edit-high-school'); + $this->assertElementNotVisible('#edit-undergraduate'); + $this->assertElementVisible('#edit-graduate'); + $this->assertElementNotVisible('#edit-average'); + + $this->assertSession()->checkboxNotChecked('Check here if you have provided information above'); + $page->fillField('Please describe your graduate studies', 'Some text'); + $this->assertSession()->checkboxChecked('Check here if you have provided information above'); + $page->fillField('Please describe your graduate studies', ''); + // Empty and filled states are triggered on keyup event. + $page->findField('Please describe your graduate studies')->keyUp('a'); + $this->assertSession()->checkboxNotChecked('Check here if you have provided information above'); + + // Feedback testing. + $this->assertElementNotVisible('#edit-feedback'); + $page->checkField('Check here if you want to add more information.'); + $this->assertElementVisible('#edit-feedback'); + $page->uncheckField('Check here if you want to add more information.'); + $this->assertElementNotVisible('#edit-feedback'); + } + +} diff --git a/core/tests/Drupal/FunctionalJavascriptTests/JavascriptTestBase.php b/core/tests/Drupal/FunctionalJavascriptTests/JavascriptTestBase.php index 41e5b79..2862d4c 100644 --- a/core/tests/Drupal/FunctionalJavascriptTests/JavascriptTestBase.php +++ b/core/tests/Drupal/FunctionalJavascriptTests/JavascriptTestBase.php @@ -43,6 +43,7 @@ protected function initMink() { * Optional message to show alongside the assertion. */ protected function assertElementVisible($css_selector, $message = '') { + $message = $message ?: "Element ($css_selector) is visible."; $this->assertTrue($this->getSession()->getDriver()->isVisible(CssSelector::toXPath($css_selector)), $message); } @@ -55,6 +56,7 @@ protected function assertElementVisible($css_selector, $message = '') { * Optional message to show alongside the assertion. */ protected function assertElementNotVisible($css_selector, $message = '') { + $message = $message ?: "Element ($css_selector) is not visible."; $this->assertFalse($this->getSession()->getDriver()->isVisible(CssSelector::toXPath($css_selector)), $message); }