Skip to content

Commit f8f7ffd

Browse files
feat(item-option): add inner and container parts (#30929)
Issue number: N/A --------- ## What is the current behavior? The inner structural elements of item-option are not exposed as shadow parts, preventing users from being able to customize their styles directly. ## What is the new behavior? - Exposes `inner` and `container` shadow parts - Adds e2e test coverage for customizing the shadow parts ## Does this introduce a breaking change? - [ ] Yes - [x] No --------- Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
1 parent 5cdeb7f commit f8f7ffd

3 files changed

Lines changed: 86 additions & 2 deletions

File tree

core/api.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -987,6 +987,8 @@ ion-item-option,css-prop,--background,ios
987987
ion-item-option,css-prop,--background,md
988988
ion-item-option,css-prop,--color,ios
989989
ion-item-option,css-prop,--color,md
990+
ion-item-option,part,container
991+
ion-item-option,part,inner
990992
ion-item-option,part,native
991993

992994
ion-item-options,none

core/src/components/item-option/item-option.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import type { Color } from '../../interface';
1717
* @slot end - Content is placed to the right of the option text in LTR, and to the left in RTL.
1818
*
1919
* @part native - The native HTML button or anchor element that wraps all child elements.
20+
* @part inner - The inner wrapper element that arranges the option content.
21+
* @part container - The container element that wraps the start, icon-only, default, and end slots.
2022
*/
2123
@Component({
2224
tag: 'ion-item-option',
@@ -109,9 +111,9 @@ export class ItemOption implements ComponentInterface, AnchorInterface, ButtonIn
109111
})}
110112
>
111113
<TagType {...attrs} class="button-native" part="native" disabled={disabled}>
112-
<span class="button-inner">
114+
<span class="button-inner" part="inner">
113115
<slot name="top"></slot>
114-
<div class="horizontal-wrapper">
116+
<div class="horizontal-wrapper" part="container">
115117
<slot name="start"></slot>
116118
<slot name="icon-only"></slot>
117119
<slot></slot>
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { expect } from '@playwright/test';
2+
import { configs, test } from '@utils/test/playwright';
3+
4+
/**
5+
* This behavior does not vary across modes/directions
6+
*/
7+
configs({ directions: ['ltr'], modes: ['md'] }).forEach(({ title, config }) => {
8+
test.describe(title('item-option: custom'), () => {
9+
test.describe('CSS shadow parts', () => {
10+
test('should be able to customize native part', async ({ page }) => {
11+
await page.setContent(
12+
`
13+
<style>
14+
ion-item-option::part(native) {
15+
background-color: red;
16+
}
17+
</style>
18+
19+
<ion-item-option>Option</ion-item-option>
20+
`,
21+
config
22+
);
23+
24+
const itemOption = page.locator('ion-item-option');
25+
const backgroundColor = await itemOption.evaluate((el) => {
26+
const shadowRoot = el.shadowRoot;
27+
const native = shadowRoot?.querySelector('.button-native');
28+
return native ? window.getComputedStyle(native).backgroundColor : '';
29+
});
30+
expect(backgroundColor).toBe('rgb(255, 0, 0)');
31+
});
32+
33+
test('should be able to customize inner part', async ({ page }) => {
34+
await page.setContent(
35+
`
36+
<style>
37+
ion-item-option::part(inner) {
38+
background-color: green;
39+
}
40+
</style>
41+
42+
<ion-item-option>Option</ion-item-option>
43+
`,
44+
config
45+
);
46+
47+
const itemOption = page.locator('ion-item-option');
48+
const backgroundColor = await itemOption.evaluate((el) => {
49+
const shadowRoot = el.shadowRoot;
50+
const inner = shadowRoot?.querySelector('.button-inner');
51+
return inner ? window.getComputedStyle(inner).backgroundColor : '';
52+
});
53+
expect(backgroundColor).toBe('rgb(0, 128, 0)');
54+
});
55+
56+
test('should be able to customize container part', async ({ page }) => {
57+
await page.setContent(
58+
`
59+
<style>
60+
ion-item-option::part(container) {
61+
background-color: blue;
62+
}
63+
</style>
64+
65+
<ion-item-option>Option</ion-item-option>
66+
`,
67+
config
68+
);
69+
70+
const itemOption = page.locator('ion-item-option');
71+
const backgroundColor = await itemOption.evaluate((el) => {
72+
const shadowRoot = el.shadowRoot;
73+
const container = shadowRoot?.querySelector('.horizontal-wrapper');
74+
return container ? window.getComputedStyle(container).backgroundColor : '';
75+
});
76+
expect(backgroundColor).toBe('rgb(0, 0, 255)');
77+
});
78+
});
79+
});
80+
});

0 commit comments

Comments
 (0)