Merge branch 'main' into chore-update-next-from-main

This commit is contained in:
Brandy Carney
2024-10-30 12:58:29 -04:00
39 changed files with 447 additions and 119 deletions

View File

@ -3,6 +3,23 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.3.4](https://github.com/ionic-team/ionic-framework/compare/v8.3.3...v8.3.4) (2024-10-30)
### Bug Fixes
* **angular:** add missing 'compareWith' input to standalone ion-radio-group ([#29870](https://github.com/ionic-team/ionic-framework/issues/29870)) ([47ba703](https://github.com/ionic-team/ionic-framework/commit/47ba703a57d1ca506f943f6b790d0bf7583d79cb)), closes [#29826](https://github.com/ionic-team/ionic-framework/issues/29826)
* **backdrop:** remove tabindex for improved accessibility ([#29956](https://github.com/ionic-team/ionic-framework/issues/29956)) ([7294e96](https://github.com/ionic-team/ionic-framework/commit/7294e969bb913692eaf28e54860614f445132713)), closes [#29773](https://github.com/ionic-team/ionic-framework/issues/29773)
* **input, textarea:** ensure screen readers announce helper and error text when focused ([#29958](https://github.com/ionic-team/ionic-framework/issues/29958)) ([5a73145](https://github.com/ionic-team/ionic-framework/commit/5a7314553a8def87bd19275640c92dd72a6ef1a4))
* **overlay:** hide from screen readers while animating ([#29951](https://github.com/ionic-team/ionic-framework/issues/29951)) ([cb60073](https://github.com/ionic-team/ionic-framework/commit/cb6007363a8d42b5f126945427c2bfc3d7209c21)), closes [#29857](https://github.com/ionic-team/ionic-framework/issues/29857)
* **overlays:** do not hide root when toast appears ([#29962](https://github.com/ionic-team/ionic-framework/issues/29962)) ([322d7c9](https://github.com/ionic-team/ionic-framework/commit/322d7c98cf6613df0b0db3f119e3f892e6a17e7b)), closes [#29773](https://github.com/ionic-team/ionic-framework/issues/29773)
* **overlays:** hide the focus trap div from screen readers ([#29970](https://github.com/ionic-team/ionic-framework/issues/29970)) ([c3b58f1](https://github.com/ionic-team/ionic-framework/commit/c3b58f1620bcb74db43e3983ef570b7b982abd83)), closes [#29858](https://github.com/ionic-team/ionic-framework/issues/29858)
* **vue:** incorrect view rendered when using router.go(-n) ([#29877](https://github.com/ionic-team/ionic-framework/issues/29877)) ([e32fbe0](https://github.com/ionic-team/ionic-framework/commit/e32fbe02102fe80db29f73c26496a40852032354)), closes [#28201](https://github.com/ionic-team/ionic-framework/issues/28201) [#28201](https://github.com/ionic-team/ionic-framework/issues/28201) [#29847](https://github.com/ionic-team/ionic-framework/issues/29847)
## [8.3.3](https://github.com/ionic-team/ionic-framework/compare/v8.3.2...v8.3.3) (2024-10-16)

View File

@ -3,6 +3,21 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.3.4](https://github.com/ionic-team/ionic-framework/compare/v8.3.3...v8.3.4) (2024-10-30)
### Bug Fixes
* **backdrop:** remove tabindex for improved accessibility ([#29956](https://github.com/ionic-team/ionic-framework/issues/29956)) ([7294e96](https://github.com/ionic-team/ionic-framework/commit/7294e969bb913692eaf28e54860614f445132713)), closes [#29773](https://github.com/ionic-team/ionic-framework/issues/29773)
* **input, textarea:** ensure screen readers announce helper and error text when focused ([#29958](https://github.com/ionic-team/ionic-framework/issues/29958)) ([5a73145](https://github.com/ionic-team/ionic-framework/commit/5a7314553a8def87bd19275640c92dd72a6ef1a4))
* **overlay:** hide from screen readers while animating ([#29951](https://github.com/ionic-team/ionic-framework/issues/29951)) ([cb60073](https://github.com/ionic-team/ionic-framework/commit/cb6007363a8d42b5f126945427c2bfc3d7209c21)), closes [#29857](https://github.com/ionic-team/ionic-framework/issues/29857)
* **overlays:** do not hide root when toast appears ([#29962](https://github.com/ionic-team/ionic-framework/issues/29962)) ([322d7c9](https://github.com/ionic-team/ionic-framework/commit/322d7c98cf6613df0b0db3f119e3f892e6a17e7b)), closes [#29773](https://github.com/ionic-team/ionic-framework/issues/29773)
* **overlays:** hide the focus trap div from screen readers ([#29970](https://github.com/ionic-team/ionic-framework/issues/29970)) ([c3b58f1](https://github.com/ionic-team/ionic-framework/commit/c3b58f1620bcb74db43e3983ef570b7b982abd83)), closes [#29858](https://github.com/ionic-team/ionic-framework/issues/29858)
## [8.3.3](https://github.com/ionic-team/ionic-framework/compare/v8.3.2...v8.3.3) (2024-10-16)
**Note:** Version bump only for package @ionic/core

View File

@ -1,12 +1,12 @@
{
"name": "@ionic/core",
"version": "8.3.3",
"version": "8.3.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "8.3.3",
"version": "8.3.4",
"license": "MIT",
"dependencies": {
"@phosphor-icons/core": "^2.1.1",

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "8.3.3",
"version": "8.3.4",
"description": "Base components for Ionic",
"keywords": [
"ionic",

View File

@ -388,7 +388,7 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
>
<ion-backdrop tappable={this.backdropDismiss} />
<div tabindex="0"></div>
<div tabindex="0" aria-hidden="true"></div>
<div class="action-sheet-wrapper ion-overlay-wrapper" ref={(el) => (this.wrapperEl = el)}>
<div class="action-sheet-container">
@ -449,7 +449,7 @@ export class ActionSheet implements ComponentInterface, OverlayInterface {
</div>
</div>
<div tabindex="0"></div>
<div tabindex="0" aria-hidden="true"></div>
</Host>
);
}

View File

@ -759,7 +759,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
>
<ion-backdrop tappable={this.backdropDismiss} />
<div tabindex="0"></div>
<div tabindex="0" aria-hidden="true"></div>
<div class="alert-wrapper ion-overlay-wrapper" ref={(el) => (this.wrapperEl = el)}>
<div class="alert-head">
@ -781,7 +781,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
{this.renderAlertButtons()}
</div>
<div tabindex="0"></div>
<div tabindex="0" aria-hidden="true"></div>
</Host>
);
}

View File

@ -56,7 +56,6 @@ export class Backdrop implements ComponentInterface {
const theme = getIonTheme(this);
return (
<Host
tabindex="-1"
aria-hidden="true"
class={{
[theme]: true,

View File

@ -38,6 +38,8 @@ import { getCounterText } from './input.utils';
export class Input implements ComponentInterface {
private nativeInput?: HTMLInputElement;
private inputId = `ion-input-${inputIds++}`;
private helperTextId = `${this.inputId}-helper-text`;
private errorTextId = `${this.inputId}-error-text`;
private inheritedAttributes: Attributes = {};
private isComposing = false;
private slotMutationController?: SlotMutationController;
@ -655,9 +657,30 @@ export class Input implements ComponentInterface {
* Renders the helper text or error text values
*/
private renderHintText() {
const { helperText, errorText } = this;
const { helperText, errorText, helperTextId, errorTextId } = this;
return [<div class="helper-text">{helperText}</div>, <div class="error-text">{errorText}</div>];
return [
<div id={helperTextId} class="helper-text">
{helperText}
</div>,
<div id={errorTextId} class="error-text">
{errorText}
</div>,
];
}
private getHintTextID(): string | undefined {
const { el, helperText, errorText, helperTextId, errorTextId } = this;
if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
return errorTextId;
}
if (helperText) {
return helperTextId;
}
return undefined;
}
private renderCounter() {
@ -905,6 +928,8 @@ export class Input implements ComponentInterface {
onKeyDown={this.onKeydown}
onCompositionstart={this.onCompositionStart}
onCompositionend={this.onCompositionEnd}
aria-describedby={this.getHintTextID()}
aria-invalid={this.getHintTextID() === this.errorTextId}
{...this.inheritedAttributes}
/>
{clearInput && !readonly && !disabled && (

View File

@ -93,6 +93,19 @@ configs({ modes: ['md', 'ionic-md'], directions: ['ltr'] }).forEach(({ title, sc
await expect(helperText).toHaveText('my helper');
await expect(errorText).toBeHidden();
});
test('input should have an aria-describedby attribute when helper text is present', async ({ page }) => {
await page.setContent(
`<ion-input helper-text="my helper" error-text="my error" label="my input"></ion-input>`,
config
);
const input = page.locator('ion-input input');
const helperText = page.locator('ion-input .helper-text');
const helperTextId = await helperText.getAttribute('id');
const ariaDescribedBy = await input.getAttribute('aria-describedby');
expect(ariaDescribedBy).toBe(helperTextId);
});
test('error text should be visible when input is invalid', async ({ page }) => {
await page.setContent(
`<ion-input class="ion-invalid ion-touched" helper-text="my helper" error-text="my error" label="my input"></ion-input>`,
@ -122,6 +135,48 @@ configs({ modes: ['md', 'ionic-md'], directions: ['ltr'] }).forEach(({ title, sc
const bottomEl = page.locator('ion-input .input-bottom');
await expect(bottomEl).toHaveScreenshot(screenshot(`input-error-custom-color`));
});
test('input should have an aria-describedby attribute when error text is present', async ({ page }) => {
await page.setContent(
`<ion-input class="ion-invalid ion-touched" helper-text="my helper" error-text="my error" label="my input"></ion-input>`,
config
);
const input = page.locator('ion-input input');
const errorText = page.locator('ion-input .error-text');
const errorTextId = await errorText.getAttribute('id');
const ariaDescribedBy = await input.getAttribute('aria-describedby');
expect(ariaDescribedBy).toBe(errorTextId);
});
test('input should have aria-invalid attribute when input is invalid', async ({ page }) => {
await page.setContent(
`<ion-input class="ion-invalid ion-touched" helper-text="my helper" error-text="my error" label="my input"></ion-input>`,
config
);
const input = page.locator('ion-input input');
await expect(input).toHaveAttribute('aria-invalid');
});
test('input should not have aria-invalid attribute when input is valid', async ({ page }) => {
await page.setContent(
`<ion-input helper-text="my helper" error-text="my error" label="my input"></ion-input>`,
config
);
const input = page.locator('ion-input input');
await expect(input).not.toHaveAttribute('aria-invalid');
});
test('input should not have aria-describedby attribute when no hint or error text is present', async ({
page,
}) => {
await page.setContent(`<ion-input label="my input"></ion-input>`, config);
const input = page.locator('ion-input input');
await expect(input).not.toHaveAttribute('aria-describedby');
});
});
test.describe('input: hint text rendering', () => {
test.describe('regular inputs', () => {

View File

@ -358,7 +358,7 @@ export class Loading implements ComponentInterface, OverlayInterface {
>
<ion-backdrop visible={this.showBackdrop} tappable={this.backdropDismiss} />
<div tabindex="0"></div>
<div tabindex="0" aria-hidden="true"></div>
<div class="loading-wrapper ion-overlay-wrapper">
{spinner && (
@ -370,7 +370,7 @@ export class Loading implements ComponentInterface, OverlayInterface {
{message !== undefined && this.renderLoadingMessage(msgId)}
</div>
<div tabindex="0"></div>
<div tabindex="0" aria-hidden="true"></div>
</Host>
);
}

View File

@ -376,7 +376,7 @@ export class Picker implements ComponentInterface, OverlayInterface {
>
<ion-backdrop visible={this.showBackdrop} tappable={this.backdropDismiss}></ion-backdrop>
<div tabindex="0"></div>
<div tabindex="0" aria-hidden="true"></div>
<div class="picker-wrapper ion-overlay-wrapper" role="dialog">
<div class="picker-toolbar">
@ -396,7 +396,7 @@ export class Picker implements ComponentInterface, OverlayInterface {
</div>
</div>
<div tabindex="0"></div>
<div tabindex="0" aria-hidden="true"></div>
</Host>
);
}

View File

@ -27,6 +27,19 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co
await expect(helperText).toHaveText('my helper');
await expect(errorText).toBeHidden();
});
test('textarea should have an aria-describedby attribute when helper text is present', async ({ page }) => {
await page.setContent(
`<ion-textarea helper-text="my helper" error-text="my error" label="my textarea"></ion-textarea>`,
config
);
const textarea = page.locator('ion-textarea textarea');
const helperText = page.locator('ion-textarea .helper-text');
const helperTextId = await helperText.getAttribute('id');
const ariaDescribedBy = await textarea.getAttribute('aria-describedby');
expect(ariaDescribedBy).toBe(helperTextId);
});
test('error text should be visible when textarea is invalid', async ({ page }) => {
await page.setContent(
`<ion-textarea class="ion-invalid ion-touched" helper-text="my helper" error-text="my error" label="my textarea"></ion-textarea>`,
@ -55,6 +68,48 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, co
const errorText = page.locator('ion-textarea .error-text');
await expect(errorText).toHaveScreenshot(screenshot(`textarea-error-custom-color`));
});
test('textarea should have an aria-describedby attribute when error text is present', async ({ page }) => {
await page.setContent(
`<ion-textarea class="ion-invalid ion-touched" helper-text="my helper" error-text="my error" label="my textarea"></ion-textarea>`,
config
);
const textarea = page.locator('ion-textarea textarea');
const errorText = page.locator('ion-textarea .error-text');
const errorTextId = await errorText.getAttribute('id');
const ariaDescribedBy = await textarea.getAttribute('aria-describedby');
expect(ariaDescribedBy).toBe(errorTextId);
});
test('textarea should have aria-invalid attribute when input is invalid', async ({ page }) => {
await page.setContent(
`<ion-textarea class="ion-invalid ion-touched" helper-text="my helper" error-text="my error" label="my textarea"></ion-textarea>`,
config
);
const textarea = page.locator('ion-textarea textarea');
await expect(textarea).toHaveAttribute('aria-invalid');
});
test('textarea should not have aria-invalid attribute when input is valid', async ({ page }) => {
await page.setContent(
`<ion-textarea helper-text="my helper" error-text="my error" label="my textarea"></ion-textarea>`,
config
);
const textarea = page.locator('ion-textarea textarea');
await expect(textarea).not.toHaveAttribute('aria-invalid');
});
test('textarea should not have aria-describedby attribute when no hint or error text is present', async ({
page,
}) => {
await page.setContent(`<ion-textarea label="my textarea"></ion-textarea>`, config);
const textarea = page.locator('ion-textarea textarea');
await expect(textarea).not.toHaveAttribute('aria-describedby');
});
});
test.describe('textarea: hint text rendering', () => {
test.describe('regular textareas', () => {

View File

@ -47,6 +47,8 @@ import type { TextareaChangeEventDetail, TextareaInputEventDetail } from './text
export class Textarea implements ComponentInterface {
private nativeInput?: HTMLTextAreaElement;
private inputId = `ion-textarea-${textareaIds++}`;
private helperTextId = `${this.inputId}-helper-text`;
private errorTextId = `${this.inputId}-error-text`;
/**
* `true` if the textarea was cleared as a result of the user typing
* with `clearOnEdit` enabled.
@ -601,9 +603,30 @@ export class Textarea implements ComponentInterface {
* Renders the helper text or error text values
*/
private renderHintText() {
const { helperText, errorText } = this;
const { helperText, errorText, helperTextId, errorTextId } = this;
return [<div class="helper-text">{helperText}</div>, <div class="error-text">{errorText}</div>];
return [
<div id={helperTextId} class="helper-text">
{helperText}
</div>,
<div id={errorTextId} class="error-text">
{errorText}
</div>,
];
}
private getHintTextID(): string | undefined {
const { el, helperText, errorText, helperTextId, errorTextId } = this;
if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
return errorTextId;
}
if (helperText) {
return helperTextId;
}
return undefined;
}
private renderCounter() {
@ -742,6 +765,8 @@ export class Textarea implements ComponentInterface {
onBlur={this.onBlur}
onFocus={this.onFocus}
onKeyDown={this.onKeyDown}
aria-describedby={this.getHintTextID()}
aria-invalid={this.getHintTextID() === this.errorTextId}
{...this.inheritedAttributes}
>
{value}

View File

@ -510,11 +510,21 @@ export const present = async <OverlayPresentOptions>(
return;
}
/**
* Due to accessibility guidelines, toasts do not have
* focus traps.
*
* All other overlays should have focus traps to prevent
* the keyboard focus from leaving the overlay.
*/
if (overlay.el.tagName !== 'ION-TOAST') {
setRootAriaHidden(true);
}
document.body.classList.add(BACKDROP_NO_SCROLL);
hideOverlaysFromScreenReaders(overlay.el);
hideUnderlyingOverlaysFromScreenReaders(overlay.el);
hideAnimatingOverlayFromScreenReaders(overlay.el);
overlay.presented = true;
overlay.willPresent.emit();
@ -560,6 +570,11 @@ export const present = async <OverlayPresentOptions>(
* it would still have aria-hidden on being presented again.
* Removing it here ensures the overlay is visible to screen
* readers.
*
* If this overlay was being presented, then it was hidden
* from screen readers during the animation. Now that the
* animation is complete, we can reveal the overlay to
* screen readers.
*/
overlay.el.removeAttribute('aria-hidden');
};
@ -630,13 +645,26 @@ export const dismiss = async <OverlayDismissOptions>(
return false;
}
const lastOverlay = doc !== undefined && getPresentedOverlays(doc).length === 1;
/**
* For accessibility, toasts lack focus traps and dont receive
* `aria-hidden` on the root element when presented.
*
* All other overlays use focus traps to keep keyboard focus
* within the overlay, setting `aria-hidden` on the root element
* to enhance accessibility.
*
* Therefore, we must remove `aria-hidden` from the root element
* when the last non-toast overlay is dismissed.
*/
const overlaysNotToast = doc !== undefined ? getPresentedOverlays(doc).filter((o) => o.tagName !== 'ION-TOAST') : [];
const lastOverlayNotToast = overlaysNotToast.length === 1 && overlaysNotToast[0].id === overlay.el.id;
/**
* If this is the last visible overlay then
* we want to re-add the root to the accessibility tree.
* If this is the last visible overlay that is not a toast
* then we want to re-add the root to the accessibility tree.
*/
if (lastOverlay) {
if (lastOverlayNotToast) {
setRootAriaHidden(false);
document.body.classList.remove(BACKDROP_NO_SCROLL);
}
@ -644,6 +672,13 @@ export const dismiss = async <OverlayDismissOptions>(
overlay.presented = false;
try {
/**
* There is no need to show the overlay to screen readers during
* the dismiss animation. This is because the overlay will be removed
* from the DOM after the animation is complete.
*/
hideAnimatingOverlayFromScreenReaders(overlay.el);
// Overlay contents should not be clickable during dismiss
overlay.el.style.setProperty('pointer-events', 'none');
overlay.willDismiss.emit({ data, role });
@ -930,6 +965,29 @@ export const createTriggerController = () => {
};
};
/**
* The overlay that is being animated also needs to hide from screen
* readers during its animation. This ensures that assistive technologies
* like TalkBack do not announce or interact with the content until the
* animation is complete, avoiding confusion for users.
*
* If the overlay is being presented, it prevents focus rings from appearing
* in incorrect positions due to the transition (specifically `transform`
* styles), ensuring that when aria-hidden is removed, the focus rings are
* correctly displayed in the final location of the elements.
*
* @param overlay - The overlay that is being animated.
*/
const hideAnimatingOverlayFromScreenReaders = (overlay: HTMLIonOverlayElement) => {
if (doc === undefined) return;
/**
* Once the animation is complete, this attribute will be removed.
* This is done at the end of the `present` method.
*/
overlay.setAttribute('aria-hidden', 'true');
};
/**
* Ensure that underlying overlays have aria-hidden if necessary so that screen readers
* cannot move focus to these elements. Note that we cannot rely on focus/focusin/focusout
@ -940,7 +998,7 @@ export const createTriggerController = () => {
* @param newTopMostOverlay - The overlay that is being presented. Since the overlay has not been
* fully presented yet at the time this function is called it will not be included in the getPresentedOverlays result.
*/
const hideOverlaysFromScreenReaders = (newTopMostOverlay: HTMLIonOverlayElement) => {
const hideUnderlyingOverlaysFromScreenReaders = (newTopMostOverlay: HTMLIonOverlayElement) => {
if (doc === undefined) return;
const overlays = getPresentedOverlays(doc);

View File

@ -3,5 +3,5 @@
"core",
"packages/*"
],
"version": "8.3.3"
"version": "8.3.4"
}

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.3.4](https://github.com/ionic-team/ionic-framework/compare/v8.3.3...v8.3.4) (2024-10-30)
**Note:** Version bump only for package @ionic/angular-server
## [8.3.3](https://github.com/ionic-team/ionic-framework/compare/v8.3.2...v8.3.3) (2024-10-16)
**Note:** Version bump only for package @ionic/angular-server

View File

@ -1,15 +1,15 @@
{
"name": "@ionic/angular-server",
"version": "8.3.3",
"version": "8.3.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular-server",
"version": "8.3.3",
"version": "8.3.4",
"license": "MIT",
"dependencies": {
"@ionic/core": "^8.3.3"
"@ionic/core": "^8.3.4"
},
"devDependencies": {
"@angular-eslint/eslint-plugin": "^16.0.0",
@ -1031,9 +1031,9 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"dependencies": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",
@ -7188,9 +7188,9 @@
"dev": true
},
"@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"requires": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/angular-server",
"version": "8.3.3",
"version": "8.3.4",
"description": "Angular SSR Module for Ionic",
"keywords": [
"ionic",
@ -62,6 +62,6 @@
},
"prettier": "@ionic/prettier-config",
"dependencies": {
"@ionic/core": "^8.3.3"
"@ionic/core": "^8.3.4"
}
}

View File

@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.3.4](https://github.com/ionic-team/ionic-framework/compare/v8.3.3...v8.3.4) (2024-10-30)
### Bug Fixes
* **angular:** add missing 'compareWith' input to standalone ion-radio-group ([#29870](https://github.com/ionic-team/ionic-framework/issues/29870)) ([47ba703](https://github.com/ionic-team/ionic-framework/commit/47ba703a57d1ca506f943f6b790d0bf7583d79cb)), closes [#29826](https://github.com/ionic-team/ionic-framework/issues/29826)
## [8.3.3](https://github.com/ionic-team/ionic-framework/compare/v8.3.2...v8.3.3) (2024-10-16)
**Note:** Version bump only for package @ionic/angular

View File

@ -1,15 +1,15 @@
{
"name": "@ionic/angular",
"version": "8.3.3",
"version": "8.3.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular",
"version": "8.3.3",
"version": "8.3.4",
"license": "MIT",
"dependencies": {
"@ionic/core": "^8.3.3",
"@ionic/core": "^8.3.4",
"ionicons": "^7.0.0",
"jsonc-parser": "^3.0.0",
"tslib": "^2.3.0"
@ -1398,9 +1398,9 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"dependencies": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",
@ -9820,9 +9820,9 @@
"dev": true
},
"@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"requires": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "8.3.3",
"version": "8.3.4",
"description": "Angular specific wrappers for @ionic/core",
"keywords": [
"ionic",
@ -47,7 +47,7 @@
}
},
"dependencies": {
"@ionic/core": "^8.3.3",
"@ionic/core": "^8.3.4",
"ionicons": "^7.0.0",
"jsonc-parser": "^3.0.0",
"tslib": "^2.3.0"

View File

@ -16,7 +16,7 @@ import { defineCustomElement } from '@ionic/core/components/ion-radio-group.js';
import { ProxyCmp, proxyOutputs } from './angular-component-lib/utils';
const RADIO_GROUP_INPUTS = ['allowEmptySelection', 'name', 'value'];
const RADIO_GROUP_INPUTS = ['allowEmptySelection', 'compareWith', 'name', 'value'];
/**
* Pulling the provider into an object and using PURE works

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.3.4](https://github.com/ionic-team/ionic-framework/compare/v8.3.3...v8.3.4) (2024-10-30)
**Note:** Version bump only for package @ionic/docs
## [8.3.3](https://github.com/ionic-team/ionic-framework/compare/v8.3.2...v8.3.3) (2024-10-16)
**Note:** Version bump only for package @ionic/docs

View File

@ -1,12 +1,12 @@
{
"name": "@ionic/docs",
"version": "8.3.3",
"version": "8.3.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/docs",
"version": "8.3.3",
"version": "8.3.4",
"license": "MIT"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/docs",
"version": "8.3.3",
"version": "8.3.4",
"description": "Pre-packaged API documentation for the Ionic docs.",
"main": "core.json",
"types": "core.d.ts",

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.3.4](https://github.com/ionic-team/ionic-framework/compare/v8.3.3...v8.3.4) (2024-10-30)
**Note:** Version bump only for package @ionic/react-router
## [8.3.3](https://github.com/ionic-team/ionic-framework/compare/v8.3.2...v8.3.3) (2024-10-16)
**Note:** Version bump only for package @ionic/react-router

View File

@ -1,15 +1,15 @@
{
"name": "@ionic/react-router",
"version": "8.3.3",
"version": "8.3.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/react-router",
"version": "8.3.3",
"version": "8.3.4",
"license": "MIT",
"dependencies": {
"@ionic/react": "^8.3.3",
"@ionic/react": "^8.3.4",
"tslib": "*"
},
"devDependencies": {
@ -238,9 +238,9 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"dependencies": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",
@ -414,11 +414,11 @@
}
},
"node_modules/@ionic/react": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/react/-/react-8.3.3.tgz",
"integrity": "sha512-BQVke+4QF1viPmwYFV/Bfseh4AhLnA0svP8UvKTP45plJ2KDXF/IbFVNn+FWtjByrqYU4PldUgF01+O4yPGiRw==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/react/-/react-8.3.4.tgz",
"integrity": "sha512-CIoTHg/1nJJN11IjmsUqeQB1nIP4SxQyo2nBH+MhzeVCMv8Tj00Y4rU/9RYzKfRI7Zfsi9MOwVrwpGhPWi4KWA==",
"dependencies": {
"@ionic/core": "8.3.3",
"@ionic/core": "8.3.4",
"ionicons": "^7.0.0",
"tslib": "*"
},
@ -4057,9 +4057,9 @@
"dev": true
},
"@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"requires": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",
@ -4163,11 +4163,11 @@
"requires": {}
},
"@ionic/react": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/react/-/react-8.3.3.tgz",
"integrity": "sha512-BQVke+4QF1viPmwYFV/Bfseh4AhLnA0svP8UvKTP45plJ2KDXF/IbFVNn+FWtjByrqYU4PldUgF01+O4yPGiRw==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/react/-/react-8.3.4.tgz",
"integrity": "sha512-CIoTHg/1nJJN11IjmsUqeQB1nIP4SxQyo2nBH+MhzeVCMv8Tj00Y4rU/9RYzKfRI7Zfsi9MOwVrwpGhPWi4KWA==",
"requires": {
"@ionic/core": "8.3.3",
"@ionic/core": "8.3.4",
"ionicons": "^7.0.0",
"tslib": "*"
}

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/react-router",
"version": "8.3.3",
"version": "8.3.4",
"description": "React Router wrapper for @ionic/react",
"keywords": [
"ionic",
@ -36,7 +36,7 @@
"dist/"
],
"dependencies": {
"@ionic/react": "^8.3.3",
"@ionic/react": "^8.3.4",
"tslib": "*"
},
"peerDependencies": {

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.3.4](https://github.com/ionic-team/ionic-framework/compare/v8.3.3...v8.3.4) (2024-10-30)
**Note:** Version bump only for package @ionic/react
## [8.3.3](https://github.com/ionic-team/ionic-framework/compare/v8.3.2...v8.3.3) (2024-10-16)

View File

@ -1,15 +1,15 @@
{
"name": "@ionic/react",
"version": "8.3.3",
"version": "8.3.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/react",
"version": "8.3.3",
"version": "8.3.4",
"license": "MIT",
"dependencies": {
"@ionic/core": "^8.3.3",
"@ionic/core": "^8.3.4",
"ionicons": "^7.0.0",
"tslib": "*"
},
@ -736,9 +736,9 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"dependencies": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",
@ -12315,9 +12315,9 @@
"dev": true
},
"@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"requires": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/react",
"version": "8.3.3",
"version": "8.3.4",
"description": "React specific wrapper for @ionic/core",
"keywords": [
"ionic",
@ -39,7 +39,7 @@
"css/"
],
"dependencies": {
"@ionic/core": "^8.3.3",
"@ionic/core": "^8.3.4",
"ionicons": "^7.0.0",
"tslib": "*"
},

View File

@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.3.4](https://github.com/ionic-team/ionic-framework/compare/v8.3.3...v8.3.4) (2024-10-30)
### Bug Fixes
* **vue:** incorrect view rendered when using router.go(-n) ([#29877](https://github.com/ionic-team/ionic-framework/issues/29877)) ([e32fbe0](https://github.com/ionic-team/ionic-framework/commit/e32fbe02102fe80db29f73c26496a40852032354)), closes [#28201](https://github.com/ionic-team/ionic-framework/issues/28201) [#28201](https://github.com/ionic-team/ionic-framework/issues/28201) [#29847](https://github.com/ionic-team/ionic-framework/issues/29847)
## [8.3.3](https://github.com/ionic-team/ionic-framework/compare/v8.3.2...v8.3.3) (2024-10-16)
**Note:** Version bump only for package @ionic/vue-router

View File

@ -84,4 +84,25 @@ describe('Location History', () => {
expect(locationHistory.canGoBack(1, 0, 1)).toEqual(true);
expect(locationHistory.canGoBack(2, 0, 1)).toEqual(false);
});
it('should correctly find the last location', () => {
const [home, pageA, pageB, pageC] = [
{ pathname: '/home' },
{ pathname: '/page-a', pushedByRoute: '/home' },
{ pathname: '/page-b', pushedByRoute: '/page-a' },
{ pathname: '/page-c', pushedByRoute: '/page-b' },
];
locationHistory.add(home);
locationHistory.add(pageA);
locationHistory.add(pageB);
locationHistory.add(pageC);
expect(locationHistory.findLastLocation(pageB)).toEqual(pageA);
expect(locationHistory.findLastLocation(pageB, -2)).toEqual(home);
expect(locationHistory.findLastLocation(pageC)).toEqual(pageB);
expect(locationHistory.findLastLocation(pageC, -2)).toEqual(pageA);
expect(locationHistory.findLastLocation(pageC, -3)).toEqual(home);
});
});

View File

@ -1,15 +1,15 @@
{
"name": "@ionic/vue-router",
"version": "8.3.3",
"version": "8.3.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/vue-router",
"version": "8.3.3",
"version": "8.3.4",
"license": "MIT",
"dependencies": {
"@ionic/vue": "^8.3.3"
"@ionic/vue": "^8.3.4"
},
"devDependencies": {
"@ionic/eslint-config": "^0.3.0",
@ -661,9 +661,9 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"dependencies": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",
@ -852,11 +852,11 @@
}
},
"node_modules/@ionic/vue": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-8.3.3.tgz",
"integrity": "sha512-6EAPWdmQDvazP4ZsCRjWlG91Kx+1vYxX1gHx02Xvcc7Dn8hRrMW58IAAQ0BqSYsrHNFTKVtwD3cJbmnut8Ghyg==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-8.3.4.tgz",
"integrity": "sha512-s7P5mTd078CbPK2dpIxxWGNirQacG3sXhJulJ1L0J6+6VI+HHHLKy4ueTRgPl5GLDmjGgdMCBuYu1n4K2nNXFg==",
"dependencies": {
"@ionic/core": "8.3.3",
"@ionic/core": "8.3.4",
"ionicons": "^7.0.0"
}
},
@ -7878,9 +7878,9 @@
"dev": true
},
"@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"requires": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",
@ -7993,11 +7993,11 @@
"requires": {}
},
"@ionic/vue": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-8.3.3.tgz",
"integrity": "sha512-6EAPWdmQDvazP4ZsCRjWlG91Kx+1vYxX1gHx02Xvcc7Dn8hRrMW58IAAQ0BqSYsrHNFTKVtwD3cJbmnut8Ghyg==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/vue/-/vue-8.3.4.tgz",
"integrity": "sha512-s7P5mTd078CbPK2dpIxxWGNirQacG3sXhJulJ1L0J6+6VI+HHHLKy4ueTRgPl5GLDmjGgdMCBuYu1n4K2nNXFg==",
"requires": {
"@ionic/core": "8.3.3",
"@ionic/core": "8.3.4",
"ionicons": "^7.0.0"
}
},

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/vue-router",
"version": "8.3.3",
"version": "8.3.4",
"description": "Vue Router integration for @ionic/vue",
"scripts": {
"test.spec": "jest",
@ -44,7 +44,7 @@
},
"homepage": "https://github.com/ionic-team/ionic-framework#readme",
"dependencies": {
"@ionic/vue": "^8.3.3"
"@ionic/vue": "^8.3.4"
},
"devDependencies": {
"@ionic/eslint-config": "^0.3.0",

View File

@ -239,15 +239,11 @@ export const createLocationHistory = () => {
}
}
}
if (delta < -1) {
return locationHistory[locationHistory.length - 1 + delta];
} else {
for (let i = locationHistory.length - 2; i >= 0; i--) {
const ri = locationHistory[i];
if (ri) {
if (ri.pathname === routeInfo.pushedByRoute) {
return ri;
}
return locationHistory[i + 1 + delta]
}
}
}

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.3.4](https://github.com/ionic-team/ionic-framework/compare/v8.3.3...v8.3.4) (2024-10-30)
**Note:** Version bump only for package @ionic/vue
## [8.3.3](https://github.com/ionic-team/ionic-framework/compare/v8.3.2...v8.3.3) (2024-10-16)

View File

@ -1,15 +1,15 @@
{
"name": "@ionic/vue",
"version": "8.3.3",
"version": "8.3.4",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/vue",
"version": "8.3.3",
"version": "8.3.4",
"license": "MIT",
"dependencies": {
"@ionic/core": "^8.3.3",
"@ionic/core": "^8.3.4",
"ionicons": "^7.0.0"
},
"devDependencies": {
@ -208,9 +208,9 @@
"dev": true
},
"node_modules/@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"dependencies": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",
@ -3970,9 +3970,9 @@
"dev": true
},
"@ionic/core": {
"version": "8.3.3",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.3.tgz",
"integrity": "sha512-f2PXV0jFIFPdjP+NbmQ1mXqRQ4KWi0U0jdQd3wDYsJFWQLmWXhW7Yp/4lCDdl0ouMeZRB2phddqFct1c7H6PyA==",
"version": "8.3.4",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-8.3.4.tgz",
"integrity": "sha512-MkgaQ9+oQwj3AK/i25MkLgKUUH3/nvNjd4YlmUyFjPG7l2IBHsrLlSmuFGCyniB+doI9Hynu3T9CZP7NS4RjRQ==",
"requires": {
"@stencil/core": "4.20.0",
"ionicons": "^7.2.2",

View File

@ -1,6 +1,6 @@
{
"name": "@ionic/vue",
"version": "8.3.3",
"version": "8.3.4",
"description": "Vue specific wrapper for @ionic/core",
"scripts": {
"eslint": "eslint src",
@ -66,7 +66,7 @@
"vue-router": "^4.0.16"
},
"dependencies": {
"@ionic/core": "^8.3.3",
"@ionic/core": "^8.3.4",
"ionicons": "^7.0.0"
},
"vetur": {