test(item-sliding): migrate tests to Playwright (#26092)
@@ -1,19 +0,0 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
test('item-sliding: basic', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/item-sliding/test/basic?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
|
||||
test('item-sliding:rtl: basic', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/item-sliding/test/basic?ionic:_testing=true&rtl=true',
|
||||
});
|
||||
|
||||
const compare = await page.compareScreenshot();
|
||||
expect(compare).toMatchScreenshot();
|
||||
});
|
||||
@@ -19,33 +19,17 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Item Sliding - Basic</ion-title>
|
||||
<ion-buttons slot="secondary">
|
||||
<ion-button onclick="changeDynamicText()">Dynamic</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-buttons slot="primary">
|
||||
<ion-button onclick="reload()">Reload</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-refresher id="refresher">
|
||||
<ion-refresher-content
|
||||
pulling-text="Pull to refresh..."
|
||||
refreshing-spinner="bubbles"
|
||||
refreshing-text="Refreshing..."
|
||||
>
|
||||
</ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<div class="ion-padding">
|
||||
<ion-button expand="block" onclick="toggleSliding()">Toggle sliding</ion-button>
|
||||
<ion-button expand="block" onclick="toggleDynamicOptions()">Toggle Dynamic Options</ion-button>
|
||||
<ion-button expand="block" onclick="closeOpened()">Close Opened Items</ion-button>
|
||||
<ion-button expand="block" onclick="openItem('start')">Open Item Start</ion-button>
|
||||
<ion-button expand="block" onclick="openItem('end')">Open Item End</ion-button>
|
||||
<ion-button expand="block" onclick="openItemOneSide()">Open Item with only one side</ion-button>
|
||||
<ion-button expand="block" onclick="setDynaicItem()">Swap dynamic item</ion-button>
|
||||
<ion-button expand="block" id="openItemStart" onclick="openItem('start')">Open Item Start</ion-button>
|
||||
<ion-button expand="block" id="openItemEnd" onclick="openItem('end')">Open Item End</ion-button>
|
||||
<ion-button expand="block" id="openItemOneSide" onclick="openItemOneSide()"
|
||||
>Open Item with only one side</ion-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<ion-list id="list">
|
||||
@@ -59,66 +43,6 @@
|
||||
</ion-item>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item6">
|
||||
<ion-item>
|
||||
<ion-label> One Line, dynamic option and text </ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options class="show-options">
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="start" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
<span class="more-text"></span>
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" onclick="archive('item6')">
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
<span class="archive-text"></span>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="two-options">
|
||||
<ion-item>
|
||||
<ion-label> Two options, one dynamic option and text </ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options side="start">
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="icon-only" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
<ion-item-options side="end" class="show-options">
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="start" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
<span class="more-text"></span>
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" onclick="archive('two-options')">
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
<span class="archive-text"></span>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item100">
|
||||
<ion-item href="#">
|
||||
<ion-label>
|
||||
<h2>HubStruck Notifications</h2>
|
||||
<p>A new message from a repo in your network</p>
|
||||
<p>Oceanic Next has joined your network</p>
|
||||
</ion-label>
|
||||
<ion-note slot="end"> 10:45 AM </ion-note>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-options side="start">
|
||||
<ion-item-option onclick="noclose('item100')"> No close </ion-item-option>
|
||||
</ion-item-options>
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option color="danger" onclick="unread('item100')">
|
||||
<ion-icon slot="icon-only" name="trash"></ion-icon>
|
||||
</ion-item-option>
|
||||
<ion-item-option onclick="unread('item100')">
|
||||
<ion-icon slot="icon-only" name="star"></ion-icon>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item100">
|
||||
<ion-item href="#">
|
||||
<ion-label>
|
||||
@@ -141,15 +65,15 @@
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item0">
|
||||
<ion-item onclick="clickedItem('item0')">
|
||||
<ion-item>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<h2>RIGHT side - no icons</h2>
|
||||
<p>Hey do you want to go to the game tonight?</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options class="sliding-enabled">
|
||||
<ion-item-option color="primary" onclick="archive('item0')">Archive</ion-item-option>
|
||||
<ion-item-option color="danger" onclick="del('item0')">Delete</ion-item-option>
|
||||
<ion-item-option color="primary">Archive</ion-item-option>
|
||||
<ion-item-option color="danger">Delete</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
@@ -161,8 +85,8 @@
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options side="start" class="sliding-enabled">
|
||||
<ion-item-option color="primary" onclick="archive('item1')">Archive</ion-item-option>
|
||||
<ion-item-option color="danger" onclick="del('item1')">Delete</ion-item-option>
|
||||
<ion-item-option color="primary">Archive</ion-item-option>
|
||||
<ion-item-option color="danger">Delete</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
@@ -174,138 +98,24 @@
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options side="start" class="sliding-enabled">
|
||||
<ion-item-option color="secondary" expandable onclick="unread('item2')">
|
||||
<ion-item-option color="secondary" expandable>
|
||||
<ion-icon slot="start" name="checkmark"></ion-icon>
|
||||
Unread
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item-options side="end" class="sliding-enabled">
|
||||
<ion-item-option color="primary" onclick="archive('item2')">
|
||||
<ion-icon slot="start" name="mail"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option color="danger" onclick="del('item2')" expandable>
|
||||
<ion-icon slot="start" name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item3">
|
||||
<ion-item detail>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<h2>RIGHT/LEFT side - icons (slot="start")</h2>
|
||||
<p>I think I figured out how to get more Mountain Dew</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options side="start" icon-start class="sliding-enabled">
|
||||
<ion-item-option color="secondary" expandable onclick="unread('item3')">
|
||||
<ion-icon slot="start" name="checkmark"></ion-icon>
|
||||
Unread
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item-options icon-start>
|
||||
<ion-item-option color="primary" onclick="archive('item3')">
|
||||
<ion-icon slot="start" name="mail"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option color="danger" onclick="del('item3')" expandable class="sliding-enabled">
|
||||
<ion-icon slot="start" name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item4">
|
||||
<ion-item>
|
||||
<ion-icon name="mail" slot="start"></ion-icon>
|
||||
<ion-label> One Line w/ Icon, div only text </ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options icon-start>
|
||||
<ion-item-option color="primary" onclick="archive('item4')" expandable class="sliding-enabled">
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item5" class="sliding-enabled">
|
||||
<ion-item>
|
||||
<ion-avatar slot="start">
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==" />
|
||||
</ion-avatar>
|
||||
<ion-label> One Line w/ Avatar, div only text </ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary" expandable>
|
||||
<ion-icon slot="start" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
More
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" onclick="archive('item5')">
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option color="light" onclick="del('item5')">
|
||||
<ion-icon slot="start" name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item7">
|
||||
<ion-item>
|
||||
<ion-label> One Line, dynamic icon-start option </ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options icon-start>
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="start" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
<span class="more-text"></span>
|
||||
<ion-icon slot="start" name="mail"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" onclick="archive('item7')">
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
<span class="archive-text"></span>
|
||||
<ion-item-option color="danger" expandable>
|
||||
<ion-icon slot="start" name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item8">
|
||||
<ion-item>
|
||||
<ion-thumbnail slot="start">
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==" />
|
||||
</ion-thumbnail>
|
||||
<ion-label>
|
||||
<h2>DOWNLOAD</h2>
|
||||
<p>Paragraph text.</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary" onclick="archive('item8')">
|
||||
<ion-icon name="archive"></ion-icon>Archive
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary" expandable onclick="download('item8')">
|
||||
<ion-icon slot="start" name="download" class="download-hide"></ion-icon>
|
||||
<div class="download-hide">Download</div>
|
||||
|
||||
<ion-icon slot="start" class="download-spinner" name="refresh"></ion-icon>
|
||||
<div class="download-spinner">Loading...</div>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item9">
|
||||
<ion-item>
|
||||
<ion-thumbnail slot="start">
|
||||
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAAAAAAALAAAAAABAAEAAAICTAEAOw==" />
|
||||
</ion-thumbnail>
|
||||
<ion-label>
|
||||
<h2>ion-item-sliding without options (no sliding)</h2>
|
||||
<p>Paragraph text.</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="item10">
|
||||
<ion-item detail>
|
||||
<ion-label class="ion-text-wrap">
|
||||
@@ -351,81 +161,10 @@
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="dynamic-item">
|
||||
<ion-item>
|
||||
<ion-label>Dynamic First Item</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option color="tertiary" expandable> First Item Options </ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<h2>Normal ion-item (no sliding)</h2>
|
||||
<p>Paragraph text.</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item onclick="clickedItem('item9')">
|
||||
<ion-label class="ion-text-wrap">
|
||||
<h2>Normal button (no sliding)</h2>
|
||||
<p>Hey do you want to go to the game tonight?</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</div>
|
||||
</ion-list>
|
||||
|
||||
<script>
|
||||
const setDynaicItem = () => {
|
||||
const sliding = document.querySelector('#dynamic-item');
|
||||
sliding.innerHTML = `
|
||||
<ion-item>
|
||||
<ion-label>Dynamic Second Item</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option color="tertiary" expandable>
|
||||
Second Item Options
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
`;
|
||||
};
|
||||
|
||||
var dynamicSlidingEnabled = document.getElementsByClassName('sliding-enabled');
|
||||
|
||||
// Toggle the dynamic options
|
||||
var dynamicShowOptions = document.getElementsByClassName('show-options');
|
||||
toggleDynamicOptions();
|
||||
|
||||
function toggleDynamicOptions() {
|
||||
// TODO the element needs to be removed / added to the DOM
|
||||
}
|
||||
|
||||
// Change the text for the more and archive buttons
|
||||
var dynamicText = true;
|
||||
var moreTextSpans = document.getElementsByClassName('more-text');
|
||||
var archiveTextSpans = document.getElementsByClassName('archive-text');
|
||||
changeDynamicText();
|
||||
|
||||
function changeDynamicText() {
|
||||
dynamicText = !dynamicText;
|
||||
|
||||
for (var i = 0; i < moreTextSpans.length; i++) {
|
||||
var moreText = dynamicText ? 'Changed More' : 'Dynamic More';
|
||||
moreTextSpans[i].innerHTML = moreText;
|
||||
}
|
||||
|
||||
for (var i = 0; i < archiveTextSpans.length; i++) {
|
||||
var archiveText = dynamicText ? 'Changed Archive' : 'Dynamic Archive';
|
||||
archiveTextSpans[i].innerHTML = archiveText;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSliding() {
|
||||
// this.slidingEnabled = !this.slidingEnabled;
|
||||
}
|
||||
|
||||
function closeOpened() {
|
||||
var list = document.getElementById('list');
|
||||
list.closeSlidingItems();
|
||||
@@ -441,89 +180,14 @@
|
||||
item.open();
|
||||
}
|
||||
|
||||
function noclose(item) {
|
||||
var itemEle = document.getElementById(item);
|
||||
console.log('no close', itemEle);
|
||||
}
|
||||
|
||||
function unread(item) {
|
||||
closeSlidingItem('UNREAD', item);
|
||||
}
|
||||
|
||||
function archive(item) {
|
||||
closeSlidingItem('ARCHIVE', item);
|
||||
}
|
||||
|
||||
function del(item) {
|
||||
closeSlidingItem('DELETE', item);
|
||||
}
|
||||
|
||||
function download(item) {
|
||||
var itemEle = document.getElementById(item);
|
||||
console.log(itemEle);
|
||||
itemEle.classList.add('downloading');
|
||||
setTimeout(() => {
|
||||
alert('Item was downloaded!');
|
||||
|
||||
itemEle.classList.remove('downloading');
|
||||
itemEle.close();
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
function closeSlidingItem(option, item) {
|
||||
var itemEle = document.getElementById(item);
|
||||
|
||||
// TODO open alert instead
|
||||
if (itemEle) {
|
||||
itemEle.close();
|
||||
}
|
||||
console.log(option, itemEle);
|
||||
}
|
||||
|
||||
function clickedItem(item) {
|
||||
var itemEle = document.getElementById(item);
|
||||
console.log('Clicked, ion-item', itemEle);
|
||||
}
|
||||
|
||||
function reload() {
|
||||
window.location.reload();
|
||||
}
|
||||
document.addEventListener('ionSwipe', (ev) => console.log('SWIPE!!', ev.detail));
|
||||
document.addEventListener('ionDrag', (ev) => {
|
||||
// console.log('DRAG!!', ev.detail);
|
||||
|
||||
let slidingRatio = ev.target.getSlidingRatio();
|
||||
console.log('sliding', slidingRatio);
|
||||
|
||||
if (slidingRatio > 0) {
|
||||
// positive
|
||||
console.log('right side');
|
||||
} else {
|
||||
// negative
|
||||
console.log('left side');
|
||||
}
|
||||
if (Math.abs(slidingRatio) > 1) {
|
||||
console.log('overscroll');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
.download-spinner {
|
||||
display: none;
|
||||
}
|
||||
|
||||
svg circle {
|
||||
stroke: white;
|
||||
}
|
||||
|
||||
.downloading .download-spinner {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.downloading .download-hide {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
|
||||
@@ -1,14 +1,51 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
import { dragElementBy, test } from '@utils/test/playwright';
|
||||
|
||||
import { testSlidingItem } from '../test.utils';
|
||||
|
||||
test.describe('item-sliding: basic', () => {
|
||||
test.beforeEach(async ({ skip }) => {
|
||||
skip.mode('ios', "item-sliding doesn't have mode-specific styling");
|
||||
});
|
||||
|
||||
test('should not have visual regressions', async ({ page, browserName }, testInfo) => {
|
||||
// TODO(FW-2608)
|
||||
test.fixme(
|
||||
testInfo.project.metadata.rtl === true && (browserName === 'firefox' || browserName === 'webkit'),
|
||||
'https://github.com/ionic-team/ionic-framework/issues/26103'
|
||||
);
|
||||
|
||||
await page.goto(`/src/components/item-sliding/test/basic`);
|
||||
const item = page.locator('#item2');
|
||||
|
||||
await testSlidingItem(page, item, 'start', true);
|
||||
await testSlidingItem(page, item, 'end');
|
||||
});
|
||||
|
||||
test('should open when swiped', async ({ page, skip }) => {
|
||||
skip.rtl();
|
||||
skip.browser(
|
||||
(browserName: string) => browserName !== 'chromium',
|
||||
'dragElementBy is flaky outside of Chrome browsers.'
|
||||
);
|
||||
|
||||
await page.goto(`/src/components/item-sliding/test/basic`);
|
||||
const item = page.locator('#item2');
|
||||
|
||||
await dragElementBy(item, page, -150);
|
||||
await page.waitForChanges();
|
||||
|
||||
// item-sliding doesn't have an easy way to tell whether it's fully open so just screenshot it
|
||||
expect(await item.screenshot()).toMatchSnapshot(`item-sliding-gesture-${page.getSnapshotSettings()}.png`);
|
||||
});
|
||||
|
||||
test('should not scroll when the item-sliding is swiped', async ({ page, skip }) => {
|
||||
skip.browser('webkit', 'mouse.wheel is not available in WebKit');
|
||||
skip.rtl();
|
||||
|
||||
await page.goto(`/src/components/item-sliding/test/basic`);
|
||||
|
||||
const itemSlidingEl = page.locator('#two-options');
|
||||
const itemSlidingEl = page.locator('#item2');
|
||||
const scrollEl = page.locator('ion-content .inner-scroll');
|
||||
|
||||
expect(await scrollEl.evaluate((el: HTMLElement) => el.scrollTop)).toEqual(0);
|
||||
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 20 KiB |
108
core/src/components/item-sliding/test/icons/index.html
Normal file
@@ -0,0 +1,108 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Item Sliding - Icons</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
|
||||
/>
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Item Sliding - Icons</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item-sliding id="iconsOnly">
|
||||
<ion-item>
|
||||
<ion-label>Icons Only</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="danger">
|
||||
<ion-icon slot="icon-only" name="trash"></ion-icon>
|
||||
</ion-item-option>
|
||||
<ion-item-option>
|
||||
<ion-icon slot="icon-only" name="star"></ion-icon>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="iconsStart">
|
||||
<ion-item>
|
||||
<ion-label>Icons in Start Slot</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="start" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
More
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary">
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="iconsEnd">
|
||||
<ion-item>
|
||||
<ion-label>Icons in End Slot</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="end" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
More
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary">
|
||||
<ion-icon slot="end" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="iconsTop">
|
||||
<ion-item>
|
||||
<ion-label>Icons in Top Slot</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="top" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
More
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary">
|
||||
<ion-icon slot="top" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<ion-item-sliding id="iconsBottom">
|
||||
<ion-item>
|
||||
<ion-label>Icons in Bottom Slot</ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="bottom" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
More
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary">
|
||||
<ion-icon slot="bottom" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,21 @@
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
import { testSlidingItem } from '../test.utils';
|
||||
|
||||
test.describe('item-sliding: icons', () => {
|
||||
test('should not have visual regressions', async ({ page, browserName, skip }, testInfo) => {
|
||||
// TODO(FW-2608)
|
||||
test.fixme(
|
||||
testInfo.project.metadata.rtl === true && (browserName === 'firefox' || browserName === 'webkit'),
|
||||
'https://github.com/ionic-team/ionic-framework/issues/26103'
|
||||
);
|
||||
|
||||
skip.mode('ios', "item-sliding doesn't have mode-specific styling");
|
||||
await page.goto(`/src/components/item-sliding/test/icons`);
|
||||
|
||||
const itemIDs = ['iconsOnly', 'iconsStart', 'iconsEnd', 'iconsTop', 'iconsBottom'];
|
||||
for (const itemID of itemIDs) {
|
||||
await testSlidingItem(page, page.locator(`#${itemID}`), itemID);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 8.3 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
@@ -1,51 +0,0 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
import { openItemSliding } from '../test.utils';
|
||||
|
||||
test('item-sliding: interactive', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/item-sliding/test/interactive?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const compares = [];
|
||||
compares.push(await page.compareScreenshot());
|
||||
|
||||
const items = await page.$$('ion-item-sliding');
|
||||
expect(items.length).toEqual(3);
|
||||
|
||||
await slideAndDelete(items[0], page);
|
||||
|
||||
const itemsAfterFirstSlide = await page.$$('ion-item-sliding');
|
||||
expect(itemsAfterFirstSlide.length).toEqual(2);
|
||||
|
||||
await slideAndDelete(items[1], page);
|
||||
|
||||
const itemsAfterSecondSlide = await page.$$('ion-item-sliding');
|
||||
expect(itemsAfterSecondSlide.length).toEqual(1);
|
||||
|
||||
for (const compare of compares) {
|
||||
expect(compare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
|
||||
const slideAndDelete = async (item: any, page: any) => {
|
||||
try {
|
||||
// Get the element's ID
|
||||
const id = await (await item.getProperty('id')).jsonValue();
|
||||
await openItemSliding(`#${id}`, page);
|
||||
await deleteItemSliding(item, page, id);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteItemSliding = async (item: any, page: any, id: string) => {
|
||||
// Click the "delete" option
|
||||
const options = await item.$$('ion-item-option');
|
||||
await options[0].click();
|
||||
|
||||
// Wait for element to be removed from DOM
|
||||
await page.waitForSelector(id, { hidden: true });
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Item Sliding - Standalone</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 nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-list></ion-list>
|
||||
|
||||
<script>
|
||||
let lists = document.getElementsByTagName('ion-list');
|
||||
for (var i = 0; i < lists.length; i++) {
|
||||
for (var j = 0; j < 3; j++) {
|
||||
addSlidingItem(lists[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function addSlidingItem(list) {
|
||||
const slidingItem = document.createElement('ion-item-sliding');
|
||||
slidingItem.innerHTML = `
|
||||
<ion-item>
|
||||
<ion-label>
|
||||
<h2>Item Options Both Sides</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option color="danger" expandable onclick="remove(this)">
|
||||
<ion-icon name="trash"></ion-icon>
|
||||
Delete
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
`;
|
||||
|
||||
slidingItem.id = `item-${list.childElementCount}`;
|
||||
|
||||
list.appendChild(slidingItem);
|
||||
}
|
||||
|
||||
function remove(optionElement) {
|
||||
const slidingElement = optionElement.parentNode.parentNode;
|
||||
lists[0].removeChild(slidingElement);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,59 +0,0 @@
|
||||
import { newE2EPage } from '@stencil/core/testing';
|
||||
|
||||
import { closeItemSliding, openItemSliding } from '../test.utils';
|
||||
|
||||
test('item-sliding: standalone', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/item-sliding/test/standalone?ionic:_testing=true',
|
||||
});
|
||||
|
||||
const compares = [];
|
||||
compares.push(await page.compareScreenshot());
|
||||
|
||||
// Pass sliding item with start icons in option
|
||||
await openItemSliding('#startItem', page);
|
||||
compares.push(await page.compareScreenshot(`start icons open`));
|
||||
await closeItemSliding(page);
|
||||
|
||||
// Pass sliding item with top icons in option
|
||||
await openItemSliding('#topItem', page);
|
||||
compares.push(await page.compareScreenshot(`top icons open`));
|
||||
await closeItemSliding(page);
|
||||
|
||||
// Pass sliding item with anchor option
|
||||
await openItemSliding('#anchorItem', page);
|
||||
compares.push(await page.compareScreenshot(`anchor option`));
|
||||
await closeItemSliding(page);
|
||||
|
||||
for (const compare of compares) {
|
||||
expect(compare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
|
||||
test('item-sliding:rtl: standalone', async () => {
|
||||
const page = await newE2EPage({
|
||||
url: '/src/components/item-sliding/test/standalone?ionic:_testing=true&rtl=true',
|
||||
});
|
||||
|
||||
const compares = [];
|
||||
compares.push(await page.compareScreenshot());
|
||||
|
||||
// Pass sliding item with start icons in option
|
||||
await openItemSliding('#startItem', page, true);
|
||||
compares.push(await page.compareScreenshot(`start icons open`));
|
||||
await closeItemSliding(page);
|
||||
|
||||
// Pass sliding item with top icons in option
|
||||
await openItemSliding('#topItem', page, true);
|
||||
compares.push(await page.compareScreenshot(`top icons open`));
|
||||
await closeItemSliding(page);
|
||||
|
||||
// Pass sliding item with anchor option
|
||||
await openItemSliding('#anchorItem', page, true);
|
||||
compares.push(await page.compareScreenshot(`anchor option`));
|
||||
await closeItemSliding(page);
|
||||
|
||||
for (const compare of compares) {
|
||||
expect(compare).toMatchScreenshot();
|
||||
}
|
||||
});
|
||||
@@ -1,165 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Item Sliding - Standalone</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 nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Sliding item with text options on both sides -->
|
||||
<ion-item-sliding>
|
||||
<ion-item-options side="start">
|
||||
<ion-item-option onClick="favorite(item)">Favorite</ion-item-option>
|
||||
<ion-item-option color="danger" onClick="share(item)">Share</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Item Options</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option onClick="unread(item)">Unread</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<!-- Sliding item with expandable options on both sides -->
|
||||
<ion-item-sliding>
|
||||
<ion-item-options side="start">
|
||||
<ion-item-option color="danger" expandable> Delete </ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item>
|
||||
<ion-label>Expandable Options</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option color="tertiary" expandable> Archive </ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<!-- Multi-line sliding item with icon options on both sides -->
|
||||
<ion-item-sliding id="item100">
|
||||
<ion-item href="#">
|
||||
<ion-label>
|
||||
<h2>HubStruck Notifications</h2>
|
||||
<p>A new message in your network</p>
|
||||
<p>Oceanic Next has joined your network</p>
|
||||
</ion-label>
|
||||
<ion-note slot="end"> 10:45 AM </ion-note>
|
||||
</ion-item>
|
||||
|
||||
<ion-item-options side="start">
|
||||
<ion-item-option>
|
||||
<ion-icon slot="icon-only" name="heart"></ion-icon>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
|
||||
<ion-item-options side="end">
|
||||
<ion-item-option color="danger">
|
||||
<ion-icon slot="icon-only" name="trash"></ion-icon>
|
||||
</ion-item-option>
|
||||
<ion-item-option>
|
||||
<ion-icon slot="icon-only" name="star"></ion-icon>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<!-- Sliding item with icon start options on end side -->
|
||||
<ion-item-sliding id="startItem">
|
||||
<ion-item>
|
||||
<ion-label> Sliding Item, Icons Start </ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="start" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
More
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary">
|
||||
<ion-icon slot="start" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<!-- Sliding item with icon end options on end side -->
|
||||
<ion-item-sliding>
|
||||
<ion-item>
|
||||
<ion-label> Sliding Item, Icons End </ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="end" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
More
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary">
|
||||
<ion-icon slot="end" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<!-- Sliding item with icon top options on end side -->
|
||||
<ion-item-sliding id="topItem">
|
||||
<ion-item>
|
||||
<ion-label> Sliding Item, Icons Top </ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="top" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
More
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary">
|
||||
<ion-icon slot="top" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<!-- Sliding item with icon bottom options on end side -->
|
||||
<ion-item-sliding>
|
||||
<ion-item>
|
||||
<ion-label> Sliding Item, Icons Bottom </ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option color="primary">
|
||||
<ion-icon slot="bottom" ios="ellipsis-horizontal" md="ellipsis-vertical"></ion-icon>
|
||||
More
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary">
|
||||
<ion-icon slot="bottom" name="archive"></ion-icon>
|
||||
Archive
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
|
||||
<!-- Sliding item with one anchor option -->
|
||||
<ion-item-sliding id="anchorItem">
|
||||
<ion-item>
|
||||
<ion-label> Sliding Item, Anchor Icon Option </ion-label>
|
||||
</ion-item>
|
||||
<ion-item-options>
|
||||
<ion-item-option href="#" color="primary">
|
||||
<ion-icon slot="icon-only" name="star"></ion-icon>
|
||||
</ion-item-option>
|
||||
<ion-item-option color="secondary">
|
||||
<ion-icon slot="icon-only" name="archive"></ion-icon>
|
||||
</ion-item-option>
|
||||
</ion-item-options>
|
||||
</ion-item-sliding>
|
||||
</body>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
@@ -1,58 +1,33 @@
|
||||
// Opens a sliding item by simulating a drag event
|
||||
export const openItemSliding = async (id: string, page: any, rtl = false) => {
|
||||
try {
|
||||
const slidingItem = await page.$(id);
|
||||
import { expect } from '@playwright/test';
|
||||
import type { Locator } from '@playwright/test';
|
||||
import type { E2EPage } from '@utils/test/playwright';
|
||||
|
||||
// Simulate a drag
|
||||
const boundingBox = await slidingItem.boundingBox();
|
||||
const centerX = parseFloat(boundingBox.x + boundingBox.width / 2);
|
||||
const centerY = parseFloat(boundingBox.y + boundingBox.height / 2);
|
||||
|
||||
// In LTR start the drag at the center, move halfway in between the
|
||||
// center and 0, then end at 0
|
||||
//
|
||||
// 0 boundingBox.width
|
||||
// |---------|---------|---------|---------|
|
||||
// endX halfX centerX
|
||||
|
||||
let halfX = centerX / 2;
|
||||
let endX = 0;
|
||||
|
||||
// In RTL start at the center, move halfway in between the center and
|
||||
// the total width, then end at the total width
|
||||
//
|
||||
// 0 boundingBox.width
|
||||
// |---------|---------|---------|---------|
|
||||
// centerX halfX endX
|
||||
|
||||
if (rtl) {
|
||||
halfX = centerX + centerX / 2;
|
||||
endX = boundingBox.width;
|
||||
}
|
||||
|
||||
// Start in the center of the item
|
||||
await page.mouse.move(centerX, centerY);
|
||||
await page.mouse.down();
|
||||
|
||||
// Move halfway first
|
||||
await page.mouse.move(halfX, centerY);
|
||||
|
||||
// Move all of the way to the end
|
||||
await page.mouse.move(endX, centerY);
|
||||
await page.mouse.up();
|
||||
|
||||
// Add a timeout to make sure the item is open
|
||||
await page.waitForTimeout(2000);
|
||||
} catch (err) {
|
||||
throw err;
|
||||
export const testSlidingItem = async (
|
||||
page: E2EPage,
|
||||
item: Locator,
|
||||
screenshotNameSuffix: string,
|
||||
openStart = false
|
||||
) => {
|
||||
// passing a param into the eval callback is tricky due to execution context
|
||||
// so just do the check outside the callback instead to make things easy
|
||||
if (openStart) {
|
||||
await item.evaluate(async (el: HTMLIonItemSlidingElement) => {
|
||||
await el.open('start');
|
||||
});
|
||||
} else {
|
||||
await item.evaluate(async (el: HTMLIonItemSlidingElement) => {
|
||||
await el.open('end');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Close a sliding item after taking a screenshot
|
||||
// to allow other sliding items to open
|
||||
export const closeItemSliding = async (page: any) => {
|
||||
await page.mouse.move(0, 0);
|
||||
await page.mouse.down();
|
||||
await page.mouse.up();
|
||||
await page.waitForTimeout(1000);
|
||||
// opening animation takes longer than waitForChanges accounts for
|
||||
await page.waitForTimeout(500);
|
||||
|
||||
expect(await item.screenshot()).toMatchSnapshot(
|
||||
`item-sliding-${screenshotNameSuffix}-${page.getSnapshotSettings()}.png`
|
||||
);
|
||||
|
||||
await item.evaluate(async (el: HTMLIonItemSlidingElement) => {
|
||||
await el.close();
|
||||
});
|
||||
};
|
||||
|
||||