diff --git a/core/package-lock.json b/core/package-lock.json
index a5b0230238..b25dedceea 100644
--- a/core/package-lock.json
+++ b/core/package-lock.json
@@ -24,7 +24,7 @@
"@stencil/angular-output-target": "^0.4.0",
"@stencil/react-output-target": "^0.2.1",
"@stencil/sass": "^2.0.0",
- "@stencil/vue-output-target": "^0.6.2",
+ "@stencil/vue-output-target": "^0.7.0",
"@types/jest": "^27.5.2",
"@types/node": "^14.6.0",
"@types/swiper": "5.4.0",
@@ -1632,12 +1632,12 @@
}
},
"node_modules/@stencil/vue-output-target": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.6.2.tgz",
- "integrity": "sha512-Oh7SLFbOUchCSCbGe/Dqal2xSYPKCFQiVKnvzvS0dsHP/XS7rfHqp3qptW6JCp9lBoo3wmmBurHfldqxhLlnag==",
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.7.0.tgz",
+ "integrity": "sha512-iPEgnT2z6HsfWVRWVZk5C1AaMZnbJjB+c/hvtWoO7B3aErTJB8Up6oFk/t3IRsr12aNuZ4fUra0FEDx9WweH0Q==",
"dev": true,
"peerDependencies": {
- "@stencil/core": "^2.9.0"
+ "@stencil/core": "^2.9.0 || ^3.0.0-beta.0"
}
},
"node_modules/@stylelint/postcss-css-in-js": {
@@ -11638,11 +11638,10 @@
"requires": {}
},
"@stencil/vue-output-target": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.6.2.tgz",
- "integrity": "sha512-Oh7SLFbOUchCSCbGe/Dqal2xSYPKCFQiVKnvzvS0dsHP/XS7rfHqp3qptW6JCp9lBoo3wmmBurHfldqxhLlnag==",
- "dev": true,
- "requires": {}
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/@stencil/vue-output-target/-/vue-output-target-0.7.0.tgz",
+ "integrity": "sha512-iPEgnT2z6HsfWVRWVZk5C1AaMZnbJjB+c/hvtWoO7B3aErTJB8Up6oFk/t3IRsr12aNuZ4fUra0FEDx9WweH0Q==",
+ "dev": true
},
"@stylelint/postcss-css-in-js": {
"version": "0.37.2",
diff --git a/core/package.json b/core/package.json
index b21a116fb6..bc82a5944d 100644
--- a/core/package.json
+++ b/core/package.json
@@ -46,7 +46,7 @@
"@stencil/angular-output-target": "^0.4.0",
"@stencil/react-output-target": "^0.2.1",
"@stencil/sass": "^2.0.0",
- "@stencil/vue-output-target": "^0.6.2",
+ "@stencil/vue-output-target": "^0.7.0",
"@types/jest": "^27.5.2",
"@types/node": "^14.6.0",
"@types/swiper": "5.4.0",
diff --git a/core/src/components/item/item.tsx b/core/src/components/item/item.tsx
index f68d8f94d1..06ef7b426f 100644
--- a/core/src/components/item/item.tsx
+++ b/core/src/components/item/item.tsx
@@ -226,9 +226,12 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
}
}
+ componentWillLoad() {
+ this.inheritedAriaAttributes = inheritAttributes(this.el, ['aria-label']);
+ }
+
componentDidLoad() {
raf(() => {
- this.inheritedAriaAttributes = inheritAttributes(this.el, ['aria-label']);
this.setMultipleInputs();
this.focusable = this.isFocusable();
});
diff --git a/core/src/components/item/test/a11y/item.e2e.ts b/core/src/components/item/test/a11y/item.e2e.ts
index ff85a14d75..e958af90d5 100644
--- a/core/src/components/item/test/a11y/item.e2e.ts
+++ b/core/src/components/item/test/a11y/item.e2e.ts
@@ -12,4 +12,17 @@ test.describe('item: axe', () => {
.analyze();
expect(results.violations).toEqual([]);
});
+
+ test('should reflect aria-label', async ({ page }) => {
+ await page.setContent(`
+
+
+ `);
+
+ const item1 = await page.locator('#item-1 .item-native');
+ const item2 = await page.locator('#item-2 .item-native');
+
+ expect(await item1.getAttribute('aria-label')).toEqual('test');
+ expect(await item2.getAttribute('aria-label')).toEqual('test');
+ });
});
diff --git a/packages/vue/src/vue-component-lib/utils.ts b/packages/vue/src/vue-component-lib/utils.ts
index e48debacfa..9381dc917b 100644
--- a/packages/vue/src/vue-component-lib/utils.ts
+++ b/packages/vue/src/vue-component-lib/utils.ts
@@ -9,7 +9,7 @@ const MODEL_VALUE = 'modelValue';
const ROUTER_LINK_VALUE = 'routerLink';
const NAV_MANAGER = 'navManager';
const ROUTER_PROP_PREFIX = 'router';
-
+const ARIA_PROP_PREFIX = 'aria';
/**
* Starting in Vue 3.1.0, all properties are
* added as keys to the props object, even if
@@ -30,26 +30,31 @@ const getComponentClasses = (classes: unknown) => {
return (classes as string)?.split(' ') || [];
};
-const getElementClasses = (ref: Ref, componentClasses: Set, defaultClasses: string[] = []) => {
- return [ ...Array.from(ref.value?.classList || []), ...defaultClasses ]
- .filter((c: string, i, self) => !componentClasses.has(c) && self.indexOf(c) === i);
+const getElementClasses = (
+ ref: Ref,
+ componentClasses: Set,
+ defaultClasses: string[] = []
+) => {
+ return [...Array.from(ref.value?.classList || []), ...defaultClasses].filter(
+ (c: string, i, self) => !componentClasses.has(c) && self.indexOf(c) === i
+ );
};
/**
-* Create a callback to define a Vue component wrapper around a Web Component.
-*
-* @prop name - The component tag name (i.e. `ion-button`)
-* @prop componentProps - An array of properties on the
-* component. These usually match up with the @Prop definitions
-* in each component's TSX file.
-* @prop customElement - An option custom element instance to pass
-* to customElements.define. Only set if `includeImportCustomElements: true` in your config.
-* @prop modelProp - The prop that v-model binds to (i.e. value)
-* @prop modelUpdateEvent - The event that is fired from your Web Component when the value changes (i.e. ionChange)
-* @prop externalModelUpdateEvent - The external event to fire from your Vue component when modelUpdateEvent fires. This is used for ensuring that v-model references have been
-* correctly updated when a user's event callback fires.
-*/
-export const defineContainer = (
+ * Create a callback to define a Vue component wrapper around a Web Component.
+ *
+ * @prop name - The component tag name (i.e. `ion-button`)
+ * @prop componentProps - An array of properties on the
+ * component. These usually match up with the @Prop definitions
+ * in each component's TSX file.
+ * @prop customElement - An option custom element instance to pass
+ * to customElements.define. Only set if `includeImportCustomElements: true` in your config.
+ * @prop modelProp - The prop that v-model binds to (i.e. value)
+ * @prop modelUpdateEvent - The event that is fired from your Web Component when the value changes (i.e. ionChange)
+ * @prop externalModelUpdateEvent - The external event to fire from your Vue component when modelUpdateEvent fires. This is used for ensuring that v-model references have been
+ * correctly updated when a user's event callback fires.
+ */
+export const defineContainer = (
name: string,
defineCustomElement: any,
componentProps: string[] = [],
@@ -58,10 +63,10 @@ export const defineContainer = (
externalModelUpdateEvent?: string
) => {
/**
- * Create a Vue component wrapper around a Web Component.
- * Note: The `props` here are not all properties on a component.
- * They refer to whatever properties are set on an instance of a component.
- */
+ * Create a Vue component wrapper around a Web Component.
+ * Note: The `props` here are not all properties on a component.
+ * They refer to whatever properties are set on an instance of a component.
+ */
if (defineCustomElement !== undefined) {
defineCustomElement();
@@ -116,12 +121,12 @@ export const defineContainer = (
} else {
console.warn('Tried to navigate, but no router was found. Make sure you have mounted Vue Router.');
}
- }
+ };
return () => {
modelPropValue = props[modelProp];
- getComponentClasses(attrs.class).forEach(value => {
+ getComponentClasses(attrs.class).forEach((value) => {
classes.add(value);
});
@@ -133,13 +138,13 @@ export const defineContainer = (
if (!ev.defaultPrevented) {
handleRouterLink(ev);
}
- }
+ };
let propsToAdd: any = {
ref: containerRef,
class: getElementClasses(containerRef, classes),
onClick: handleClick,
- onVnodeBeforeMount: (modelUpdateEvent) ? onVnodeBeforeMount : undefined
+ onVnodeBeforeMount: modelUpdateEvent ? onVnodeBeforeMount : undefined,
};
/**
@@ -150,7 +155,7 @@ export const defineContainer = (
*/
for (const key in props) {
const value = props[key];
- if (props.hasOwnProperty(key) && value !== EMPTY_PROP) {
+ if ((props.hasOwnProperty(key) && value !== EMPTY_PROP) || key.startsWith(ARIA_PROP_PREFIX)) {
propsToAdd[key] = value;
}
}
@@ -165,27 +170,27 @@ export const defineContainer = (
if (props[MODEL_VALUE] !== EMPTY_PROP) {
propsToAdd = {
...propsToAdd,
- [modelProp]: props[MODEL_VALUE]
- }
+ [modelProp]: props[MODEL_VALUE],
+ };
} else if (modelPropValue !== EMPTY_PROP) {
propsToAdd = {
...propsToAdd,
- [modelProp]: modelPropValue
- }
+ [modelProp]: modelPropValue,
+ };
}
}
return h(name, propsToAdd, slots.default && slots.default());
- }
+ };
});
Container.displayName = name;
Container.props = {
- [ROUTER_LINK_VALUE]: DEFAULT_EMPTY_PROP
+ [ROUTER_LINK_VALUE]: DEFAULT_EMPTY_PROP,
};
- componentProps.forEach(componentProp => {
+ componentProps.forEach((componentProp) => {
Container.props[componentProp] = DEFAULT_EMPTY_PROP;
});