Skip to content
Draft
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
18 changes: 18 additions & 0 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1741,6 +1741,20 @@ 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,css-prop,--select-text-gap,ios
ion-select,css-prop,--select-text-gap,md
ion-select,css-prop,--select-text-media-border-color,ios
ion-select,css-prop,--select-text-media-border-color,md
ion-select,css-prop,--select-text-media-border-radius,ios
ion-select,css-prop,--select-text-media-border-radius,md
ion-select,css-prop,--select-text-media-border-style,ios
ion-select,css-prop,--select-text-media-border-style,md
ion-select,css-prop,--select-text-media-border-width,ios
ion-select,css-prop,--select-text-media-border-width,md
ion-select,css-prop,--select-text-media-height,ios
ion-select,css-prop,--select-text-media-height,md
ion-select,css-prop,--select-text-media-width,ios
ion-select,css-prop,--select-text-media-width,md
ion-select,part,bottom
ion-select,part,container
ion-select,part,error-text
Expand All @@ -1760,7 +1774,11 @@ ion-select-modal,prop,multiple,boolean | undefined,undefined,false,false
ion-select-modal,prop,options,SelectModalOption[],[],false,false

ion-select-option,shadow
ion-select-option,prop,description,string | undefined,undefined,false,false
ion-select-option,prop,disabled,boolean,false,false,false
ion-select-option,prop,justify,"end" | "space-between" | "start" | undefined,undefined,false,false
ion-select-option,prop,labelPlacement,"end" | "start" | undefined,undefined,false,false
ion-select-option,prop,mode,"ios" | "md",undefined,false,false
ion-select-option,prop,value,any,undefined,false,false

ion-skeleton-text,shadow
Expand Down
35 changes: 35 additions & 0 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3142,11 +3142,27 @@ export namespace Components {
"options": SelectModalOption[];
}
interface IonSelectOption {
/**
* Text that is placed underneath the option text to provide additional details about the option.
*/
"description"?: string;
/**
* If `true`, the user cannot interact with the select option. This property does not apply when `interface="action-sheet"` as `ion-action-sheet` does not allow for disabled buttons.
* @default false
*/
"disabled": boolean;
/**
* How to pack the label and the option's selection control within a line. `"start"`: The label and radio will appear on the left in LTR and on the right in RTL. `"end"`: The label and radio will appear on the right in LTR and on the left in RTL. `"space-between"`: The label and radio will appear on opposite ends of the line with space between the two elements. Applies to the `alert`, `popover`, and `modal` interfaces, but has no visible effect on radio options in `popover` or `modal` on `md` (the radio control is hidden there). When unset, the interface picks a default based on mode and control type.
*/
"justify"?: 'start' | 'end' | 'space-between';
/**
* Where the label is placed relative to the option's selection control (radio circle or checkbox box) when the option is rendered in an `alert`, `popover`, or `modal` interface. `"start"`: The label will appear to the left of the radio in LTR and to the right in RTL. `"end"`: The label will appear to the right of the radio in LTR and to the left in RTL. Applies to the `alert`, `popover`, and `modal` interfaces, but has no visible effect on radio options in `popover` or `modal` on `md` (the radio control is hidden there). When unset, the interface picks a default based on mode and control type.
*/
"labelPlacement"?: 'start' | 'end';
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* The text value of the option.
*/
Expand Down Expand Up @@ -8344,11 +8360,27 @@ declare namespace LocalJSX {
"options"?: SelectModalOption[];
}
interface IonSelectOption {
/**
* Text that is placed underneath the option text to provide additional details about the option.
*/
"description"?: string;
/**
* If `true`, the user cannot interact with the select option. This property does not apply when `interface="action-sheet"` as `ion-action-sheet` does not allow for disabled buttons.
* @default false
*/
"disabled"?: boolean;
/**
* How to pack the label and the option's selection control within a line. `"start"`: The label and radio will appear on the left in LTR and on the right in RTL. `"end"`: The label and radio will appear on the right in LTR and on the left in RTL. `"space-between"`: The label and radio will appear on opposite ends of the line with space between the two elements. Applies to the `alert`, `popover`, and `modal` interfaces, but has no visible effect on radio options in `popover` or `modal` on `md` (the radio control is hidden there). When unset, the interface picks a default based on mode and control type.
*/
"justify"?: 'start' | 'end' | 'space-between';
/**
* Where the label is placed relative to the option's selection control (radio circle or checkbox box) when the option is rendered in an `alert`, `popover`, or `modal` interface. `"start"`: The label will appear to the left of the radio in LTR and to the right in RTL. `"end"`: The label will appear to the right of the radio in LTR and to the left in RTL. Applies to the `alert`, `popover`, and `modal` interfaces, but has no visible effect on radio options in `popover` or `modal` on `md` (the radio control is hidden there). When unset, the interface picks a default based on mode and control type.
*/
"labelPlacement"?: 'start' | 'end';
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* The text value of the option.
*/
Expand Down Expand Up @@ -9528,6 +9560,9 @@ declare namespace LocalJSX {
interface IonSelectOptionAttributes {
"disabled": boolean;
"value": string;
"description": string;
"labelPlacement": 'start' | 'end';
"justify": 'start' | 'end' | 'space-between';
}
interface IonSelectPopoverAttributes {
"header": string;
Expand Down
1 change: 1 addition & 0 deletions core/src/components/action-sheet/action-sheet.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@use "../select-option/select-option.overlay";
@import "./action-sheet.vars";

// Action Sheet
Expand Down
19 changes: 18 additions & 1 deletion core/src/components/action-sheet/action-sheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@ import {
safeCall,
setOverlayId,
} from '@utils/overlays';
import { renderOptionLabel } from '@utils/select-option-render';
import { getClassMap } from '@utils/theme';

import { getIonMode } from '../../global/ionic-global';
import type { AnimationBuilder, CssClassMap, FrameworkDelegate, OverlayInterface } from '../../interface';
import type { OverlayEventDetail } from '../../utils/overlays-interface';
import type { SelectActionSheetButton } from '../select/select-interface';

import type { ActionSheetButton } from './action-sheet-interface';
import { iosEnterAnimation } from './animations/ios.enter';
Expand Down Expand Up @@ -562,6 +564,21 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
htmlAttrs['aria-checked'] = isActiveRadio ? 'true' : 'false';
}

/**
* Cast to `SelectActionSheetButton` to access rich content
* fields (`startContent`, `endContent`, `description`)
* that are passed through from `ion-select` but not
* part of the public `ActionSheetButton` interface.
*/
const richButton = b as SelectActionSheetButton;
const optionLabelOptions = {
id: buttonId,
label: richButton.text,
startContent: richButton.startContent,
endContent: richButton.endContent,
description: richButton.description,
};

return (
<button
{...htmlAttrs}
Expand All @@ -583,7 +600,7 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
>
<span class="action-sheet-button-inner">
{b.icon && <ion-icon icon={b.icon} aria-hidden="true" lazy={false} class="action-sheet-icon" />}
{b.text}
{renderOptionLabel(optionLabelOptions, 'action-sheet-button-label', true)}
</span>
{mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
</button>
Expand Down
2 changes: 2 additions & 0 deletions core/src/components/action-sheet/test/basic/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
.my-color-class {
--background: #292929;
--button-background-selected: #222222;
--button-background-activated: #393838;
--button-background-activated-opacity: 1;

--color: #dfdfdf;
--button-color: #dfdfdf;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { configs, test } from '@utils/test/playwright';

import { ActionSheetFixture } from '../basic/fixture';

/**
* This behavior does not vary across directions.
*/
configs({ directions: ['ltr'], modes: ['ios', 'md'] }).forEach(({ config, screenshot, title }) => {
test.describe(title('action sheet: states'), () => {
/**
* `(any-hover: hover)` evaluates to "none" in all three mobile-emulated
* projects, suppressing the hover rules:
*
* - Chromium and WebKit suppress it because of hasTouch and isMobile.
* - Headless Firefox doesn't detect input devices and reports no hover
* capability regardless of context options, so override it via
* launchOptions.firefoxUserPrefs. Bit values: 4 = FINE (mouse),
* 8 = HOVER, 12 = FINE | HOVER.
*
* Viewport, userAgent, and scale factor remain mobile-sized.
*/
test.use({
hasTouch: false,
isMobile: false,
});

test('should render all button states', async ({ page }) => {
await page.goto(`/src/components/action-sheet/test/states`, config);

const actionSheetFixture = new ActionSheetFixture(page, screenshot);

await actionSheetFixture.open('#basic');

const defaultButton = page.locator('ion-action-sheet button.action-sheet-button').first();
await defaultButton.hover();

await actionSheetFixture.screenshot('states');
});
});
});
97 changes: 97 additions & 0 deletions core/src/components/action-sheet/test/states/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Action Sheet - States</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<script type="module">
import { actionSheetController } from '../../../../dist/ionic/index.esm.js';
window.actionSheetController = actionSheetController;
</script>

<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Action Sheet - States</ion-title>
</ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
<button class="expand" id="basic" onclick="presentBasic()">Basic</button>
</ion-content>
</ion-app>

<style>
#delete-button {
color: #eb445a;
}
</style>
<script>
window.addEventListener('ionActionSheetDidDismiss', function (e) {
console.log('didDismiss', e);
});

async function presentBasic() {
const actionSheet = await actionSheetController.create({
header: 'Albums',
subHeader: 'This is a sub header inside of an action sheet',
buttons: [
{
text: 'Default',
icon: 'ellipse-outline',
},
{
text: 'Activated',
icon: 'flash',
cssClass: 'ion-activated',
},
{
text: 'Focused',
icon: 'eye',
cssClass: 'ion-focused',
},
{
text: 'Selected',
icon: 'checkmark-circle',
role: 'selected',
},
{
text: 'Disabled',
icon: 'ban',
disabled: true,
},
],
});

await actionSheet.present();

// Swap the first button's label to "Hovered" while it's hovered so
// the state is obvious.
const defaultButton = actionSheet.querySelector('button.action-sheet-button');
const label = defaultButton?.querySelector('.action-sheet-button-label');

if (label) {
const original = label.textContent;

defaultButton.addEventListener('mouseenter', () => {
label.textContent = 'Hovered';
});

defaultButton.addEventListener('mouseleave', () => {
label.textContent = original;
});
}
}
</script>
</body>
</html>
1 change: 1 addition & 0 deletions core/src/components/alert/alert.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@use "../select-option/select-option.overlay";
@import "./alert.vars";

// Alert
Expand Down
Loading
Loading