feat(select): add wrapper and bottom shadow parts (#30951)

This commit is contained in:
Maria Hutt
2026-02-17 12:50:22 -08:00
committed by GitHub
parent 55735df3fa
commit 5cea5aeb44
3 changed files with 64 additions and 3 deletions

View File

@@ -1758,14 +1758,17 @@ ion-select,css-prop,--placeholder-opacity,ios
ion-select,css-prop,--placeholder-opacity,md
ion-select,css-prop,--ripple-color,ios
ion-select,css-prop,--ripple-color,md
ion-select,part,bottom
ion-select,part,container
ion-select,part,error-text
ion-select,part,helper-text
ion-select,part,icon
ion-select,part,inner
ion-select,part,label
ion-select,part,placeholder
ion-select,part,supporting-text
ion-select,part,text
ion-select,part,wrapper
ion-select-modal,scoped
ion-select-modal,prop,cancelText,string,'Close',false,false

View File

@@ -45,6 +45,9 @@ import type { SelectChangeEventDetail, SelectInterface, SelectCompareFn } from '
* @part supporting-text - Supporting text displayed beneath the select.
* @part helper-text - Supporting text displayed beneath the select when the select is valid.
* @part error-text - Supporting text displayed beneath the select when the select is invalid and touched.
* @part bottom - The container element for helper text, error text, and counter.
* @part wrapper - The clickable label element that wraps the entire form field (label text, slots, selected values or placeholder, and toggle icons).
* @part inner - The inner element of the wrapper that manages the slots, selected values or placeholder, and toggle icons.
*/
@Component({
tag: 'ion-select',
@@ -1173,7 +1176,11 @@ export class Select implements ComponentInterface {
return;
}
return <div class="select-bottom">{this.renderHintText()}</div>;
return (
<div class="select-bottom" part="bottom">
{this.renderHintText()}
</div>
);
}
render() {
@@ -1246,9 +1253,9 @@ export class Select implements ComponentInterface {
[`select-label-placement-${labelPlacement}`]: true,
})}
>
<label class="select-wrapper" id="select-label" onClick={this.onLabelClick}>
<label class="select-wrapper" id="select-label" onClick={this.onLabelClick} part="wrapper">
{this.renderLabelContainer()}
<div class="select-wrapper-inner">
<div class="select-wrapper-inner" part="inner">
<slot name="start"></slot>
<div class="native-wrapper" ref={(el) => (this.nativeWrapperEl = el)} part="container">
{this.renderSelectText()}

View File

@@ -73,6 +73,57 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
await expect(wrapper).toHaveScreenshot(screenshot(`select-custom-parts-diff`));
});
test('should be able to customize wrapper and bottom using css parts', async ({ page }) => {
test.info().annotations.push({
type: 'issue',
description: 'https://github.com/ionic-team/ionic-framework/issues/29918',
});
await page.setContent(
`
<style>
ion-select::part(wrapper) {
background-color: red;
}
ion-select::part(inner) {
background-color: orange;
}
ion-select::part(bottom) {
background-color: green;
}
</style>
<ion-select label="Select" label-placement="stacked" placeholder="Fruits" helper-text="Helper text">
<ion-select-option value="a">Apple</ion-select-option>
</ion-select>
`,
config
);
const select = page.locator('ion-select');
const wrapper = select.locator('.select-wrapper');
const wrapperInner = select.locator('.select-wrapper-inner');
const bottom = select.locator('.select-bottom');
const wrapperBackgroundColor = await wrapper.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
const wrapperInnerBackgroundColor = await wrapperInner.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
const bottomBackgroundColor = await bottom.evaluate((el) => {
return window.getComputedStyle(el).backgroundColor;
});
expect(wrapperBackgroundColor).toBe('rgb(255, 0, 0)');
expect(wrapperInnerBackgroundColor).toBe('rgb(255, 165, 0)');
expect(bottomBackgroundColor).toBe('rgb(0, 128, 0)');
});
test('should render custom cancel text when prop is provided with alert interface', async ({ page }) => {
await page.setContent(
`