Skip to content

Commit

Permalink
Merge branch 'next-10244/slider-images-drag-and-drop' into 'trunk'
Browse files Browse the repository at this point in the history
NEXT-10244 - Slider images drag and drop

See merge request shopware/6/product/platform!10330
  • Loading branch information
jleifeld committed Apr 25, 2023
2 parents 88e2f68 + 14dc985 commit 97604dd
Show file tree
Hide file tree
Showing 13 changed files with 364 additions and 6 deletions.
15 changes: 15 additions & 0 deletions changelog/_unreleased/2023-03-09-slider-images-drag-and-drop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
title: Slider Images Drag & Drop
issue: NEXT-10244
---
# Administration
* Added `moveItem` method in `/core/service/util.service.ts` to sort when drag and drop item.
* Changed in `app/asyncComponent/media/sw-media-list-selection-v2/index.js`
* Changed `mediaItems` computed to add position index to image.
* Added `onMediaItemDragSort` method to handle drag and drop item.
* Added `v-draggable` and `v-droppable` properties in `/app/asyncComponent/media/sw-media-list-selection-v2/sw-media-list-selection-v2.html.twig` to handle drag and drop item.
* Added `item-sort` event in `module/sw-cms/elements/image-gallery/config/sw-cms-el-config-image-gallery.html.twig` to handle drag and drop item event of Image gallery.
* Added `onItemSort` method in `/module/sw-cms/elements/image-gallery/config/index.js` to handle drag and drop item of Image gallery.
* Added `item-sort` event in `module/sw-cms/elements/image-slider/config/sw-cms-el-config-image-slider.html.twig` to handle drag and drop item event of Image Slider.
* Added `onItemSort` method in `/module/sw-cms/elements/image-slider/config/index.js` to handle drag and drop item of Image Slider.
* Changed `scss` in `/app/asyncComponent/media/sw-media-list-selection-item-v2/sw-media-list-selection-item-v2.scss` to keep the drag item ratio.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ $sw-media-list-selection-item-v2-placeholder-color: lighten($color-gray-300, 5%)
max-height: 100%;
max-width: 100%;
}

&::after {
content: "";
display: block;
padding-bottom: 100%;
}
}

&:hover {
Expand Down Expand Up @@ -67,4 +73,8 @@ $sw-media-list-selection-item-v2-placeholder-color: lighten($color-gray-300, 5%)
.sw-media-list-selection-item-v2__placeholder-icon {
color: $sw-media-list-selection-item-v2-placeholder-color;
}

&.is--drag-element {
position: absolute;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import template from './sw-media-list-selection-v2.html.twig';
import './sw-media-list-selection-v2.scss';

const { Mixin, Context } = Shopware;
const utils = Shopware.Utils;

/**
* @package content
Expand Down Expand Up @@ -65,7 +66,9 @@ export default {

const items = [...this.entityMediaItems];
items.splice(this.currentCount, 0, ...this.createPlaceholders(columnCount - this.currentCount));

items.forEach((item, index) => {
item.position = index;
});
return items;
},

Expand Down Expand Up @@ -149,6 +152,17 @@ export default {
this.entity.isLoading = false;
},

onMediaItemDragSort(dragData, dropData, validDrop) {
if (validDrop !== true || (dropData.position > this.currentCount) || (dragData.position > this.currentCount)) {
return;
}
this.$emit('item-sort', dragData, dropData);
},

onDeboundDragDrop: utils.debounce(function debouncedDragDrop(dragData, dropData, validDrop) {
this.onMediaItemDragSort(dragData, dropData, validDrop);
}, 500),

removeItem(mediaItem, index) {
this.$emit('item-remove', mediaItem, index);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
<sw-media-list-selection-item-v2
v-for="(mediaItem, i) in mediaItems"
:key="i"
v-draggable="{ dragGroup: 'media-items', data: mediaItem, onDragEnter: onDeboundDragDrop }"
v-droppable="{ dragGroup: 'media-items', data: mediaItem }"
:item="mediaItem"
@item-remove="removeItem(mediaItem, i)"
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* @package content
*/
import { shallowMount } from '@vue/test-utils';
import SwMediaListSelectionV2 from 'src/app/asyncComponent/media/sw-media-list-selection-v2';
import swMediaListSelectionItemV2 from 'src/app/asyncComponent/media/sw-media-list-selection-item-v2';

Shopware.Component.register('sw-media-list-selection-v2', SwMediaListSelectionV2);
Shopware.Component.register('sw-media-list-selection-item-v2', swMediaListSelectionItemV2);

const entityMediaItems = [
{
id: '1',
url: 'http://shopware.com/image1.jpg',
position: 3,

},
{
id: '2',
url: 'http://shopware.com/image2.jpg',
position: 1,
},
{
id: '3',
url: 'http://shopware.com/image3.jpg',
position: 2,
},
];
async function createWrapper() {
return shallowMount(await Shopware.Component.build('sw-media-list-selection-v2'), {

provide: {
mediaService: {},
},
stubs: {
'sw-upload-listener': true,
'sw-media-upload-v2': true,
'sw-media-list-selection-item-v2': await Shopware.Component.build('sw-media-list-selection-item-v2'),
'sw-icon': true,
'sw-media-preview-v2': true,
'sw-context-button': true,
'sw-context-menu-item': true,
},
propsData: {
entity: {},
entityMediaItems: entityMediaItems,
},
});
}

describe('components/media/sw-media-list-selection-v2', () => {
it('should be a Vue.JS component', async () => {
const wrapper = await createWrapper();

expect(wrapper.vm).toBeTruthy();
});

it('should set the position property for each item by index in computed', async () => {
const wrapper = await createWrapper();
const mediaItems = wrapper.vm.mediaItems;

mediaItems.forEach((item, index) => {
expect(item.position).toBe(index);
});
});

it('should emit item-sort event when drag and drop item valid', async () => {
const wrapper = await createWrapper();

await wrapper.vm.onMediaItemDragSort({ id: 2, position: 1 }, { id: 1, position: 2 }, true);
await wrapper.vm.$nextTick();

expect(wrapper.emitted()['item-sort']).toBeTruthy();
expect(wrapper.emitted()['item-sort'][0]).toEqual([{ id: 2, position: 1 }, { id: 1, position: 2 }]);
});

it('should not emit item-sort event when drag and drop item valid', async () => {
const wrapper = await createWrapper();

await wrapper.vm.onMediaItemDragSort();
await wrapper.vm.$nextTick();

expect(wrapper.emitted('item-sort')).not.toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import UtilService from './util.service';

describe('src/core/service/util.service.spec.js', () => {
describe('moveItem', () => {
let entity = new MutationObserver(() => {});

beforeEach(() => {
entity = [
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
];
});

it('should move the item correctly', () => {
const oldIndex = 1;
const newIndex = 3;

UtilService.moveItem(entity, oldIndex, newIndex);

expect(entity).toEqual([
{ id: 1 },
{ id: 3 },
{ id: 4 },
{ id: 2 },
]);
});

it('should not move the item if the new index is null', () => {
const oldIndex = 2;
const newIndex = null;

UtilService.moveItem(entity, oldIndex, newIndex);

expect(entity).toEqual([
{ id: 1 },
{ id: 2 },
{ id: 4 },
{ id: 3 },
]);
});

it('should not move the item if the old index is out of bounds', () => {
const oldIndex = -1;
const newIndex = 2;

UtilService.moveItem(entity, oldIndex, newIndex);

expect(entity).toEqual([
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
]);
});

it('should not move the item if the new index is the same as the old index', () => {
const oldIndex = 2;
const newIndex = 2;

UtilService.moveItem(entity, oldIndex, newIndex);

expect(entity).toEqual([
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
]);
});

it('should not move the item if it does not exist', () => {
const oldIndex = 10;
const newIndex = 2;

UtilService.moveItem(entity, oldIndex, newIndex);

expect(entity).toEqual([
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
]);
});

it('should not move the item if entity does not exist', () => {
entity = [null, 1, null];
const oldIndex = 0;
const newIndex = 1;

UtilService.moveItem(entity, oldIndex, newIndex);

expect(entity).toEqual([null, 1, null]);
});
});
});

Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ export default {
fileReader,
sort,
array,
moveItem,
};

/**
Expand All @@ -155,3 +156,33 @@ function createId(): string {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-member-access
return uuidv4().replace(/-/g, '');
}

// eslint-disable-next-line sw-deprecation-rules/private-feature-declarations
export function moveItem(
entity: MutationObserver[],
oldIndex: number,
newIndex: number,
) {
if (newIndex === null) {
newIndex = entity.length;
}

if (oldIndex < 0 || oldIndex >= entity.length || newIndex === oldIndex) {
return;
}

const movedItem = entity.find((_, index) => index === oldIndex);
if (!movedItem) {
return;
}

const remainingItems = entity.filter((_, index) => index !== oldIndex);

const orderedItems = [
...remainingItems.slice(0, newIndex),
movedItem,
...remainingItems.slice(newIndex),
];

entity.splice(0, entity.length, ...orderedItems);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import template from './sw-cms-el-config-image-gallery.html.twig';
import './sw-cms-el-config-image-gallery.scss';

const { Mixin } = Shopware;
const { cloneDeep } = Shopware.Utils.object;
const { moveItem, object: { cloneDeep } } = Shopware.Utils;
const Criteria = Shopware.Data.Criteria;

/**
Expand Down Expand Up @@ -248,6 +248,14 @@ export default {
}
},

onItemSort(dragData, dropData) {
moveItem(this.mediaItems, dragData.position, dropData.position);
moveItem(this.element.config.sliderItems.value, dragData.position, dropData.position);

this.updateMediaDataValue();
this.emitUpdateEl();
},

onChangeMinHeight(value) {
this.element.config.minHeight.value = value === null ? '' : value;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
@upload-finish="onImageUpload"
@item-remove="onItemRemove"
@open-sidebar="onOpenMediaModal"
@item-sort="onItemSort"
/>
{% endblock %}

Expand Down
Loading

0 comments on commit 97604dd

Please sign in to comment.