Skip to content
Permalink
Browse files

fix(reorder-group): add ability to reorder items inside shadow (#17747)

- adds ability to reorder items from within a shadow dom component
- adds interactive test

fixes #17746
  • Loading branch information...
adamlacombe authored and brandyscarney committed Mar 26, 2019
1 parent a8a48a4 commit 352797e93295a228e3033bcb370e253b641efac8
@@ -64,7 +64,7 @@ export class ReorderGroup implements ComponentInterface {
}

this.gesture = (await import('../../utils/gesture')).createGesture({
el: this.doc.body,
el: this.el,
queue: this.queue,
gestureName: 'reorder',
gesturePriority: 110,
@@ -0,0 +1,60 @@
import * as pd from '@stencil/core/dist/testing/puppeteer/puppeteer-declarations';
import { newE2EPage } from '@stencil/core/testing';

import { getElementProperty, queryDeep } from '../../../../utils/test/utils';
import { moveReorderItem } from '../test.utils';

test('reorder: interactive', async () => {
const page = await newE2EPage({
url: '/src/components/reorder-group/test/interactive?ionic:_testing=true'
});

const compares = [];
compares.push(await page.compareScreenshot('reorder: interactive before move'));

const items = await page.$$('ion-reorder');
const getItemId = await getElementProperty(items[0], 'id');
expect(getItemId).toEqual('item-0');

await moveItem(getItemId, page, 'down', 1);

const itemsAfterFirstMove = await page.$$('ion-reorder');
expect(await getElementProperty(itemsAfterFirstMove[0], 'id')).toEqual('item-1');

await moveItem(getItemId, page, 'up', 1);

const itemsAfterSecondMove = await page.$$('ion-reorder');
expect(await getElementProperty(itemsAfterSecondMove[0], 'id')).toEqual('item-0');

compares.push(await page.compareScreenshot('reorder: interactive after move; before shadow move'));

const shadowDomList = await queryDeep(page, 'test-reorder-list-shadow-dom', 'ion-list');

const itemsInShadowRoot = await shadowDomList.$$('ion-reorder');
const getShadowItemId = await getElementProperty(itemsInShadowRoot[0], 'id');
expect(getShadowItemId).toEqual('item-0');

await moveItem(getShadowItemId, page, 'down', 1, 'test-reorder-list-shadow-dom', 'ion-list');

const itemsInShadowRootAfterFirstMove = await shadowDomList.$$('ion-reorder');
expect(await getElementProperty(itemsInShadowRootAfterFirstMove[0], 'id')).toEqual('item-1');

await moveItem(getShadowItemId, page, 'up', 1, 'test-reorder-list-shadow-dom', 'ion-list');

const itemsInShadowRootAfterSecondMove = await shadowDomList.$$('ion-reorder');
expect(await getElementProperty(itemsInShadowRootAfterSecondMove[0], 'id')).toEqual('item-0');

compares.push(await page.compareScreenshot('reorder: interactive after shadow move'));

for (const compare of compares) {
expect(compare).toMatchScreenshot();
}
});

async function moveItem(id: string, page: pd.E2EPage, direction: 'up' | 'down' = 'up', numberOfSpaces = 1, ...parentSelectors: string[]) {
try {
await moveReorderItem(`#${id}`, page, direction, numberOfSpaces, ...parentSelectors);
} catch (err) {
throw err;
}
}
@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html dir="ltr">

<head>
<meta charset="UTF-8">
<title>Reorder - Interactive</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link href="../../../../../css/core.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script src="../../../../../dist/ionic.js"></script>
</head>

<body>
<ion-list><ion-reorder-group disabled="false"></ion-reorder-group></ion-list>

<script>
class TestReorderListShadowDom extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
wrapper.innerHTML = `<ion-list><ion-reorder-group disabled="false"></ion-reorder-group></ion-list>`;
shadow.appendChild(wrapper);
}
}
customElements.define('test-reorder-list-shadow-dom', TestReorderListShadowDom);
function addReorderItem(list) {
const reorderItem = document.createElement('ion-reorder');
reorderItem.innerHTML = `<ion-item>
<ion-label>
<h2>Item ${list.childElementCount}</h2>
</ion-label>
</ion-item>`;
reorderItem.id = `item-${list.childElementCount}`;
list.appendChild(reorderItem);
}
const testShadowDomEl = document.createElement('test-reorder-list-shadow-dom');
document.body.appendChild(testShadowDomEl);
let lists = Array.from(document.getElementsByTagName('ion-reorder-group'));
lists.push(testShadowDomEl.shadowRoot.querySelector('ion-reorder-group'));
for (var i = 0; i < lists.length; i++) {
lists[i].addEventListener('ionItemReorder', ({detail}) => detail.complete(true));
for (var j = 0; j < 3; j++) addReorderItem(lists[i]);
}
</script>
</body>
</html>
@@ -0,0 +1,22 @@
import * as pd from '@stencil/core/dist/testing/puppeteer/puppeteer-declarations';

import { dragElementBy, queryDeep } from '../../../utils/test/utils';

/**
* Moves a reorder item by simulating a drag event
*/
export async function moveReorderItem(id: string, page: pd.E2EPage, direction: 'up' | 'down' = 'up', numberOfSpaces = 1, ...parentSelectors: string[]) {
try {
const reorderItem = parentSelectors && parentSelectors.length > 0 ? await (await queryDeep(page, ...parentSelectors)).$(id) : await page.$(id);

if (!reorderItem) { throw new Error('Reorder Item is undefined'); }

const boundingBox = await reorderItem.boundingBox();
if (!boundingBox) { throw new Error('Reorder Item bounding box is undefined'); }

await dragElementBy(reorderItem, page, 0, (direction === 'up') ? -(boundingBox.height * numberOfSpaces) : (boundingBox.height * numberOfSpaces));

} catch (err) {
throw err;
}
}
@@ -1,3 +1,6 @@
import { E2EPage } from '@stencil/core/testing';
import { ElementHandle } from 'puppeteer';

export function generateE2EUrl(component: string, type: string, rtl = false): string {
let url = `/src/components/${component}/test/${type}?ionic:_testing=true`;
if (rtl) {
@@ -14,6 +17,16 @@ export function cleanScreenshotName(screenshotName: string): string {
.toLowerCase();
}

/**
* Gets the value of a property on an element
*/
export const getElementProperty = async (element: any, property: string): Promise<string> => {
const getProperty = await element.getProperty(property);
if (!getProperty) { return ''; }

return getProperty.jsonValue();
};

/**
* Listens for an event and fires a callback
* @param page - The Puppeteer `page` object
@@ -87,3 +100,22 @@ export const waitForFunctionTestContext = async (fn: any, params: any, interval
}, interval);
});
};

/**
* Pierce through shadow roots
* https://github.com/GoogleChrome/puppeteer/issues/858#issuecomment-359763824
*/
export async function queryDeep(page: E2EPage, ...selectors: string[]): Promise<ElementHandle> {
const shadowSelectorFn = (el: Element, selector: string): Element | null => (el && el.shadowRoot) && el.shadowRoot.querySelector(selector);

return new Promise(async resolve => {
const [ firstSelector, ...restSelectors ] = selectors;
let parentElement = await page.$(firstSelector);

for (const selector of restSelectors) {
parentElement = await page.evaluateHandle(shadowSelectorFn, parentElement, selector) as any;
}

if (parentElement) { resolve(parentElement); }
});
}

0 comments on commit 352797e

Please sign in to comment.
You can’t perform that action at this time.