Skip to content

Commit

Permalink
chore: sync next with feature-8.0 (#29225)
Browse files Browse the repository at this point in the history
Issue number: N/A

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- Syncs the `next` branch with the latest from `feature-8.0`.


## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/.github/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
  • Loading branch information
sean-perkins committed Mar 27, 2024
2 parents 7eae6ec + 7cfee53 commit 6d6fd4a
Show file tree
Hide file tree
Showing 21 changed files with 571 additions and 22 deletions.
5 changes: 5 additions & 0 deletions BREAKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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).
Expand Down
12 changes: 9 additions & 3 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
49 changes: 48 additions & 1 deletion core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -4323,6 +4342,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: {
Expand Down Expand Up @@ -5147,6 +5172,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;
Expand Down Expand Up @@ -6689,6 +6715,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.
Expand Down Expand Up @@ -8121,7 +8166,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.
*/
Expand Down Expand Up @@ -9064,6 +9109,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;
Expand Down Expand Up @@ -9162,6 +9208,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>;
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
152 changes: 152 additions & 0 deletions core/src/components/input-password-toggle/input-password-toggle.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
}
}
Original file line number Diff line number Diff line change
@@ -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([]);
});
});
});
Loading

0 comments on commit 6d6fd4a

Please sign in to comment.