diff --git a/patches/npm/@openeuropa+bcl-theme-default+0.24.0.patch b/patches/npm/@openeuropa+bcl-theme-default+0.24.0.patch deleted file mode 100644 index 4cf671681..000000000 --- a/patches/npm/@openeuropa+bcl-theme-default+0.24.0.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/@openeuropa/bcl-theme-default/templates/bcl-pagination/pagination.html.twig b/node_modules/@openeuropa/bcl-theme-default/templates/bcl-pagination/pagination.html.twig -index d33f381..ed77687 100644 ---- a/node_modules/@openeuropa/bcl-theme-default/templates/bcl-pagination/pagination.html.twig -+++ b/node_modules/@openeuropa/bcl-theme-default/templates/bcl-pagination/pagination.html.twig -@@ -57,7 +57,7 @@ - {% set _alignment = alignment|default('') %} - {% set _aria_label = aria_label|default('') %} - {% set _enable_prev_next_icon = enable_prev_next_icon|default(false) %} --{% set _enable_first_last_icon = enable_first_last_icon|default(true) %} -+{% set _enable_first_last_icon = enable_first_last_icon ?? true %} - {% set _items = items|default([]) %} - {% set _first = first|default({}) %} - {% set _next = next|default({}) %} diff --git a/patches/npm/@openeuropa+bcl-theme-default+0.24.1.patch b/patches/npm/@openeuropa+bcl-theme-default+0.24.1.patch new file mode 100644 index 000000000..21e545131 --- /dev/null +++ b/patches/npm/@openeuropa+bcl-theme-default+0.24.1.patch @@ -0,0 +1,26 @@ +diff --git a/node_modules/@openeuropa/bcl-theme-default/templates/bcl-carousel/carousel.html.twig b/node_modules/@openeuropa/bcl-theme-default/templates/bcl-carousel/carousel.html.twig +index 6e08be9..ddc41ad 100644 +--- a/node_modules/@openeuropa/bcl-theme-default/templates/bcl-carousel/carousel.html.twig ++++ b/node_modules/@openeuropa/bcl-theme-default/templates/bcl-carousel/carousel.html.twig +@@ -111,7 +111,7 @@ + {% if _item.image is defined and _item.image is not empty %} + {{ _item.image }} + {% endif %} +- {% if (_item.caption is defined and _item.caption is not empty) or (_item.link is defined and _item.link is not empty) %} ++ {% if (_item.caption is defined and _item.caption is not empty) or (_item.link is defined and _item.link is not empty) or (_item.caption_title is defined and _item.caption_title is not empty) %} + {% set _caption_classes = 'carousel-caption' %} + {% if _item.caption is not empty and _item.link is empty %} + {% set _caption_classes = _caption_classes ~ ' d-none d-md-block' %} +diff --git a/node_modules/@openeuropa/bcl-theme-default/templates/bcl-pagination/pagination.html.twig b/node_modules/@openeuropa/bcl-theme-default/templates/bcl-pagination/pagination.html.twig +index d33f381..ed77687 100644 +--- a/node_modules/@openeuropa/bcl-theme-default/templates/bcl-pagination/pagination.html.twig ++++ b/node_modules/@openeuropa/bcl-theme-default/templates/bcl-pagination/pagination.html.twig +@@ -57,7 +57,7 @@ + {% set _alignment = alignment|default('') %} + {% set _aria_label = aria_label|default('') %} + {% set _enable_prev_next_icon = enable_prev_next_icon|default(false) %} +-{% set _enable_first_last_icon = enable_first_last_icon|default(true) %} ++{% set _enable_first_last_icon = enable_first_last_icon ?? true %} + {% set _items = items|default([]) %} + {% set _first = first|default({}) %} + {% set _next = next|default({}) %} diff --git a/templates/patterns/carousel/carousel.ui_patterns.yml b/templates/patterns/carousel/carousel.ui_patterns.yml index 2fecbeeed..e615388a8 100644 --- a/templates/patterns/carousel/carousel.ui_patterns.yml +++ b/templates/patterns/carousel/carousel.ui_patterns.yml @@ -42,13 +42,21 @@ carousel: label: 'Slides' description: 'Each slide is an image, a caption and interval of slide movement.' preview: - - caption: 'First slide label' + - caption: 'First slide caption' + caption_title: 'First slide title' interval: 0 image: src: 'https://placeimg.com/1000/500/arch' alt: 'alt image 1' - - caption: 'Second slide label' + link: + label: First link + path: https://link.one + - caption: 'Second slide caption' + caption_title: 'Second slide title' interval: 0 image: src: 'https://placeimg.com/1000/500/people' alt: 'alt image 1' + link: + label: Second link + path: https://link.two diff --git a/templates/patterns/carousel/pattern-carousel.html.twig b/templates/patterns/carousel/pattern-carousel.html.twig index e96bf4df2..ef86cb6b0 100644 --- a/templates/patterns/carousel/pattern-carousel.html.twig +++ b/templates/patterns/carousel/pattern-carousel.html.twig @@ -12,22 +12,33 @@ {% endblock %} {% endset %} {% set _item = slide|merge({ - 'image': _image + 'image': _image, }) %} + {% if slide.link %} + {% set _item = _item|merge({ + 'link': slide.link|merge({ + 'icon_position': 'before', + 'icon': { + 'name': 'link', + 'path': bcl_icon_path, + }, + }), + }) %} + {% endif %} {% set _slides = _slides|merge([_item]) %} {% endfor %} {% include '@oe-bcl/carousel' with { 'id': carousel_id, - 'autoplay': autoplay, - 'autoinit': autoinit, + 'autoplay': autoplay ?? true, + 'autoinit': autoinit ?? true, 'dark': dark_mode, 'active_item': active_item, 'items': _slides, - 'fade': fade, - 'with_controls': show_controls, + 'fade': fade ?? false, + 'with_controls': show_controls ?? true, 'prev_label': 'Previous'|t, 'next_label': 'Next'|t, - 'with_indicators': show_indicators, - 'disable_touch': disable_touch, + 'with_indicators': show_indicators ?? false, + 'disable_touch': disable_touch ?? false, 'attributes': attributes, } only %} diff --git a/tests/src/Kernel/fixtures/markup_rendering_patterns/carousel.yml b/tests/src/Kernel/fixtures/markup_rendering_patterns/carousel.yml index 3742d7f22..cb475f49c 100644 --- a/tests/src/Kernel/fixtures/markup_rendering_patterns/carousel.yml +++ b/tests/src/Kernel/fixtures/markup_rendering_patterns/carousel.yml @@ -1,4 +1,4 @@ -carousel_with_fade_show_controls_with_indicator_without_disable_touch_without_autoinit: +carousel_with_all_settings: render: '#type': pattern '#id': carousel @@ -8,90 +8,244 @@ carousel_with_fade_show_controls_with_indicator_without_disable_touch_without_au show_controls: true show_indicators: true autoplay: true - autoinit: false - disable_touch: false - active_item: 2 + autoinit: true + disable_touch: true slides: - - caption: 'first slide label' + - caption: 'First slide caption' + caption_title: 'First slide title' interval: 0 image: src: 'https://images.site.example1' alt: 'alt image 1' - - caption: 'second slide label' + assertions: + pattern: + class: Drupal\Tests\oe_bootstrap_theme\PatternAssertion\CarouselPatternAssert + expected: + settings: + fade: true + show_controls: true + show_indicators: true + autoplay: true + autoinit: true + disable_touch: true + items: + - image: + src: 'https://images.site.example1' + alt: 'alt image 1' + caption_title: 'First slide title' + caption: 'First slide caption' +carousel_with_all_settings_false: + render: + '#type': pattern + '#id': carousel + '#fields': + settings: + fade: false + show_controls: false + show_indicators: false + autoplay: false + autoinit: false + disable_touch: false + slides: + - caption: 'First slide caption' + caption_title: 'First slide title' interval: 0 image: - src: 'https://images.site.example2' - alt: 'alt image 2' + src: 'https://images.site.example1' + alt: 'alt image 1' assertions: - count: - 'div.carousel.slide.carousel-fade[data-drupal-selector="carousel"]': 1 - 'div.carousel-dark': 0 - 'div.carousel-indicators button[type="button"]': 2 - 'div.carousel-indicators button[aria-label="Slide 1"]': 1 - 'div.carousel-indicators button[aria-label="Slide 2"]': 1 - 'div.carousel-indicators button[data-bs-slide-to="0"]': 1 - 'div.carousel-indicators button[data-bs-slide-to="1"]': 1 - 'div.carousel-indicators button.active': 1 - 'div.carousel-inner': 1 - 'div.carousel-item': 2 - 'div.carousel-item.active': 1 - 'button.carousel-control-prev[data-bs-slide="prev"]': 1 - 'button.carousel-control-next[data-bs-slide="next"]': 1 - 'img.d-block.w-100': 2 - 'div.carousel-item:nth-child(1) img[src="https://images.site.example1"]': 1 - 'div.carousel-item:nth-child(1) img[alt="alt image 1"]': 1 - 'div.carousel-item:nth-child(2) img[src="https://images.site.example2"]': 1 - 'div.carousel-item:nth-child(2) img[alt="alt image 2"]': 1 - 'div[data-bs-touch="false"]': 0 - 'div[data-bs-ride="carousel"]': 0 - equals: - 'div.carousel-item:nth-child(1) div.carousel-caption': 'first slide label' - 'div.carousel-item:nth-child(2) div.carousel-caption': 'second slide label' - 'button.carousel-control-prev > span.visually-hidden': 'Previous' - 'button.carousel-control-next > span.visually-hidden': 'Next' -carousel_without_fade_without_controls_without_indicators_with_disable_touch_with_autoinit_dark: + pattern: + class: Drupal\Tests\oe_bootstrap_theme\PatternAssertion\CarouselPatternAssert + expected: + settings: + fade: false + show_controls: false + show_indicators: false + autoplay: false + autoinit: false + disable_touch: false + items: + - image: + src: 'https://images.site.example1' + alt: 'alt image 1' + caption_title: 'First slide title' + caption: 'First slide caption' +carousel_with_settings_combination: render: '#type': pattern '#id': carousel '#fields': settings: fade: false - show_controls: false + show_controls: true show_indicators: false autoplay: true - autoinit: true + autoinit: false disable_touch: true - dark_mode: true - active_item: 1 slides: - - caption: 'first slide label' + - caption: 'First slide caption' + caption_title: 'First slide title' interval: 0 image: src: 'https://images.site.example1' alt: 'alt image 1' - - caption: 'second slide label' + assertions: + pattern: + class: Drupal\Tests\oe_bootstrap_theme\PatternAssertion\CarouselPatternAssert + expected: + settings: + fade: false + show_controls: true + show_indicators: false + autoplay: true + autoinit: false + disable_touch: true + items: + - image: + src: 'https://images.site.example1' + alt: 'alt image 1' + caption_title: 'First slide title' + caption: 'First slide caption' +carousel_without_settings_assert_defaults: + render: + '#type': pattern + '#id': carousel + '#fields': + slides: + - caption: 'First slide caption' + caption_title: 'First slide title' interval: 0 image: + src: 'https://images.site.example1' + alt: 'alt image 1' + assertions: + pattern: + class: Drupal\Tests\oe_bootstrap_theme\PatternAssertion\CarouselPatternAssert + expected: + settings: + fade: false + show_controls: true + show_indicators: false + autoplay: true + autoinit: true + disable_touch: false + items: + - image: + src: 'https://images.site.example1' + alt: 'alt image 1' + caption_title: 'First slide title' + caption: 'First slide caption' +carousel_just_images: + render: + '#type': pattern + '#id': carousel + '#fields': + slides: + - image: + src: 'https://images.site.example1' + alt: 'alt image 1' + - image: src: 'https://images.site.example2' alt: 'alt image 2' assertions: - count: - 'div.carousel.slide.carousel[data-drupal-selector="carousel"]': 1 - 'div.carousel-dark': 1 - 'div.fade': 0 - 'div.carousel-indicators': 0 - 'div.carousel-inner': 1 - 'div.carousel-item': 2 - 'div.carousel-item.active': 1 - 'button.carousel-control-prev[data-bs-slide="prev"]': 0 - 'button.carousel-control-next[data-bs-slide="next"]': 0 - 'img.d-block.w-100': 2 - 'div.carousel-item:nth-child(1) img[src="https://images.site.example1"]': 1 - 'div.carousel-item:nth-child(1) img[alt="alt image 1"]': 1 - 'div.carousel-item:nth-child(2) img[src="https://images.site.example2"]': 1 - 'div.carousel-item:nth-child(2) img[alt="alt image 2"]': 1 - 'div[data-bs-touch="false"]': 1 - 'div[data-bs-ride="carousel"]': 1 - equals: - 'div.carousel-item:nth-child(1) div.carousel-caption': 'first slide label' - 'div.carousel-item:nth-child(2) div.carousel-caption': 'second slide label' + pattern: + class: Drupal\Tests\oe_bootstrap_theme\PatternAssertion\CarouselPatternAssert + expected: + settings: + fade: false + show_controls: true + show_indicators: false + autoplay: true + autoinit: true + disable_touch: false + items: + - image: + src: 'https://images.site.example1' + alt: 'alt image 1' + - image: + src: 'https://images.site.example2' + alt: 'alt image 2' +carousel_all_item_values: + render: + '#type': pattern + '#id': carousel + '#fields': + slides: + - image: + src: 'https://images.site.example1' + alt: 'alt image 1' + caption_title: 'First slide title' + caption: 'First slide caption' + caption_classes: 'caption_classes-1' + interval: 1 + link: + label: First link + path: https://link.one + - image: + src: 'https://images.site.example2' + alt: 'alt image 2' + caption_title: 'Second slide title' + caption: 'Second slide caption' + caption_classes: 'caption_classes-2' + interval: 2 + link: + label: Second link + path: https://link.two + assertions: + pattern: + class: Drupal\Tests\oe_bootstrap_theme\PatternAssertion\CarouselPatternAssert + expected: + settings: + fade: false + show_controls: true + show_indicators: false + autoplay: true + autoinit: true + disable_touch: false + items: + - image: + src: 'https://images.site.example1' + alt: 'alt image 1' + caption_title: 'First slide title' + caption: 'First slide caption' + caption_classes: 'caption_classes-1' + interval: 1 + link: + label: First link + path: https://link.one + - image: + src: 'https://images.site.example2' + alt: 'alt image 2' + caption_title: 'Second slide title' + caption: 'Second slide caption' + caption_classes: 'caption_classes-2' + interval: 2 + link: + label: Second link + path: https://link.two +carousel_title_present_no_caption_no_link: + render: + '#type': pattern + '#id': carousel + '#fields': + slides: + - image: + src: 'https://images.site.example1' + alt: 'alt image 1' + caption_title: 'Slide title' + assertions: + pattern: + class: Drupal\Tests\oe_bootstrap_theme\PatternAssertion\CarouselPatternAssert + expected: + settings: + fade: false + show_controls: true + show_indicators: false + autoplay: true + autoinit: true + disable_touch: false + items: + - image: + src: 'https://images.site.example1' + alt: 'alt image 1' + caption_title: 'Slide title' diff --git a/tests/src/PatternAssertion/BasePatternAssert.php b/tests/src/PatternAssertion/BasePatternAssert.php index 712d60145..3f400275c 100644 --- a/tests/src/PatternAssertion/BasePatternAssert.php +++ b/tests/src/PatternAssertion/BasePatternAssert.php @@ -92,12 +92,14 @@ public function assertVariant(string $variant, string $html): void { * The DomCrawler where to check the element. */ protected function assertElementAttribute($expected, string $selector, string $attribute, Crawler $crawler): void { + $this->assertElementExists($selector, $crawler); + $element = $crawler->filter($selector); + if (is_null($expected)) { - $this->assertElementNotExists($selector, $crawler); + self::assertNull($element->attr($attribute)); return; } - $this->assertElementExists($selector, $crawler); - $element = $crawler->filter($selector); + self::assertEquals($expected, $element->attr($attribute)); } @@ -125,6 +127,30 @@ protected function assertElementText($expected, string $selector, Crawler $crawl )); } + /** + * Asserts text is contained in a particular element. + * + * @param string|null $expected + * The expected value. + * @param string $selector + * The CSS selector to find the element. + * @param \Symfony\Component\DomCrawler\Crawler $crawler + * The DomCrawler where to check the element. + */ + protected function assertElementTextContains(?string $expected, string $selector, Crawler $crawler): void { + if (is_null($expected)) { + $this->assertElementNotExists($selector, $crawler); + return; + } + $this->assertElementExists($selector, $crawler); + $element = $crawler->filter($selector); + $actual = trim($element->text()); + self::assertStringContainsString($expected, $actual, \sprintf( + 'Expected text "%s" is not present in "%s" found in the selector "%s".', + $expected, $actual, $selector + )); + } + /** * Asserts the rendered html of a particular element. * diff --git a/tests/src/PatternAssertion/CarouselPatternAssert.php b/tests/src/PatternAssertion/CarouselPatternAssert.php new file mode 100644 index 000000000..9da362dd3 --- /dev/null +++ b/tests/src/PatternAssertion/CarouselPatternAssert.php @@ -0,0 +1,114 @@ + [ + [$this, 'assertItems'], + ], + 'settings' => [ + [$this, 'assertSettings'], + ], + ]; + } + + /** + * {@inheritdoc} + */ + protected function assertBaseElements(string $html, string $variant): void { + $crawler = new Crawler($html); + + $this->assertElementExists('.carousel .carousel-inner', $crawler); + } + + /** + * Asserts the carousel pattern settings. + * + * @param array[] $expected + * The expected settings. + * @param \Symfony\Component\DomCrawler\Crawler $crawler + * The crawler. + */ + protected function assertSettings(array $expected, Crawler $crawler): void { + if (!empty($expected['fade'])) { + $this->assertElementExists('.carousel-fade', $crawler); + } + else { + $this->assertElementNotExists('.carousel-fade', $crawler); + } + + $expected_previous = $expected['show_controls'] ? 'Previous' : NULL; + $expected_next = $expected['show_controls'] ? 'Next' : NULL; + $this->assertElementText($expected_previous, '.carousel-control-prev', $crawler); + $this->assertElementText($expected_next, '.carousel-control-next', $crawler); + + if (!empty($expected['show_indicators'])) { + $this->assertElementExists('.carousel .carousel-indicators button', $crawler); + } + else { + $this->assertElementNotExists('.carousel .carousel-indicators', $crawler); + } + + $expected_autoplay = !empty($expected['autoplay']) ? NULL : 'false'; + $this->assertElementAttribute($expected_autoplay, '.carousel', 'data-bs-interval', $crawler); + + $expected_autoinit = !empty($expected['autoinit']) ? 'carousel' : NULL; + $this->assertElementAttribute($expected_autoinit, '.carousel', 'data-bs-ride', $crawler); + + $expected_disable_touch = !empty($expected['disable_touch']) ? 'false' : NULL; + $this->assertElementAttribute($expected_disable_touch, '.carousel', 'data-bs-touch', $crawler); + } + + /** + * Asserts the carousel pattern items. + * + * @param array[] $expected + * The expected item values. + * @param \Symfony\Component\DomCrawler\Crawler $crawler + * The crawler. + */ + protected function assertItems(array $expected, Crawler $crawler): void { + $items = $crawler->filter('.carousel .carousel-inner .carousel-item'); + self::assertSameSize($expected, $items); + + foreach ($expected as $index => $expected_item) { + $item = $items->eq($index); + + $this->assertImage($expected_item['image'], 'img', $item); + $this->assertElementText($expected_item['caption_title'] ?? NULL, '.carousel-caption h5', $item); + + if (isset($expected_item['caption'])) { + $this->assertElementTextContains($expected_item['caption'], '.carousel-caption', $item); + } + + if (isset($expected_item['caption_classes'])) { + $this->assertElementExists('.' . $expected_item['caption_classes'], $item); + } + + $this->assertElementAttribute($expected_item['interval'] ?? 0, '.carousel-item', 'data-bs-interval', $item); + + if (isset($expected_item['link'])) { + $this->assertElementText($expected_item['link']['label'], '.carousel-caption a', $item); + $this->assertElementAttribute($expected_item['link']['path'], '.carousel-caption a', 'href', $item); + $this->assertElementExists('.carousel-caption a svg', $item); + } + else { + $this->assertElementNotExists('.carousel-caption a', $item); + } + } + } + +}