summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorAlex Pott <alex.a.pott@googlemail.com>2020-05-31 12:21:06 +0100
committerAlex Pott <alex.a.pott@googlemail.com>2020-05-31 12:21:06 +0100
commit0c74be7ae2dddfcddf933480331186394d5bb50e (patch)
treea74eb9fd7a0ceea0b6df190edf77326b8c6d71aa
parent40d62a67e71946458586ad4a450b2a9f769ffbeb (diff)
downloaddrupal-0c74be7ae2dddfcddf933480331186394d5bb50e.tar.gz
drupal-0c74be7ae2dddfcddf933480331186394d5bb50e.zip
Issue #2891603 by eiriksm, alexpott, charlietoleary, Grayle, drclaw, fgm: Contextual links can't handle multiple occurrences of the same contextual links (again)
-rw-r--r--core/modules/contextual/js/contextual.es6.js4
-rw-r--r--core/modules/contextual/js/contextual.js2
-rw-r--r--core/modules/contextual/tests/modules/contextual_test/config/optional/views.view.contextual_recent.yml327
-rw-r--r--core/modules/contextual/tests/src/FunctionalJavascript/DuplicateContextualLinksTest.php58
4 files changed, 389 insertions, 2 deletions
diff --git a/core/modules/contextual/js/contextual.es6.js b/core/modules/contextual/js/contextual.es6.js
index 2b26ee2a837..86f76980e25 100644
--- a/core/modules/contextual/js/contextual.es6.js
+++ b/core/modules/contextual/js/contextual.es6.js
@@ -186,7 +186,9 @@
// Drupal.contextual.collection.
window.setTimeout(() => {
initContextual(
- $context.find(`[data-contextual-id="${contextualID.id}"]`),
+ $context
+ .find(`[data-contextual-id="${contextualID.id}"]:empty`)
+ .eq(0),
html,
);
});
diff --git a/core/modules/contextual/js/contextual.js b/core/modules/contextual/js/contextual.js
index 97a7453d3f6..62c9e2c3215 100644
--- a/core/modules/contextual/js/contextual.js
+++ b/core/modules/contextual/js/contextual.js
@@ -108,7 +108,7 @@
if (html && html.length) {
window.setTimeout(function () {
- initContextual($context.find("[data-contextual-id=\"".concat(contextualID.id, "\"]")), html);
+ initContextual($context.find("[data-contextual-id=\"".concat(contextualID.id, "\"]:empty")).eq(0), html);
});
return;
}
diff --git a/core/modules/contextual/tests/modules/contextual_test/config/optional/views.view.contextual_recent.yml b/core/modules/contextual/tests/modules/contextual_test/config/optional/views.view.contextual_recent.yml
new file mode 100644
index 00000000000..b3c1ea4c846
--- /dev/null
+++ b/core/modules/contextual/tests/modules/contextual_test/config/optional/views.view.contextual_recent.yml
@@ -0,0 +1,327 @@
+langcode: en
+status: true
+dependencies:
+ module:
+ - node
+ - user
+id: contextual_recent
+label: 'Recent content'
+module: node
+description: 'Recent content.'
+tag: default
+base_table: node_field_data
+base_field: nid
+display:
+ default:
+ display_plugin: default
+ id: default
+ display_title: Master
+ position: 0
+ display_options:
+ access:
+ type: perm
+ options:
+ perm: 'access content'
+ cache:
+ type: tag
+ options: { }
+ query:
+ type: views_query
+ options:
+ disable_sql_rewrite: false
+ distinct: false
+ replica: false
+ query_comment: ''
+ query_tags: { }
+ exposed_form:
+ type: basic
+ options:
+ submit_button: Apply
+ reset_button: false
+ reset_button_label: Reset
+ exposed_sorts_label: 'Sort by'
+ expose_sort_order: true
+ sort_asc_label: Asc
+ sort_desc_label: Desc
+ pager:
+ type: some
+ options:
+ items_per_page: 10
+ offset: 0
+ style:
+ type: html_list
+ options:
+ grouping: { }
+ row_class: ''
+ default_row_class: true
+ type: ul
+ wrapper_class: item-list
+ class: ''
+ row:
+ type: fields
+ fields:
+ title:
+ id: title
+ table: node_field_data
+ field: title
+ entity_type: node
+ entity_field: title
+ label: ''
+ exclude: false
+ alter:
+ alter_text: false
+ make_link: false
+ absolute: false
+ trim: false
+ word_boundary: false
+ ellipsis: false
+ strip_tags: false
+ html: false
+ hide_empty: false
+ empty_zero: false
+ relationship: none
+ group_type: group
+ admin_label: ''
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: false
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_alter_empty: true
+ type: string
+ settings:
+ link_to_entity: true
+ plugin_id: field
+ changed:
+ id: changed
+ table: node_field_data
+ field: changed
+ relationship: none
+ group_type: group
+ admin_label: ''
+ label: ''
+ exclude: false
+ alter:
+ alter_text: false
+ text: ''
+ make_link: false
+ path: ''
+ absolute: false
+ external: false
+ replace_spaces: false
+ path_case: none
+ trim_whitespace: false
+ alt: ''
+ rel: ''
+ link_class: ''
+ prefix: ''
+ suffix: ''
+ target: ''
+ nl2br: false
+ max_length: 0
+ word_boundary: true
+ ellipsis: true
+ more_link: false
+ more_link_text: ''
+ more_link_path: ''
+ strip_tags: false
+ trim: false
+ preserve_tags: ''
+ html: false
+ element_type: ''
+ element_class: ''
+ element_label_type: ''
+ element_label_class: ''
+ element_label_colon: false
+ element_wrapper_type: ''
+ element_wrapper_class: ''
+ element_default_classes: true
+ empty: ''
+ hide_empty: false
+ empty_zero: false
+ hide_alter_empty: true
+ click_sort_column: value
+ type: timestamp_ago
+ settings: { }
+ group_column: value
+ group_columns: { }
+ group_rows: true
+ delta_limit: 0
+ delta_offset: 0
+ delta_reversed: false
+ delta_first_last: false
+ multi_type: separator
+ separator: ', '
+ field_api_classes: false
+ entity_type: node
+ entity_field: changed
+ plugin_id: field
+ filters:
+ status_extra:
+ id: status_extra
+ table: node_field_data
+ field: status_extra
+ relationship: none
+ group_type: group
+ admin_label: ''
+ operator: '='
+ value: false
+ group: 1
+ exposed: false
+ expose:
+ operator_id: ''
+ label: ''
+ description: ''
+ use_operator: false
+ operator: ''
+ identifier: ''
+ required: false
+ remember: false
+ multiple: false
+ remember_roles:
+ authenticated: authenticated
+ operator_limit_selection: false
+ operator_list: { }
+ is_grouped: false
+ group_info:
+ label: ''
+ description: ''
+ identifier: ''
+ optional: true
+ widget: select
+ multiple: false
+ remember: false
+ default_group: All
+ default_group_multiple: { }
+ group_items: { }
+ entity_type: node
+ plugin_id: node_status
+ langcode:
+ id: langcode
+ table: node_field_data
+ field: langcode
+ relationship: none
+ group_type: group
+ admin_label: ''
+ operator: in
+ value:
+ '***LANGUAGE_language_content***': '***LANGUAGE_language_content***'
+ group: 1
+ exposed: false
+ expose:
+ operator_id: ''
+ label: ''
+ description: ''
+ use_operator: false
+ operator: ''
+ identifier: ''
+ required: false
+ remember: false
+ multiple: false
+ remember_roles:
+ authenticated: authenticated
+ reduce: false
+ operator_limit_selection: false
+ operator_list: { }
+ is_grouped: false
+ group_info:
+ label: ''
+ description: ''
+ identifier: ''
+ optional: true
+ widget: select
+ multiple: false
+ remember: false
+ default_group: All
+ default_group_multiple: { }
+ group_items: { }
+ entity_type: node
+ entity_field: langcode
+ plugin_id: language
+ sorts:
+ changed:
+ id: changed
+ table: node_field_data
+ field: changed
+ relationship: none
+ group_type: group
+ admin_label: ''
+ order: DESC
+ exposed: false
+ expose:
+ label: ''
+ granularity: second
+ entity_type: node
+ entity_field: changed
+ plugin_id: date
+ title: 'Recent content'
+ header: { }
+ footer: { }
+ empty:
+ area_text_custom:
+ id: area_text_custom
+ table: views
+ field: area_text_custom
+ relationship: none
+ group_type: group
+ admin_label: ''
+ empty: true
+ tokenize: false
+ content: 'No content available.'
+ plugin_id: text_custom
+ relationships:
+ uid:
+ id: uid
+ table: node_field_data
+ field: uid
+ relationship: none
+ group_type: group
+ admin_label: author
+ required: true
+ entity_type: node
+ entity_field: uid
+ plugin_id: standard
+ arguments: { }
+ display_extenders: { }
+ use_more: false
+ use_more_always: false
+ use_more_text: More
+ link_url: ''
+ link_display: '0'
+ cache_metadata:
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - user
+ - 'user.node_grants:view'
+ - user.permissions
+ max-age: -1
+ tags: { }
+ block_1:
+ display_plugin: block
+ id: block_1
+ display_title: Block
+ position: 2
+ display_options:
+ display_extenders: { }
+ defaults:
+ style: false
+ row: false
+ row:
+ type: 'entity:node'
+ options:
+ relationship: none
+ view_mode: teaser
+ cache_metadata:
+ contexts:
+ - 'languages:language_content'
+ - 'languages:language_interface'
+ - user
+ - 'user.node_grants:view'
+ - user.permissions
+ max-age: -1
+ tags: { }
diff --git a/core/modules/contextual/tests/src/FunctionalJavascript/DuplicateContextualLinksTest.php b/core/modules/contextual/tests/src/FunctionalJavascript/DuplicateContextualLinksTest.php
new file mode 100644
index 00000000000..d5c62742284
--- /dev/null
+++ b/core/modules/contextual/tests/src/FunctionalJavascript/DuplicateContextualLinksTest.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Drupal\Tests\contextual\FunctionalJavascript;
+
+use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
+
+/**
+ * Tests the UI for correct contextual links.
+ *
+ * @group contextual
+ */
+class DuplicateContextualLinksTest extends WebDriverTestBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ public static $modules = [
+ 'block',
+ 'contextual',
+ 'node',
+ 'views',
+ 'views_ui',
+ 'contextual_test',
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
+ protected $defaultTheme = 'stark';
+
+ /**
+ * Tests the contextual links with same id.
+ */
+ public function testSameContextualLinks() {
+ $this->drupalPlaceBlock('views_block:contextual_recent-block_1', ['id' => 'first']);
+ $this->drupalPlaceBlock('views_block:contextual_recent-block_1', ['id' => 'second']);
+ $this->drupalCreateContentType(['type' => 'page']);
+ $this->drupalCreateNode();
+ $this->drupalLogin($this->drupalCreateUser([
+ 'access content',
+ 'access contextual links',
+ 'administer nodes',
+ 'administer blocks',
+ 'administer views',
+ 'edit any page content',
+ ]));
+ // Ensure same contextual links work correct with fresh and cached page.
+ foreach (['fresh', 'cached'] as $state) {
+ $this->drupalGet('user');
+ $contextual_id = '[data-contextual-id^="node:node=1"]';
+ $this->assertJsCondition("(typeof jQuery !== 'undefined' && jQuery('[data-contextual-id]:empty').length === 0)");
+ $this->getSession()->executeScript("jQuery('#block-first $contextual_id .trigger').trigger('click');");
+ $contextual_links = $this->assertSession()->waitForElementVisible('css', "#block-first $contextual_id .contextual-links");
+ $this->assertTrue($contextual_links->isVisible(), "Contextual links are visible with $state page.");
+ }
+ }
+
+}