Skip to content

Commit c8a4180

Browse files
merge release-8.4.1 (#30049)
v8.4.1
2 parents 32b0a6c + a90097c commit c8a4180

35 files changed

+304
-116
lines changed

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,21 @@
33
All notable changes to this project will be documented in this file.
44
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
55

6+
## [8.4.1](https://github.com/ionic-team/ionic-framework/compare/v8.4.0...v8.4.1) (2024-11-27)
7+
8+
9+
### Bug Fixes
10+
11+
* **header:** use aria attributes to hide small title when collapsed ([#30027](https://github.com/ionic-team/ionic-framework/issues/30027)) ([23763ab](https://github.com/ionic-team/ionic-framework/commit/23763abf797f9a4ba8262225760f718e9dcc4782)), closes [#29347](https://github.com/ionic-team/ionic-framework/issues/29347)
12+
* **menu:** hide from screen readers while animating ([#30036](https://github.com/ionic-team/ionic-framework/issues/30036)) ([845071c](https://github.com/ionic-team/ionic-framework/commit/845071c97a856d45eb5e0bb81d9c270bc38bb604))
13+
* **overlays:** announce info after opening based on platform ([#30025](https://github.com/ionic-team/ionic-framework/issues/30025)) ([f6188c4](https://github.com/ionic-team/ionic-framework/commit/f6188c47e9278fe69fd9d250c65156edbe5ef32e))
14+
* **overlays:** focus management with checkbox/radio ([#30026](https://github.com/ionic-team/ionic-framework/issues/30026)) ([8ee42bb](https://github.com/ionic-team/ionic-framework/commit/8ee42bbc1e0bf4731d20040c7853756722f1a4b2))
15+
* **toast:** swipe gesture works with custom container layout ([#29999](https://github.com/ionic-team/ionic-framework/issues/29999)) ([470decc](https://github.com/ionic-team/ionic-framework/commit/470decca7b6b89ef74095ef0bb7909b93640cd78)), closes [#29998](https://github.com/ionic-team/ionic-framework/issues/29998)
16+
17+
18+
19+
20+
621
# [8.4.0](https://github.com/ionic-team/ionic-framework/compare/v8.3.4...v8.4.0) (2024-11-04)
722

823

core/CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,21 @@
33
All notable changes to this project will be documented in this file.
44
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
55

6+
## [8.4.1](https://github.com/ionic-team/ionic-framework/compare/v8.4.0...v8.4.1) (2024-11-27)
7+
8+
9+
### Bug Fixes
10+
11+
* **header:** use aria attributes to hide small title when collapsed ([#30027](https://github.com/ionic-team/ionic-framework/issues/30027)) ([23763ab](https://github.com/ionic-team/ionic-framework/commit/23763abf797f9a4ba8262225760f718e9dcc4782)), closes [#29347](https://github.com/ionic-team/ionic-framework/issues/29347)
12+
* **menu:** hide from screen readers while animating ([#30036](https://github.com/ionic-team/ionic-framework/issues/30036)) ([845071c](https://github.com/ionic-team/ionic-framework/commit/845071c97a856d45eb5e0bb81d9c270bc38bb604))
13+
* **overlays:** announce info after opening based on platform ([#30025](https://github.com/ionic-team/ionic-framework/issues/30025)) ([f6188c4](https://github.com/ionic-team/ionic-framework/commit/f6188c47e9278fe69fd9d250c65156edbe5ef32e))
14+
* **overlays:** focus management with checkbox/radio ([#30026](https://github.com/ionic-team/ionic-framework/issues/30026)) ([8ee42bb](https://github.com/ionic-team/ionic-framework/commit/8ee42bbc1e0bf4731d20040c7853756722f1a4b2))
15+
* **toast:** swipe gesture works with custom container layout ([#29999](https://github.com/ionic-team/ionic-framework/issues/29999)) ([470decc](https://github.com/ionic-team/ionic-framework/commit/470decca7b6b89ef74095ef0bb7909b93640cd78)), closes [#29998](https://github.com/ionic-team/ionic-framework/issues/29998)
16+
17+
18+
19+
20+
621
# [8.4.0](https://github.com/ionic-team/ionic-framework/compare/v8.3.4...v8.4.0) (2024-11-04)
722

823

core/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ionic/core",
3-
"version": "8.4.0",
3+
"version": "8.4.1",
44
"description": "Base components for Ionic",
55
"keywords": [
66
"ionic",

core/src/components.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2303,6 +2303,7 @@ export namespace Components {
23032303
* The name of the control, which is submitted with the form data.
23042304
*/
23052305
"name": string;
2306+
"setFocus": () => Promise<void>;
23062307
/**
23072308
* the value of the radio group.
23082309
*/

core/src/components/header/header.utils.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,34 @@ export const handleToolbarIntersection = (
167167

168168
export const setHeaderActive = (headerIndex: HeaderIndex, active = true) => {
169169
const headerEl = headerIndex.el;
170+
const toolbars = headerIndex.toolbars;
171+
const ionTitles = toolbars.map((toolbar) => toolbar.ionTitleEl);
170172

171173
if (active) {
172174
headerEl.classList.remove('header-collapse-condense-inactive');
173-
headerEl.removeAttribute('aria-hidden');
175+
176+
ionTitles.forEach((ionTitle) => {
177+
if (ionTitle) {
178+
ionTitle.removeAttribute('aria-hidden');
179+
}
180+
});
174181
} else {
175182
headerEl.classList.add('header-collapse-condense-inactive');
176-
headerEl.setAttribute('aria-hidden', 'true');
183+
184+
/**
185+
* The small title should only be accessed by screen readers
186+
* when the large title collapses into the small title due
187+
* to scrolling.
188+
*
189+
* Originally, the header was given `aria-hidden="true"`
190+
* but this caused issues with screen readers not being
191+
* able to access any focusable elements within the header.
192+
*/
193+
ionTitles.forEach((ionTitle) => {
194+
if (ionTitle) {
195+
ionTitle.setAttribute('aria-hidden', 'true');
196+
}
197+
});
177198
}
178199
};
179200

core/src/components/header/test/condense/header.e2e.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@ import { configs, test } from '@utils/test/playwright';
33

44
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
55
test.describe(title('header: condense'), () => {
6-
test('should be hidden from screen readers when collapsed', async ({ page }) => {
6+
test('should hide small title from screen readers when collapsed', async ({ page }) => {
7+
test.info().annotations.push({
8+
type: 'issue',
9+
description: 'https://github.com/ionic-team/ionic-framework/issues/29347',
10+
});
11+
712
await page.goto('/src/components/header/test/condense', config);
813
const largeTitleHeader = page.locator('#largeTitleHeader');
914
const smallTitleHeader = page.locator('#smallTitleHeader');
15+
const smallTitle = smallTitleHeader.locator('ion-title');
1016
const content = page.locator('ion-content');
1117

12-
await expect(smallTitleHeader).toHaveAttribute('aria-hidden', 'true');
18+
await expect(smallTitle).toHaveAttribute('aria-hidden', 'true');
1319

1420
await expect(largeTitleHeader).toHaveScreenshot(screenshot(`header-condense-large-title-initial-diff`));
1521

@@ -24,15 +30,15 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, screenshot, c
2430
* Playwright can't do .not.toHaveAttribute() because a value is expected,
2531
* and toHaveAttribute can't accept a value of type null.
2632
*/
27-
const ariaHidden = await smallTitleHeader.getAttribute('aria-hidden');
33+
const ariaHidden = await smallTitle.getAttribute('aria-hidden');
2834
expect(ariaHidden).toBeNull();
2935

3036
await content.evaluate(async (el: HTMLIonContentElement) => {
3137
await el.scrollToTop();
3238
});
3339
await page.locator('#smallTitleHeader.header-collapse-condense-inactive').waitFor();
3440

35-
await expect(smallTitleHeader).toHaveAttribute('aria-hidden', 'true');
41+
await expect(smallTitle).toHaveAttribute('aria-hidden', 'true');
3642
});
3743
});
3844
});

core/src/components/menu/menu.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { Attributes } from '@utils/helpers';
88
import { inheritAriaAttributes, assert, clamp, isEndSide as isEnd } from '@utils/helpers';
99
import { menuController } from '@utils/menu-controller';
1010
import { BACKDROP, GESTURE, getPresentedOverlay } from '@utils/overlays';
11+
import { isPlatform } from '@utils/platform';
1112
import { hostContext } from '@utils/theme';
1213

1314
import { config } from '../../global/config';
@@ -631,6 +632,23 @@ export class Menu implements ComponentInterface, MenuI {
631632
private beforeAnimation(shouldOpen: boolean, role?: string) {
632633
assert(!this.isAnimating, '_before() should not be called while animating');
633634

635+
/**
636+
* When the menu is presented on an Android device, TalkBack's focus rings
637+
* may appear in the wrong position due to the transition (specifically
638+
* `transform` styles). This occurs because the focus rings are initially
639+
* displayed at the starting position of the elements before the transition
640+
* begins. This workaround ensures the focus rings do not appear in the
641+
* incorrect location.
642+
*
643+
* If this solution is applied to iOS devices, then it leads to a bug where
644+
* the overlays cannot be accessed by screen readers. This is due to
645+
* VoiceOver not being able to update the accessibility tree when the
646+
* `aria-hidden` is removed.
647+
*/
648+
if (isPlatform('android')) {
649+
this.el.setAttribute('aria-hidden', 'true');
650+
}
651+
634652
// this places the menu into the correct location before it animates in
635653
// this css class doesn't actually kick off any animations
636654
this.el.classList.add(SHOW_MENU);
@@ -687,6 +705,17 @@ export class Menu implements ComponentInterface, MenuI {
687705
}
688706

689707
if (isOpen) {
708+
/**
709+
* When the menu is presented on an Android device, TalkBack's focus rings
710+
* may appear in the wrong position due to the transition (specifically
711+
* `transform` styles). The menu is hidden from screen readers during the
712+
* transition to prevent this. Once the transition is complete, the menu
713+
* is shown again.
714+
*/
715+
if (isPlatform('android')) {
716+
this.el.removeAttribute('aria-hidden');
717+
}
718+
690719
// emit open event
691720
this.ionDidOpen.emit();
692721

@@ -703,6 +732,8 @@ export class Menu implements ComponentInterface, MenuI {
703732
// start focus trapping
704733
document.addEventListener('focus', this.handleFocus, true);
705734
} else {
735+
this.el.removeAttribute('aria-hidden');
736+
706737
// remove css classes and unhide content from screen readers
707738
this.el.classList.remove(SHOW_MENU);
708739

core/src/components/radio-group/radio-group.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ComponentInterface, EventEmitter } from '@stencil/core';
2-
import { Component, Element, Event, Host, Listen, Prop, Watch, h } from '@stencil/core';
2+
import { Component, Element, Event, Host, Listen, Method, Prop, Watch, h } from '@stencil/core';
33
import { renderHiddenInput } from '@utils/helpers';
44

55
import { getIonMode } from '../../global/ionic-global';
@@ -217,6 +217,13 @@ export class RadioGroup implements ComponentInterface {
217217
}
218218
}
219219

220+
/** @internal */
221+
@Method()
222+
async setFocus() {
223+
const radioToFocus = this.getRadios().find((r) => r.tabIndex !== -1);
224+
radioToFocus?.setFocus();
225+
}
226+
220227
render() {
221228
const { label, labelId, el, name, value } = this;
222229
const mode = getIonMode(this);

core/src/components/segment/test/segment-events.e2e.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from '@playwright/test';
2-
import { configs, test } from '@utils/test/playwright';
2+
import { configs, test, dragElementBy } from '@utils/test/playwright';
33

44
/**
55
* This behavior does not vary across modes/directions.
@@ -105,8 +105,7 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
105105
});
106106
});
107107

108-
// TODO FW-3021
109-
test.describe.skip('when the pointer is released', () => {
108+
test.describe('when the pointer is released', () => {
110109
test('should emit if the value has changed', async ({ page }) => {
111110
test.info().annotations.push({
112111
type: 'issue',
@@ -136,14 +135,22 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
136135

137136
const ionChangeSpy = await page.spyOnEvent('ionChange');
138137

138+
const segment = page.locator('ion-segment');
139139
const firstButton = page.locator('ion-segment-button[value="1"]');
140140
const lastButton = page.locator('ion-segment-button[value="3"]');
141141

142-
await firstButton.hover();
143-
await page.mouse.down();
144-
145-
await lastButton.hover();
146-
await page.mouse.up();
142+
/*
143+
* `dragByX` should represent the total width of all segment buttons,
144+
* excluding the first half of the first button and the second half
145+
* of the last button. This calculation accounts for dragging from
146+
* the center of the first button to the center of the last button.
147+
*/
148+
const segmentWidth = await segment.boundingBox().then((box) => (box ? box.width : 0));
149+
const firstButtonWidth = await firstButton.boundingBox().then((box) => (box ? box.width : 0));
150+
const lastButtonWidth = await lastButton.boundingBox().then((box) => (box ? box.width : 0));
151+
const dragByX = segmentWidth - firstButtonWidth / 2 - lastButtonWidth / 2;
152+
153+
await dragElementBy(firstButton, page, dragByX);
147154

148155
expect(ionChangeSpy).toHaveReceivedEventDetail({ value: '3' });
149156
expect(ionChangeSpy).toHaveReceivedEventTimes(1);

0 commit comments

Comments
 (0)