Loading js/project_browser.admin.js 0 → 100644 +145 −0 Original line number Diff line number Diff line /** * @file * Tabledrag behaviors. * * This code is borrowed from block's JavaScript. * * @see core/modules/block/js/block.es6.js */ (function ($, window, Drupal, once) { /** * Enable/Disable a Plugin in the table via select list. * * This behavior is dependent on the tableDrag behavior, since it uses the * objects initialized in that behavior to update the row. * * @type {Drupal~behavior} * * @prop {Drupal~behaviorAttach} attach * Attaches the tableDrag behavior plugin settings form. */ Drupal.behaviors.projectBrowserPluginSourceDrag = { attach(context, settings) { // Only proceed if tableDrag is present and we are on the settings page. if (!Drupal.tableDrag || !Drupal.tableDrag.project_browser) { return; } /** * Function to update the last placed row with the correct classes. * * @param {jQuery} table * The jQuery object representing the table to inspect. * @param {jQuery} rowObject * The jQuery object representing the table row. */ function updateLastPlaced(table, rowObject) { const $rowObject = $(rowObject); if (!$rowObject.is('.drag-previous')) { table.find('.drag-previous').removeClass('drag-previous'); $rowObject.addClass('drag-previous'); } } /** * Update source plugin weights in the given region. * * @param {jQuery} table * Table with draggable items. * @param {string} region * Machine name of region containing source plugin to update. */ function updateSourcePluginWeights(table, region) { // Calculate minimum weight. let weight = -Math.round(table.find('.draggable').length / 2); // Update the source plugin weights. table .find(`.status-title-${region}`) .nextUntil('.status-title') .find('select.source-weight') .val( // Increment the weight before assigning it to prevent using the // absolute minimum available weight. This way we always have an // unused upper and lower bound, which makes manually setting the // weights easier for users who prefer to do it that way. () => ++weight, ); } const table = $('#project_browser'); // Get the tableDrag object. const tableDrag = Drupal.tableDrag.project_browser; // Add a handler for when a row is swapped. tableDrag.row.prototype.onSwap = function (swappedRow) { updateLastPlaced(table, this); }; // Add a handler so when a row is dropped, update fields dropped into // new regions. tableDrag.onDrop = function () { const dragObject = this; const $rowElement = $(dragObject.rowObject.element); const regionRow = $rowElement.prevAll('tr.status-title').get(0); const regionName = regionRow.classList[1].replace('status-title-', ''); const regionField = $rowElement.find('select.source-status-select'); // Update region and weight fields if the region has been changed. if (!regionField.is(`.source-status-${regionName}`)) { const weightField = $rowElement.find('select.source-weight'); const oldRegionName = weightField[0].className.replace( /([^ ]+[ ]+)*source-weight-([^ ]+)([ ]+[^ ]+)*/, '$2', ); regionField .removeClass(`source-status-${oldRegionName}`) .addClass(`source-status-${regionName}`); weightField .removeClass(`source-weight-${oldRegionName}`) .addClass(`source-weight-${regionName}`); regionField.val(regionName); } updateSourcePluginWeights(table, regionName); }; // Add the behavior to each region select list. $(once('source-status-select', 'select.source-status-select', context)).on( 'change', function (event) { // Make our new row and select field. const row = $(this).closest('tr'); const select = $(this); // Find the correct region and insert the row as the last in the // region. tableDrag.rowObject = new tableDrag.row(row[0]); const regionMessage = table.find( `.status-title-${select[0].value}`, ); const regionItems = regionMessage.nextUntil( '.status-title', ); if (regionItems.length) { regionItems.last().after(row); } // We found that regionMessage is the last row. else { regionMessage.after(row); } updateSourcePluginWeights(table, select[0].value); // Update last placed source plugin indication. updateLastPlaced(table, row); // Show unsaved changes warning. if (!tableDrag.changed) { $(Drupal.theme('tableDragChangedWarning')) .insertBefore(tableDrag.table) .hide() .fadeIn('slow'); tableDrag.changed = true; } // Remove focus from selectbox. select.trigger('blur'); }, ); }, }; })(jQuery, window, Drupal, once); project_browser.libraries.yml +8 −0 Original line number Diff line number Diff line Loading @@ -12,3 +12,11 @@ svelte: - core/drupal - core/drupal.debounce - core/drupal.dialog tabledrag: js: js/project_browser.admin.js: {} dependencies: - core/jquery - core/drupal - core/once src/Form/SettingsForm.php +120 −12 Original line number Diff line number Diff line Loading @@ -69,6 +69,20 @@ class SettingsForm extends ConfigFormBase { return ['project_browser.admin_settings']; } /** * Returns an array containing the table headers. * * @return array * The table header. */ protected function getTableHeader() { return [ $this->t('Source'), $this->t('Status'), $this->t('Weight'), ]; } /** * {@inheritdoc} */ Loading @@ -82,27 +96,121 @@ class SettingsForm extends ConfigFormBase { ]; } $types = []; foreach ($source_plugins as $source) { $types[$source['id']] = (string) $source['label']; $enabled_sources = $config->get('enabled_sources'); // Sort the source plugins by the order they're stored in config. $sorted_arr = array_merge(array_flip($enabled_sources), $source_plugins); $source_plugins = array_merge($sorted_arr, $source_plugins); $weight_delta = round(count($source_plugins) / 2); $table = [ '#type' => 'table', '#header' => $this->getTableHeader(), '#attributes' => [ 'id' => 'project_browser', ], ]; $options = [ 'enabled' => $this->t('Enabled'), 'disabled' => $this->t('Disabled'), ]; foreach ($options as $status => $title) { $table['#tabledrag'][] = [ 'action' => 'match', 'relationship' => 'sibling', 'group' => 'source-status-select', 'subgroup' => 'source-status-' . $status, 'hidden' => FALSE, ]; $table['#tabledrag'][] = [ 'action' => 'order', 'relationship' => 'sibling', 'group' => 'source-weight', 'subgroup' => 'source-weight-' . $status, ]; $table['status-' . $status] = [ '#attributes' => [ 'class' => ['status-title', 'status-title-' . $status], 'no_striping' => TRUE, ], ]; $table['status-' . $status]['title'] = [ '#plain_text' => $title, '#wrapper_attributes' => [ 'colspan' => 3, ], ]; // Plugin rows. foreach ($source_plugins as $plugin_name => $plugin_definition) { // Only include plugins in their respective section. if (($status === 'enabled') !== in_array($plugin_name, $enabled_sources, TRUE)) { continue; } $form['enabled_sources'] = [ '#type' => 'checkboxes', '#title' => $this->t('Enabled source plugins for Project Browser'), '#default_value' => $config->get('enabled_sources') ?: [], '#options' => $types, '#description' => $this->t('Check to select source plugin for Project Browser. At least one checkbox needs to be checked.'), $label = (string) $plugin_definition['label']; $plugin_key_exists = array_search($plugin_name, $enabled_sources); $table[$plugin_name] = [ '#attributes' => [ 'class' => [ 'draggable', ], ], ]; $table[$plugin_name]['source'] = [ '#plain_text' => $label, '#wrapper_attributes' => [ 'id' => 'source--' . $plugin_name, ], ]; $table[$plugin_name]['status'] = [ '#type' => 'select', '#default_value' => $status, '#required' => TRUE, '#title' => $this->t('Status for @source source', ['@source' => $label]), '#title_display' => 'invisible', '#options' => $options, '#attributes' => [ 'class' => ['source-status-select', 'source-status-' . $status], ], ]; $table[$plugin_name]['weight'] = [ '#type' => 'weight', '#default_value' => ($plugin_key_exists === FALSE) ? 0 : $plugin_key_exists, '#delta' => $weight_delta, '#title' => $this->t('Weight for @source source', ['@source' => $label]), '#title_display' => 'invisible', '#attributes' => [ 'class' => ['source-weight', 'source-weight-' . $status], ], ]; } } $form['enabled_sources'] = $table; $form['#attached']['library'][] = 'project_browser/tabledrag'; return parent::buildForm($form, $form_state); } /** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { $all_plugins = $form_state->getValue('enabled_sources'); if (!array_key_exists('enabled', array_count_values(array_column($all_plugins, 'status')))) { $form_state->setErrorByName('enabled_sources', $this->t('At least one source plugin must be enabled.')); } } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $all_plugins = $form_state->getValue('enabled_sources'); $enabled_plugins = array_filter($all_plugins, fn($source) => $source['status'] === 'enabled'); $this->config('project_browser.admin_settings') ->set('enabled_sources', array_filter(array_values($form_state->getValue('enabled_sources')))) ->set('enabled_sources', array_keys($enabled_plugins)) ->save(); $this->cacheBin->deleteAll(); parent::submitForm($form, $form_state); Loading tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php +2 −6 Original line number Diff line number Diff line Loading @@ -35,12 +35,8 @@ class ProjectBrowserPluginTest extends WebDriverTestBase { 'administer modules', 'administer site configuration', ])); // Update configuration, disable drupalorg_mockapi source. $this->drupalGet('admin/config/development/project_browser'); $edit = [ 'enabled_sources[drupalorg_mockapi]' => FALSE, ]; $this->submitForm($edit, 'Save configuration'); // Update configuration, enable only random_data source. $this->config('project_browser.admin_settings')->set('enabled_sources', ['random_data'])->save(TRUE); } /** Loading tests/src/FunctionalJavascript/ProjectBrowserUiTest.php +60 −6 Original line number Diff line number Diff line Loading @@ -633,7 +633,6 @@ class ProjectBrowserUiTest extends WebDriverTestBase { * Tests recommended filters. */ public function testRecommendedFilter(): void { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); // Clear filters. $this->drupalGet('admin/modules/browse'); Loading @@ -659,13 +658,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $assert_session->pageTextContains('At least two source plugins are required to configure this feature.'); // Enable module for extra source plugin. $this->container->get('module_installer')->install(['project_browser_devel'], TRUE); // Update configuration, enable multiple sources. // Configuration updated when Project Browser Devel module enabled above. $this->drupalGet('admin/config/development/project_browser'); $assert_session->pageTextNotContains('At least two source plugins are required to configure this feature.'); $edit = [ 'enabled_sources[drupalorg_mockapi]' => TRUE, ]; $this->submitForm($edit, 'Save configuration'); // Test categories with multiple plugin enabled. $this->drupalGet('admin/modules/browse'); $this->svelteInitHelper('css', '.pb-categories input[type="checkbox"]'); Loading @@ -684,4 +679,63 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->assertNotEquals('9 Results Sorted by Project Usage', $this->getElementText('.grid--1 output')); } /** * Tests tabledrag on configuration page. */ public function testTabledrag(): void { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); $this->container->get('module_installer')->install(['project_browser_devel'], TRUE); $this->drupalGet('admin/modules/browse'); $assert_session->waitForElementVisible('css', '#project-browser .project'); // Count tabs. $tab_count = $page->findAll('css', '.plugin-tabs button'); $this->assertCount(2, $tab_count); // Verify that Drupal.org mockapi is first tab. $first_tab = $page->find('css', '.plugin-tabs button:nth-child(1)'); $this->assertEquals('drupalorg_mockapi', $first_tab->getValue()); // Re-order plugins. $this->drupalGet('admin/config/development/project_browser'); $first_plugin = $page->find('css', '#source--drupalorg_mockapi'); $second_plugin = $page->find('css', '#source--random_data'); $first_plugin->find('css', '.handle')->dragTo($second_plugin); $assert_session->assertWaitOnAjaxRequest(); $this->submitForm([], 'Save'); // Verify that Random data is first tab. $this->drupalGet('admin/modules/browse'); $assert_session->waitForElementVisible('css', '#project-browser .project'); $first_tab = $page->find('css', '.plugin-tabs button:nth-child(1)'); $this->assertEquals('random_data', $first_tab->getValue()); // Disable Drupal.org mockapi plugin. $this->drupalGet('admin/config/development/project_browser'); $enabled_row = $page->find('css', '#source--drupalorg_mockapi'); $disabled_region_row = $page->find('css', '.status-title-disabled'); $enabled_row->find('css', '.handle')->dragTo($disabled_region_row); $assert_session->assertWaitOnAjaxRequest(); $this->submitForm([], 'Save'); $assert_session->pageTextContains('The configuration options have been saved.'); // Verify that only Random data plugin is enabled. $this->drupalGet('admin/modules/browse'); $this->svelteInitHelper('css', '.pb-categories input[type="checkbox"]'); $assert_session->elementsCount('css', '.pb-categories input[type="checkbox"]', 20); // Enable only Drupal.org mockapi plugin through config update. // It is done this way because dragging was not working reliably for enabling Drupal.org mockapi plugin. $this->config('project_browser.admin_settings')->set('enabled_sources', ['drupalorg_mockapi'])->save(TRUE); $this->drupalGet('admin/config/development/project_browser'); $this->assertTrue($assert_session->optionExists('edit-enabled-sources-drupalorg-mockapi-status', 'enabled')->isSelected()); $this->assertTrue($assert_session->optionExists('edit-enabled-sources-random-data-status', 'disabled')->isSelected()); // Verify that only Drupal.org mockapi plugin is enabled. $this->drupalGet('admin/modules/browse'); $this->svelteInitHelper('css', '.pb-categories input[type="checkbox"]'); $assert_session->elementsCount('css', '.pb-categories input[type="checkbox"]', 54); } } Loading
js/project_browser.admin.js 0 → 100644 +145 −0 Original line number Diff line number Diff line /** * @file * Tabledrag behaviors. * * This code is borrowed from block's JavaScript. * * @see core/modules/block/js/block.es6.js */ (function ($, window, Drupal, once) { /** * Enable/Disable a Plugin in the table via select list. * * This behavior is dependent on the tableDrag behavior, since it uses the * objects initialized in that behavior to update the row. * * @type {Drupal~behavior} * * @prop {Drupal~behaviorAttach} attach * Attaches the tableDrag behavior plugin settings form. */ Drupal.behaviors.projectBrowserPluginSourceDrag = { attach(context, settings) { // Only proceed if tableDrag is present and we are on the settings page. if (!Drupal.tableDrag || !Drupal.tableDrag.project_browser) { return; } /** * Function to update the last placed row with the correct classes. * * @param {jQuery} table * The jQuery object representing the table to inspect. * @param {jQuery} rowObject * The jQuery object representing the table row. */ function updateLastPlaced(table, rowObject) { const $rowObject = $(rowObject); if (!$rowObject.is('.drag-previous')) { table.find('.drag-previous').removeClass('drag-previous'); $rowObject.addClass('drag-previous'); } } /** * Update source plugin weights in the given region. * * @param {jQuery} table * Table with draggable items. * @param {string} region * Machine name of region containing source plugin to update. */ function updateSourcePluginWeights(table, region) { // Calculate minimum weight. let weight = -Math.round(table.find('.draggable').length / 2); // Update the source plugin weights. table .find(`.status-title-${region}`) .nextUntil('.status-title') .find('select.source-weight') .val( // Increment the weight before assigning it to prevent using the // absolute minimum available weight. This way we always have an // unused upper and lower bound, which makes manually setting the // weights easier for users who prefer to do it that way. () => ++weight, ); } const table = $('#project_browser'); // Get the tableDrag object. const tableDrag = Drupal.tableDrag.project_browser; // Add a handler for when a row is swapped. tableDrag.row.prototype.onSwap = function (swappedRow) { updateLastPlaced(table, this); }; // Add a handler so when a row is dropped, update fields dropped into // new regions. tableDrag.onDrop = function () { const dragObject = this; const $rowElement = $(dragObject.rowObject.element); const regionRow = $rowElement.prevAll('tr.status-title').get(0); const regionName = regionRow.classList[1].replace('status-title-', ''); const regionField = $rowElement.find('select.source-status-select'); // Update region and weight fields if the region has been changed. if (!regionField.is(`.source-status-${regionName}`)) { const weightField = $rowElement.find('select.source-weight'); const oldRegionName = weightField[0].className.replace( /([^ ]+[ ]+)*source-weight-([^ ]+)([ ]+[^ ]+)*/, '$2', ); regionField .removeClass(`source-status-${oldRegionName}`) .addClass(`source-status-${regionName}`); weightField .removeClass(`source-weight-${oldRegionName}`) .addClass(`source-weight-${regionName}`); regionField.val(regionName); } updateSourcePluginWeights(table, regionName); }; // Add the behavior to each region select list. $(once('source-status-select', 'select.source-status-select', context)).on( 'change', function (event) { // Make our new row and select field. const row = $(this).closest('tr'); const select = $(this); // Find the correct region and insert the row as the last in the // region. tableDrag.rowObject = new tableDrag.row(row[0]); const regionMessage = table.find( `.status-title-${select[0].value}`, ); const regionItems = regionMessage.nextUntil( '.status-title', ); if (regionItems.length) { regionItems.last().after(row); } // We found that regionMessage is the last row. else { regionMessage.after(row); } updateSourcePluginWeights(table, select[0].value); // Update last placed source plugin indication. updateLastPlaced(table, row); // Show unsaved changes warning. if (!tableDrag.changed) { $(Drupal.theme('tableDragChangedWarning')) .insertBefore(tableDrag.table) .hide() .fadeIn('slow'); tableDrag.changed = true; } // Remove focus from selectbox. select.trigger('blur'); }, ); }, }; })(jQuery, window, Drupal, once);
project_browser.libraries.yml +8 −0 Original line number Diff line number Diff line Loading @@ -12,3 +12,11 @@ svelte: - core/drupal - core/drupal.debounce - core/drupal.dialog tabledrag: js: js/project_browser.admin.js: {} dependencies: - core/jquery - core/drupal - core/once
src/Form/SettingsForm.php +120 −12 Original line number Diff line number Diff line Loading @@ -69,6 +69,20 @@ class SettingsForm extends ConfigFormBase { return ['project_browser.admin_settings']; } /** * Returns an array containing the table headers. * * @return array * The table header. */ protected function getTableHeader() { return [ $this->t('Source'), $this->t('Status'), $this->t('Weight'), ]; } /** * {@inheritdoc} */ Loading @@ -82,27 +96,121 @@ class SettingsForm extends ConfigFormBase { ]; } $types = []; foreach ($source_plugins as $source) { $types[$source['id']] = (string) $source['label']; $enabled_sources = $config->get('enabled_sources'); // Sort the source plugins by the order they're stored in config. $sorted_arr = array_merge(array_flip($enabled_sources), $source_plugins); $source_plugins = array_merge($sorted_arr, $source_plugins); $weight_delta = round(count($source_plugins) / 2); $table = [ '#type' => 'table', '#header' => $this->getTableHeader(), '#attributes' => [ 'id' => 'project_browser', ], ]; $options = [ 'enabled' => $this->t('Enabled'), 'disabled' => $this->t('Disabled'), ]; foreach ($options as $status => $title) { $table['#tabledrag'][] = [ 'action' => 'match', 'relationship' => 'sibling', 'group' => 'source-status-select', 'subgroup' => 'source-status-' . $status, 'hidden' => FALSE, ]; $table['#tabledrag'][] = [ 'action' => 'order', 'relationship' => 'sibling', 'group' => 'source-weight', 'subgroup' => 'source-weight-' . $status, ]; $table['status-' . $status] = [ '#attributes' => [ 'class' => ['status-title', 'status-title-' . $status], 'no_striping' => TRUE, ], ]; $table['status-' . $status]['title'] = [ '#plain_text' => $title, '#wrapper_attributes' => [ 'colspan' => 3, ], ]; // Plugin rows. foreach ($source_plugins as $plugin_name => $plugin_definition) { // Only include plugins in their respective section. if (($status === 'enabled') !== in_array($plugin_name, $enabled_sources, TRUE)) { continue; } $form['enabled_sources'] = [ '#type' => 'checkboxes', '#title' => $this->t('Enabled source plugins for Project Browser'), '#default_value' => $config->get('enabled_sources') ?: [], '#options' => $types, '#description' => $this->t('Check to select source plugin for Project Browser. At least one checkbox needs to be checked.'), $label = (string) $plugin_definition['label']; $plugin_key_exists = array_search($plugin_name, $enabled_sources); $table[$plugin_name] = [ '#attributes' => [ 'class' => [ 'draggable', ], ], ]; $table[$plugin_name]['source'] = [ '#plain_text' => $label, '#wrapper_attributes' => [ 'id' => 'source--' . $plugin_name, ], ]; $table[$plugin_name]['status'] = [ '#type' => 'select', '#default_value' => $status, '#required' => TRUE, '#title' => $this->t('Status for @source source', ['@source' => $label]), '#title_display' => 'invisible', '#options' => $options, '#attributes' => [ 'class' => ['source-status-select', 'source-status-' . $status], ], ]; $table[$plugin_name]['weight'] = [ '#type' => 'weight', '#default_value' => ($plugin_key_exists === FALSE) ? 0 : $plugin_key_exists, '#delta' => $weight_delta, '#title' => $this->t('Weight for @source source', ['@source' => $label]), '#title_display' => 'invisible', '#attributes' => [ 'class' => ['source-weight', 'source-weight-' . $status], ], ]; } } $form['enabled_sources'] = $table; $form['#attached']['library'][] = 'project_browser/tabledrag'; return parent::buildForm($form, $form_state); } /** * {@inheritdoc} */ public function validateForm(array &$form, FormStateInterface $form_state) { $all_plugins = $form_state->getValue('enabled_sources'); if (!array_key_exists('enabled', array_count_values(array_column($all_plugins, 'status')))) { $form_state->setErrorByName('enabled_sources', $this->t('At least one source plugin must be enabled.')); } } /** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $all_plugins = $form_state->getValue('enabled_sources'); $enabled_plugins = array_filter($all_plugins, fn($source) => $source['status'] === 'enabled'); $this->config('project_browser.admin_settings') ->set('enabled_sources', array_filter(array_values($form_state->getValue('enabled_sources')))) ->set('enabled_sources', array_keys($enabled_plugins)) ->save(); $this->cacheBin->deleteAll(); parent::submitForm($form, $form_state); Loading
tests/src/FunctionalJavascript/ProjectBrowserPluginTest.php +2 −6 Original line number Diff line number Diff line Loading @@ -35,12 +35,8 @@ class ProjectBrowserPluginTest extends WebDriverTestBase { 'administer modules', 'administer site configuration', ])); // Update configuration, disable drupalorg_mockapi source. $this->drupalGet('admin/config/development/project_browser'); $edit = [ 'enabled_sources[drupalorg_mockapi]' => FALSE, ]; $this->submitForm($edit, 'Save configuration'); // Update configuration, enable only random_data source. $this->config('project_browser.admin_settings')->set('enabled_sources', ['random_data'])->save(TRUE); } /** Loading
tests/src/FunctionalJavascript/ProjectBrowserUiTest.php +60 −6 Original line number Diff line number Diff line Loading @@ -633,7 +633,6 @@ class ProjectBrowserUiTest extends WebDriverTestBase { * Tests recommended filters. */ public function testRecommendedFilter(): void { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); // Clear filters. $this->drupalGet('admin/modules/browse'); Loading @@ -659,13 +658,9 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $assert_session->pageTextContains('At least two source plugins are required to configure this feature.'); // Enable module for extra source plugin. $this->container->get('module_installer')->install(['project_browser_devel'], TRUE); // Update configuration, enable multiple sources. // Configuration updated when Project Browser Devel module enabled above. $this->drupalGet('admin/config/development/project_browser'); $assert_session->pageTextNotContains('At least two source plugins are required to configure this feature.'); $edit = [ 'enabled_sources[drupalorg_mockapi]' => TRUE, ]; $this->submitForm($edit, 'Save configuration'); // Test categories with multiple plugin enabled. $this->drupalGet('admin/modules/browse'); $this->svelteInitHelper('css', '.pb-categories input[type="checkbox"]'); Loading @@ -684,4 +679,63 @@ class ProjectBrowserUiTest extends WebDriverTestBase { $this->assertNotEquals('9 Results Sorted by Project Usage', $this->getElementText('.grid--1 output')); } /** * Tests tabledrag on configuration page. */ public function testTabledrag(): void { $page = $this->getSession()->getPage(); $assert_session = $this->assertSession(); $this->container->get('module_installer')->install(['project_browser_devel'], TRUE); $this->drupalGet('admin/modules/browse'); $assert_session->waitForElementVisible('css', '#project-browser .project'); // Count tabs. $tab_count = $page->findAll('css', '.plugin-tabs button'); $this->assertCount(2, $tab_count); // Verify that Drupal.org mockapi is first tab. $first_tab = $page->find('css', '.plugin-tabs button:nth-child(1)'); $this->assertEquals('drupalorg_mockapi', $first_tab->getValue()); // Re-order plugins. $this->drupalGet('admin/config/development/project_browser'); $first_plugin = $page->find('css', '#source--drupalorg_mockapi'); $second_plugin = $page->find('css', '#source--random_data'); $first_plugin->find('css', '.handle')->dragTo($second_plugin); $assert_session->assertWaitOnAjaxRequest(); $this->submitForm([], 'Save'); // Verify that Random data is first tab. $this->drupalGet('admin/modules/browse'); $assert_session->waitForElementVisible('css', '#project-browser .project'); $first_tab = $page->find('css', '.plugin-tabs button:nth-child(1)'); $this->assertEquals('random_data', $first_tab->getValue()); // Disable Drupal.org mockapi plugin. $this->drupalGet('admin/config/development/project_browser'); $enabled_row = $page->find('css', '#source--drupalorg_mockapi'); $disabled_region_row = $page->find('css', '.status-title-disabled'); $enabled_row->find('css', '.handle')->dragTo($disabled_region_row); $assert_session->assertWaitOnAjaxRequest(); $this->submitForm([], 'Save'); $assert_session->pageTextContains('The configuration options have been saved.'); // Verify that only Random data plugin is enabled. $this->drupalGet('admin/modules/browse'); $this->svelteInitHelper('css', '.pb-categories input[type="checkbox"]'); $assert_session->elementsCount('css', '.pb-categories input[type="checkbox"]', 20); // Enable only Drupal.org mockapi plugin through config update. // It is done this way because dragging was not working reliably for enabling Drupal.org mockapi plugin. $this->config('project_browser.admin_settings')->set('enabled_sources', ['drupalorg_mockapi'])->save(TRUE); $this->drupalGet('admin/config/development/project_browser'); $this->assertTrue($assert_session->optionExists('edit-enabled-sources-drupalorg-mockapi-status', 'enabled')->isSelected()); $this->assertTrue($assert_session->optionExists('edit-enabled-sources-random-data-status', 'disabled')->isSelected()); // Verify that only Drupal.org mockapi plugin is enabled. $this->drupalGet('admin/modules/browse'); $this->svelteInitHelper('css', '.pb-categories input[type="checkbox"]'); $assert_session->elementsCount('css', '.pb-categories input[type="checkbox"]', 54); } }