diff --git a/composer.json b/composer.json index 13cec5bfc..597033285 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "openeuropa/oe_corporate_countries": "~2.0", "openeuropa/oe_media": "~1.12", "openeuropa/oe_multilingual": "~1.8", - "openeuropa/oe_paragraphs": "~1.10", + "openeuropa/oe_paragraphs": "dev-EWPP-1862", "openeuropa/oe_search": "1.x-dev", "openeuropa/oe_webtools": "~1.12", "openeuropa/oe_contact_forms": "~1.1", diff --git a/oe_theme.theme b/oe_theme.theme index 80834ad29..817c2e6e5 100644 --- a/oe_theme.theme +++ b/oe_theme.theme @@ -10,11 +10,13 @@ declare(strict_types = 1); use Drupal\block\BlockInterface; use Drupal\Component\Utility\Html; use Drupal\Core\Cache\CacheableMetadata; +use Drupal\Core\Cache\CacheableDependencyInterface; use Drupal\Core\Entity\Entity\EntityViewDisplay; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element; use Drupal\Core\Template\Attribute; use Drupal\Core\Url; +use Drupal\Core\Session\AccountInterface; use Drupal\media\MediaInterface; use Drupal\media\Plugin\media\Source\Image; use Drupal\media\Plugin\media\Source\OEmbed; @@ -1711,3 +1713,153 @@ function oe_theme_preprocess_oe_theme_helper_site_navigation(&$variables) { unset($variables['site_name']); } } + +/** + * Process common fields of illustration list paragraphs. + * + * @param array $variables + * Render array. + */ +function _oe_theme_preprocess_paragraph_oe_illustration_list(array &$variables) { + $paragraph = $variables['paragraph']; + if (!$paragraph->get('field_oe_title')->isEmpty()) { + $variables['title'] = $paragraph->get('field_oe_title')->value; + } + $variables['zebra'] = (bool) $paragraph->get('field_oe_illustration_alternate')->value; + $variables['column'] = $paragraph->get('field_oe_illustration_columns')->value; + + // First part of the variant. Second part should be added in the preprocess + // of the specific paragraph. + $variant_value = $paragraph->get('oe_paragraphs_variant')->first()->value; + $variables['variant'] = $variant_value === 'oe_illustration_vertical' ? 'vertical' : 'horizontal'; +} + +/** + * Validate and prepare media entity to be processed in theme preprocesses. + * + * @param mixed $media + * MediaInterface entity will be processed only. + * @param \Drupal\Core\Session\AccountInterface $user + * Active user. + * @param \Drupal\Core\Cache\CacheableDependencyInterface $cacheability + * Object to collect cacheable dependencies. + * @param string $language_id + * Language of the Media to return. + * + * @return \Drupal\media\MediaInterface|null + * Media entity or null if media isn't accessible. + */ +function _oe_theme_prepare_media($media, AccountInterface $user, CacheableDependencyInterface $cacheability, string $language_id): ?MediaInterface { + if (!$media instanceof MediaInterface) { + // The media entity is not available. + return NULL; + } + $cacheability->addCacheableDependency($media); + + // Run access checks on the media entity. + $access = $media->access('view', $user, TRUE); + $cacheability->addCacheableDependency($access); + if (!$access->isAllowed()) { + // Media isn't accessible. + return NULL; + } + // Retrieve the correct media translation. + return \Drupal::service('entity.repository')->getTranslationFromContext($media, $language_id); +} + +/** + * Implements hook_preprocess_paragraph(). + */ +function oe_theme_preprocess_paragraph__oe_illustration_list_flags(array &$variables): void { + _oe_theme_preprocess_paragraph_oe_illustration_list($variables); + + $variables['variant'] .= '_icons'; + $variables['items'] = []; + + $paragraph = $variables['paragraph']; + $ratio = $paragraph->get('field_oe_illustration_ratio')->first()->value; + + /** @var \Drupal\paragraphs\Entity\Paragraph $sub_paragraph */ + foreach ($paragraph->get('field_oe_paragraphs')->referencedEntities() as $sub_paragraph) { + // Get sub-paragraph translation. + $sub_paragraph = \Drupal::service('entity.repository') + ->getTranslationFromContext($sub_paragraph, $paragraph->language()->getId()); + $description_list = $sub_paragraph->get('field_oe_illustration_text'); + $icon = $sub_paragraph->get('field_oe_flag')->value; + $icon .= $ratio === 'square' ? '-square' : ''; + $variables['items'][] = [ + 'title' => $description_list->term, + 'description' => $description_list->description, + 'icon' => $icon, + ]; + } +} + +/** + * Implements hook_preprocess_paragraph(). + */ +function oe_theme_preprocess_paragraph__oe_illustration_list_icons(array &$variables): void { + _oe_theme_preprocess_paragraph_oe_illustration_list($variables); + + $variables['variant'] .= '_icons'; + $variables['items'] = []; + + $paragraph = $variables['paragraph']; + /** @var \Drupal\paragraphs\Entity\Paragraph $sub_paragraph */ + foreach ($paragraph->get('field_oe_paragraphs')->referencedEntities() as $sub_paragraph) { + // Get sub-paragraph translation. + $sub_paragraph = \Drupal::service('entity.repository') + ->getTranslationFromContext($sub_paragraph, $paragraph->language()->getId()); + $description_list = $sub_paragraph->get('field_oe_illustration_text'); + $variables['items'][] = [ + 'title' => $description_list->term, + 'description' => $description_list->description, + 'icon' => $sub_paragraph->get('field_oe_icon')->value, + ]; + } +} + +/** + * Implements hook_preprocess_paragraph(). + */ +function oe_theme_preprocess_paragraph__oe_illustration_list_images(array &$variables): void { + _oe_theme_preprocess_paragraph_oe_illustration_list($variables); + + $paragraph = $variables['paragraph']; + $ratio = $paragraph->get('field_oe_illustration_ratio')->first()->value; + $variables['variant'] = $variables['variant'] . '_images_' . $ratio; + + $variables['items'] = []; + + $entity_repository = \Drupal::service('entity.repository'); + $cacheability = CacheableMetadata::createFromRenderArray($variables); + /** @var \Drupal\paragraphs\Entity\Paragraph $sub_paragraph */ + foreach ($paragraph->get('field_oe_paragraphs')->referencedEntities() as $sub_paragraph) { + // Get sub-paragraph translation. + $language_id = $paragraph->language()->getId(); + $sub_paragraph = $entity_repository->getTranslationFromContext($sub_paragraph, $language_id); + // Get the image from attached media. + $image = []; + $media = _oe_theme_prepare_media($sub_paragraph->get('field_oe_media')->entity, $variables['user'], $cacheability, $language_id); + if ($media) { + $source = $media->getSource(); + if ($source instanceof Image && ($file_entity = $media->get('oe_media_image')->entity)) { + $uri = $file_entity->getFileUri(); + $cacheability->addCacheableDependency($file_entity); + $values = [ + 'src' => file_create_url($uri), + 'alt' => $source->getMetadata($media, 'thumbnail_alt_value') ?? $media->label(), + ]; + $image = ImageValueObject::fromArray($values); + } + } + + $description_list = $sub_paragraph->get('field_oe_illustration_text'); + $variables['items'][] = [ + 'title' => $description_list->term, + 'description' => $description_list->description, + 'image' => $image, + ]; + } + $cacheability->applyTo($variables); +} diff --git a/templates/paragraphs/paragraph--oe-illustration-list-flags.html.twig b/templates/paragraphs/paragraph--oe-illustration-list-flags.html.twig new file mode 100644 index 000000000..75ed620ac --- /dev/null +++ b/templates/paragraphs/paragraph--oe-illustration-list-flags.html.twig @@ -0,0 +1,20 @@ +{# +/** + * @file + * Theme override to display the 'Illustration list with flags' type paragraph. + * + * @see ./modules/contrib/paragraphs/templates/paragraph.html.twig + */ +#} +{% if title is not empty %} +

+ {{ title }} +

+{% endif %} + +{{ pattern('list_with_illustration', { + 'variant': variant, + 'column': column, + 'zebra': zebra, + 'items': items, +}) }} diff --git a/templates/paragraphs/paragraph--oe-illustration-list-icons.html.twig b/templates/paragraphs/paragraph--oe-illustration-list-icons.html.twig new file mode 100644 index 000000000..00c739927 --- /dev/null +++ b/templates/paragraphs/paragraph--oe-illustration-list-icons.html.twig @@ -0,0 +1,20 @@ +{# +/** + * @file + * Theme override to display the 'Illustration list with icons' type paragraph. + * + * @see ./modules/contrib/paragraphs/templates/paragraph.html.twig + */ +#} +{% if title is not empty %} +

+ {{ title }} +

+{% endif %} + +{{ pattern('list_with_illustration', { + 'variant': variant, + 'column': column, + 'zebra': zebra, + 'items': items, +}) }} diff --git a/templates/paragraphs/paragraph--oe-illustration-list-images.html.twig b/templates/paragraphs/paragraph--oe-illustration-list-images.html.twig new file mode 100644 index 000000000..7bd049e79 --- /dev/null +++ b/templates/paragraphs/paragraph--oe-illustration-list-images.html.twig @@ -0,0 +1,20 @@ +{# +/** + * @file + * Theme override to display the 'Illustration list with images' type paragraph. + * + * @see ./modules/contrib/paragraphs/templates/paragraph.html.twig + */ +#} +{% if title is not empty %} +

+ {{ title }} +

+{% endif %} + +{{ pattern('list_with_illustration', { + 'variant': variant, + 'column': column, + 'zebra': zebra, + 'items': items, +}) }} diff --git a/tests/src/Kernel/Paragraphs/IllustrationListsParagraphsTest.php b/tests/src/Kernel/Paragraphs/IllustrationListsParagraphsTest.php new file mode 100644 index 000000000..41e0875b0 --- /dev/null +++ b/tests/src/Kernel/Paragraphs/IllustrationListsParagraphsTest.php @@ -0,0 +1,438 @@ +installEntitySchema('media'); + + module_load_include('install', 'media'); + media_install(); + $this->container->get('module_handler')->loadInclude('oe_paragraphs_media_field_storage', 'install'); + oe_paragraphs_media_field_storage_install(FALSE); + + $this->installConfig([ + 'media', + 'options', + 'oe_media', + 'oe_paragraphs_illustrations_lists', + ]); + } + + /** + * Tests the rendering of the "Illustration list with flags" paragraph. + */ + public function testIllustrationListFlagsRendering(): void { + // Create multiple paragraphs to be referenced in the illustration list. + $items = []; + + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_flag', + 'field_oe_illustration_text' => [ + [ + 'term' => 'Term 1', + 'description' => 'Description 1', + ], + ], + 'field_oe_flag' => 'austria', + ]); + $paragraph->save(); + $items[] = $paragraph; + + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_flag', + 'field_oe_illustration_text' => [ + [ + 'term' => 'Term 2', + ], + ], + 'field_oe_flag' => 'belgium', + ]); + $paragraph->save(); + $items[] = $paragraph; + + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_flag', + 'field_oe_illustration_text' => [ + [ + 'description' => 'Description 3', + ], + ], + 'field_oe_flag' => 'france', + ]); + $paragraph->save(); + $items[] = $paragraph; + + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_flag', + 'field_oe_illustration_text' => [], + 'field_oe_flag' => 'finland', + ]); + $paragraph->save(); + $items[] = $paragraph; + + $list_paragraph = Paragraph::create([ + 'type' => 'oe_illustration_list_flags', + 'oe_paragraphs_variant' => 'default', + 'field_oe_title' => 'Illustration with flags test', + 'field_oe_paragraphs' => $items, + 'field_oe_illustration_columns' => 2, + 'field_oe_illustration_ratio' => 'landscape', + ]); + $html = $this->renderParagraph($list_paragraph); + + // Assert paragraph header. + $crawler = new Crawler($html); + $heading = $crawler->filter('h2.ecl-u-type-heading-2'); + $this->assertCount(1, $heading); + $this->assertEquals('Illustration with flags test', trim($heading->text())); + + // Assert rendered items. + $expected_values = [ + 'column' => 2, + 'zebra' => FALSE, + 'items' => [ + [ + 'title' => 'Term 1', + 'description' => 'Description 1', + 'icon' => 'austria', + ], [ + 'title' => 'Term 2', + 'icon' => 'belgium', + ], [ + 'description' => 'Description 3', + 'icon' => 'france', + ], [ + 'icon' => 'finland', + ], + ], + ]; + $assert = new ListWithIllustrationAssert(); + $assert->assertPattern($expected_values, $html); + $assert->assertVariant('horizontal_icons', $html); + + // Assert number of columns and ratio. + $list_paragraph->set('field_oe_illustration_columns', 4); + $list_paragraph->set('field_oe_illustration_ratio', 'square')->save(); + $html = $this->renderParagraph($list_paragraph); + $expected_values = [ + 'column' => 4, + 'zebra' => FALSE, + 'items' => [ + [ + 'title' => 'Term 1', + 'description' => 'Description 1', + 'icon' => 'austria-square', + ], [ + 'title' => 'Term 2', + 'icon' => 'belgium-square', + ], [ + 'description' => 'Description 3', + 'icon' => 'france-square', + ], [ + 'icon' => 'finland-square', + ], + ], + ]; + $assert->assertPattern($expected_values, $html); + + // Assert vertical variant. + $list_paragraph->set('oe_paragraphs_variant', 'oe_illustration_vertical')->save(); + $html = $this->renderParagraph($list_paragraph); + unset($expected_values['column']); + $assert->assertPattern($expected_values, $html); + $assert->assertVariant('vertical_icons', $html); + + // Assert vertical variant with zebra. + $list_paragraph->set('field_oe_illustration_alternate', TRUE)->save(); + $html = $this->renderParagraph($list_paragraph); + $expected_values['zebra'] = TRUE; + $assert->assertPattern($expected_values, $html); + $assert->assertVariant('vertical_icons', $html); + } + + /** + * Tests the rendering of the "Illustration list with icons" paragraph. + */ + public function testIllustrationListIconsRendering(): void { + $items = []; + + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_icon', + 'field_oe_illustration_text' => [ + [ + 'term' => 'Term 1', + 'description' => 'Description 1', + ], + ], + 'field_oe_icon' => 'data', + ]); + $paragraph->save(); + $items[] = $paragraph; + + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_icon', + 'field_oe_illustration_text' => [ + [ + 'term' => 'Term 2', + ], + ], + 'field_oe_icon' => 'facebook', + ]); + $paragraph->save(); + $items[] = $paragraph; + + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_icon', + 'field_oe_illustration_text' => [ + [ + 'description' => 'Description 3', + ], + ], + 'field_oe_icon' => 'global', + ]); + $paragraph->save(); + $items[] = $paragraph; + + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_icon', + 'field_oe_illustration_text' => [], + 'field_oe_icon' => 'package', + ]); + $paragraph->save(); + $items[] = $paragraph; + + $list_paragraph = Paragraph::create([ + 'type' => 'oe_illustration_list_icons', + 'oe_paragraphs_variant' => 'default', + 'field_oe_title' => 'Illustration with icons test', + 'field_oe_paragraphs' => $items, + 'field_oe_illustration_columns' => 2, + ]); + $html = $this->renderParagraph($list_paragraph); + + // Assert paragraph header. + $crawler = new Crawler($html); + $heading = $crawler->filter('h2.ecl-u-type-heading-2'); + $this->assertCount(1, $heading); + $this->assertEquals('Illustration with icons test', trim($heading->text())); + + // Assert rendered items. + $expected_values = [ + 'column' => 2, + 'zebra' => FALSE, + 'items' => [ + [ + 'title' => 'Term 1', + 'description' => 'Description 1', + 'icon' => 'data', + ], [ + 'title' => 'Term 2', + 'icon' => 'facebook', + ], [ + 'description' => 'Description 3', + 'icon' => 'global', + ], [ + 'icon' => 'package', + ], + ], + ]; + $assert = new ListWithIllustrationAssert(); + $assert->assertPattern($expected_values, $html); + $assert->assertVariant('horizontal_icons', $html); + + // Assert number of columns. + $list_paragraph->set('field_oe_illustration_columns', 3)->save(); + $html = $this->renderParagraph($list_paragraph); + $expected_values['column'] = 3; + $assert->assertPattern($expected_values, $html); + + // Assert vertical variant. + $list_paragraph->set('oe_paragraphs_variant', 'oe_illustration_vertical')->save(); + $html = $this->renderParagraph($list_paragraph); + unset($expected_values['column']); + $assert->assertPattern($expected_values, $html); + $assert->assertVariant('vertical_icons', $html); + + // Assert vertical variant with zebra. + $list_paragraph->set('field_oe_illustration_alternate', TRUE)->save(); + $html = $this->renderParagraph($list_paragraph); + $expected_values['zebra'] = TRUE; + $assert->assertPattern($expected_values, $html); + $assert->assertVariant('vertical_icons', $html); + } + + /** + * Tests the rendering of the "Illustration list with icons" paragraph. + */ + public function testIllustrationListImagesRendering(): void { + // Create media image. + $file = file_save_data(file_get_contents(drupal_get_path('theme', 'oe_theme') . '/tests/fixtures/example_1.jpeg'), 'public://example_1.jpeg'); + $file->setPermanent(); + $file->save(); + + $media = Media::create([ + 'bundle' => 'image', + 'name' => 'test image', + 'oe_media_image' => [ + 'target_id' => $file->id(), + 'alt' => 'Alt', + ], + ]); + $media->save(); + + // Create Illustration list with images paragraph. + $items = []; + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_image', + 'field_oe_illustration_text' => [ + [ + 'term' => 'Term 1', + 'description' => 'Description 1', + ], + ], + 'field_oe_media' => [$media], + ]); + $paragraph->save(); + $items[] = $paragraph; + + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_image', + 'field_oe_illustration_text' => [ + [ + 'term' => 'Term 2', + ], + ], + 'field_oe_media' => [$media], + ]); + $paragraph->save(); + $items[] = $paragraph; + + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_image', + 'field_oe_illustration_text' => [ + [ + 'description' => 'Description 3', + ], + ], + 'field_oe_media' => [$media], + ]); + $paragraph->save(); + $items[] = $paragraph; + + $paragraph = Paragraph::create([ + 'type' => 'oe_illustration_item_image', + 'field_oe_illustration_text' => [], + 'field_oe_media' => [$media], + ]); + $paragraph->save(); + $items[] = $paragraph; + + $list_paragraph = Paragraph::create([ + 'type' => 'oe_illustration_list_images', + 'oe_paragraphs_variant' => 'default', + 'field_oe_title' => 'Illustration with images test', + 'field_oe_paragraphs' => $items, + 'field_oe_illustration_columns' => 2, + 'field_oe_illustration_ratio' => 'landscape', + ]); + $html = $this->renderParagraph($list_paragraph); + + // Assert paragraph header. + $crawler = new Crawler($html); + $heading = $crawler->filter('h2.ecl-u-type-heading-2'); + $this->assertCount(1, $heading); + $this->assertEquals('Illustration with images test', trim($heading->text())); + + // Assert rendered items. + $expected_values = [ + 'column' => 2, + 'zebra' => FALSE, + 'items' => [ + [ + 'title' => 'Term 1', + 'description' => 'Description 1', + 'image' => [ + 'src' => 'example_1.jpeg', + 'alt' => 'Alt', + ], + ], [ + 'title' => 'Term 2', + 'image' => [ + 'src' => 'example_1.jpeg', + 'alt' => 'Alt', + ], + ], [ + 'description' => 'Description 3', + 'image' => [ + 'src' => 'example_1.jpeg', + 'alt' => 'Alt', + ], + ], [ + 'image' => [ + 'src' => 'example_1.jpeg', + 'alt' => 'Alt', + ], + ], + ], + ]; + $assert = new ListWithIllustrationAssert(); + $assert->assertPattern($expected_values, $html); + $assert->assertVariant('horizontal_images_landscape', $html); + + // Assert number of columns and ratio. + $list_paragraph->set('field_oe_illustration_columns', 3); + $list_paragraph->set('field_oe_illustration_ratio', 'square')->save(); + $html = $this->renderParagraph($list_paragraph); + $expected_values['column'] = 3; + $assert->assertPattern($expected_values, $html); + $assert->assertVariant('horizontal_images_square', $html); + + // Assert vertical variant. + $list_paragraph->set('oe_paragraphs_variant', 'oe_illustration_vertical')->save(); + $html = $this->renderParagraph($list_paragraph); + unset($expected_values['column']); + $assert->assertPattern($expected_values, $html); + $assert->assertVariant('vertical_images_square', $html); + + // Assert vertical variant with zebra and ratio. + $list_paragraph->set('field_oe_illustration_alternate', TRUE); + $list_paragraph->set('field_oe_illustration_ratio', 'landscape')->save(); + $html = $this->renderParagraph($list_paragraph); + $expected_values['zebra'] = TRUE; + $assert->assertPattern($expected_values, $html); + $assert->assertVariant('vertical_images_landscape', $html); + } + +}