Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 0 additions & 23 deletions core/src/components/checkbox/test/basic/checkbox.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,28 +98,5 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
await checkbox.evaluate((el: HTMLIonCheckboxElement) => (el.checked = true));
expect(ionChange).not.toHaveReceivedEvent();
});

test('clicking padded space within item should click the checkbox', async ({ page }) => {
await page.setContent(
`
<ion-item>
<ion-checkbox>Size</ion-checkbox>
</ion-item>
`,
config
);
const itemNative = page.locator('.item-native');
const ionChange = await page.spyOnEvent('ionChange');

// Clicks the padded space within the item
await itemNative.click({
position: {
x: 5,
y: 5,
},
});

expect(ionChange).toHaveReceivedEvent();
});
});
});
67 changes: 67 additions & 0 deletions core/src/components/checkbox/test/item/checkbox.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,70 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
});
});
});

configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('checkbox: item functionality'), () => {
test('clicking padded space within item should click the checkbox', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/27169',
});

await page.setContent(
`
<ion-item>
<ion-checkbox>Size</ion-checkbox>
</ion-item>
`,
config
);
const item = page.locator('ion-item');
const ionChange = await page.spyOnEvent('ionChange');

// Clicks the padded space within the item
await item.click({
position: {
x: 5,
y: 5,
},
});

expect(ionChange).toHaveReceivedEvent();
});

test('clicking padded space within item should fire one click event', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/29758',
});

await page.setContent(
`
<ion-item>
<ion-checkbox>
Checkbox
</ion-checkbox>
</ion-item>
`,
config
);

const item = page.locator('ion-item');
const onClick = await page.spyOnEvent('click');

// Click the padding area (5px from left edge)
await item.click({
position: {
x: 5,
y: 5,
},
});

expect(onClick).toHaveReceivedEventTimes(1);

// Verify that the event target is the checkbox and not the item
const event = onClick.events[0];
expect((event.target as HTMLElement).tagName.toLowerCase()).toBe('ion-checkbox');
});
});
});
4 changes: 4 additions & 0 deletions core/src/components/input/input.scss
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@

width: 100%;
max-width: 100%;

// Ensure the input fills the full height of the native wrapper.
// This prevents the wrapper from being the click event target.
height: 100%;
max-height: 100%;

border: 0;
Expand Down
28 changes: 27 additions & 1 deletion core/src/components/input/input.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, forceUpdate, h } from '@stencil/core';
import {
Build,
Component,
Element,
Event,
Host,
Listen,
Method,
Prop,
State,
Watch,
forceUpdate,
h,
} from '@stencil/core';
import type { NotchController } from '@utils/forms';
import { createNotchController } from '@utils/forms';
import type { Attributes } from '@utils/helpers';
Expand Down Expand Up @@ -363,6 +376,19 @@ export class Input implements ComponentInterface {
forceUpdate(this);
}

/**
* This prevents the native input from emitting the click event.
* Instead, the click event from the ion-input is emitted.
*/
@Listen('click', { capture: true })
onClickCapture(ev: Event) {
const nativeInput = this.nativeInput;
if (nativeInput && ev.target === nativeInput) {
ev.stopPropagation();
this.el.click();
}
}

componentWillLoad() {
this.inheritedAttributes = {
...inheritAriaAttributes(this.el),
Expand Down
91 changes: 89 additions & 2 deletions core/src/components/input/test/item/input.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ configs().forEach(({ title, screenshot, config }) => {
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('input: item functionality'), () => {
test('clicking padded space within item should focus the input', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/21982',
});

await page.setContent(
`
<ion-item>
Expand All @@ -57,11 +62,12 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
`,
config
);
const itemNative = page.locator('.item-native');

const item = page.locator('ion-item');
const input = page.locator('ion-input input');

// Clicks the padded space within the item
await itemNative.click({
await item.click({
position: {
x: 5,
y: 5,
Expand All @@ -70,5 +76,86 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>

await expect(input).toBeFocused();
});

test('clicking padded space within item should fire one click event', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/29761',
});

await page.setContent(
`
<ion-item>
<ion-input label="Input"></ion-input>
</ion-item>
`,
config
);

const item = page.locator('ion-item');
const onClick = await page.spyOnEvent('click');

// Click the padding area (5px from left edge)
await item.click({
position: {
x: 5,
y: 5,
},
});

expect(onClick).toHaveReceivedEventTimes(1);

// Verify that the event target is the input and not the item
const event = onClick.events[0];
expect((event.target as HTMLElement).tagName.toLowerCase()).toBe('ion-input');
});

test('clicking native wrapper should fire one click event', async ({ page }) => {
await page.setContent(
`
<ion-item>
<ion-input label="Input"></ion-input>
</ion-item>
`,
config
);

const nativeWrapper = page.locator('.native-wrapper');
const onClick = await page.spyOnEvent('click');

await nativeWrapper.click({
position: {
x: 5,
y: 5,
},
});

expect(onClick).toHaveReceivedEventTimes(1);

// Verify that the event target is the input and not the native wrapper
const event = onClick.events[0];
expect((event.target as HTMLElement).tagName.toLowerCase()).toBe('ion-input');
});

test('clicking native input within item should fire click event with target as ion-input', async ({ page }) => {
await page.setContent(
`
<ion-item>
<ion-input label="Input"></ion-input>
</ion-item>
`,
config
);

const nativeInput = page.locator('.native-input');
const onClick = await page.spyOnEvent('click');

await nativeInput.click();
expect(onClick).toHaveReceivedEventTimes(1);

// Verify that the event target is the ion-input and not the native input
const event = onClick.events[0];
expect((event.target as HTMLElement).tagName.toLowerCase()).toBe('ion-input');
});
});
});
10 changes: 8 additions & 2 deletions core/src/components/item/item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
if (firstInteractive !== undefined && !multipleInputs) {
const path = ev.composedPath();
const target = path[0] as HTMLElement;

if (ev.isTrusted) {
/**
* Dispatches a click event to the first interactive element,
Expand All @@ -304,9 +305,14 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
*/
if (firstInteractive.tagName === 'ION-INPUT' || firstInteractive.tagName === 'ION-TEXTAREA') {
(firstInteractive as HTMLIonInputElement | HTMLIonTextareaElement).setFocus();
} else {
firstInteractive.click();
}
firstInteractive.click();
/**
* Stop the item event from being triggered
* as the firstInteractive click event will also
* trigger the item click event.
*/
ev.stopImmediatePropagation();
}
}
}
Expand Down
48 changes: 45 additions & 3 deletions core/src/components/radio/test/item/radio.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,16 @@ configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, screenshot, co
await expect(list).toHaveScreenshot(screenshot(`radio-stacked-label-in-item`));
});
});
});

test.describe(title('radio: ionChange'), () => {
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
test.describe(title('radio: item functionality'), () => {
test('clicking padded space within item should click the radio', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/27169',
});

await page.setContent(
`
<ion-radio-group>
Expand All @@ -93,11 +100,11 @@ configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, screenshot, co
`,
config
);
const itemNative = page.locator('.item-native');
const item = page.locator('ion-item');
const ionChange = await page.spyOnEvent('ionChange');

// Clicks the padded space within the item
await itemNative.click({
await item.click({
position: {
x: 5,
y: 5,
Expand All @@ -106,5 +113,40 @@ configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, screenshot, co

expect(ionChange).toHaveReceivedEvent();
});

test('clicking padded space within item should fire one click event', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/29758',
});

await page.setContent(
`
<ion-item>
<ion-radio>
Radio
</ion-radio>
</ion-item>
`,
config
);

const item = page.locator('ion-item');
const onClick = await page.spyOnEvent('click');

// Click the padding area (5px from left edge)
await item.click({
position: {
x: 5,
y: 5,
},
});

expect(onClick).toHaveReceivedEventTimes(1);

// Verify that the event target is the radio and not the item
const event = onClick.events[0];
expect((event.target as HTMLElement).tagName.toLowerCase()).toBe('ion-radio');
});
});
});
28 changes: 0 additions & 28 deletions core/src/components/select/test/basic/select.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -294,34 +294,6 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
await select.evaluate((el: HTMLIonSelectElement) => (el.value = 'banana'));
await expect(ionChange).not.toHaveReceivedEvent();
});

test('clicking padded space within item should click the select', async ({ page }) => {
await page.setContent(
`
<ion-item>
<ion-select label="Fruit" interface="action-sheet">
<ion-select-option value="apple">Apple</ion-select-option>
<ion-select-option value="banana">Banana</ion-select-option>
</ion-select>
</ion-item>
`,
config
);
const itemNative = page.locator('.item-native');
const ionActionSheetDidPresent = await page.spyOnEvent('ionActionSheetDidPresent');

// Clicks the padded space within the item
await itemNative.click({
position: {
x: 5,
y: 5,
},
});

await ionActionSheetDidPresent.next();

expect(ionActionSheetDidPresent).toHaveReceivedEvent();
});
});
});

Expand Down
Loading
Loading