mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-09 16:16:41 +08:00
fix(item, card): aria-label is reflected to the inner button (#26028)
Resolves #25885
This commit is contained in:
@ -1,9 +1,11 @@
|
|||||||
import type { ComponentInterface } from '@stencil/core';
|
import type { ComponentInterface } from '@stencil/core';
|
||||||
import { Component, Host, Prop, h } from '@stencil/core';
|
import { Element, Component, Host, Prop, h } from '@stencil/core';
|
||||||
|
|
||||||
import { getIonMode } from '../../global/ionic-global';
|
import { getIonMode } from '../../global/ionic-global';
|
||||||
import type { AnimationBuilder, Color, Mode, RouterDirection } from '../../interface';
|
import type { AnimationBuilder, Color, Mode, RouterDirection } from '../../interface';
|
||||||
import type { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
|
import type { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
|
||||||
|
import type { Attributes } from '../../utils/helpers';
|
||||||
|
import { inheritAttributes } from '../../utils/helpers';
|
||||||
import { createColorClasses, openURL } from '../../utils/theme';
|
import { createColorClasses, openURL } from '../../utils/theme';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,6 +22,9 @@ import { createColorClasses, openURL } from '../../utils/theme';
|
|||||||
shadow: true,
|
shadow: true,
|
||||||
})
|
})
|
||||||
export class Card implements ComponentInterface, AnchorInterface, ButtonInterface {
|
export class Card implements ComponentInterface, AnchorInterface, ButtonInterface {
|
||||||
|
private inheritedAriaAttributes: Attributes = {};
|
||||||
|
|
||||||
|
@Element() el!: HTMLElement;
|
||||||
/**
|
/**
|
||||||
* The color to use from your application's color palette.
|
* The color to use from your application's color palette.
|
||||||
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
|
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
|
||||||
@ -81,6 +86,10 @@ export class Card implements ComponentInterface, AnchorInterface, ButtonInterfac
|
|||||||
*/
|
*/
|
||||||
@Prop() target: string | undefined;
|
@Prop() target: string | undefined;
|
||||||
|
|
||||||
|
componentWillLoad() {
|
||||||
|
this.inheritedAriaAttributes = inheritAttributes(this.el, ['aria-label']);
|
||||||
|
}
|
||||||
|
|
||||||
private isClickable(): boolean {
|
private isClickable(): boolean {
|
||||||
return this.href !== undefined || this.button;
|
return this.href !== undefined || this.button;
|
||||||
}
|
}
|
||||||
@ -91,7 +100,7 @@ export class Card implements ComponentInterface, AnchorInterface, ButtonInterfac
|
|||||||
if (!clickable) {
|
if (!clickable) {
|
||||||
return [<slot></slot>];
|
return [<slot></slot>];
|
||||||
}
|
}
|
||||||
const { href, routerAnimation, routerDirection } = this;
|
const { href, routerAnimation, routerDirection, inheritedAriaAttributes } = this;
|
||||||
const TagType = clickable ? (href === undefined ? 'button' : 'a') : ('div' as any);
|
const TagType = clickable ? (href === undefined ? 'button' : 'a') : ('div' as any);
|
||||||
const attrs =
|
const attrs =
|
||||||
TagType === 'button'
|
TagType === 'button'
|
||||||
@ -106,6 +115,7 @@ export class Card implements ComponentInterface, AnchorInterface, ButtonInterfac
|
|||||||
return (
|
return (
|
||||||
<TagType
|
<TagType
|
||||||
{...attrs}
|
{...attrs}
|
||||||
|
{...inheritedAriaAttributes}
|
||||||
class="card-native"
|
class="card-native"
|
||||||
part="native"
|
part="native"
|
||||||
disabled={this.disabled}
|
disabled={this.disabled}
|
||||||
|
|||||||
17
core/src/components/card/test/aria.spec.ts
Normal file
17
core/src/components/card/test/aria.spec.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { newSpecPage } from '@stencil/core/testing';
|
||||||
|
|
||||||
|
import { Card } from '../card';
|
||||||
|
|
||||||
|
describe('card: button', () => {
|
||||||
|
it('should reflect aria-label to button', async () => {
|
||||||
|
const page = await newSpecPage({
|
||||||
|
components: [Card],
|
||||||
|
html: `<ion-card button="true" aria-label="Test"></ion-card>`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const button = page.body.querySelector('ion-card')!.shadowRoot!.querySelector('button')!;
|
||||||
|
const ariaLabel = button.getAttribute('aria-label');
|
||||||
|
|
||||||
|
expect(ariaLabel).toEqual('Test');
|
||||||
|
});
|
||||||
|
});
|
||||||
40
core/src/components/card/test/basic/index.html
Normal file
40
core/src/components/card/test/basic/index.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" dir="ltr">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Card - Basic</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>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<ion-app>
|
||||||
|
<ion-header>
|
||||||
|
<ion-toolbar>
|
||||||
|
<ion-title>Card - Basic</ion-title>
|
||||||
|
</ion-toolbar>
|
||||||
|
</ion-header>
|
||||||
|
|
||||||
|
<ion-content class="ion-padding">
|
||||||
|
<ion-card>
|
||||||
|
<ion-card-header>
|
||||||
|
<ion-card-subtitle>Card Subtitle</ion-card-subtitle>
|
||||||
|
<ion-card-title>Card Title</ion-card-title>
|
||||||
|
</ion-card-header>
|
||||||
|
|
||||||
|
<ion-card-content>
|
||||||
|
Keep close to Nature's heart... and break clear away, once in awhile, and climb a mountain or spend a week
|
||||||
|
in the woods. Wash your spirit clean.
|
||||||
|
</ion-card-content>
|
||||||
|
</ion-card>
|
||||||
|
</ion-content>
|
||||||
|
</ion-app>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -5,7 +5,8 @@ import { chevronForward } from 'ionicons/icons';
|
|||||||
import { getIonMode } from '../../global/ionic-global';
|
import { getIonMode } from '../../global/ionic-global';
|
||||||
import type { AnimationBuilder, Color, CssClassMap, RouterDirection, StyleEventDetail } from '../../interface';
|
import type { AnimationBuilder, Color, CssClassMap, RouterDirection, StyleEventDetail } from '../../interface';
|
||||||
import type { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
|
import type { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
|
||||||
import { raf } from '../../utils/helpers';
|
import type { Attributes } from '../../utils/helpers';
|
||||||
|
import { inheritAttributes, raf } from '../../utils/helpers';
|
||||||
import { printIonError } from '../../utils/logging';
|
import { printIonError } from '../../utils/logging';
|
||||||
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
|
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
|
||||||
import type { InputChangeEventDetail } from '../input/input-interface';
|
import type { InputChangeEventDetail } from '../input/input-interface';
|
||||||
@ -38,6 +39,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
|
|||||||
private labelColorStyles = {};
|
private labelColorStyles = {};
|
||||||
private itemStyles = new Map<string, CssClassMap>();
|
private itemStyles = new Map<string, CssClassMap>();
|
||||||
private clickListener?: (ev: Event) => void;
|
private clickListener?: (ev: Event) => void;
|
||||||
|
private inheritedAriaAttributes: Attributes = {};
|
||||||
|
|
||||||
@Element() el!: HTMLIonItemElement;
|
@Element() el!: HTMLIonItemElement;
|
||||||
|
|
||||||
@ -226,6 +228,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
|
|||||||
|
|
||||||
componentDidLoad() {
|
componentDidLoad() {
|
||||||
raf(() => {
|
raf(() => {
|
||||||
|
this.inheritedAriaAttributes = inheritAttributes(this.el, ['aria-label']);
|
||||||
this.setMultipleInputs();
|
this.setMultipleInputs();
|
||||||
this.focusable = this.isFocusable();
|
this.focusable = this.isFocusable();
|
||||||
});
|
});
|
||||||
@ -359,12 +362,14 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
|
|||||||
target,
|
target,
|
||||||
routerAnimation,
|
routerAnimation,
|
||||||
routerDirection,
|
routerDirection,
|
||||||
|
inheritedAriaAttributes,
|
||||||
} = this;
|
} = this;
|
||||||
const childStyles = {} as any;
|
const childStyles = {} as any;
|
||||||
const mode = getIonMode(this);
|
const mode = getIonMode(this);
|
||||||
const clickable = this.isClickable();
|
const clickable = this.isClickable();
|
||||||
const canActivate = this.canActivate();
|
const canActivate = this.canActivate();
|
||||||
const TagType = clickable ? (href === undefined ? 'button' : 'a') : ('div' as any);
|
const TagType = clickable ? (href === undefined ? 'button' : 'a') : ('div' as any);
|
||||||
|
|
||||||
const attrs =
|
const attrs =
|
||||||
TagType === 'button'
|
TagType === 'button'
|
||||||
? { type: this.type }
|
? { type: this.type }
|
||||||
@ -390,6 +395,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
|
|||||||
const ariaDisabled = disabled || childStyles['item-interactive-disabled'] ? 'true' : null;
|
const ariaDisabled = disabled || childStyles['item-interactive-disabled'] ? 'true' : null;
|
||||||
const fillValue = fill || 'none';
|
const fillValue = fill || 'none';
|
||||||
const inList = hostContext('ion-list', this.el);
|
const inList = hostContext('ion-list', this.el);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Host
|
<Host
|
||||||
aria-disabled={ariaDisabled}
|
aria-disabled={ariaDisabled}
|
||||||
@ -412,7 +418,14 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
|
|||||||
}}
|
}}
|
||||||
role={inList ? 'listitem' : null}
|
role={inList ? 'listitem' : null}
|
||||||
>
|
>
|
||||||
<TagType {...attrs} class="item-native" part="native" disabled={disabled} {...clickFn}>
|
<TagType
|
||||||
|
{...attrs}
|
||||||
|
{...inheritedAriaAttributes}
|
||||||
|
class="item-native"
|
||||||
|
part="native"
|
||||||
|
disabled={disabled}
|
||||||
|
{...clickFn}
|
||||||
|
>
|
||||||
<slot name="start"></slot>
|
<slot name="start"></slot>
|
||||||
<div class="item-inner">
|
<div class="item-inner">
|
||||||
<div class="input-wrapper">
|
<div class="input-wrapper">
|
||||||
|
|||||||
Reference in New Issue
Block a user