diff --git a/.drone.yml b/.drone.yml index 9f62b3d9b..c3942b2a7 100644 --- a/.drone.yml +++ b/.drone.yml @@ -23,6 +23,12 @@ services: environment: - MYSQL_ALLOW_EMPTY_PASSWORD=yes - MYSQL_DATABASE=drupal + selenium: + image: git.fpfis.tech.ec.europa.eu/fpfis/dependency_proxy/containers/selenium/standalone-chrome:4.1.3-20220405 + environment: + - DISPLAY=:99 + - NODE_MAX_INSTANCES=5 + - NODE_MAX_SESSION=5 pipeline: npm-build: diff --git a/bcl-builder.config.js b/bcl-builder.config.js index 3dd6a5260..e1cb3e23b 100644 --- a/bcl-builder.config.js +++ b/bcl-builder.config.js @@ -8,6 +8,15 @@ const includePaths = [nodeModules]; module.exports = { styles: [ + { + entry: path.resolve(outputFolder, "resources/sass/copyright_overlay.scss"), + dest: path.resolve(outputFolder, "assets/css/copyright-overlay.style.min.css"), + options: { + includePaths, + minify: true, + sourceMap: "file", + }, + }, { entry: path.resolve(outputFolder, "resources/sass/oe_bootstrap_theme.style.scss"), dest: path.resolve(outputFolder, "assets/css/oe_bootstrap_theme.style.min.css"), diff --git a/docker-compose.yml b/docker-compose.yml index 18792e588..cc7b3827c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,16 @@ services: MYSQL_ALLOW_EMPTY_PASSWORD: "yes" # ports: # - 3306:3306 + selenium: + image: selenium/standalone-chrome:4.1.3 + environment: + - DISPLAY=:99 + - VNC_NO_PASSWORD=1 + ports: + - '5900:5900' + expose: + - '4444' + shm_size: 2g node: image: node:18.12.0 user: "node" diff --git a/includes/pattern.inc b/includes/pattern.inc index 53c441610..f49df434c 100644 --- a/includes/pattern.inc +++ b/includes/pattern.inc @@ -99,6 +99,14 @@ function oe_bootstrap_theme_preprocess_pattern_accordion(array &$variables): voi $variables['accordion_id'] = Html::getUniqueId('bcl-accordion'); } +/** + * Implements hook_preprocess_HOOK(). + */ +function oe_bootstrap_theme_preprocess_pattern_copyright_overlay(array &$variables): void { + $variables['modal_id'] = Html::getUniqueId('bcl-copyright-modal'); + $variables['copy_selector'] = Html::getUniqueId('copyright-content'); +} + /** * Implements hook_preprocess_HOOK(). */ diff --git a/modules/oe_bootstrap_theme_helper/tests/src/Kernel/TwigExtensionTest.php b/modules/oe_bootstrap_theme_helper/tests/src/Kernel/TwigExtensionTest.php index 2e637d025..c65370d14 100644 --- a/modules/oe_bootstrap_theme_helper/tests/src/Kernel/TwigExtensionTest.php +++ b/modules/oe_bootstrap_theme_helper/tests/src/Kernel/TwigExtensionTest.php @@ -9,8 +9,8 @@ use Drupal\Core\Render\RenderContext; use Drupal\Core\Template\Attribute; use Drupal\Core\Url; -use Drupal\oe_bootstrap_theme\ValueObject\ImageValueObject; use Drupal\Tests\oe_bootstrap_theme\Kernel\AbstractKernelTestBase; +use Drupal\oe_bootstrap_theme\ValueObject\ImageValueObject; use PHPUnit\Framework\ExpectationFailedException; use Symfony\Component\DomCrawler\Crawler; use Twig\Markup; diff --git a/modules/oe_bootstrap_theme_helper/tests/src/Unit/BootstrapTableFilterTest.php b/modules/oe_bootstrap_theme_helper/tests/src/Unit/BootstrapTableFilterTest.php index 05acfab3b..7d1858f66 100644 --- a/modules/oe_bootstrap_theme_helper/tests/src/Unit/BootstrapTableFilterTest.php +++ b/modules/oe_bootstrap_theme_helper/tests/src/Unit/BootstrapTableFilterTest.php @@ -4,8 +4,8 @@ namespace Drupal\Tests\oe_bootstrap_theme_helper\Unit; -use Drupal\oe_bootstrap_theme_helper\Plugin\Filter\BootstrapTable; use Drupal\Tests\UnitTestCase; +use Drupal\oe_bootstrap_theme_helper\Plugin\Filter\BootstrapTable; /** * Tests the bootstrap table filter. diff --git a/oe_bootstrap_theme.libraries.yml b/oe_bootstrap_theme.libraries.yml index a08b6b1a3..c42dfe4b1 100644 --- a/oe_bootstrap_theme.libraries.yml +++ b/oe_bootstrap_theme.libraries.yml @@ -21,3 +21,17 @@ drupal.progress: inpage_navigation: js: resources/js/inpage_navigation.js: {} + +copy_clipboard: + js: + resources/js/copy_clipboard.js: {} + dependencies: + - core/drupal + - core/once + +pattern.copyright_overlay: + css: + theme: + assets/css/copyright-overlay.style.min.css: {} + dependencies: + - oe_bootstrap_theme/copy_clipboard diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 22592d771..4fd1e0480 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -6,6 +6,8 @@ + + diff --git a/resources/js/copy_clipboard.js b/resources/js/copy_clipboard.js new file mode 100644 index 000000000..2a75f7144 --- /dev/null +++ b/resources/js/copy_clipboard.js @@ -0,0 +1,32 @@ +/** + * @file + * Attaches behaviors to copy text to clipboard for elements with data-copy-target attribute. + */ +(function (Drupal) { + + const handleClick = function (event) { + const element = event.currentTarget; + const targetSelector = element.getAttribute('data-copy-target'); + const targetElement = document.querySelector(targetSelector); + + if (targetElement) { + const copyText = targetElement.innerText || targetElement.value; + navigator.clipboard.writeText(copyText); + } + }; + + Drupal.behaviors.copyClipboard = { + attach: function (context) { + once('oebt-clipcopy', '[data-copy-target]', context).forEach(function (element) { + element.addEventListener('click', handleClick); + }); + }, + + detach: function (context, settings, trigger) { + once.remove('oebt-clipcopy', '[data-copy-target]', context).forEach(function (element) { + element.removeEventListener('click', handleClick); + }); + } + }; + +})(Drupal); diff --git a/resources/sass/copyright_overlay.scss b/resources/sass/copyright_overlay.scss new file mode 100644 index 000000000..3c03a34e3 --- /dev/null +++ b/resources/sass/copyright_overlay.scss @@ -0,0 +1,20 @@ +.copyright-overlay { + background-color: rgba(var(--bs-white-rgb),var(--bs-bg-opacity)); + color: var(--bs-gray-700); + padding: 0 0.5rem; + display: inline-flex; + .copyright-preview { + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 7rem; + font-size: 0.875rem; + } + .copyright-trigger { + font-size: 0.875rem; + } + .copyright-header { + font-size: 1.25rem; + } +} diff --git a/runner.yml.dist b/runner.yml.dist index a38b056db..e6c93b650 100644 --- a/runner.yml.dist +++ b/runner.yml.dist @@ -30,6 +30,11 @@ drupal: - "vendor" - "${drupal.root}" +selenium: + host: "http://selenium" + port: "4444" + browser: "chrome" + commands: drupal:site-setup: - { task: "run", command: "drupal:symlink-project" } diff --git a/templates/patterns/copyright_overlay/copyright_overlay.ui_patterns.yml b/templates/patterns/copyright_overlay/copyright_overlay.ui_patterns.yml new file mode 100644 index 000000000..c0ff1a00e --- /dev/null +++ b/templates/patterns/copyright_overlay/copyright_overlay.ui_patterns.yml @@ -0,0 +1,30 @@ +copyright_overlay: + label: 'Copyright overlay' + description: 'A component to show copyright information.' + fields: + trigger_label: + type: text + label: 'Trigger label' + description: 'The label of the modal trigger. Optional.' + preview: 'View more' + modal_title: + type: text + label: 'Modal title' + description: 'The title of the modal header. Optional.' + preview: 'Copyright' + content: + type: render + label: 'Content' + description: 'Copyright overlay content.' + preview: '© Copyright ipsum amet John Doe on Doe Images.' + settings: + trigger_attributes: + type: attributes + label: Trigger attributes + description: Extra attributes for the modal trigger + allow_token: true + attributes: + type: attributes + label: Copyright overlay attributes + description: Extra attributes for the copyright overlay + allow_token: true diff --git a/templates/patterns/copyright_overlay/pattern-copyright-overlay--preview.html.twig b/templates/patterns/copyright_overlay/pattern-copyright-overlay--preview.html.twig new file mode 100644 index 000000000..e5f63d69b --- /dev/null +++ b/templates/patterns/copyright_overlay/pattern-copyright-overlay--preview.html.twig @@ -0,0 +1,14 @@ +{# +/** + * @file + * Template override for preview mode of "Copyright overlay" pattern. + */ +#} + +{{ pattern('copyright_overlay', { + content: content, +}) }} + +{{ pattern('copyright_overlay', { + content: '© Second copyright element. Suspendisse vel mauris vitae ipsum blandit condimentum ut eget quam.', +}) }} diff --git a/templates/patterns/copyright_overlay/pattern-copyright-overlay.html.twig b/templates/patterns/copyright_overlay/pattern-copyright-overlay.html.twig new file mode 100644 index 000000000..36f1b35d9 --- /dev/null +++ b/templates/patterns/copyright_overlay/pattern-copyright-overlay.html.twig @@ -0,0 +1,51 @@ +{# +/** + * @file + * Copyright overlay pattern. + */ +#} +{{ attach_library('oe_bootstrap_theme/pattern.copyright_overlay') }} + +{% set _footer %} + {% include '@oe-bcl/button' with { + label: 'Copy Copyright'|t, + attributes: create_attribute({ + 'data-bs-dismiss': 'modal', + 'data-copy-target': '#' ~ copy_selector + }) + } only %} +{% endset %} + +{% set _content %} + + {{ content }} + +{% endset %} + + + + {{ content }} + + + {{ pattern('link', { + label: trigger_label|default('View more'|t), + path: '#', + attributes: trigger_attributes + .addClass('copyright-trigger') + .setAttribute('data-bs-toggle', 'modal') + .setAttribute('data-bs-target', '#' ~ modal_id), + }) }} + + {{ pattern('modal', { + 'title': modal_title|default('Copyright'|t), + 'show_close_button': true, + 'body': _content, + 'footer': _footer, + 'attributes': create_attribute({ + id: modal_id + }) + }) }} + diff --git a/tests/src/Functional/CheckboxSwitchWidgetTest.php b/tests/src/Functional/CheckboxSwitchWidgetTest.php index f953de74b..afe9cc597 100644 --- a/tests/src/Functional/CheckboxSwitchWidgetTest.php +++ b/tests/src/Functional/CheckboxSwitchWidgetTest.php @@ -4,10 +4,10 @@ namespace Drupal\Tests\oe_bootstrap_theme\Functional; +use Drupal\Tests\BrowserTestBase; use Drupal\entity_test\Entity\EntityTest; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; -use Drupal\Tests\BrowserTestBase; /** * Tests the checkbox switch third party settings on compatible widgets. diff --git a/tests/src/Functional/ThemeSettingsTest.php b/tests/src/Functional/ThemeSettingsTest.php index 27d8e8123..2946f92e9 100644 --- a/tests/src/Functional/ThemeSettingsTest.php +++ b/tests/src/Functional/ThemeSettingsTest.php @@ -4,8 +4,8 @@ namespace Drupal\Tests\oe_bootstrap_theme\Functional; -use Drupal\oe_bootstrap_theme\BackwardCompatibility; use Drupal\Tests\BrowserTestBase; +use Drupal\oe_bootstrap_theme\BackwardCompatibility; /** * Tests the theme settings. diff --git a/tests/src/FunctionalJavascript/CopyClipboardTest.php b/tests/src/FunctionalJavascript/CopyClipboardTest.php new file mode 100644 index 000000000..4a12dab7e --- /dev/null +++ b/tests/src/FunctionalJavascript/CopyClipboardTest.php @@ -0,0 +1,77 @@ +drupalLogin($this->drupalCreateUser([], NULL, TRUE)); + + $this->drupalGet('/patterns/copyright_overlay'); + + // Mock the clipboard API. + $this->getSession()->executeScript(<<getSession()->getPage()->findAll('css', '.copyright-overlay'); + + $this->assertCopyrightCopiedToClipboard($elements['0'], '© Copyright ipsum amet John Doe on Doe Images.'); + $this->assertCopyrightCopiedToClipboard($elements['1'], '© Second copyright element. Suspendisse vel mauris vitae ipsum blandit condimentum ut eget quam.'); + } + + /** + * Asserts that the text in the element corresponds to the clipboard. + * + * @param \Behat\Mink\Element\NodeElement $element + * The copyright element. + * @param string $expected + * The expected result. + */ + protected function assertCopyrightCopiedToClipboard(NodeElement $element, string $expected): void { + $assert_session = $this->assertSession(); + + $assert_session->elementExists('css', '.copyright-trigger', $element)->click(); + $modal = $assert_session->waitForElementVisible('css', '.modal.show'); + + $assert_session->elementExists('css', '[data-copy-target]', $modal)->click(); + + $actual = $this->getSession()->evaluateScript('return window.copiedText;'); + + $this->assertEquals($expected, $actual); + } + +} diff --git a/tests/src/Kernel/MarkupRenderingTest.php b/tests/src/Kernel/MarkupRenderingTest.php index f49e5356a..42c74cebf 100644 --- a/tests/src/Kernel/MarkupRenderingTest.php +++ b/tests/src/Kernel/MarkupRenderingTest.php @@ -59,6 +59,7 @@ class MarkupRenderingTest extends KernelTestBase implements FormInterface { 'card_layout', 'carousel', 'columns', + 'copyright_overlay', 'content_banner', 'date_block', 'description_list', diff --git a/tests/src/Kernel/ValueObject/ImageValueObjectTest.php b/tests/src/Kernel/ValueObject/ImageValueObjectTest.php index a322b0cae..3869801e6 100644 --- a/tests/src/Kernel/ValueObject/ImageValueObjectTest.php +++ b/tests/src/Kernel/ValueObject/ImageValueObjectTest.php @@ -4,13 +4,13 @@ namespace Drupal\Tests\oe_bootstrap_theme\Kernel\ValueObject; +use Drupal\Tests\oe_bootstrap_theme\Kernel\AbstractKernelTestBase; use Drupal\entity_test\Entity\EntityTest; use Drupal\field\Entity\FieldConfig; use Drupal\field\Entity\FieldStorageConfig; use Drupal\file\Entity\File; use Drupal\image\Entity\ImageStyle; use Drupal\oe_bootstrap_theme\ValueObject\ImageValueObject; -use Drupal\Tests\oe_bootstrap_theme\Kernel\AbstractKernelTestBase; /** * Test image value object with image field type. Extracted from oe_theme. diff --git a/tests/src/Kernel/fixtures/markup_rendering_patterns/copyright_overlay.yml b/tests/src/Kernel/fixtures/markup_rendering_patterns/copyright_overlay.yml new file mode 100644 index 000000000..7dc48da1d --- /dev/null +++ b/tests/src/Kernel/fixtures/markup_rendering_patterns/copyright_overlay.yml @@ -0,0 +1,78 @@ +copyright_overlay: + render: + '#type': pattern + '#id': copyright_overlay + '#fields': + content: 'Default ipsum dolor sit amet.' + assert: + contains: + '.modal-header h5': 'Copyright' + 'span.copyright-preview': 'Default ipsum dolor sit amet' + 'div.copyright-content': 'Default ipsum dolor sit amet' + '.copyright-trigger[data-bs-target="#bcl-copyright-modal"]': 'View more' + count: + '.copyright-trigger[data-bs-target="#bcl-copyright-modal"]': 1 + 'div#bcl-copyright-modal': 1 + '.copyright-overlay': 1 + 'button[data-copy-target="#copyright-content"]': 1 +copyright_overlay_with_custom_trigger_label: + render: + '#type': pattern + '#id': copyright_overlay + '#fields': + content: 'Lorem Ipsum dolor sit amet' + trigger_label: 'Show more' + assert: + contains: + '.modal-header h5': 'Copyright' + 'span.copyright-preview': 'Lorem Ipsum dolor sit amet' + 'div.copyright-content': 'Lorem Ipsum dolor sit amet' + '.copyright-trigger[data-bs-target="#bcl-copyright-modal"]': 'Show more' + count: + '.copyright-trigger[data-bs-target="#bcl-copyright-modal"]': 1 + 'div#bcl-copyright-modal': 1 + '.copyright-overlay': 1 + 'button[data-copy-target="#copyright-content"]': 1 +copyright_overlay_with_custom_modal_title: + render: + '#type': pattern + '#id': copyright_overlay + '#fields': + content: 'Aenean eget accumsan neque, eu accumsan eros.' + modal_title: 'New title' + assert: + contains: + '.modal-header h5': 'New title' + 'span.copyright-preview': 'Aenean eget accumsan neque, eu accumsan eros.' + 'div.copyright-content': 'Aenean eget accumsan neque, eu accumsan eros.' + '.copyright-trigger[data-bs-target="#bcl-copyright-modal"]': 'View more' + count: + '.copyright-trigger[data-bs-target="#bcl-copyright-modal"]': 1 + 'div#bcl-copyright-modal': 1 + '.copyright-overlay': 1 + 'button[data-copy-target="#copyright-content"]': 1 +copyright_overlay_with_attributes: + render: + '#type': pattern + '#id': copyright_overlay + '#fields': + content: 'Pellentesque ullamcorper erat sed arcu dapibus.' + '#settings': + attributes: 'class="test-class" id="test-id" data-test="attr-value"' + trigger_attributes: 'class="my-trigger-class" data-test="trigger-attr-value"' + assert: + contains: + '.modal-header h5': 'Copyright' + 'span.copyright-preview': 'Pellentesque ullamcorper erat sed arcu dapibus.' + 'div.copyright-content': 'Pellentesque ullamcorper erat sed arcu dapibus.' + '.copyright-trigger[data-bs-target="#bcl-copyright-modal"]': 'View more' + count: + '.copyright-trigger[data-bs-target="#bcl-copyright-modal"]': 1 + 'div#bcl-copyright-modal': 1 + '.copyright-overlay': 1 + 'button[data-copy-target="#copyright-content"]': 1 + '.copyright-overlay.test-class': 1 + '.copyright-overlay#test-id': 1 + '.copyright-overlay[data-test="attr-value"]': 1 + '.copyright-trigger.my-trigger-class': 1 + '.copyright-trigger[data-test="trigger-attr-value"]': 1