Skip to content
Open
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
2 changes: 2 additions & 0 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,9 @@ ion-item,css-prop,--ripple-color,ios
ion-item,css-prop,--ripple-color,md
ion-item,css-prop,--transition,ios
ion-item,css-prop,--transition,md
ion-item,part,container
ion-item,part,detail-icon
ion-item,part,inner
ion-item,part,native

ion-item-divider,shadow
Expand Down
6 changes: 4 additions & 2 deletions core/src/components/item/item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import type { RouterDirection } from '../router/utils/interface';
* @slot end - Content is placed to the right of the item text in LTR, and to the left in RTL.
*
* @part native - The native HTML button, anchor or div element that wraps all child elements.
* @part inner - The inner container element that wraps the item content.
* @part container - The wrapper element that contains the default slot.
* @part detail-icon - The chevron icon for the item. Only applies when `detail="true"`.
*/
@Component({
Expand Down Expand Up @@ -390,8 +392,8 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
{...clickFn}
>
<slot name="start" onSlotchange={this.updateInteractivityOnSlotChange}></slot>
<div class="item-inner">
<div class="input-wrapper">
<div class="item-inner" part="inner">
<div class="input-wrapper" part="container">
<slot onSlotchange={this.updateInteractivityOnSlotChange}></slot>
</div>
<slot name="end" onSlotchange={this.updateInteractivityOnSlotChange}></slot>
Expand Down
49 changes: 0 additions & 49 deletions core/src/components/item/test/css-variables/index.html

This file was deleted.

17 changes: 0 additions & 17 deletions core/src/components/item/test/css-variables/item.e2e.ts

This file was deleted.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
174 changes: 174 additions & 0 deletions core/src/components/item/test/custom/item.e2e.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { expect } from '@playwright/test';
import { configs, test } from '@utils/test/playwright';

/**
* This behavior does not vary across modes/directions
*/
configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, config }) => {
test.describe(title('item: custom'), () => {
test.describe(title('CSS shadow parts'), () => {
test('should be able to customize native part', async ({ page }) => {
await page.setContent(
`
<style>
ion-item::part(native) {
background-color: red;
}
</style>

<ion-item>
<ion-label>Item</ion-label>
</ion-item>
`,
config
);

const item = page.locator('ion-item');
const backgroundColor = await item.evaluate((el) => {
const shadowRoot = el.shadowRoot;
const native = shadowRoot?.querySelector('.item-native');
return native ? window.getComputedStyle(native).backgroundColor : '';
});
expect(backgroundColor).toBe('rgb(255, 0, 0)');
});

test('should be able to customize inner part', async ({ page }) => {
await page.setContent(
`
<style>
ion-item::part(inner) {
background-color: green;
}
</style>

<ion-item>
<ion-label>Item</ion-label>
</ion-item>
`,
config
);

const item = page.locator('ion-item');
const backgroundColor = await item.evaluate((el) => {
const shadowRoot = el.shadowRoot;
const inner = shadowRoot?.querySelector('.item-inner');
return inner ? window.getComputedStyle(inner).backgroundColor : '';
});
expect(backgroundColor).toBe('rgb(0, 128, 0)');
});

test('should be able to customize container part', async ({ page }) => {
await page.setContent(
`
<style>
ion-item::part(container) {
background-color: blue;
}
</style>

<ion-item>
<ion-label>Item</ion-label>
</ion-item>
`,
config
);

const item = page.locator('ion-item');
const backgroundColor = await item.evaluate((el) => {
const shadowRoot = el.shadowRoot;
const container = shadowRoot?.querySelector('.input-wrapper');
return container ? window.getComputedStyle(container).backgroundColor : '';
});
expect(backgroundColor).toBe('rgb(0, 0, 255)');
});

test('should be able to customize detail-icon part', async ({ page }) => {
await page.setContent(
`
<style>
ion-item::part(detail-icon) {
background-color: red;
}
</style>

<ion-item detail="true">
<ion-label>Item</ion-label>
</ion-item>
`,
config
);

const item = page.locator('ion-item');
const backgroundColor = await item.evaluate((el) => {
const shadowRoot = el.shadowRoot;
const detailIcon = shadowRoot?.querySelector('.item-detail-icon');
return detailIcon ? window.getComputedStyle(detailIcon).backgroundColor : '';
});
expect(backgroundColor).toBe('rgb(255, 0, 0)');
});
});

test.describe(title('CSS variables'), () => {
test('should be able to customize background using css variables', async ({ page }) => {
await page.setContent(
`
<style>
ion-item {
--background: red;
}
</style>

<ion-item>
<ion-label>Item</ion-label>
</ion-item>
`,
config
);

const item = page.locator('ion-item');
const backgroundColor = await item.evaluate((el) => {
const shadowRoot = el.shadowRoot;
const native = shadowRoot?.querySelector('.item-native');
return native ? window.getComputedStyle(native).backgroundColor : '';
});
expect(backgroundColor).toBe('rgb(255, 0, 0)');
});

test('should be able to customize padding using css variables', async ({ page }) => {
await page.setContent(
`
<style>
ion-item {
--padding-top: 20px;
--padding-bottom: 20px;
--padding-start: 10px;
--padding-end: 10px;
}
</style>

<ion-item>
<ion-label>Item</ion-label>
</ion-item>
`,
config
);

const item = page.locator('ion-item');
const paddingValues = await item.evaluate((el) => {
const shadowRoot = el.shadowRoot;
const native = shadowRoot?.querySelector('.item-native');
return {
paddingTop: native ? window.getComputedStyle(native).paddingTop : '',
paddingBottom: native ? window.getComputedStyle(native).paddingBottom : '',
paddingStart: native ? window.getComputedStyle(native).paddingLeft : '',
paddingEnd: native ? window.getComputedStyle(native).paddingRight : '',
};
});
expect(paddingValues.paddingTop).toBe('20px');
expect(paddingValues.paddingBottom).toBe('20px');
expect(paddingValues.paddingStart).toBe('10px');
expect(paddingValues.paddingEnd).toBe('10px');
});
});
});
});
Loading