diff --git a/.changeset/eight-windows-shout.md b/.changeset/eight-windows-shout.md
new file mode 100644
index 00000000000..5849541ebac
--- /dev/null
+++ b/.changeset/eight-windows-shout.md
@@ -0,0 +1,7 @@
+---
+"@siemens/ix-angular": minor
+"@siemens/ix": minor
+"@siemens/ix-vue": minor
+---
+
+feat(core): improve component a11y
diff --git a/packages/angular/src/components.ts b/packages/angular/src/components.ts
index 9eb6d100afe..2bf486f7404 100644
--- a/packages/angular/src/components.ts
+++ b/packages/angular/src/components.ts
@@ -350,14 +350,14 @@ export declare interface IxCardTitle extends Components.IxCardTitle {}
@ProxyCmp({
- inputs: ['categories', 'disabled', 'filterState', 'hideIcon', 'i18nPlainText', 'icon', 'labelCategories', 'nonSelectableCategories', 'placeholder', 'readonly', 'repeatCategories', 'staticOperator', 'suggestions']
+ inputs: ['ariaLabel', 'categories', 'disabled', 'filterState', 'hideIcon', 'i18nPlainText', 'icon', 'labelCategories', 'nonSelectableCategories', 'placeholder', 'readonly', 'repeatCategories', 'staticOperator', 'suggestions']
})
@Component({
selector: 'ix-category-filter',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
- inputs: ['categories', 'disabled', 'filterState', 'hideIcon', 'i18nPlainText', 'icon', 'labelCategories', 'nonSelectableCategories', 'placeholder', 'readonly', 'repeatCategories', 'staticOperator', 'suggestions'],
+ inputs: ['ariaLabel', 'categories', 'disabled', 'filterState', 'hideIcon', 'i18nPlainText', 'icon', 'labelCategories', 'nonSelectableCategories', 'placeholder', 'readonly', 'repeatCategories', 'staticOperator', 'suggestions'],
})
export class IxCategoryFilter {
protected el: HTMLElement;
diff --git a/packages/core/component-doc.json b/packages/core/component-doc.json
index 4297aec4e69..106b9a8ef0a 100644
--- a/packages/core/component-doc.json
+++ b/packages/core/component-doc.json
@@ -583,7 +583,7 @@
"type": "string"
}
],
- "optional": false,
+ "optional": true,
"required": false
},
{
@@ -604,7 +604,7 @@
"type": "string"
}
],
- "optional": false,
+ "optional": true,
"required": false
},
{
@@ -625,7 +625,7 @@
"type": "string"
}
],
- "optional": false,
+ "optional": true,
"required": false
},
{
@@ -651,7 +651,7 @@
"type": "string"
}
],
- "optional": false,
+ "optional": true,
"required": false
}
],
@@ -2154,6 +2154,33 @@
]
},
"props": [
+ {
+ "name": "ariaLabel",
+ "type": "string",
+ "complexType": {
+ "original": "string",
+ "resolved": "string",
+ "references": {}
+ },
+ "mutable": false,
+ "attr": "aria-label",
+ "reflectToAttr": false,
+ "docs": "Aria label for the filter input field",
+ "docsTags": [
+ {
+ "name": "since",
+ "text": "2.6.0"
+ }
+ ],
+ "default": "'Filter'",
+ "values": [
+ {
+ "type": "string"
+ }
+ ],
+ "optional": false,
+ "required": false
+ },
{
"name": "categories",
"type": "{ [id: string]: { label: string; options: string[]; }; }",
@@ -6302,6 +6329,7 @@
"reflectToAttr": false,
"docs": "Display a chevron icon in list items. Defaults to 'false'",
"docsTags": [],
+ "default": "false",
"values": [
{
"type": "boolean"
@@ -6399,6 +6427,7 @@
"reflectToAttr": false,
"docs": "Show chevron on right side of the event list item",
"docsTags": [],
+ "default": "false",
"values": [
{
"type": "boolean"
@@ -6435,7 +6464,7 @@
"type": "string"
}
],
- "optional": false,
+ "optional": true,
"required": false
},
{
@@ -6451,6 +6480,7 @@
"reflectToAttr": false,
"docs": "Disable event list item",
"docsTags": [],
+ "default": "false",
"values": [
{
"type": "boolean"
@@ -6482,7 +6512,7 @@
"type": "string"
}
],
- "optional": false,
+ "optional": true,
"required": false
},
{
@@ -6498,6 +6528,7 @@
"reflectToAttr": false,
"docs": "Show event list item as selected",
"docsTags": [],
+ "default": "false",
"values": [
{
"type": "boolean"
@@ -9281,7 +9312,7 @@
"type": "string"
}
],
- "optional": false,
+ "optional": true,
"required": false
},
{
@@ -12186,7 +12217,7 @@
"type": "string"
}
],
- "optional": false,
+ "optional": true,
"required": false
},
{
@@ -12229,7 +12260,7 @@
"type": "string"
}
],
- "optional": false,
+ "optional": true,
"required": false
},
{
diff --git a/packages/core/src/components.d.ts b/packages/core/src/components.d.ts
index 4291b42557c..f315b252d37 100644
--- a/packages/core/src/components.d.ts
+++ b/packages/core/src/components.d.ts
@@ -163,20 +163,20 @@ export namespace Components {
* Optional description text that will be displayed underneath the username. Note: Only working if avatar is part of the ix-application-header
* @since 2.1.0
*/
- "extra": string;
+ "extra"?: string;
/**
* Display an avatar image
*/
- "image": string;
+ "image"?: string;
/**
* Display the initials of the user. Will be overwritten by image
*/
- "initials": string;
+ "initials"?: string;
/**
* If set an info card displaying the username will be placed inside the dropdown. Note: Only working if avatar is part of the ix-application-header
* @since 2.1.0
*/
- "username": string;
+ "username"?: string;
}
interface IxBasicNavigation {
/**
@@ -366,6 +366,11 @@ export namespace Components {
interface IxCardTitle {
}
interface IxCategoryFilter {
+ /**
+ * Aria label for the filter input field
+ * @since 2.6.0
+ */
+ "ariaLabel": string;
/**
* Configuration object hash used to populate the dropdown menu for type-ahead and quick selection functionality. Each ID maps to an object with a label and an array of options to select from.
*/
@@ -987,7 +992,7 @@ export namespace Components {
* @link https://ix.siemens.io/docs/theming/colors/
* @deprecated since 2.1.0 use `item-color`
*/
- "color": string;
+ "color"?: string;
/**
* Disable event list item
*/
@@ -996,7 +1001,7 @@ export namespace Components {
* Color of the status indicator. You can find a list of all available colors in our documentation. Example values are `--theme-color-alarm` or `color-alarm`
* @link https://ix.siemens.io/docs/theming/colors/
*/
- "itemColor": string;
+ "itemColor"?: string;
/**
* Show event list item as selected
*/
@@ -1348,7 +1353,7 @@ export namespace Components {
/**
* Should only be set if you use ix-menu standalone
*/
- "applicationName": string;
+ "applicationName"?: string;
/**
* Internal
*/
@@ -1740,7 +1745,7 @@ export namespace Components {
/**
* Title of the side panel
*/
- "heading": string;
+ "heading"?: string;
/**
* Define if the pane should have a collapsed state
*/
@@ -1748,7 +1753,7 @@ export namespace Components {
/**
* Name of the icon
*/
- "icon": string;
+ "icon"?: string;
"ignoreLayoutSettings": boolean;
"isMobile": boolean;
/**
@@ -4442,6 +4447,11 @@ declare namespace LocalJSX {
interface IxCardTitle {
}
interface IxCategoryFilter {
+ /**
+ * Aria label for the filter input field
+ * @since 2.6.0
+ */
+ "ariaLabel"?: string;
/**
* Configuration object hash used to populate the dropdown menu for type-ahead and quick selection functionality. Each ID maps to an object with a label and an array of options to select from.
*/
diff --git a/packages/core/src/components/avatar/avatar.scss b/packages/core/src/components/avatar/avatar.scss
index 5a69d88c526..d1f668bea67 100644
--- a/packages/core/src/components/avatar/avatar.scss
+++ b/packages/core/src/components/avatar/avatar.scss
@@ -106,7 +106,7 @@
@include btn-base-variant('invisible-primary', false);
- li {
+ .avatar {
transform: scale(0.8);
}
}
diff --git a/packages/core/src/components/avatar/avatar.tsx b/packages/core/src/components/avatar/avatar.tsx
index 04b1f4f2b5e..1e6cbfc55c6 100644
--- a/packages/core/src/components/avatar/avatar.tsx
+++ b/packages/core/src/components/avatar/avatar.tsx
@@ -53,23 +53,23 @@ function DefaultAvatar(props: { initials?: string }) {
);
}
-function AvatarImage(props: { image: string; initials: string }) {
+function AvatarImage(props: { image?: string; initials?: string }) {
return (
-
+
{props.image ? (
) : (
)}
-
+
);
}
function UserInfo(props: {
- image: string;
- initials: string;
+ image?: string;
+ initials?: string;
userName: string;
- extra: string;
+ extra?: string;
}) {
return (
@@ -97,19 +97,19 @@ function UserInfo(props: {
shadow: true,
})
export class Avatar {
- @Element() hostElement: HTMLIxAvatarElement;
+ @Element() hostElement!: HTMLIxAvatarElement;
/**
* Display an avatar image
*
*/
- @Prop() image: string;
+ @Prop() image?: string;
/**
* Display the initials of the user. Will be overwritten by image
*
*/
- @Prop() initials: string;
+ @Prop() initials?: string;
/**
* If set an info card displaying the username will be placed inside the dropdown.
@@ -117,7 +117,7 @@ export class Avatar {
*
* @since 2.1.0
*/
- @Prop() username: string;
+ @Prop() username?: string;
/**
* Optional description text that will be displayed underneath the username.
@@ -125,13 +125,13 @@ export class Avatar {
*
* @since 2.1.0
*/
- @Prop() extra: string;
+ @Prop() extra?: string;
@State() isClosestApplicationHeader = false;
@State() hasSlottedElements = false;
- private slotElement: HTMLSlotElement;
- private dropdownElement: HTMLIxDropdownElement;
+ private slotElement?: HTMLSlotElement;
+ private dropdownElement?: HTMLIxDropdownElement;
componentWillLoad() {
const closest = closestElement('ix-application-header', this.hostElement);
@@ -143,10 +143,15 @@ export class Avatar {
}
private resolveAvatarTrigger() {
- return new Promise((resolve) => {
- readTask(() =>
- resolve(this.hostElement.shadowRoot.querySelector('button'))
- );
+ return new Promise((resolve, reject) => {
+ readTask(() => {
+ const button = this.hostElement.shadowRoot!.querySelector('button');
+ if (button) {
+ resolve(button);
+ } else {
+ reject(new Error('ix-avatar - trigger element not found'));
+ }
+ });
});
}
diff --git a/packages/core/src/components/category-filter/category-filter.scss b/packages/core/src/components/category-filter/category-filter.scss
index 61b2fa31a97..7cecadf8d92 100644
--- a/packages/core/src/components/category-filter/category-filter.scss
+++ b/packages/core/src/components/category-filter/category-filter.scss
@@ -95,9 +95,7 @@
.list-unstyled {
display: flex;
flex-wrap: wrap;
- list-style: none;
- padding: 0;
- margin: 0;
+ height: 100%;
overflow-y: auto;
}
@@ -126,7 +124,7 @@
height: 100%;
}
- ul > li,
+ .list-unstyled > span:not(.category-preview),
input {
padding-inline-start: 0;
padding-top: 2px;
diff --git a/packages/core/src/components/category-filter/category-filter.tsx b/packages/core/src/components/category-filter/category-filter.tsx
index 6a572a05459..105f54c94ad 100644
--- a/packages/core/src/components/category-filter/category-filter.tsx
+++ b/packages/core/src/components/category-filter/category-filter.tsx
@@ -138,6 +138,13 @@ export class CategoryFilter {
*/
@Prop() i18nPlainText = 'Filter by text';
+ /**
+ * Aria label for the filter input field
+ *
+ * @since 2.6.0
+ */
+ @Prop() ariaLabel = 'Filter';
+
/**
* Event dispatched whenever a category gets selected in the dropdown
*/
@@ -710,9 +717,9 @@ export class CategoryFilter {
size="16"
>
{!this.readonly && !this.disabled && this.getResetButton()}
diff --git a/packages/core/src/components/event-list-item/event-list-item.tsx b/packages/core/src/components/event-list-item/event-list-item.tsx
index 4519e60db6b..d86eddd65f5 100644
--- a/packages/core/src/components/event-list-item/event-list-item.tsx
+++ b/packages/core/src/components/event-list-item/event-list-item.tsx
@@ -34,7 +34,7 @@ export class EventListItem {
* @deprecated since 2.1.0 use `item-color`
*/
// eslint-disable-next-line @stencil-community/reserved-member-names
- @Prop() color: string;
+ @Prop() color?: string;
/**
* Color of the status indicator.
@@ -43,27 +43,27 @@ export class EventListItem {
*
* @link https://ix.siemens.io/docs/theming/colors/
*/
- @Prop() itemColor: string;
+ @Prop() itemColor?: string;
/**
* Show event list item as selected
*/
- @Prop() selected: boolean;
+ @Prop() selected = false;
/**
* Disable event list item
*/
- @Prop() disabled: boolean;
+ @Prop() disabled = false;
/**
* Show chevron on right side of the event list item
*/
- @Prop() chevron: boolean;
+ @Prop() chevron = false;
/**
* Event list item click
*/
- @Event() itemClick: EventEmitter;
+ @Event() itemClick!: EventEmitter;
@Listen('click', { passive: true })
handleItemClick() {
@@ -87,7 +87,8 @@ export class EventListItem {
disabled: this.disabled,
}}
>
-
)}
-
+
);
}
diff --git a/packages/core/src/components/event-list/event-list.scss b/packages/core/src/components/event-list/event-list.scss
index f88c0acd2a6..3210978083a 100644
--- a/packages/core/src/components/event-list/event-list.scss
+++ b/packages/core/src/components/event-list/event-list.scss
@@ -14,13 +14,6 @@
display: block;
position: relative;
-
- ul {
- list-style: none;
- padding: 0;
- margin-top: 0;
- margin-bottom: 0;
- }
}
:host(.item-size-l) {
diff --git a/packages/core/src/components/event-list/event-list.tsx b/packages/core/src/components/event-list/event-list.tsx
index e4a87d43c29..115e9923faf 100644
--- a/packages/core/src/components/event-list/event-list.tsx
+++ b/packages/core/src/components/event-list/event-list.tsx
@@ -48,7 +48,7 @@ export class EventList {
/**
* Display a chevron icon in list items. Defaults to 'false'
*/
- @Prop() chevron: boolean;
+ @Prop() chevron = false;
@Watch('chevron')
watchChevron(chevron: boolean | undefined) {
@@ -111,7 +111,7 @@ export class EventList {
const keyframes = [{ opacity: 1, easing: 'easeInSine' }, { opacity: 0 }];
- const listElement = this.hostElement.shadowRoot.querySelector('ul');
+ const listElement = this.hostElement.shadowRoot!.querySelector('ul');
anime({
targets: listElement,
@@ -166,9 +166,9 @@ export class EventList {
compact: this.compact,
}}
>
-
+
-
+
);
}
diff --git a/packages/core/src/components/event-list/test/event-list.spec.tsx b/packages/core/src/components/event-list/test/event-list.spec.tsx
deleted file mode 100644
index 513c9cdc0ad..00000000000
--- a/packages/core/src/components/event-list/test/event-list.spec.tsx
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * SPDX-FileCopyrightText: 2023 Siemens AG
- *
- * SPDX-License-Identifier: MIT
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
-
-import { newSpecPage } from '@stencil/core/testing';
-//@ts-ignore
-import { createMutationObserver } from '../../utils/mutation-observer';
-import { EventList } from '../event-list';
-
-jest.mock('../../utils/mutation-observer');
-
-describe('event-list', () => {
- beforeEach(() => {
- //@ts-ignore
- createMutationObserver = jest.fn(() => ({
- observe: jest.fn(),
- }));
- });
-
- it('renders', async () => {
- const page = await newSpecPage({
- components: [EventList],
- html: `
-
-
-
- `,
- });
- expect(page.root).toEqualHtml(`
-
-
-
-
-
-
- `);
- });
-
- it('compact', async () => {
- const page = await newSpecPage({
- components: [EventList],
- html: `
-
-
-
- `,
- });
-
- const eventList = page.doc.querySelector(
- 'ix-event-list'
- ) as HTMLIxEventListElement;
-
- eventList.compact = true;
-
- await page.waitForChanges();
-
- expect(page.root).toEqualHtml(`
-
-
-
-
-
-
- `);
- });
-});
diff --git a/packages/core/src/components/menu-item/menu-item.tsx b/packages/core/src/components/menu-item/menu-item.tsx
index e5c3573a880..328bcc5575c 100644
--- a/packages/core/src/components/menu-item/menu-item.tsx
+++ b/packages/core/src/components/menu-item/menu-item.tsx
@@ -76,11 +76,11 @@ export class MenuItem {
@State() tooltip?: string;
@State() menuExpanded: boolean = false;
- private buttonRef = makeRef();
+ private readonly buttonRef = makeRef();
private isHostedInsideCategory = false;
- private menuExpandedDisposer: Disposable;
+ private menuExpandedDisposer?: Disposable;
- private observer: MutationObserver = createMutationObserver(() => {
+ private readonly observer: MutationObserver = createMutationObserver(() => {
this.setTooltip();
});
@@ -91,7 +91,7 @@ export class MenuItem {
this.onIconChange();
this.onTabIconChange();
- this.menuExpanded = menuController.nativeElement.expand;
+ this.menuExpanded = menuController.nativeElement?.expand || false;
this.menuExpandedDisposer = menuController.expandChange.on(
(expand) => (this.menuExpanded = expand)
);
@@ -102,7 +102,7 @@ export class MenuItem {
}
setTooltip() {
- this.tooltip = this.label ?? this.hostElement.textContent;
+ this.tooltip = this.label ?? this.hostElement.textContent ?? undefined;
}
connectedCallback() {
@@ -172,7 +172,6 @@ export class MenuItem {