diff --git a/addons/website/static/src/snippets/s_image_gallery/000.xml b/addons/website/static/src/snippets/s_image_gallery/000.xml
index 4d1c55e8874..e78d7b1c5d3 100644
--- a/addons/website/static/src/snippets/s_image_gallery/000.xml
+++ b/addons/website/static/src/snippets/s_image_gallery/000.xml
@@ -14,7 +14,7 @@
-
![]()
+
diff --git a/addons/website/static/src/snippets/s_image_gallery/options.js b/addons/website/static/src/snippets/s_image_gallery/options.js
index e19f7c9aaa6..a52591554e5 100644
--- a/addons/website/static/src/snippets/s_image_gallery/options.js
+++ b/addons/website/static/src/snippets/s_image_gallery/options.js
@@ -43,14 +43,14 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
* @private
*/
_grid() {
- const imgs = this._getItemsGallery();
+ const imgs = this._getImgHolderEls();
var $row = $('', {class: 'row s_nb_column_fixed'});
var columns = this._getColumns();
var colClass = 'col-lg-' + (12 / columns);
var $container = this._replaceContent($row);
imgs.forEach((img, index) => {
- const $img = $(img.cloneNode());
+ const $img = $(img.cloneNode(true));
var $col = $('', {class: colClass});
$col.append($img).appendTo($row);
if ((index + 1) % columns === 0) {
@@ -67,7 +67,7 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
* @returns {Promise}
*/
_masonry() {
- const imgs = this._getItemsGallery();
+ const imgs = this._getImgHolderEls();
var columns = this._getColumns();
var colClass = 'col-lg-' + (12 / columns);
var cols = [];
@@ -100,7 +100,7 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
// Only on Chrome: appended images are sometimes invisible
// and not correctly loaded from cache, we use a clone of the
// image to force the loading.
- smallestColEl.append(imgEl.cloneNode());
+ smallestColEl.append(imgEl.cloneNode(true));
await wUtils.onceAllImagesLoaded(this.$target);
}
resolve();
@@ -138,15 +138,16 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
_nomode() {
var $row = $('', {class: 'row s_nb_column_fixed'});
const imgs = this._getItemsGallery();
+ const imgHolderEls = this._getImgHolderEls();
this._replaceContent($row);
- imgs.forEach((img) => {
+ imgs.forEach((img, index) => {
var wrapClass = 'col-lg-3';
if (img.width >= img.height * 2 || img.width > 600) {
wrapClass = 'col-lg-6';
}
- var $wrap = $('', {class: wrapClass}).append(img);
+ var $wrap = $('', {class: wrapClass}).append(imgHolderEls[index]);
$row.append($wrap);
});
},
@@ -157,10 +158,14 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
*/
_slideshow() {
const imageEls = this._getItemsGallery();
+ const imgHolderEls = this._getImgHolderEls();
const images = Array.from(imageEls).map((img) => ({
// Use getAttribute to get the attribute value otherwise .src
// returns the absolute url.
src: img.getAttribute('src'),
+ // TODO: remove me in master. This is not needed anymore as the
+ // images of the rendered `website.gallery.slideshow` are replaced
+ // by the elements of `imgHolderEls`.
alt: img.getAttribute('alt'),
}));
var currentInterval = this.$target.find('.carousel:first').attr('data-bs-interval');
@@ -170,10 +175,23 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
title: "",
interval: currentInterval || 0,
id: 'slideshow_' + new Date().getTime(),
+ // TODO: in master, remove `attrClass` and `attStyle` from `params`.
+ // This is not needed anymore as the images of the rendered
+ // `website.gallery.slideshow` are replaced by the elements of
+ // `imgHolderEls`.
attrClass: imageEls.length > 0 ? imageEls[0].className : '',
attrStyle: imageEls.length > 0 ? imageEls[0].style.cssText : '',
},
$slideshow = $(renderToElement('website.gallery.slideshow', params));
+ const imgSlideshowEls = $slideshow[0].querySelectorAll("img[data-o-main-image]");
+ imgSlideshowEls.forEach((imgSlideshowEl, index) => {
+ // Replace the template image by the original one. This is needed in
+ // order to keep the characteristics of the image such as the
+ // filter, the width, the quality, the link on which the users are
+ // redirected once they click on the image etc...
+ imgSlideshowEl.after(imgHolderEls[index]);
+ imgSlideshowEl.remove();
+ });
this._replaceContent($slideshow);
this.$("img").toArray().forEach((img, index) => {
$(img).attr({contenteditable: true, 'data-index': index});
@@ -192,6 +210,17 @@ options.registry.GalleryLayout = options.registry.CarouselHandler.extend({
imgs.sort((a, b) => this._getIndex(a) - this._getIndex(b));
return imgs;
},
+ /**
+ * Returns the images, or the images holder if this holder is an anchor,
+ * sorted by index.
+ *
+ * @private
+ * @returns {Array.}
+ */
+ _getImgHolderEls: function () {
+ const imgEls = this._getItemsGallery();
+ return imgEls.map(imgEl => imgEl.closest("a") || imgEl);
+ },
/**
* Returns the index associated to a given image.
*
diff --git a/addons/website/static/tests/tours/snippet_image_gallery.js b/addons/website/static/tests/tours/snippet_image_gallery.js
index 677b891587c..31999540fce 100644
--- a/addons/website/static/tests/tours/snippet_image_gallery.js
+++ b/addons/website/static/tests/tours/snippet_image_gallery.js
@@ -17,7 +17,7 @@ wTourUtils.registerWebsitePreviewTour('snippet_image_gallery', {
{
content: 'Check that the modal has opened properly',
trigger: 'iframe .s_gallery_lightbox img',
- run: () => {}, // This is a check.
+ isCheck: true,
},
]);
@@ -52,12 +52,42 @@ wTourUtils.registerWebsitePreviewTour("snippet_image_gallery_remove", {
}, {
content: "Check that the Snippet Editor of the clicked image has been loaded",
trigger: "we-customizeblock-options span:contains('Image'):not(:contains('Image Gallery'))",
- run: () => null,
+ isCheck: true,
}, {
content: "Click on Remove Block",
trigger: ".o_we_customize_panel we-title:has(span:contains('Image Gallery')) we-button[title='Remove Block']",
}, {
content: "Check that the Image Gallery snippet has been removed",
trigger: "iframe #wrap:not(:has(.s_image_gallery))",
- run: () => null,
+ isCheck: true,
+}]);
+
+wTourUtils.registerWebsitePreviewTour("snippet_image_gallery_reorder", {
+ test: true,
+ url: "/",
+ edition: true,
+}, () => [
+ wTourUtils.dragNDrop({
+ id: "s_image_gallery",
+ name: "Image Gallery",
+ }),
+{
+ content: "Click on the first image of the snippet",
+ trigger: "iframe .s_image_gallery .carousel-item.active img",
+},
+ wTourUtils.changeOption('ImageTools', 'we-select:contains("Filter") we-toggler'),
+ wTourUtils.changeOption('ImageTools', '[data-gl-filter="blur"]'),
+{
+ content: "Check that the image has the correct filter",
+ trigger: ".snippet-option-ImageTools we-select:contains('Filter') we-toggler:contains('Blur')",
+ isCheck: true,
+}, {
+ content: "Click on move to next",
+ trigger: ".snippet-option-GalleryElement we-button[data-position='next']",
+}, {
+ content: "Check that the moved image still has the correct filter",
+ // FIXME somehow checking what the editor panel shows here is not reliable
+ // unless you add a big delay before checking.
+ trigger: "iframe .s_image_gallery .carousel-item.active img[data-index='1'][data-gl-filter='blur']",
+ isCheck: true,
}]);
diff --git a/addons/website/static/tests/tours/snippet_images_wall.js b/addons/website/static/tests/tours/snippet_images_wall.js
index 2f4885b1747..44605257c4e 100644
--- a/addons/website/static/tests/tours/snippet_images_wall.js
+++ b/addons/website/static/tests/tours/snippet_images_wall.js
@@ -35,6 +35,7 @@ const reselectSignImageSteps = [
...preventRaceConditionSteps,
{
content: "Select footer",
+ extra_trigger: "iframe .s_image_gallery .o_masonry_col:nth-child(3):has(img[data-index='5'])",
trigger: "iframe footer",
}, selectSignImageStep];
@@ -52,11 +53,18 @@ wTourUtils.registerWebsitePreviewTour("snippet_images_wall", {
}),
selectSignImageStep,
{
+ content: "Click on add a link",
+ trigger: ".snippet-option-ReplaceMedia we-button[data-set-link]",
+}, {
+ content: "Change the link of the image",
+ trigger: ".snippet-option-ReplaceMedia [data-set-url] input",
+ run: "text /contactus",
+}, {
content: "Click on move to previous",
trigger: ".snippet-option-GalleryElement we-button[data-position='prev']",
}, {
content: "Check if sign is in second column",
- trigger: "iframe .s_image_gallery .o_masonry_col:nth-child(2):has(img[data-index='1'][data-original-src*='library_image_14'])",
+ trigger: "iframe .s_image_gallery .o_masonry_col:nth-child(2):has(a[href='/contactus'] img[data-index='1'][data-original-src*='library_image_14'])",
isCheck: true,
},
...reselectSignImageSteps,
diff --git a/addons/website/tests/test_snippets.py b/addons/website/tests/test_snippets.py
index 1c52b76b2f8..30ad0d656fa 100644
--- a/addons/website/tests/test_snippets.py
+++ b/addons/website/tests/test_snippets.py
@@ -112,3 +112,6 @@ class TestSnippets(HttpCase):
def test_drag_and_drop_on_non_editable(self):
self.start_tour(self.env['website'].get_client_action_url('/'), 'test_drag_and_drop_on_non_editable', login='admin')
+
+ def test_snippet_image_gallery_reorder(self):
+ self.start_tour(self.env['website'].get_client_action_url('/'), "snippet_image_gallery_reorder", login='admin')