mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
Compare commits
7 Commits
sp/next
...
st/test-he
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dffb10875 | ||
|
|
5234224700 | ||
|
|
6d6fd4af1b | ||
|
|
7cfee535ac | ||
|
|
167e311474 | ||
|
|
6c500fd6b2 | ||
|
|
6e477b743e |
@@ -30,6 +30,7 @@ This is a comprehensive list of the breaking changes introduced in the major ver
|
||||
- [Progress bar](#version-8x-progress-bar)
|
||||
- [Radio](#version-8x-radio)
|
||||
- [Range](#version-8x-range)
|
||||
- [Searchbar](#version-8x-searchbar)
|
||||
- [Select](#version-8x-select)
|
||||
- [Textarea](#version-8x-textarea)
|
||||
- [Toggle](#version-8x-toggle)
|
||||
@@ -264,6 +265,10 @@ For more information on styling toast buttons, refer to the [Toast Theming docum
|
||||
|
||||
- The `legacy` property and support for the legacy syntax, which involved placing an `ion-range` inside of an `ion-item` with an `ion-label`, have been removed. Ionic will also no longer attempt to automatically associate form controls with sibling `<label>` elements as these label elements are now used inside the form control. Developers should provide a label (either visible text or `aria-label`) directly to the form control. For more information on migrating from the legacy range syntax, refer to the [Range documentation](https://ionicframework.com/docs/api/range#migrating-from-legacy-range-syntax).
|
||||
|
||||
<h4 id="version-8x-searchbar">Searchbar</h4>
|
||||
|
||||
- The `autocapitalize` property now defaults to `'off'`.
|
||||
|
||||
<h4 id="version-8x-select">Select</h4>
|
||||
|
||||
- The `legacy` property and support for the legacy syntax, which involved placing an `ion-select` inside of an `ion-item` with an `ion-label`, have been removed. Ionic will also no longer attempt to automatically associate form controls with sibling `<label>` elements as these label elements are now used inside the form control. Developers should provide a label (either visible text or `aria-label`) directly to the form control. For more information on migrating from the legacy select syntax, refer to the [Select documentation](https://ionicframework.com/docs/api/select#migrating-from-legacy-select-syntax).
|
||||
|
||||
17
core/api.txt
17
core/api.txt
@@ -603,7 +603,7 @@ ion-input,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secon
|
||||
ion-input,prop,counter,boolean,false,false,false
|
||||
ion-input,prop,counterFormatter,((inputLength: number, maxLength: number) => string) | undefined,undefined,false,false
|
||||
ion-input,prop,debounce,number | undefined,undefined,false,false
|
||||
ion-input,prop,disabled,boolean,false,false,false
|
||||
ion-input,prop,disabled,boolean,false,false,true
|
||||
ion-input,prop,enterkeyhint,"done" | "enter" | "go" | "next" | "previous" | "search" | "send" | undefined,undefined,false,false
|
||||
ion-input,prop,errorText,string | undefined,undefined,false,false
|
||||
ion-input,prop,fill,"outline" | "solid" | undefined,undefined,false,false
|
||||
@@ -620,7 +620,7 @@ ion-input,prop,multiple,boolean | undefined,undefined,false,false
|
||||
ion-input,prop,name,string,this.inputId,false,false
|
||||
ion-input,prop,pattern,string | undefined,undefined,false,false
|
||||
ion-input,prop,placeholder,string | undefined,undefined,false,false
|
||||
ion-input,prop,readonly,boolean,false,false,false
|
||||
ion-input,prop,readonly,boolean,false,false,true
|
||||
ion-input,prop,required,boolean,false,false,false
|
||||
ion-input,prop,shape,"round" | undefined,undefined,false,false
|
||||
ion-input,prop,spellcheck,boolean,false,false,false
|
||||
@@ -653,6 +653,12 @@ ion-input,css-prop,--placeholder-font-style
|
||||
ion-input,css-prop,--placeholder-font-weight
|
||||
ion-input,css-prop,--placeholder-opacity
|
||||
|
||||
ion-input-password-toggle,shadow
|
||||
ion-input-password-toggle,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
|
||||
ion-input-password-toggle,prop,hideIcon,string | undefined,undefined,false,false
|
||||
ion-input-password-toggle,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-input-password-toggle,prop,showIcon,string | undefined,undefined,false,false
|
||||
|
||||
ion-item,shadow
|
||||
ion-item,prop,button,boolean,false,false,false
|
||||
ion-item,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
|
||||
@@ -1265,7 +1271,7 @@ ion-row,prop,theme,"ios" | "md" | "ionic",undefined,false,false
|
||||
|
||||
ion-searchbar,scoped
|
||||
ion-searchbar,prop,animated,boolean,false,false,false
|
||||
ion-searchbar,prop,autocapitalize,string,undefined,true,false
|
||||
ion-searchbar,prop,autocapitalize,string,'off',false,false
|
||||
ion-searchbar,prop,autocomplete,"name" | "email" | "tel" | "url" | "on" | "off" | "honorific-prefix" | "given-name" | "additional-name" | "family-name" | "honorific-suffix" | "nickname" | "username" | "new-password" | "current-password" | "one-time-code" | "organization-title" | "organization" | "street-address" | "address-line1" | "address-line2" | "address-line3" | "address-level4" | "address-level3" | "address-level2" | "address-level1" | "country" | "country-name" | "postal-code" | "cc-name" | "cc-given-name" | "cc-additional-name" | "cc-family-name" | "cc-number" | "cc-exp" | "cc-exp-month" | "cc-exp-year" | "cc-csc" | "cc-type" | "transaction-currency" | "transaction-amount" | "language" | "bday" | "bday-day" | "bday-month" | "bday-year" | "sex" | "tel-country-code" | "tel-national" | "tel-area-code" | "tel-local" | "tel-extension" | "impp" | "photo",'off',false,false
|
||||
ion-searchbar,prop,autocorrect,"off" | "on",'off',false,false
|
||||
ion-searchbar,prop,cancelButtonIcon,string,config.get('backButtonIcon', arrowBackSharp) as string,false,false
|
||||
@@ -1671,4 +1677,7 @@ ion-toolbar,css-prop,--opacity
|
||||
ion-toolbar,css-prop,--padding-bottom
|
||||
ion-toolbar,css-prop,--padding-end
|
||||
ion-toolbar,css-prop,--padding-start
|
||||
ion-toolbar,css-prop,--padding-top
|
||||
ion-toolbar,css-prop,--padding-top
|
||||
|
||||
test-header,scoped
|
||||
test-header,prop,testTitle,string | undefined,undefined,false,false
|
||||
64
core/src/components.d.ts
vendored
64
core/src/components.d.ts
vendored
@@ -1471,6 +1471,25 @@ export namespace Components {
|
||||
*/
|
||||
"value"?: string | number | null;
|
||||
}
|
||||
interface IonInputPasswordToggle {
|
||||
/**
|
||||
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
|
||||
*/
|
||||
"color"?: Color;
|
||||
/**
|
||||
* The icon that can be used to represent hiding a password. If not set, the "eyeOff" Ionicon will be used.
|
||||
*/
|
||||
"hideIcon"?: string;
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
"mode"?: "ios" | "md";
|
||||
/**
|
||||
* The icon that can be used to represent showing a password. If not set, the "eye" Ionicon will be used.
|
||||
*/
|
||||
"showIcon"?: string;
|
||||
"type": TextFieldTypes;
|
||||
}
|
||||
interface IonItem {
|
||||
/**
|
||||
* If `true`, a button tag will be rendered and the item will be tappable.
|
||||
@@ -3741,6 +3760,9 @@ export namespace Components {
|
||||
*/
|
||||
"theme"?: "ios" | "md" | "ionic";
|
||||
}
|
||||
interface TestHeader {
|
||||
"testTitle"?: string;
|
||||
}
|
||||
}
|
||||
export interface IonAccordionGroupCustomEvent<T> extends CustomEvent<T> {
|
||||
detail: T;
|
||||
@@ -4323,6 +4345,12 @@ declare global {
|
||||
prototype: HTMLIonInputElement;
|
||||
new (): HTMLIonInputElement;
|
||||
};
|
||||
interface HTMLIonInputPasswordToggleElement extends Components.IonInputPasswordToggle, HTMLStencilElement {
|
||||
}
|
||||
var HTMLIonInputPasswordToggleElement: {
|
||||
prototype: HTMLIonInputPasswordToggleElement;
|
||||
new (): HTMLIonInputPasswordToggleElement;
|
||||
};
|
||||
interface HTMLIonItemElement extends Components.IonItem, HTMLStencilElement {
|
||||
}
|
||||
var HTMLIonItemElement: {
|
||||
@@ -5112,6 +5140,12 @@ declare global {
|
||||
prototype: HTMLIonToolbarElement;
|
||||
new (): HTMLIonToolbarElement;
|
||||
};
|
||||
interface HTMLTestHeaderElement extends Components.TestHeader, HTMLStencilElement {
|
||||
}
|
||||
var HTMLTestHeaderElement: {
|
||||
prototype: HTMLTestHeaderElement;
|
||||
new (): HTMLTestHeaderElement;
|
||||
};
|
||||
interface HTMLElementTagNameMap {
|
||||
"ion-accordion": HTMLIonAccordionElement;
|
||||
"ion-accordion-group": HTMLIonAccordionGroupElement;
|
||||
@@ -5147,6 +5181,7 @@ declare global {
|
||||
"ion-infinite-scroll": HTMLIonInfiniteScrollElement;
|
||||
"ion-infinite-scroll-content": HTMLIonInfiniteScrollContentElement;
|
||||
"ion-input": HTMLIonInputElement;
|
||||
"ion-input-password-toggle": HTMLIonInputPasswordToggleElement;
|
||||
"ion-item": HTMLIonItemElement;
|
||||
"ion-item-divider": HTMLIonItemDividerElement;
|
||||
"ion-item-group": HTMLIonItemGroupElement;
|
||||
@@ -5205,6 +5240,7 @@ declare global {
|
||||
"ion-toast": HTMLIonToastElement;
|
||||
"ion-toggle": HTMLIonToggleElement;
|
||||
"ion-toolbar": HTMLIonToolbarElement;
|
||||
"test-header": HTMLTestHeaderElement;
|
||||
}
|
||||
}
|
||||
declare namespace LocalJSX {
|
||||
@@ -6689,6 +6725,25 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"value"?: string | number | null;
|
||||
}
|
||||
interface IonInputPasswordToggle {
|
||||
/**
|
||||
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
|
||||
*/
|
||||
"color"?: Color;
|
||||
/**
|
||||
* The icon that can be used to represent hiding a password. If not set, the "eyeOff" Ionicon will be used.
|
||||
*/
|
||||
"hideIcon"?: string;
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
"mode"?: "ios" | "md";
|
||||
/**
|
||||
* The icon that can be used to represent showing a password. If not set, the "eye" Ionicon will be used.
|
||||
*/
|
||||
"showIcon"?: string;
|
||||
"type"?: TextFieldTypes;
|
||||
}
|
||||
interface IonItem {
|
||||
/**
|
||||
* If `true`, a button tag will be rendered and the item will be tappable.
|
||||
@@ -8121,7 +8176,7 @@ declare namespace LocalJSX {
|
||||
/**
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
||||
*/
|
||||
"autocapitalize": string;
|
||||
"autocapitalize"?: string;
|
||||
/**
|
||||
* Set the input's autocomplete property.
|
||||
*/
|
||||
@@ -9029,6 +9084,9 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"theme"?: "ios" | "md" | "ionic";
|
||||
}
|
||||
interface TestHeader {
|
||||
"testTitle"?: string;
|
||||
}
|
||||
interface IntrinsicElements {
|
||||
"ion-accordion": IonAccordion;
|
||||
"ion-accordion-group": IonAccordionGroup;
|
||||
@@ -9064,6 +9122,7 @@ declare namespace LocalJSX {
|
||||
"ion-infinite-scroll": IonInfiniteScroll;
|
||||
"ion-infinite-scroll-content": IonInfiniteScrollContent;
|
||||
"ion-input": IonInput;
|
||||
"ion-input-password-toggle": IonInputPasswordToggle;
|
||||
"ion-item": IonItem;
|
||||
"ion-item-divider": IonItemDivider;
|
||||
"ion-item-group": IonItemGroup;
|
||||
@@ -9122,6 +9181,7 @@ declare namespace LocalJSX {
|
||||
"ion-toast": IonToast;
|
||||
"ion-toggle": IonToggle;
|
||||
"ion-toolbar": IonToolbar;
|
||||
"test-header": TestHeader;
|
||||
}
|
||||
}
|
||||
export { LocalJSX as JSX };
|
||||
@@ -9162,6 +9222,7 @@ declare module "@stencil/core" {
|
||||
"ion-infinite-scroll": LocalJSX.IonInfiniteScroll & JSXBase.HTMLAttributes<HTMLIonInfiniteScrollElement>;
|
||||
"ion-infinite-scroll-content": LocalJSX.IonInfiniteScrollContent & JSXBase.HTMLAttributes<HTMLIonInfiniteScrollContentElement>;
|
||||
"ion-input": LocalJSX.IonInput & JSXBase.HTMLAttributes<HTMLIonInputElement>;
|
||||
"ion-input-password-toggle": LocalJSX.IonInputPasswordToggle & JSXBase.HTMLAttributes<HTMLIonInputPasswordToggleElement>;
|
||||
"ion-item": LocalJSX.IonItem & JSXBase.HTMLAttributes<HTMLIonItemElement>;
|
||||
"ion-item-divider": LocalJSX.IonItemDivider & JSXBase.HTMLAttributes<HTMLIonItemDividerElement>;
|
||||
"ion-item-group": LocalJSX.IonItemGroup & JSXBase.HTMLAttributes<HTMLIonItemGroupElement>;
|
||||
@@ -9220,6 +9281,7 @@ declare module "@stencil/core" {
|
||||
"ion-toast": LocalJSX.IonToast & JSXBase.HTMLAttributes<HTMLIonToastElement>;
|
||||
"ion-toggle": LocalJSX.IonToggle & JSXBase.HTMLAttributes<HTMLIonToggleElement>;
|
||||
"ion-toolbar": LocalJSX.IonToolbar & JSXBase.HTMLAttributes<HTMLIonToolbarElement>;
|
||||
"test-header": LocalJSX.TestHeader & JSXBase.HTMLAttributes<HTMLTestHeaderElement>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@@ -4,7 +4,7 @@ import { configs, test } from '@utils/test/playwright';
|
||||
/**
|
||||
* Fill="clear" does not render differently based on the direction.
|
||||
*/
|
||||
configs({ directions: ['ltr'], themes: ['ios', 'md', 'ionic'] }).forEach(({ title, config, screenshot }) => {
|
||||
configs({ directions: ['ltr'], modes: ['ios', 'md', 'ionic-md'] }).forEach(({ title, config, screenshot }) => {
|
||||
test.describe(title('button: fill: clear'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.goto(`/src/components/button/test/clear`, config);
|
||||
|
||||
@@ -74,7 +74,7 @@ configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
/**
|
||||
* Shape="rectangular" is only available in the Ionic theme.
|
||||
*/
|
||||
configs({ directions: ['ltr'], themes: ['ionic'] }).forEach(({ title, screenshot, config }) => {
|
||||
configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('button: shape'), () => {
|
||||
test.describe('rectangular', () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
// TODO: FW-6077 - Limit this test to just the Ionic theme on MD mode.
|
||||
configs({ directions: ['ltr'], themes: ['ionic', 'md', 'ios'] }).forEach(({ title, screenshot, config }) => {
|
||||
configs({ directions: ['ltr'], modes: ['ionic-md', 'md', 'ios'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('button: size'), () => {
|
||||
test('should render small buttons', async ({ page }) => {
|
||||
await page.setContent(
|
||||
@@ -65,7 +64,7 @@ configs({ directions: ['ltr'], themes: ['ionic', 'md', 'ios'] }).forEach(({ titl
|
||||
/**
|
||||
* The following tests are specific to the Ionic theme and do not depend on the text direction.
|
||||
*/
|
||||
configs({ directions: ['ltr'], themes: ['ionic'] }).forEach(({ title, screenshot, config }) => {
|
||||
configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('button: size'), () => {
|
||||
test('should render xsmall buttons', async ({ page }) => {
|
||||
await page.setContent(`<ion-button size="xsmall" fill="solid">X-Small Button</ion-button>`, config);
|
||||
|
||||
@@ -31,10 +31,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.options-popover {
|
||||
--width: 300px;
|
||||
}
|
||||
|
||||
ion-modal.ios,
|
||||
ion-popover.datetime-popover.ios {
|
||||
--width: 350px;
|
||||
@@ -49,206 +45,36 @@
|
||||
ion-datetime {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
body.dark {
|
||||
--ion-color-primary: #428cff;
|
||||
--ion-color-primary-rgb: 66, 140, 255;
|
||||
--ion-color-primary-contrast: #ffffff;
|
||||
--ion-color-primary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-primary-shade: #3a7be0;
|
||||
--ion-color-primary-tint: #5598ff;
|
||||
|
||||
--ion-color-secondary: #50c8ff;
|
||||
--ion-color-secondary-rgb: 80, 200, 255;
|
||||
--ion-color-secondary-contrast: #ffffff;
|
||||
--ion-color-secondary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-secondary-shade: #46b0e0;
|
||||
--ion-color-secondary-tint: #62ceff;
|
||||
|
||||
--ion-color-tertiary: #6a64ff;
|
||||
--ion-color-tertiary-rgb: 106, 100, 255;
|
||||
--ion-color-tertiary-contrast: #ffffff;
|
||||
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-tertiary-shade: #5d58e0;
|
||||
--ion-color-tertiary-tint: #7974ff;
|
||||
|
||||
--ion-color-success: #2fdf75;
|
||||
--ion-color-success-rgb: 47, 223, 117;
|
||||
--ion-color-success-contrast: #000000;
|
||||
--ion-color-success-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-success-shade: #29c467;
|
||||
--ion-color-success-tint: #44e283;
|
||||
|
||||
--ion-color-warning: #ffd534;
|
||||
--ion-color-warning-rgb: 255, 213, 52;
|
||||
--ion-color-warning-contrast: #000000;
|
||||
--ion-color-warning-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-warning-shade: #e0bb2e;
|
||||
--ion-color-warning-tint: #ffd948;
|
||||
|
||||
--ion-color-danger: #ff4961;
|
||||
--ion-color-danger-rgb: 255, 73, 97;
|
||||
--ion-color-danger-contrast: #ffffff;
|
||||
--ion-color-danger-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-danger-shade: #e04055;
|
||||
--ion-color-danger-tint: #ff5b71;
|
||||
|
||||
--ion-color-dark: #f4f5f8;
|
||||
--ion-color-dark-rgb: 244, 245, 248;
|
||||
--ion-color-dark-contrast: #000000;
|
||||
--ion-color-dark-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-dark-shade: #d7d8da;
|
||||
--ion-color-dark-tint: #f5f6f9;
|
||||
|
||||
--ion-color-medium: #989aa2;
|
||||
--ion-color-medium-rgb: 152, 154, 162;
|
||||
--ion-color-medium-contrast: #000000;
|
||||
--ion-color-medium-contrast-rgb: 0, 0, 0;
|
||||
--ion-color-medium-shade: #86888f;
|
||||
--ion-color-medium-tint: #a2a4ab;
|
||||
|
||||
--ion-color-light: #222428;
|
||||
--ion-color-light-rgb: 34, 36, 40;
|
||||
--ion-color-light-contrast: #ffffff;
|
||||
--ion-color-light-contrast-rgb: 255, 255, 255;
|
||||
--ion-color-light-shade: #1e2023;
|
||||
--ion-color-light-tint: #383a3e;
|
||||
}
|
||||
|
||||
/*
|
||||
* iOS Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.ios body.dark {
|
||||
--ion-background-color: #000000;
|
||||
--ion-background-color-rgb: 0, 0, 0;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-color-step-50: #0d0d0d;
|
||||
--ion-color-step-100: #1a1a1a;
|
||||
--ion-color-step-150: #262626;
|
||||
--ion-color-step-200: #333333;
|
||||
--ion-color-step-250: #404040;
|
||||
--ion-color-step-300: #4d4d4d;
|
||||
--ion-color-step-350: #595959;
|
||||
--ion-color-step-400: #666666;
|
||||
--ion-color-step-450: #737373;
|
||||
--ion-color-step-500: #808080;
|
||||
--ion-color-step-550: #8c8c8c;
|
||||
--ion-color-step-600: #999999;
|
||||
--ion-color-step-650: #a6a6a6;
|
||||
--ion-color-step-700: #b3b3b3;
|
||||
--ion-color-step-750: #bfbfbf;
|
||||
--ion-color-step-800: #cccccc;
|
||||
--ion-color-step-850: #d9d9d9;
|
||||
--ion-color-step-900: #e6e6e6;
|
||||
--ion-color-step-950: #f2f2f2;
|
||||
|
||||
--ion-item-background: #000000;
|
||||
|
||||
--ion-card-background: #1c1c1d;
|
||||
}
|
||||
|
||||
.ios body.dark ion-modal {
|
||||
--ion-background-color: var(--ion-color-step-100);
|
||||
--ion-toolbar-background: var(--ion-color-step-150);
|
||||
--ion-toolbar-border-color: var(--ion-color-step-250);
|
||||
--ion-item-background: var(--ion-color-step-150);
|
||||
}
|
||||
|
||||
/*
|
||||
* Material Design Dark Theme
|
||||
* -------------------------------------------
|
||||
*/
|
||||
|
||||
.md body.dark {
|
||||
--ion-background-color: #121212;
|
||||
--ion-background-color-rgb: 18, 18, 18;
|
||||
|
||||
--ion-text-color: #ffffff;
|
||||
--ion-text-color-rgb: 255, 255, 255;
|
||||
|
||||
--ion-border-color: #222222;
|
||||
|
||||
--ion-color-step-50: #1e1e1e;
|
||||
--ion-color-step-100: #2a2a2a;
|
||||
--ion-color-step-150: #363636;
|
||||
--ion-color-step-200: #414141;
|
||||
--ion-color-step-250: #4d4d4d;
|
||||
--ion-color-step-300: #595959;
|
||||
--ion-color-step-350: #656565;
|
||||
--ion-color-step-400: #717171;
|
||||
--ion-color-step-450: #7d7d7d;
|
||||
--ion-color-step-500: #898989;
|
||||
--ion-color-step-550: #949494;
|
||||
--ion-color-step-600: #a0a0a0;
|
||||
--ion-color-step-650: #acacac;
|
||||
--ion-color-step-700: #b8b8b8;
|
||||
--ion-color-step-750: #c4c4c4;
|
||||
--ion-color-step-800: #d0d0d0;
|
||||
--ion-color-step-850: #dbdbdb;
|
||||
--ion-color-step-900: #e7e7e7;
|
||||
--ion-color-step-950: #f3f3f3;
|
||||
|
||||
--ion-item-background: #1e1e1e;
|
||||
|
||||
--ion-toolbar-background: #1f1f1f;
|
||||
|
||||
--ion-tab-bar-background: #1f1f1f;
|
||||
|
||||
--ion-card-background: #1e1e1e;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header translucent="true">
|
||||
<ion-toolbar>
|
||||
<ion-title>Datetime - Basic</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button id="popover-trigger">Options</ion-button>
|
||||
</ion-buttons>
|
||||
<ion-popover class="options-popover" trigger="popover-trigger">
|
||||
<ion-list lines="none">
|
||||
<ion-item>
|
||||
<ion-checkbox>Dark Mode</ion-checkbox>
|
||||
</ion-item>
|
||||
<ion-item detail="true" href="?ionic:mode=ios">
|
||||
<ion-label>iOS Mode</ion-label>
|
||||
</ion-item>
|
||||
<ion-item detail="true" href="?ionic:mode=md">
|
||||
<ion-label>MD Mode</ion-label>
|
||||
</ion-item>
|
||||
<test-header test-title="Datetime - Basic">
|
||||
<div slot="items">
|
||||
<ion-item>
|
||||
<ion-toggle id="titleToggle">Show Default Title</ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-toggle id="titleToggle">Show Default Title</ion-toggle>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-toggle id="buttonsToggle">Show Default Buttons</ion-toggle>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-toggle id="buttonsToggle">Show Default Buttons</ion-toggle>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-input label="Locale" placeholder="default" id="locale"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-input label="Locale" placeholder="default" id="locale"></ion-input>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-select label="Color" id="color" value="primary">
|
||||
<ion-select-option value="primary">Primary</ion-select-option>
|
||||
<ion-select-option value="secondary">Secondary</ion-select-option>
|
||||
<ion-select-option value="tertiary">Tertiary</ion-select-option>
|
||||
<ion-select-option value="success">Success</ion-select-option>
|
||||
<ion-select-option value="warning">Warning</ion-select-option>
|
||||
<ion-select-option value="danger">Danger</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-popover>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-item>
|
||||
<ion-select label="Color" id="color" value="primary">
|
||||
<ion-select-option value="primary">Primary</ion-select-option>
|
||||
<ion-select-option value="secondary">Secondary</ion-select-option>
|
||||
<ion-select-option value="tertiary">Tertiary</ion-select-option>
|
||||
<ion-select-option value="success">Success</ion-select-option>
|
||||
<ion-select-option value="warning">Warning</ion-select-option>
|
||||
<ion-select-option value="danger">Danger</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
</div>
|
||||
</test-header>
|
||||
<ion-content class="ion-padding">
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
@@ -333,14 +159,6 @@
|
||||
});
|
||||
});
|
||||
|
||||
const darkModeCheckbox = document.querySelector('ion-checkbox');
|
||||
darkModeCheckbox.addEventListener('ionChange', (ev) => {
|
||||
if (ev.detail.checked) {
|
||||
document.body.classList.add('dark');
|
||||
} else {
|
||||
document.body.classList.remove('dark');
|
||||
}
|
||||
});
|
||||
color.addEventListener('ionChange', (ev) => {
|
||||
datetime.color = ev.target.value;
|
||||
buttons.forEach((button) => {
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
import type { ComponentInterface } from '@stencil/core';
|
||||
import { Component, Element, Host, Prop, h, Watch } from '@stencil/core';
|
||||
import { printIonWarning } from '@utils/logging';
|
||||
import { createColorClasses } from '@utils/theme';
|
||||
import { eyeOff, eye } from 'ionicons/icons';
|
||||
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import type { Color, TextFieldTypes } from '../../interface';
|
||||
|
||||
/**
|
||||
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-input-password-toggle',
|
||||
/**
|
||||
* Empty CSS files are required in order for the mode to be inherited to the
|
||||
* inner ion-button. Otherwise, the setMode callback provided to Stencil will not get called
|
||||
* and we will default to MD mode.
|
||||
*/
|
||||
styleUrls: {
|
||||
ios: 'input-password-toggle.scss',
|
||||
md: 'input-password-toggle.scss',
|
||||
},
|
||||
shadow: true,
|
||||
})
|
||||
export class InputPasswordToggle implements ComponentInterface {
|
||||
private inputElRef!: HTMLIonInputElement | null;
|
||||
|
||||
@Element() el!: HTMLIonInputElement;
|
||||
|
||||
/**
|
||||
* The color to use from your application's color palette.
|
||||
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
|
||||
* For more information on colors, see [theming](/docs/theming/basics).
|
||||
*/
|
||||
@Prop({ reflect: true }) color?: Color;
|
||||
|
||||
/**
|
||||
* The icon that can be used to represent showing a password. If not set, the "eye" Ionicon will be used.
|
||||
*/
|
||||
@Prop() showIcon?: string;
|
||||
|
||||
/**
|
||||
* The icon that can be used to represent hiding a password. If not set, the "eyeOff" Ionicon will be used.
|
||||
*/
|
||||
@Prop() hideIcon?: string;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@Prop({ mutable: true }) type: TextFieldTypes = 'password';
|
||||
|
||||
/**
|
||||
* Whenever the input type changes we need to re-run validation to ensure the password
|
||||
* toggle is being used with the correct input type. If the application changes the type
|
||||
* outside of this component we also need to re-render so the correct icon is shown.
|
||||
*/
|
||||
@Watch('type')
|
||||
onTypeChange(newValue: TextFieldTypes) {
|
||||
if (newValue !== 'text' && newValue !== 'password') {
|
||||
printIonWarning(
|
||||
`ion-input-password-toggle only supports inputs of type "text" or "password". Input of type "${newValue}" is not compatible.`,
|
||||
this.el
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
const { el } = this;
|
||||
|
||||
const inputElRef = (this.inputElRef = el.closest('ion-input'));
|
||||
|
||||
if (!inputElRef) {
|
||||
printIonWarning(
|
||||
'No ancestor ion-input found for ion-input-password-toggle. This component must be slotted inside of an ion-input.',
|
||||
el
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Important: Set the type in connectedCallback because the default value
|
||||
* of this.type may not always be accurate. Usually inputs have the "password" type
|
||||
* but it is possible to have the input to initially have the "text" type. In that scenario
|
||||
* the wrong icon will show briefly before switching to the correct icon. Setting the
|
||||
* type here allows us to avoid that flicker.
|
||||
*/
|
||||
this.type = inputElRef.type;
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
this.inputElRef = null;
|
||||
}
|
||||
|
||||
private togglePasswordVisibility = () => {
|
||||
const { inputElRef } = this;
|
||||
|
||||
if (!inputElRef) {
|
||||
return;
|
||||
}
|
||||
|
||||
inputElRef.type = inputElRef.type === 'text' ? 'password' : 'text';
|
||||
};
|
||||
|
||||
render() {
|
||||
const { color, type } = this;
|
||||
|
||||
const mode = getIonMode(this);
|
||||
|
||||
const showPasswordIcon = this.showIcon ?? eye;
|
||||
const hidePasswordIcon = this.hideIcon ?? eyeOff;
|
||||
|
||||
const isPasswordVisible = type === 'text';
|
||||
|
||||
return (
|
||||
<Host
|
||||
class={createColorClasses(color, {
|
||||
[mode]: true,
|
||||
})}
|
||||
>
|
||||
<ion-button
|
||||
mode={mode}
|
||||
color={color}
|
||||
fill="clear"
|
||||
shape="round"
|
||||
aria-checked={isPasswordVisible ? 'true' : 'false'}
|
||||
aria-label="show password"
|
||||
role="switch"
|
||||
type="button"
|
||||
onPointerDown={(ev) => {
|
||||
/**
|
||||
* This prevents mobile browsers from
|
||||
* blurring the input when the password toggle
|
||||
* button is activated.
|
||||
*/
|
||||
ev.preventDefault();
|
||||
}}
|
||||
onClick={this.togglePasswordVisibility}
|
||||
>
|
||||
<ion-icon
|
||||
slot="icon-only"
|
||||
aria-hidden="true"
|
||||
icon={isPasswordVisible ? hidePasswordIcon : showPasswordIcon}
|
||||
></ion-icon>
|
||||
</ion-button>
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('input password toggle: a11y'), () => {
|
||||
test('should not have accessibility violations', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<main>
|
||||
<ion-input label="input" type="password">
|
||||
<ion-input-password-toggle slot="end"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
</main>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,76 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Input - Toggle Password</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<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>
|
||||
<style>
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(250px, 1fr));
|
||||
grid-row-gap: 20px;
|
||||
grid-column-gap: 20px;
|
||||
}
|
||||
h2 {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
|
||||
color: #6f7378;
|
||||
|
||||
margin-top: 10px;
|
||||
}
|
||||
@media screen and (max-width: 800px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Input - Basic</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content" class="ion-padding">
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Default</h2>
|
||||
<ion-input type="password" value="supersecurepassword" label="Password">
|
||||
<ion-input-password-toggle slot="end"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<h2>Custom Icon</h2>
|
||||
<ion-input type="password" value="supersecurepassword" label="Password">
|
||||
<ion-input-password-toggle show-icon="trash" slot="end"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
</div>
|
||||
<div class="grid-item">
|
||||
<h2>Custom Mode/Color</h2>
|
||||
<ion-input type="password" value="supersecurepassword" label="Password">
|
||||
<ion-input-password-toggle
|
||||
color="danger"
|
||||
mode="ios"
|
||||
show-icon="trash"
|
||||
slot="end"
|
||||
></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
</div>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,50 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('input password toggle: states'), () => {
|
||||
test('should be hidden when inside of a readonly input', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label="input" type="password" readonly="true">
|
||||
<ion-input-password-toggle slot="end"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const inputPasswordToggle = page.locator('ion-input-password-toggle');
|
||||
await expect(inputPasswordToggle).toBeHidden();
|
||||
});
|
||||
test('should be hidden when inside of a disabled input', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label="input" type="password" disabled="true">
|
||||
<ion-input-password-toggle slot="end"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const inputPasswordToggle = page.locator('ion-input-password-toggle');
|
||||
await expect(inputPasswordToggle).toBeHidden();
|
||||
});
|
||||
});
|
||||
|
||||
test.describe(title('input password toggle: rendering'), () => {
|
||||
test('should not have visual regressions', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label="input" type="password">
|
||||
<ion-input-password-toggle slot="end"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const inputPasswordToggle = page.locator('ion-input-password-toggle');
|
||||
|
||||
await expect(inputPasswordToggle).toHaveScreenshot(screenshot('input-password-toggle'));
|
||||
});
|
||||
});
|
||||
});
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 719 B |
Binary file not shown.
|
After Width: | Height: | Size: 748 B |
Binary file not shown.
|
After Width: | Height: | Size: 624 B |
@@ -0,0 +1,115 @@
|
||||
import { h } from '@stencil/core';
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Input } from '../../input/input';
|
||||
import { InputPasswordToggle } from '../input-password-toggle';
|
||||
import { Button } from '../../button/button';
|
||||
import { initialize } from '../../../global/ionic-global';
|
||||
|
||||
describe('input password toggle', () => {
|
||||
it('should toggle input type when clicked', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Input, InputPasswordToggle, Button],
|
||||
template: () => (
|
||||
<ion-input type="password">
|
||||
<ion-input-password-toggle slot="end"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
),
|
||||
});
|
||||
|
||||
const inputPasswordToggle = page.body.querySelector('ion-input-password-toggle')!;
|
||||
const button = inputPasswordToggle.shadowRoot!.querySelector('ion-button')!;
|
||||
const input = page.body.querySelector('ion-input')!;
|
||||
|
||||
expect(input.type).toBe('password');
|
||||
|
||||
button.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(input.type).toBe('text');
|
||||
|
||||
button.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(input.type).toBe('password');
|
||||
});
|
||||
|
||||
it('should render custom icons', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Input, InputPasswordToggle, Button],
|
||||
template: () => (
|
||||
<ion-input type="password">
|
||||
<ion-input-password-toggle showIcon="show" hideIcon="hide" slot="end"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
),
|
||||
});
|
||||
|
||||
const inputPasswordToggle = page.body.querySelector('ion-input-password-toggle')!;
|
||||
const button = inputPasswordToggle.shadowRoot!.querySelector('ion-button')!;
|
||||
const icon = inputPasswordToggle.shadowRoot!.querySelector('ion-icon')!;
|
||||
|
||||
// Grab the attribute to test since we are not actually passing in a valid SVG
|
||||
expect(icon.getAttribute('icon')).toBe('show');
|
||||
|
||||
button.click();
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(icon.getAttribute('icon')).toBe('hide');
|
||||
});
|
||||
|
||||
it('changing the type on the input should update the icon used in password toggle', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Input, InputPasswordToggle, Button],
|
||||
template: () => (
|
||||
<ion-input type="password">
|
||||
<ion-input-password-toggle showIcon="show" hideIcon="hide" slot="end"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
),
|
||||
});
|
||||
|
||||
const inputPasswordToggle = page.body.querySelector('ion-input-password-toggle')!;
|
||||
const input = page.body.querySelector('ion-input')!;
|
||||
const icon = inputPasswordToggle.shadowRoot!.querySelector('ion-icon')!;
|
||||
|
||||
// Grab the attribute to test since we are not actually passing in a valid SVG
|
||||
expect(icon.getAttribute('icon')).toBe('show');
|
||||
|
||||
input.type = 'text';
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(icon.getAttribute('icon')).toBe('hide');
|
||||
|
||||
input.type = 'password';
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(icon.getAttribute('icon')).toBe('show');
|
||||
});
|
||||
|
||||
it('should inherit the mode and color to internal ionic components', async () => {
|
||||
/**
|
||||
* This initialize script tells Stencil how to determine the mode on components.
|
||||
* This is required for any getIonMode internal logic to function properly in spec tests.
|
||||
*/
|
||||
initialize();
|
||||
|
||||
const page = await newSpecPage({
|
||||
components: [Input, InputPasswordToggle, Button],
|
||||
template: () => (
|
||||
<ion-input type="password" color="primary">
|
||||
<ion-input-password-toggle slot="end" mode="ios" color="danger"></ion-input-password-toggle>
|
||||
</ion-input>
|
||||
),
|
||||
});
|
||||
|
||||
const inputPasswordToggle = page.body.querySelector('ion-input-password-toggle')!;
|
||||
const button = inputPasswordToggle.shadowRoot!.querySelector('ion-button')!;
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
// mode is a virtual prop so we need to access it as an attribute
|
||||
expect(button.getAttribute('mode')).toBe('ios');
|
||||
|
||||
// color is an actual prop so we can access the element property
|
||||
expect(button.color).toBe('danger');
|
||||
});
|
||||
});
|
||||
@@ -605,3 +605,12 @@
|
||||
margin-inline-start: $form-control-label-margin;
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The input password toggle component should be hidden when the input is readonly/disabled
|
||||
* because it is not possible to edit a password.
|
||||
*/
|
||||
:host([disabled]) ::slotted(ion-input-password-toggle),
|
||||
:host([readonly]) ::slotted(ion-input-password-toggle) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ export class Input implements ComponentInterface {
|
||||
/**
|
||||
* If `true`, the user cannot interact with the input.
|
||||
*/
|
||||
@Prop() disabled = false;
|
||||
@Prop({ reflect: true }) disabled = false;
|
||||
|
||||
/**
|
||||
* A hint to the browser for which enter key to display.
|
||||
@@ -228,7 +228,7 @@ export class Input implements ComponentInterface {
|
||||
/**
|
||||
* If `true`, the user cannot modify the value.
|
||||
*/
|
||||
@Prop() readonly = false;
|
||||
@Prop({ reflect: true }) readonly = false;
|
||||
|
||||
/**
|
||||
* If `true`, the user must fill in a value before submitting a form.
|
||||
@@ -256,6 +256,20 @@ export class Input implements ComponentInterface {
|
||||
*/
|
||||
@Prop() type: TextFieldTypes = 'text';
|
||||
|
||||
/**
|
||||
* Whenever the type on the input changes we need
|
||||
* to update the internal type prop on the password
|
||||
* toggle so that that correct icon is shown.
|
||||
*/
|
||||
@Watch('type')
|
||||
onTypeChange() {
|
||||
const passwordToggle = this.el.querySelector('ion-input-password-toggle');
|
||||
|
||||
if (passwordToggle) {
|
||||
passwordToggle.type = this.type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The value of the input.
|
||||
*/
|
||||
@@ -346,6 +360,14 @@ export class Input implements ComponentInterface {
|
||||
|
||||
componentDidLoad() {
|
||||
this.originalIonInput = this.ionInput;
|
||||
|
||||
/**
|
||||
* Set the type on the password toggle in the event that this input's
|
||||
* type was set async and does not match the default type for the password toggle.
|
||||
* This can happen when the type is bound using a JS framework binding syntax
|
||||
* such as [type] in Angular.
|
||||
*/
|
||||
this.onTypeChange();
|
||||
}
|
||||
|
||||
componentDidRender() {
|
||||
|
||||
@@ -44,11 +44,7 @@
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>Picker - Basic</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<test-header test-title="Picker - Basic"></test-header>
|
||||
<ion-content class="ion-padding">
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
|
||||
@@ -80,26 +80,11 @@ export class Searchbar implements ComponentInterface {
|
||||
*/
|
||||
@Prop() animated = false;
|
||||
|
||||
/**
|
||||
* Prior to the addition of this property
|
||||
* autocapitalize was enabled by default on iOS
|
||||
* and disabled by default on Android
|
||||
* for Searchbar. The autocapitalize type on HTMLElement
|
||||
* requires that it be a string and never undefined.
|
||||
* However, setting it to a string value would be a breaking change
|
||||
* in behavior, so we use "!" to tell TypeScript that this property
|
||||
* is always defined so we can rely on the browser defaults. Browsers
|
||||
* will automatically set a default value if the developer does not set one.
|
||||
*
|
||||
* In the future, this property will default to "off" to align with
|
||||
* Input and Textarea, and the "!" will not be needed.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
|
||||
* Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
|
||||
*/
|
||||
@Prop() autocapitalize!: string;
|
||||
@Prop() autocapitalize: string = 'off';
|
||||
|
||||
/**
|
||||
* Set the input's autocomplete property.
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { isModeValidForTheme } from '../../../global/ionic-global';
|
||||
|
||||
export type Mode = 'ios' | 'md';
|
||||
export type Mode = 'ios' | 'md' | 'ionic-ios' | 'ionic-md';
|
||||
export type Direction = 'ltr' | 'rtl';
|
||||
export type Theme = 'ios' | 'md' | 'ionic';
|
||||
|
||||
@@ -19,9 +17,9 @@ export type ScreenshotFn = (fileName: string) => string;
|
||||
|
||||
export interface TestConfig {
|
||||
direction: Direction;
|
||||
theme: Theme;
|
||||
palette: Palette;
|
||||
mode: Mode;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
interface TestUtilities {
|
||||
@@ -31,14 +29,36 @@ interface TestUtilities {
|
||||
}
|
||||
|
||||
interface TestConfigOption {
|
||||
modes?: Mode[];
|
||||
directions?: Direction[];
|
||||
palettes?: Palette[];
|
||||
/**
|
||||
* The individual themes (iOS, Material Design and Ionic) to test
|
||||
* against. If unspecified, defaults iOS and Material Design
|
||||
* The available options to test against.
|
||||
* - "ios": Test against iOS theme on iOS mode.
|
||||
* - "md": Test against Material Design theme on Material Design mode.
|
||||
* - "ionic-ios": Test against Ionic theme on iOS mode.
|
||||
* - "ionic-md": Test against Ionic theme on Material Design mode.
|
||||
*
|
||||
* If unspecified, tests will run against "ios" and "md".
|
||||
*/
|
||||
themes?: Theme[];
|
||||
modes?: Mode[];
|
||||
/**
|
||||
* The text direction to test against.
|
||||
*
|
||||
* - "ltr": Test against left-to-right direction.
|
||||
* - "rtl": Test against right-to-left direction.
|
||||
*
|
||||
* If unspecified, tests will run against both directions.
|
||||
*/
|
||||
directions?: Direction[];
|
||||
/**
|
||||
* The color palette to test against.
|
||||
*
|
||||
* - "light": Test against light palette.
|
||||
* - "dark": Test against dark palette.
|
||||
* - "high-contrast": Test against high contrast light palette.
|
||||
* - "high-contrast-dark": Test against high contrast dark palette.
|
||||
*
|
||||
* If unspecified, tests will run against light theme.
|
||||
*/
|
||||
palettes?: Palette[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -48,14 +68,17 @@ interface TestConfigOption {
|
||||
* each test title is unique.
|
||||
*/
|
||||
const generateTitle = (title: string, config: TestConfig): string => {
|
||||
const { direction, palette, theme, mode } = config;
|
||||
|
||||
if (theme === mode) {
|
||||
/**
|
||||
* Fallback to the old test title naming convention
|
||||
* when the theme and mode are the same.
|
||||
*/
|
||||
const { direction, palette, mode, theme } = config;
|
||||
|
||||
/**
|
||||
* The iOS theme can only be used with the iOS mode,
|
||||
* and the MD theme can only be used with the MD mode.
|
||||
*
|
||||
* This logic enables the fallback behavior for existing tests,
|
||||
* where we only tested against a mode, which accounted for both
|
||||
* the theme and mode.
|
||||
*/
|
||||
if (theme === 'ios' || theme === 'md') {
|
||||
if (palette === 'light') {
|
||||
/**
|
||||
* Ionic has many existing tests that existed prior to
|
||||
@@ -63,10 +86,9 @@ const generateTitle = (title: string, config: TestConfig): string => {
|
||||
* compatibility, we will not include the theme in the test
|
||||
* title if the theme is set to light.
|
||||
*/
|
||||
return `${title} - ${theme}/${direction}`;
|
||||
return `${title} - ${mode}/${direction}`;
|
||||
}
|
||||
|
||||
return `${title} - ${theme}/${direction}/${palette}`;
|
||||
return `${title} - ${mode}/${direction}/${palette}`;
|
||||
}
|
||||
|
||||
return `${title} - ${theme}/${mode}/${direction}/${palette}`;
|
||||
@@ -77,13 +99,16 @@ const generateTitle = (title: string, config: TestConfig): string => {
|
||||
* and a test config.
|
||||
*/
|
||||
const generateScreenshotName = (fileName: string, config: TestConfig): string => {
|
||||
const { theme, direction, palette, mode } = config;
|
||||
|
||||
if (theme === mode) {
|
||||
/**
|
||||
* Fallback to the old screenshot naming convention
|
||||
* when the theme and mode are the same.
|
||||
*/
|
||||
const { direction, palette, mode, theme } = config;
|
||||
/**
|
||||
* The iOS theme can only be used with the iOS mode,
|
||||
* and the MD theme can only be used with the MD mode.
|
||||
*
|
||||
* This logic enables the fallback behavior for existing tests,
|
||||
* where we only tested against a mode, which accounted for both
|
||||
* the theme and mode.
|
||||
*/
|
||||
if (theme === 'ios' || theme === 'md') {
|
||||
if (palette === 'light') {
|
||||
/**
|
||||
* Ionic has many existing tests that existed prior to
|
||||
@@ -91,10 +116,9 @@ const generateScreenshotName = (fileName: string, config: TestConfig): string =>
|
||||
* compatibility, we will not include the theme in the screenshot
|
||||
* name if the theme is set to light.
|
||||
*/
|
||||
return `${fileName}-${theme}-${direction}.png`;
|
||||
return `${fileName}-${mode}-${direction}.png`;
|
||||
}
|
||||
|
||||
return `${fileName}-${theme}-${direction}-${palette}.png`;
|
||||
return `${fileName}-${mode}-${direction}-${palette}.png`;
|
||||
}
|
||||
|
||||
return `${fileName}-${theme}-${mode}-${direction}-${palette}.png`;
|
||||
@@ -104,7 +128,7 @@ const generateScreenshotName = (fileName: string, config: TestConfig): string =>
|
||||
* Given a config generate an array of test variants.
|
||||
*/
|
||||
export const configs = (testConfig: TestConfigOption = DEFAULT_TEST_CONFIG_OPTION): TestUtilities[] => {
|
||||
const { modes, themes, directions } = testConfig;
|
||||
const { modes, directions } = testConfig;
|
||||
|
||||
const configs: TestConfig[] = [];
|
||||
|
||||
@@ -113,19 +137,17 @@ export const configs = (testConfig: TestConfigOption = DEFAULT_TEST_CONFIG_OPTIO
|
||||
* fall back to the defaults.
|
||||
*/
|
||||
const processedModes = modes ?? DEFAULT_MODES;
|
||||
const processedTheme = themes ?? DEFAULT_THEMES;
|
||||
const processedDirection = directions ?? DEFAULT_DIRECTIONS;
|
||||
const processedPalette = testConfig.palettes ?? DEFAULT_PALETTES;
|
||||
|
||||
processedModes.forEach((mode) => {
|
||||
processedTheme.forEach((theme) => {
|
||||
if (!isModeValidForTheme(mode, theme)) {
|
||||
return;
|
||||
}
|
||||
processedDirection.forEach((direction) => {
|
||||
processedPalette.forEach((palette) => {
|
||||
configs.push({ theme, direction, palette, mode });
|
||||
});
|
||||
const [themeOrCombinedMode, modeName] = mode.split('-') as [Theme, Mode];
|
||||
const parsedTheme = themeOrCombinedMode;
|
||||
const parsedMode = modeName ?? themeOrCombinedMode;
|
||||
|
||||
processedDirection.forEach((direction) => {
|
||||
processedPalette.forEach((palette) => {
|
||||
configs.push({ direction, palette, mode: parsedMode, theme: parsedTheme });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -140,13 +162,11 @@ export const configs = (testConfig: TestConfigOption = DEFAULT_TEST_CONFIG_OPTIO
|
||||
};
|
||||
|
||||
const DEFAULT_MODES: Mode[] = ['ios', 'md'];
|
||||
const DEFAULT_THEMES: Theme[] = ['ios', 'md'];
|
||||
const DEFAULT_DIRECTIONS: Direction[] = ['ltr', 'rtl'];
|
||||
const DEFAULT_PALETTES: Palette[] = ['light'];
|
||||
|
||||
const DEFAULT_TEST_CONFIG_OPTION = {
|
||||
modes: DEFAULT_MODES,
|
||||
themes: DEFAULT_THEMES,
|
||||
directions: DEFAULT_DIRECTIONS,
|
||||
palettes: DEFAULT_PALETTES,
|
||||
};
|
||||
|
||||
3
core/src/utils/test/test-header.scss
Normal file
3
core/src/utils/test/test-header.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.options-popover {
|
||||
--width: 300px;
|
||||
}
|
||||
81
core/src/utils/test/test-header.tsx
Normal file
81
core/src/utils/test/test-header.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import type { ComponentInterface } from '@stencil/core';
|
||||
import { Component, Host, Prop, h } from '@stencil/core';
|
||||
import { getIonMode } from 'src/global/ionic-global';
|
||||
|
||||
@Component({
|
||||
tag: 'test-header',
|
||||
styleUrl: 'test-header.scss',
|
||||
scoped: true,
|
||||
})
|
||||
export class TestHeader implements ComponentInterface {
|
||||
@Prop() testTitle?: string;
|
||||
|
||||
locale = document.querySelector('#locale');
|
||||
|
||||
setTheme = (ev: CustomEvent) => {
|
||||
window.location.search = `?ionic:theme=${ev.detail.value}`;
|
||||
};
|
||||
|
||||
toggleDir = (ev: CustomEvent) => {
|
||||
if (ev.detail.checked) {
|
||||
document.body.setAttribute('dir', 'rtl');
|
||||
} else {
|
||||
document.body.setAttribute('dir', 'ltr');
|
||||
}
|
||||
};
|
||||
|
||||
togglePalette = (ev: CustomEvent) => {
|
||||
const paletteLinkEl = document.getElementById('palette');
|
||||
if (ev.detail.checked) {
|
||||
paletteLinkEl?.setAttribute('href', 'https://cdn.jsdelivr.net/npm/@ionic/core@next/css/palettes/dark.always.css');
|
||||
paletteLinkEl?.setAttribute('rel', 'stylesheet');
|
||||
paletteLinkEl?.setAttribute('type', 'text/css');
|
||||
} else {
|
||||
paletteLinkEl?.removeAttribute('href');
|
||||
paletteLinkEl?.removeAttribute('rel');
|
||||
paletteLinkEl?.removeAttribute('type');
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Host class={getIonMode(this)}>
|
||||
<ion-header translucent={true}>
|
||||
<ion-toolbar>
|
||||
<ion-title>{this.testTitle}</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button id="popover-trigger">Options</ion-button>
|
||||
</ion-buttons>
|
||||
{/* @ts-ignore */}
|
||||
<ion-popover class="options-popover" trigger="popover-trigger">
|
||||
<ion-list lines="none">
|
||||
<ion-item>
|
||||
<ion-toggle onIonChange={this.togglePalette}>Dark Mode</ion-toggle>
|
||||
</ion-item>
|
||||
<ion-item>
|
||||
<ion-toggle onIonChange={this.toggleDir}>RTL</ion-toggle>
|
||||
</ion-item>
|
||||
{/* <ion-item detail={true} href="?ionic:mode=ios">
|
||||
<ion-label>iOS Mode</ion-label>
|
||||
</ion-item>
|
||||
<ion-item detail={true} href="?ionic:mode=md">
|
||||
<ion-label>MD Mode</ion-label>
|
||||
</ion-item> */}
|
||||
<ion-item>
|
||||
<ion-select onIonChange={this.setTheme} interface="popover" placeholder="Theme">
|
||||
<ion-select-option value="ios">iOS Theme</ion-select-option>
|
||||
<ion-select-option value="md">MD Theme</ion-select-option>
|
||||
<ion-select-option value="ionic">Ionic Theme</ion-select-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
|
||||
<slot name="items"></slot>
|
||||
</ion-list>
|
||||
</ion-popover>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<link id="palette" />
|
||||
</Host>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ export const DIRECTIVES = [
|
||||
d.IonInfiniteScroll,
|
||||
d.IonInfiniteScrollContent,
|
||||
d.IonInput,
|
||||
d.IonInputPasswordToggle,
|
||||
d.IonItem,
|
||||
d.IonItemDivider,
|
||||
d.IonItemGroup,
|
||||
@@ -81,5 +82,6 @@ export const DIRECTIVES = [
|
||||
d.IonTitle,
|
||||
d.IonToast,
|
||||
d.IonToggle,
|
||||
d.IonToolbar
|
||||
d.IonToolbar,
|
||||
d.TestHeader
|
||||
];
|
||||
|
||||
@@ -1016,6 +1016,28 @@ where the user's interaction is typing.
|
||||
}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['color', 'hideIcon', 'mode', 'showIcon']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-input-password-toggle',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['color', 'hideIcon', 'mode', 'showIcon'],
|
||||
})
|
||||
export class IonInputPasswordToggle {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export declare interface IonInputPasswordToggle extends Components.IonInputPasswordToggle {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['button', 'color', 'detail', 'detailIcon', 'disabled', 'download', 'href', 'lines', 'mode', 'rel', 'routerAnimation', 'routerDirection', 'target', 'theme', 'type']
|
||||
})
|
||||
@@ -2405,3 +2427,25 @@ export class IonToolbar {
|
||||
export declare interface IonToolbar extends Components.IonToolbar {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['testTitle']
|
||||
})
|
||||
@Component({
|
||||
selector: 'test-header',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['testTitle'],
|
||||
})
|
||||
export class TestHeader {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export declare interface TestHeader extends Components.TestHeader {}
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import { defineCustomElement as defineIonHeader } from '@ionic/core/components/i
|
||||
import { defineCustomElement as defineIonImg } from '@ionic/core/components/ion-img.js';
|
||||
import { defineCustomElement as defineIonInfiniteScroll } from '@ionic/core/components/ion-infinite-scroll.js';
|
||||
import { defineCustomElement as defineIonInfiniteScrollContent } from '@ionic/core/components/ion-infinite-scroll-content.js';
|
||||
import { defineCustomElement as defineIonInputPasswordToggle } from '@ionic/core/components/ion-input-password-toggle.js';
|
||||
import { defineCustomElement as defineIonItem } from '@ionic/core/components/ion-item.js';
|
||||
import { defineCustomElement as defineIonItemDivider } from '@ionic/core/components/ion-item-divider.js';
|
||||
import { defineCustomElement as defineIonItemGroup } from '@ionic/core/components/ion-item-group.js';
|
||||
@@ -75,6 +76,7 @@ import { defineCustomElement as defineIonThumbnail } from '@ionic/core/component
|
||||
import { defineCustomElement as defineIonTitle } from '@ionic/core/components/ion-title.js';
|
||||
import { defineCustomElement as defineIonToast } from '@ionic/core/components/ion-toast.js';
|
||||
import { defineCustomElement as defineIonToolbar } from '@ionic/core/components/ion-toolbar.js';
|
||||
import { defineCustomElement as defineTestHeader } from '@ionic/core/components/test-header.js';
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonAccordion,
|
||||
inputs: ['disabled', 'mode', 'readonly', 'theme', 'toggleIcon', 'toggleIconSlot', 'value']
|
||||
@@ -978,6 +980,30 @@ export class IonInfiniteScrollContent {
|
||||
export declare interface IonInfiniteScrollContent extends Components.IonInfiniteScrollContent {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonInputPasswordToggle,
|
||||
inputs: ['color', 'hideIcon', 'mode', 'showIcon']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-input-password-toggle',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['color', 'hideIcon', 'mode', 'showIcon'],
|
||||
standalone: true
|
||||
})
|
||||
export class IonInputPasswordToggle {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export declare interface IonInputPasswordToggle extends Components.IonInputPasswordToggle {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineIonItem,
|
||||
inputs: ['button', 'color', 'detail', 'detailIcon', 'disabled', 'download', 'href', 'lines', 'mode', 'rel', 'routerAnimation', 'routerDirection', 'target', 'theme', 'type']
|
||||
@@ -2127,3 +2153,27 @@ export class IonToolbar {
|
||||
export declare interface IonToolbar extends Components.IonToolbar {}
|
||||
|
||||
|
||||
@ProxyCmp({
|
||||
defineCustomElementFn: defineTestHeader,
|
||||
inputs: ['testTitle']
|
||||
})
|
||||
@Component({
|
||||
selector: 'test-header',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: '<ng-content></ng-content>',
|
||||
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
|
||||
inputs: ['testTitle'],
|
||||
standalone: true
|
||||
})
|
||||
export class TestHeader {
|
||||
protected el: HTMLElement;
|
||||
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
|
||||
c.detach();
|
||||
this.el = r.nativeElement;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export declare interface TestHeader extends Components.TestHeader {}
|
||||
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import { defineCustomElement as defineIonImg } from '@ionic/core/components/ion-
|
||||
import { defineCustomElement as defineIonInfiniteScroll } from '@ionic/core/components/ion-infinite-scroll.js';
|
||||
import { defineCustomElement as defineIonInfiniteScrollContent } from '@ionic/core/components/ion-infinite-scroll-content.js';
|
||||
import { defineCustomElement as defineIonInput } from '@ionic/core/components/ion-input.js';
|
||||
import { defineCustomElement as defineIonInputPasswordToggle } from '@ionic/core/components/ion-input-password-toggle.js';
|
||||
import { defineCustomElement as defineIonItemDivider } from '@ionic/core/components/ion-item-divider.js';
|
||||
import { defineCustomElement as defineIonItemGroup } from '@ionic/core/components/ion-item-group.js';
|
||||
import { defineCustomElement as defineIonItemOptions } from '@ionic/core/components/ion-item-options.js';
|
||||
@@ -72,6 +73,7 @@ import { defineCustomElement as defineIonThumbnail } from '@ionic/core/component
|
||||
import { defineCustomElement as defineIonTitle } from '@ionic/core/components/ion-title.js';
|
||||
import { defineCustomElement as defineIonToggle } from '@ionic/core/components/ion-toggle.js';
|
||||
import { defineCustomElement as defineIonToolbar } from '@ionic/core/components/ion-toolbar.js';
|
||||
import { defineCustomElement as defineTestHeader } from '@ionic/core/components/test-header.js';
|
||||
|
||||
export const IonAccordion = /*@__PURE__*/createReactComponent<JSX.IonAccordion, HTMLIonAccordionElement>('ion-accordion', undefined, undefined, defineIonAccordion);
|
||||
export const IonAccordionGroup = /*@__PURE__*/createReactComponent<JSX.IonAccordionGroup, HTMLIonAccordionGroupElement>('ion-accordion-group', undefined, undefined, defineIonAccordionGroup);
|
||||
@@ -99,6 +101,7 @@ export const IonImg = /*@__PURE__*/createReactComponent<JSX.IonImg, HTMLIonImgEl
|
||||
export const IonInfiniteScroll = /*@__PURE__*/createReactComponent<JSX.IonInfiniteScroll, HTMLIonInfiniteScrollElement>('ion-infinite-scroll', undefined, undefined, defineIonInfiniteScroll);
|
||||
export const IonInfiniteScrollContent = /*@__PURE__*/createReactComponent<JSX.IonInfiniteScrollContent, HTMLIonInfiniteScrollContentElement>('ion-infinite-scroll-content', undefined, undefined, defineIonInfiniteScrollContent);
|
||||
export const IonInput = /*@__PURE__*/createReactComponent<JSX.IonInput, HTMLIonInputElement>('ion-input', undefined, undefined, defineIonInput);
|
||||
export const IonInputPasswordToggle = /*@__PURE__*/createReactComponent<JSX.IonInputPasswordToggle, HTMLIonInputPasswordToggleElement>('ion-input-password-toggle', undefined, undefined, defineIonInputPasswordToggle);
|
||||
export const IonItemDivider = /*@__PURE__*/createReactComponent<JSX.IonItemDivider, HTMLIonItemDividerElement>('ion-item-divider', undefined, undefined, defineIonItemDivider);
|
||||
export const IonItemGroup = /*@__PURE__*/createReactComponent<JSX.IonItemGroup, HTMLIonItemGroupElement>('ion-item-group', undefined, undefined, defineIonItemGroup);
|
||||
export const IonItemOptions = /*@__PURE__*/createReactComponent<JSX.IonItemOptions, HTMLIonItemOptionsElement>('ion-item-options', undefined, undefined, defineIonItemOptions);
|
||||
@@ -140,3 +143,4 @@ export const IonThumbnail = /*@__PURE__*/createReactComponent<JSX.IonThumbnail,
|
||||
export const IonTitle = /*@__PURE__*/createReactComponent<JSX.IonTitle, HTMLIonTitleElement>('ion-title', undefined, undefined, defineIonTitle);
|
||||
export const IonToggle = /*@__PURE__*/createReactComponent<JSX.IonToggle, HTMLIonToggleElement>('ion-toggle', undefined, undefined, defineIonToggle);
|
||||
export const IonToolbar = /*@__PURE__*/createReactComponent<JSX.IonToolbar, HTMLIonToolbarElement>('ion-toolbar', undefined, undefined, defineIonToolbar);
|
||||
export const TestHeader = /*@__PURE__*/createReactComponent<JSX.TestHeader, HTMLTestHeaderElement>('test-header', undefined, undefined, defineTestHeader);
|
||||
|
||||
@@ -35,6 +35,7 @@ import { defineCustomElement as defineIonImg } from '@ionic/core/components/ion-
|
||||
import { defineCustomElement as defineIonInfiniteScroll } from '@ionic/core/components/ion-infinite-scroll.js';
|
||||
import { defineCustomElement as defineIonInfiniteScrollContent } from '@ionic/core/components/ion-infinite-scroll-content.js';
|
||||
import { defineCustomElement as defineIonInput } from '@ionic/core/components/ion-input.js';
|
||||
import { defineCustomElement as defineIonInputPasswordToggle } from '@ionic/core/components/ion-input-password-toggle.js';
|
||||
import { defineCustomElement as defineIonItem } from '@ionic/core/components/ion-item.js';
|
||||
import { defineCustomElement as defineIonItemDivider } from '@ionic/core/components/ion-item-divider.js';
|
||||
import { defineCustomElement as defineIonItemGroup } from '@ionic/core/components/ion-item-group.js';
|
||||
@@ -77,6 +78,7 @@ import { defineCustomElement as defineIonThumbnail } from '@ionic/core/component
|
||||
import { defineCustomElement as defineIonTitle } from '@ionic/core/components/ion-title.js';
|
||||
import { defineCustomElement as defineIonToggle } from '@ionic/core/components/ion-toggle.js';
|
||||
import { defineCustomElement as defineIonToolbar } from '@ionic/core/components/ion-toolbar.js';
|
||||
import { defineCustomElement as defineTestHeader } from '@ionic/core/components/test-header.js';
|
||||
|
||||
|
||||
export const IonAccordion = /*@__PURE__*/ defineContainer<JSX.IonAccordion>('ion-accordion', defineIonAccordion, [
|
||||
@@ -436,6 +438,14 @@ export const IonInput = /*@__PURE__*/ defineContainer<JSX.IonInput, JSX.IonInput
|
||||
'value', 'ion-input');
|
||||
|
||||
|
||||
export const IonInputPasswordToggle = /*@__PURE__*/ defineContainer<JSX.IonInputPasswordToggle>('ion-input-password-toggle', defineIonInputPasswordToggle, [
|
||||
'color',
|
||||
'showIcon',
|
||||
'hideIcon',
|
||||
'type'
|
||||
]);
|
||||
|
||||
|
||||
export const IonItem = /*@__PURE__*/ defineContainer<JSX.IonItem>('ion-item', defineIonItem, [
|
||||
'color',
|
||||
'button',
|
||||
@@ -871,3 +881,8 @@ export const IonToolbar = /*@__PURE__*/ defineContainer<JSX.IonToolbar>('ion-too
|
||||
'color'
|
||||
]);
|
||||
|
||||
|
||||
export const TestHeader = /*@__PURE__*/ defineContainer<JSX.TestHeader>('test-header', defineTestHeader, [
|
||||
'testTitle'
|
||||
]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user