mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(picker-column-option): add the new component (#28591)
This commit is contained in:
@@ -918,6 +918,10 @@ ion-picker-column,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-picker-column,prop,value,number | string | undefined,undefined,false,false
|
||||
ion-picker-column,event,ionChange,PickerColumnItem,true
|
||||
|
||||
ion-picker-column-option,shadow
|
||||
ion-picker-column-option,prop,disabled,boolean,false,false,false
|
||||
ion-picker-column-option,prop,value,any,undefined,false,false
|
||||
|
||||
ion-picker-legacy,scoped
|
||||
ion-picker-legacy,prop,animated,boolean,true,false,false
|
||||
ion-picker-legacy,prop,backdropDismiss,boolean,true,false,false
|
||||
|
||||
29
core/src/components.d.ts
vendored
29
core/src/components.d.ts
vendored
@@ -1986,6 +1986,16 @@ export namespace Components {
|
||||
*/
|
||||
"value"?: string | number;
|
||||
}
|
||||
interface IonPickerColumnOption {
|
||||
/**
|
||||
* If `true`, the user cannot interact with the picker column option.
|
||||
*/
|
||||
"disabled": boolean;
|
||||
/**
|
||||
* The text value of the option.
|
||||
*/
|
||||
"value"?: any | null;
|
||||
}
|
||||
interface IonPickerLegacy {
|
||||
/**
|
||||
* If `true`, the picker will animate.
|
||||
@@ -4053,6 +4063,12 @@ declare global {
|
||||
prototype: HTMLIonPickerColumnElement;
|
||||
new (): HTMLIonPickerColumnElement;
|
||||
};
|
||||
interface HTMLIonPickerColumnOptionElement extends Components.IonPickerColumnOption, HTMLStencilElement {
|
||||
}
|
||||
var HTMLIonPickerColumnOptionElement: {
|
||||
prototype: HTMLIonPickerColumnOptionElement;
|
||||
new (): HTMLIonPickerColumnOptionElement;
|
||||
};
|
||||
interface HTMLIonPickerLegacyElementEventMap {
|
||||
"ionPickerDidPresent": void;
|
||||
"ionPickerWillPresent": void;
|
||||
@@ -4647,6 +4663,7 @@ declare global {
|
||||
"ion-note": HTMLIonNoteElement;
|
||||
"ion-picker": HTMLIonPickerElement;
|
||||
"ion-picker-column": HTMLIonPickerColumnElement;
|
||||
"ion-picker-column-option": HTMLIonPickerColumnOptionElement;
|
||||
"ion-picker-legacy": HTMLIonPickerLegacyElement;
|
||||
"ion-picker-legacy-column": HTMLIonPickerLegacyColumnElement;
|
||||
"ion-popover": HTMLIonPopoverElement;
|
||||
@@ -6616,6 +6633,16 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"value"?: string | number;
|
||||
}
|
||||
interface IonPickerColumnOption {
|
||||
/**
|
||||
* If `true`, the user cannot interact with the picker column option.
|
||||
*/
|
||||
"disabled"?: boolean;
|
||||
/**
|
||||
* The text value of the option.
|
||||
*/
|
||||
"value"?: any | null;
|
||||
}
|
||||
interface IonPickerLegacy {
|
||||
/**
|
||||
* If `true`, the picker will animate.
|
||||
@@ -8086,6 +8113,7 @@ declare namespace LocalJSX {
|
||||
"ion-note": IonNote;
|
||||
"ion-picker": IonPicker;
|
||||
"ion-picker-column": IonPickerColumn;
|
||||
"ion-picker-column-option": IonPickerColumnOption;
|
||||
"ion-picker-legacy": IonPickerLegacy;
|
||||
"ion-picker-legacy-column": IonPickerLegacyColumn;
|
||||
"ion-popover": IonPopover;
|
||||
@@ -8183,6 +8211,7 @@ declare module "@stencil/core" {
|
||||
"ion-note": LocalJSX.IonNote & JSXBase.HTMLAttributes<HTMLIonNoteElement>;
|
||||
"ion-picker": LocalJSX.IonPicker & JSXBase.HTMLAttributes<HTMLIonPickerElement>;
|
||||
"ion-picker-column": LocalJSX.IonPickerColumn & JSXBase.HTMLAttributes<HTMLIonPickerColumnElement>;
|
||||
"ion-picker-column-option": LocalJSX.IonPickerColumnOption & JSXBase.HTMLAttributes<HTMLIonPickerColumnOptionElement>;
|
||||
"ion-picker-legacy": LocalJSX.IonPickerLegacy & JSXBase.HTMLAttributes<HTMLIonPickerLegacyElement>;
|
||||
"ion-picker-legacy-column": LocalJSX.IonPickerLegacyColumn & JSXBase.HTMLAttributes<HTMLIonPickerLegacyColumnElement>;
|
||||
"ion-popover": LocalJSX.IonPopover & JSXBase.HTMLAttributes<HTMLIonPopoverElement>;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import type { ComponentInterface } from '@stencil/core';
|
||||
import { Component, Element, Host, Prop, State, Watch, h } from '@stencil/core';
|
||||
import { inheritAttributes } from '@utils/helpers';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-picker-column-option',
|
||||
shadow: true,
|
||||
})
|
||||
export class PickerColumnOption implements ComponentInterface {
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
/**
|
||||
* The aria-label of the option.
|
||||
*
|
||||
* If the value changes, then it will trigger a
|
||||
* re-render of the picker since it's a @State variable.
|
||||
* Otherwise, the `aria-label` attribute cannot be updated
|
||||
* after the component is loaded.
|
||||
*/
|
||||
@State() ariaLabel?: string | null = null;
|
||||
|
||||
/**
|
||||
* If `true`, the user cannot interact with the picker column option.
|
||||
*/
|
||||
@Prop() disabled = false;
|
||||
|
||||
/**
|
||||
* The text value of the option.
|
||||
*/
|
||||
@Prop() value?: any | null;
|
||||
|
||||
/**
|
||||
* The aria-label of the option has changed after the
|
||||
* first render and needs to be updated within the component.
|
||||
*
|
||||
* @param ariaLbl The new aria-label value.
|
||||
*/
|
||||
@Watch('aria-label')
|
||||
onAriaLabelChange(ariaLbl: string) {
|
||||
this.ariaLabel = ariaLbl;
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
const inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
|
||||
/**
|
||||
* The initial value of `aria-label` needs to be set for
|
||||
* the first render.
|
||||
*/
|
||||
this.ariaLabel = inheritedAttributes['aria-label'] || null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { value, disabled, ariaLabel } = this;
|
||||
|
||||
return (
|
||||
<Host>
|
||||
<button
|
||||
tabindex="-1"
|
||||
aria-label={ariaLabel}
|
||||
class={{
|
||||
'picker-opt': true,
|
||||
'picker-opt-disabled': !!disabled,
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<slot>{value}</slot>
|
||||
</button>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Picker Column Option - Basic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<ion-picker-column-option> my option </ion-picker-column-option>
|
||||
<ion-picker-column-option aria-label="the best one"> other option </ion-picker-column-option>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,18 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
/**
|
||||
* This behavior does not vary across directions
|
||||
*/
|
||||
configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
|
||||
test.describe(title('picker column option: a11y'), () => {
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
await page.goto(`/src/components/picker-column-option/test/a11y`, config);
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Picker Column Option - Basic</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(250px, 1fr));
|
||||
grid-row-gap: 20px;
|
||||
grid-column-gap: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
||||
color: #6f7378;
|
||||
|
||||
margin-top: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header translucent="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>Picker Column Option - Basic</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding">
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Default</h2>
|
||||
<ion-picker-column-option> my option </ion-picker-column-option>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
@@ -53,6 +53,7 @@ export const DIRECTIVES = [
|
||||
d.IonNote,
|
||||
d.IonPicker,
|
||||
d.IonPickerColumn,
|
||||
d.IonPickerColumnOption,
|
||||
d.IonPickerLegacy,
|
||||
d.IonProgressBar,
|
||||
d.IonRadio,
|
||||
|
||||
@@ -1475,6 +1475,28 @@ export declare interface IonPickerColumn extends Components.IonPickerColumn {
|
||||
}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['disabled', 'value']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-picker-column-option',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['disabled', 'value'],
|
||||
})
|
||||
export class IonPickerColumnOption {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export declare interface IonPickerColumnOption extends Components.IonPickerColumnOption {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['animated', 'backdropDismiss', 'buttons', 'columns', 'cssClass', 'duration', 'enterAnimation', 'htmlAttributes', 'isOpen', 'keyboardClose', 'leaveAnimation', 'mode', 'showBackdrop', 'trigger'],
|
||||
methods: ['present', 'dismiss', 'onDidDismiss', 'onWillDismiss', 'getColumn']
|
||||
|
||||
@@ -53,6 +53,7 @@ import { defineCustomElement as defineIonNavLink } from '@ionic/core/components/
|
||||
import { defineCustomElement as defineIonNote } from '@ionic/core/components/ion-note.js';
|
||||
import { defineCustomElement as defineIonPicker } from '@ionic/core/components/ion-picker.js';
|
||||
import { defineCustomElement as defineIonPickerColumn } from '@ionic/core/components/ion-picker-column.js';
|
||||
import { defineCustomElement as defineIonPickerColumnOption } from '@ionic/core/components/ion-picker-column-option.js';
|
||||
import { defineCustomElement as defineIonPickerLegacy } from '@ionic/core/components/ion-picker-legacy.js';
|
||||
import { defineCustomElement as defineIonProgressBar } from '@ionic/core/components/ion-progress-bar.js';
|
||||
import { defineCustomElement as defineIonRadio } from '@ionic/core/components/ion-radio.js';
|
||||
@@ -1470,6 +1471,30 @@ export declare interface IonPickerColumn extends Components.IonPickerColumn {
|
||||
}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonPickerColumnOption,
|
||||
inputs: ['disabled', 'value']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-picker-column-option',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['disabled', 'value'],
|
||||
standalone: true
|
||||
})
|
||||
export class IonPickerColumnOption {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export declare interface IonPickerColumnOption extends Components.IonPickerColumnOption {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonPickerLegacy,
|
||||
inputs: ['animated', 'backdropDismiss', 'buttons', 'columns', 'cssClass', 'duration', 'enterAnimation', 'htmlAttributes', 'isOpen', 'keyboardClose', 'leaveAnimation', 'mode', 'showBackdrop', 'trigger'],
|
||||
|
||||
@@ -46,6 +46,7 @@ import { defineCustomElement as defineIonNavLink } from '@ionic/core/components/
|
||||
import { defineCustomElement as defineIonNote } from '@ionic/core/components/ion-note.js';
|
||||
import { defineCustomElement as defineIonPicker } from '@ionic/core/components/ion-picker.js';
|
||||
import { defineCustomElement as defineIonPickerColumn } from '@ionic/core/components/ion-picker-column.js';
|
||||
import { defineCustomElement as defineIonPickerColumnOption } from '@ionic/core/components/ion-picker-column-option.js';
|
||||
import { defineCustomElement as defineIonProgressBar } from '@ionic/core/components/ion-progress-bar.js';
|
||||
import { defineCustomElement as defineIonRadio } from '@ionic/core/components/ion-radio.js';
|
||||
import { defineCustomElement as defineIonRadioGroup } from '@ionic/core/components/ion-radio-group.js';
|
||||
@@ -113,6 +114,7 @@ export const IonNavLink = /*@__PURE__*/createReactComponent<JSX.IonNavLink, HTML
|
||||
export const IonNote = /*@__PURE__*/createReactComponent<JSX.IonNote, HTMLIonNoteElement>('ion-note', undefined, undefined, defineIonNote);
|
||||
export const IonPicker = /*@__PURE__*/createReactComponent<JSX.IonPicker, HTMLIonPickerElement>('ion-picker', undefined, undefined, defineIonPicker);
|
||||
export const IonPickerColumn = /*@__PURE__*/createReactComponent<JSX.IonPickerColumn, HTMLIonPickerColumnElement>('ion-picker-column', undefined, undefined, defineIonPickerColumn);
|
||||
export const IonPickerColumnOption = /*@__PURE__*/createReactComponent<JSX.IonPickerColumnOption, HTMLIonPickerColumnOptionElement>('ion-picker-column-option', undefined, undefined, defineIonPickerColumnOption);
|
||||
export const IonProgressBar = /*@__PURE__*/createReactComponent<JSX.IonProgressBar, HTMLIonProgressBarElement>('ion-progress-bar', undefined, undefined, defineIonProgressBar);
|
||||
export const IonRadio = /*@__PURE__*/createReactComponent<JSX.IonRadio, HTMLIonRadioElement>('ion-radio', undefined, undefined, defineIonRadio);
|
||||
export const IonRadioGroup = /*@__PURE__*/createReactComponent<JSX.IonRadioGroup, HTMLIonRadioGroupElement>('ion-radio-group', undefined, undefined, defineIonRadioGroup);
|
||||
|
||||
@@ -52,6 +52,7 @@ import { defineCustomElement as defineIonNavLink } from '@ionic/core/components/
|
||||
import { defineCustomElement as defineIonNote } from '@ionic/core/components/ion-note.js';
|
||||
import { defineCustomElement as defineIonPicker } from '@ionic/core/components/ion-picker.js';
|
||||
import { defineCustomElement as defineIonPickerColumn } from '@ionic/core/components/ion-picker-column.js';
|
||||
import { defineCustomElement as defineIonPickerColumnOption } from '@ionic/core/components/ion-picker-column-option.js';
|
||||
import { defineCustomElement as defineIonProgressBar } from '@ionic/core/components/ion-progress-bar.js';
|
||||
import { defineCustomElement as defineIonRadio } from '@ionic/core/components/ion-radio.js';
|
||||
import { defineCustomElement as defineIonRadioGroup } from '@ionic/core/components/ion-radio-group.js';
|
||||
@@ -586,6 +587,12 @@ export const IonPickerColumn = /*@__PURE__*/ defineContainer<JSX.IonPickerColumn
|
||||
]);
|
||||
|
||||
|
||||
export const IonPickerColumnOption = /*@__PURE__*/ defineContainer<JSX.IonPickerColumnOption>('ion-picker-column-option', defineIonPickerColumnOption, [
|
||||
'disabled',
|
||||
'value'
|
||||
]);
|
||||
|
||||
|
||||
export const IonProgressBar = /*@__PURE__*/ defineContainer<JSX.IonProgressBar>('ion-progress-bar', defineIonProgressBar, [
|
||||
'type',
|
||||
'reversed',
|
||||
|
||||
Reference in New Issue
Block a user