mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 11:41:20 +08:00
fix(tab-button): nested interactives are not rendered (#26576)
resolves #23332
This commit is contained in:
@ -5,6 +5,8 @@ import { config } from '../../global/config';
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import type { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from '../../interface';
|
||||
import type { AnchorInterface } from '../../utils/element-interface';
|
||||
import type { Attributes } from '../../utils/helpers';
|
||||
import { inheritAttributes } from '../../utils/helpers';
|
||||
|
||||
/**
|
||||
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
|
||||
@ -20,6 +22,8 @@ import type { AnchorInterface } from '../../utils/element-interface';
|
||||
shadow: true,
|
||||
})
|
||||
export class TabButton implements ComponentInterface, AnchorInterface {
|
||||
private inheritedAttributes: Attributes = {};
|
||||
|
||||
@Element() el!: HTMLElement;
|
||||
|
||||
/**
|
||||
@ -88,6 +92,10 @@ export class TabButton implements ComponentInterface, AnchorInterface {
|
||||
}
|
||||
|
||||
componentWillLoad() {
|
||||
this.inheritedAttributes = {
|
||||
...inheritAttributes(this.el, ['aria-label']),
|
||||
};
|
||||
|
||||
if (this.layout === undefined) {
|
||||
this.layout = config.get('tabButtonLayout', 'icon-top');
|
||||
}
|
||||
@ -114,20 +122,6 @@ export class TabButton implements ComponentInterface, AnchorInterface {
|
||||
return !!this.el.querySelector('ion-icon');
|
||||
}
|
||||
|
||||
private get tabIndex() {
|
||||
if (this.disabled) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const hasTabIndex = this.el.hasAttribute('tabindex');
|
||||
|
||||
if (hasTabIndex) {
|
||||
return this.el.getAttribute('tabindex');
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private onKeyUp = (ev: KeyboardEvent) => {
|
||||
if (ev.key === 'Enter' || ev.key === ' ') {
|
||||
this.selectTab(ev);
|
||||
@ -139,7 +133,7 @@ export class TabButton implements ComponentInterface, AnchorInterface {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { disabled, hasIcon, hasLabel, tabIndex, href, rel, target, layout, selected, tab } = this;
|
||||
const { disabled, hasIcon, hasLabel, href, rel, target, layout, selected, tab, inheritedAttributes } = this;
|
||||
const mode = getIonMode(this);
|
||||
const attrs = {
|
||||
download: this.download,
|
||||
@ -152,9 +146,6 @@ export class TabButton implements ComponentInterface, AnchorInterface {
|
||||
<Host
|
||||
onClick={this.onClick}
|
||||
onKeyup={this.onKeyUp}
|
||||
role="tab"
|
||||
tabindex={tabIndex}
|
||||
aria-selected={selected ? 'true' : null}
|
||||
id={tab !== undefined ? `tab-button-${tab}` : null}
|
||||
class={{
|
||||
[mode]: true,
|
||||
@ -170,7 +161,16 @@ export class TabButton implements ComponentInterface, AnchorInterface {
|
||||
'ion-focusable': true,
|
||||
}}
|
||||
>
|
||||
<a {...attrs} tabIndex={-1} class="button-native" part="native">
|
||||
<a
|
||||
{...attrs}
|
||||
class="button-native"
|
||||
part="native"
|
||||
role="tab"
|
||||
aria-selected={selected ? 'true' : null}
|
||||
aria-disabled={disabled ? 'true' : null}
|
||||
tabindex={disabled ? '-1' : undefined}
|
||||
{...inheritedAttributes}
|
||||
>
|
||||
<span class="button-inner">
|
||||
<slot></slot>
|
||||
</span>
|
||||
|
59
core/src/components/tab-button/test/a11y/index.html
Normal file
59
core/src/components/tab-button/test/a11y/index.html
Normal file
@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Tab Button - a11y</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
|
||||
<link href="../../../../../css/core.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>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1>Tab Button</h1>
|
||||
<ion-tab-bar>
|
||||
<ion-tab-button href="#">
|
||||
<ion-label>Tab One</ion-label>
|
||||
<ion-icon aria-hidden="true" name="star"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button href="#">
|
||||
<ion-label>Tab Two</ion-label>
|
||||
<ion-icon aria-hidden="true" name="globe"></ion-icon>
|
||||
<ion-badge aria-hidden="true" color="danger">6</ion-badge>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button href="#" disabled="true">
|
||||
<ion-label>Tab Three</ion-label>
|
||||
<ion-icon aria-hidden="true" name="logo-facebook"></ion-icon>
|
||||
<ion-badge aria-hidden="true" color="primary">6</ion-badge>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button hidden>
|
||||
<ion-label>Hidden</ion-label>
|
||||
<ion-icon aria-hidden="true" name="alert"></ion-icon>
|
||||
</ion-tab-button>
|
||||
|
||||
<ion-tab-button href="#">
|
||||
<ion-label>Tab Four</ion-label>
|
||||
<ion-icon aria-hidden="true" name="chatbox"></ion-icon>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
|
||||
<ion-tab-bar>
|
||||
<ion-tab-button href="#" aria-label="Tab 1">
|
||||
<ion-icon aria-hidden="true" name="star"></ion-icon>
|
||||
</ion-tab-button>
|
||||
<ion-tab-button href="#" aria-label="Tab 2">
|
||||
<ion-icon aria-hidden="true" name="globe"></ion-icon>
|
||||
</ion-tab-button>
|
||||
<ion-tab-button href="#" aria-label="Tab 3">
|
||||
<ion-icon aria-hidden="true" name="chatbox"></ion-icon>
|
||||
</ion-tab-button>
|
||||
</ion-tab-bar>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
12
core/src/components/tab-button/test/a11y/tab-button.e2e.ts
Normal file
12
core/src/components/tab-button/test/a11y/tab-button.e2e.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import AxeBuilder from '@axe-core/playwright';
|
||||
import { expect } from '@playwright/test';
|
||||
import { test } from '@utils/test/playwright';
|
||||
|
||||
test.describe('tab-button: a11y', () => {
|
||||
test('should not have any axe violations', async ({ page }) => {
|
||||
await page.goto('/src/components/tab-button/test/a11y');
|
||||
|
||||
const results = await new AxeBuilder({ page }).analyze();
|
||||
expect(results.violations).toEqual([]);
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user