Skip to content

Commit

Permalink
fix(item-sliding): use the correct gesture direction and side for rtl (
Browse files Browse the repository at this point in the history
…#18366)

references #17012
  • Loading branch information
brandyscarney committed May 23, 2019
1 parent 7ab9479 commit 4545100
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 49 deletions.
2 changes: 1 addition & 1 deletion core/api.txt
Expand Up @@ -477,7 +477,7 @@ ion-item-sliding,method,close,close() => Promise<void>
ion-item-sliding,method,closeOpened,closeOpened() => Promise<boolean>
ion-item-sliding,method,getOpenAmount,getOpenAmount() => Promise<number>
ion-item-sliding,method,getSlidingRatio,getSlidingRatio() => Promise<number>
ion-item-sliding,method,open,open(side: string | undefined) => Promise<void>
ion-item-sliding,method,open,open(side: "start" | "end" | undefined) => Promise<void>
ion-item-sliding,event,ionDrag,void,true

ion-item,shadow
Expand Down
2 changes: 1 addition & 1 deletion core/src/components.d.ts
Expand Up @@ -2026,7 +2026,7 @@ export namespace Components {
/**
* Open the sliding item.
*/
'open': (side: string | undefined) => Promise<void>;
'open': (side: "start" | "end" | undefined) => Promise<void>;
}
interface IonItemSlidingAttributes extends StencilHTMLAttributes {
/**
Expand Down
1 change: 1 addition & 0 deletions core/src/components/item-options/item-options.tsx
Expand Up @@ -38,6 +38,7 @@ export class ItemOptions implements ComponentInterface {

hostData() {
const isEnd = isEndSide(this.win, this.side);

return {
class: {
[`${this.mode}`]: true,
Expand Down
33 changes: 19 additions & 14 deletions core/src/components/item-sliding/item-sliding.tsx
@@ -1,6 +1,7 @@
import { Component, ComponentInterface, Element, Event, EventEmitter, Method, Prop, QueueApi, State, Watch } from '@stencil/core';

import { Gesture, GestureDetail, Mode } from '../../interface';
import { Gesture, GestureDetail, Mode, Side } from '../../interface';
import { isEndSide } from '../../utils/helpers';

const SWIPE_MARGIN = 30;
const ELASTIC_FACTOR = 0.55;
Expand Down Expand Up @@ -122,9 +123,8 @@ export class ItemSliding implements ComponentInterface {
*
* @param side The side of the options to open. If a side is not provided, it will open the first set of options it finds within the item.
*/
// TODO update to work with RTL
@Method()
async open(side: string | undefined) {
async open(side: Side | undefined) {
if (this.item === null) { return; }

const optionsToOpen = this.getOptions(side);
Expand All @@ -138,6 +138,9 @@ export class ItemSliding implements ComponentInterface {
side = (optionsToOpen === this.leftOptions) ? 'start' : 'end';
}

// In RTL we want to switch the sides
side = isEndSide(window, side) ? 'end' : 'start';

const isStartOpen = this.openAmount < 0;
const isEndOpen = this.openAmount > 0;

Expand Down Expand Up @@ -185,20 +188,20 @@ export class ItemSliding implements ComponentInterface {
}

/**
* Given a side, attempt to return the ion-item-options element
* Given an optional side, return the ion-item-options element.
*
* @param side This side of the options to get. If a side is not provided it will return the first one available
* @param side This side of the options to get. If a side is not provided it will
* return the first one available.
*/
// TODO update to work with RTL
private getOptions(side?: string): HTMLIonItemOptionsElement | undefined {
if (side === undefined) {
return this.leftOptions || this.rightOptions;
} else if (side === 'start') {
return this.leftOptions;
} else {
return this.rightOptions;
}
if (side === undefined) {
return this.leftOptions || this.rightOptions;
} else if (side === 'start') {
return this.leftOptions;
} else {
return this.rightOptions;
}
}

private async updateOptions() {
const options = this.el.querySelectorAll('ion-item-options');
Expand All @@ -211,7 +214,9 @@ export class ItemSliding implements ComponentInterface {
for (let i = 0; i < options.length; i++) {
const option = await options.item(i).componentOnReady();

if (option.side === 'start') {
const side = isEndSide(window, option.side) ? 'end' : 'start';

if (side === 'start') {
this.leftOptions = option;
sides |= ItemSide.Start;
} else {
Expand Down
8 changes: 4 additions & 4 deletions core/src/components/item-sliding/readme.md
Expand Up @@ -692,15 +692,15 @@ Type: `Promise<number>`



### `open(side: string | undefined) => Promise<void>`
### `open(side: "start" | "end" | undefined) => Promise<void>`

Open the sliding item.

#### Parameters

| Name | Type | Description |
| ------ | --------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `side` | `string \| undefined` | The side of the options to open. If a side is not provided, it will open the first set of options it finds within the item. |
| Name | Type | Description |
| ------ | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `side` | `"end" \| "start" \| undefined` | The side of the options to open. If a side is not provided, it will open the first set of options it finds within the item. |

#### Returns

Expand Down
72 changes: 45 additions & 27 deletions core/src/components/item-sliding/test/basic/index.html
Expand Up @@ -62,11 +62,11 @@ <h2>No Options</h2>
</ion-item>
<ion-item-options class="show-options">
<ion-item-option color="primary">
<ion-icon name="more"></ion-icon>
<ion-icon slot="start" name="more"></ion-icon>
<span class="more-text"></span>
</ion-item-option>
<ion-item-option color="secondary" onclick="archive('item6')">
<ion-icon name="archive"></ion-icon>
<ion-icon slot="start" name="archive"></ion-icon>
<span class="archive-text"></span>
</ion-item-option>
</ion-item-options>
Expand All @@ -85,11 +85,11 @@ <h2>No Options</h2>
</ion-item-options>
<ion-item-options side="end" class="show-options">
<ion-item-option color="primary">
<ion-icon name="more"></ion-icon>
<ion-icon slot="start" name="more"></ion-icon>
<span class="more-text"></span>
</ion-item-option>
<ion-item-option color="secondary" onclick="archive('item6')">
<ion-icon name="archive"></ion-icon>
<ion-icon slot="start" name="archive"></ion-icon>
<span class="archive-text"></span>
</ion-item-option>
</ion-item-options>
Expand Down Expand Up @@ -183,16 +183,19 @@ <h2>RIGHT/LEFT side - icons</h2>
</ion-item>
<ion-item-options side="start" class="sliding-enabled">
<ion-item-option color="secondary" expandable onclick="unread('item2')">
<ion-icon name="ios-checkmark"></ion-icon>Unread
<ion-icon slot="start" name="ios-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 name="mail"></ion-icon>Archive
<ion-icon slot="start" name="mail"></ion-icon>
Archive
</ion-item-option>
<ion-item-option color="danger" onclick="del('item2')" expandable>
<ion-icon name="trash"></ion-icon>Delete
<ion-icon slot="start" name="trash"></ion-icon>
Delete
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
Expand All @@ -206,16 +209,19 @@ <h2>RIGHT/LEFT side - icons (slot="start")</h2>
</ion-item>
<ion-item-options side="start" icon-start class="sliding-enabled">
<ion-item-option color="secondary" expandable onclick="unread('item3')">
<ion-icon name="ios-checkmark"></ion-icon>Unread
<ion-icon slot="start" name="ios-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 name="mail"></ion-icon>Archive
<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 name="trash"></ion-icon>Delete
<ion-icon slot="start" name="trash"></ion-icon>
Delete
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
Expand All @@ -230,7 +236,8 @@ <h2>RIGHT/LEFT side - icons (slot="start")</h2>
</ion-item>
<ion-item-options icon-start>
<ion-item-option color="primary" onclick="archive('item4')" expandable class="sliding-enabled">
<ion-icon name="archive"></ion-icon>Archive
<ion-icon slot="start" name="archive"></ion-icon>
Archive
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
Expand All @@ -247,13 +254,16 @@ <h2>RIGHT/LEFT side - icons (slot="start")</h2>
</ion-item>
<ion-item-options>
<ion-item-option color="primary" expandable>
<ion-icon name="more"></ion-icon>More
<ion-icon slot="start" name="more"></ion-icon>
More
</ion-item-option>
<ion-item-option color="secondary" onclick="archive('item5')">
<ion-icon name="archive"></ion-icon>Archive
<ion-icon slot="start" name="archive"></ion-icon>
Archive
</ion-item-option>
<ion-item-option color="light" onclick="del('item5')">
<ion-icon name="trash"></ion-icon>Delete
<ion-icon slot="start" name="trash"></ion-icon>
Delete
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
Expand All @@ -267,11 +277,11 @@ <h2>RIGHT/LEFT side - icons (slot="start")</h2>
</ion-item>
<ion-item-options icon-start>
<ion-item-option color="primary">
<ion-icon name="more"></ion-icon>
<ion-icon slot="start" name="more"></ion-icon>
<span class="more-text"></span>
</ion-item-option>
<ion-item-option color="secondary" onclick="archive('item7')">
<ion-icon name="archive"></ion-icon>
<ion-icon slot="start" name="archive"></ion-icon>
<span class="archive-text"></span>
</ion-item-option>
</ion-item-options>
Expand All @@ -292,10 +302,10 @@ <h2>DOWNLOAD</h2>
<ion-icon name="archive"></ion-icon>Archive
</ion-item-option>
<ion-item-option color="secondary" expandable onclick="download('item8')">
<ion-icon name="download" class="download-hide"></ion-icon>
<ion-icon slot="start" name="download" class="download-hide"></ion-icon>
<div class="download-hide">Download</div>

<ion-icon class="download-spinner" name="refresh"></ion-icon>
<ion-icon slot="start" class="download-spinner" name="refresh"></ion-icon>
<div class="download-spinner">Loading...</div>
</ion-item-option>
</ion-item-options>
Expand All @@ -322,31 +332,39 @@ <h2>RIGHT/LEFT side - many buttons</h2>
</ion-item>
<ion-item-options side="start" class="sliding-enabled">
<ion-item-option color="primary" expandable>
<ion-icon name="ios-checkmark"></ion-icon>Btn 1
<ion-icon slot="start" name="ios-checkmark"></ion-icon>
Btn 1
</ion-item-option>
<ion-item-option color="secondary" expandable>
<ion-icon name="ios-checkmark"></ion-icon>Btn 2
<ion-icon slot="start" name="ios-checkmark"></ion-icon>
Btn 2
</ion-item-option>
<ion-item-option color="danger" expandable>
<ion-icon name="ios-checkmark"></ion-icon>Btn 3
<ion-icon slot="start" name="ios-checkmark"></ion-icon>
Btn 3
</ion-item-option>
<ion-item-option color="tertiary" expandable>
<ion-icon name="ios-checkmark"></ion-icon>Btn 4
<ion-icon slot="start" name="ios-checkmark"></ion-icon>
Btn 4
</ion-item-option>
</ion-item-options>

<ion-item-options side="end" class="sliding-enabled">
<ion-item-option color="primary" expandable>
<ion-icon name="mail"></ion-icon>Btn 5
<ion-icon slot="start" name="mail"></ion-icon>
Btn 5
</ion-item-option>
<ion-item-option color="secondary" expandable>
<ion-icon name="mail"></ion-icon>Btn 6
<ion-icon slot="start" name="mail"></ion-icon>
Btn 6
</ion-item-option>
<ion-item-option color="danger" expandable>
<ion-icon name="mail"></ion-icon>Btn 7
<ion-icon slot="start" name="mail"></ion-icon>
Btn 7
</ion-item-option>
<ion-item-option color="tertiary" expandable>
<ion-icon name="mail"></ion-icon>Btn 8
<ion-icon slot="start" name="mail"></ion-icon>
Btn 8
</ion-item-option>
</ion-item-options>
</ion-item-sliding>
Expand Down Expand Up @@ -412,7 +430,7 @@ <h2>Normal button (no sliding)</h2>
var item = document.getElementById('item2');
item.open(side);
}

function openItemOneSide() {
var item = document.getElementById('item1');
item.open();
Expand Down
28 changes: 28 additions & 0 deletions core/src/components/item-sliding/test/standalone/e2e.ts
Expand Up @@ -29,3 +29,31 @@ test('item-sliding: standalone', async () => {
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();
}
});
9 changes: 7 additions & 2 deletions core/src/components/item-sliding/test/test.utils.ts
@@ -1,5 +1,5 @@
// Opens a sliding item by simulating a drag event
export async function openItemSliding(id: string, page: any) {
export async function openItemSliding(id: string, page: any, rtl = false) {
try {
const slidingItem = await page.$(id);

Expand All @@ -8,10 +8,15 @@ export async function openItemSliding(id: string, page: any) {
const centerX = parseFloat(boundingBox.x + boundingBox.width / 2);
const centerY = parseFloat(boundingBox.y + boundingBox.height / 2);

let endX = 0;
if (rtl) {
endX = boundingBox.width;
}

await page.mouse.move(centerX, centerY);
await page.mouse.down();
await page.mouse.move(centerX / 2, centerY);
await page.mouse.move(0, centerY);
await page.mouse.move(endX, centerY);
await page.mouse.up();

// Add a timeout to make sure the item is open
Expand Down

0 comments on commit 4545100

Please sign in to comment.