diff --git a/.changeset/metal-crabs-develop.md b/.changeset/metal-crabs-develop.md
new file mode 100644
index 0000000000..6b982efbea
--- /dev/null
+++ b/.changeset/metal-crabs-develop.md
@@ -0,0 +1,7 @@
+---
+"@justeattakeaway/pie-card": patch
+"pie-storybook": patch
+"pie-docs": patch
+---
+
+[Fixed] - Ensure the card is not interactive when `disabled` is passed
diff --git a/apps/pie-docs/src/components/card/code/props.json b/apps/pie-docs/src/components/card/code/props.json
index 381afe9538..d1ad14c15c 100644
--- a/apps/pie-docs/src/components/card/code/props.json
+++ b/apps/pie-docs/src/components/card/code/props.json
@@ -58,7 +58,7 @@
"type": "code",
"item": ["true", "false"]
},
- "Whether or not the card should be disabled. This applies disabled styles and turns off interactivity.",
+ "Whether or not the card should be disabled. This applies disabled styles and turns off interactivity.
If the card is used as a link, the `href` attribute will be removed so the link can no longer be navigated.",
{
"type": "code",
"item": ["false"]
diff --git a/apps/pie-storybook/stories/pie-card.stories.ts b/apps/pie-storybook/stories/pie-card.stories.ts
index 4c2213c4f2..aa5436b501 100644
--- a/apps/pie-storybook/stories/pie-card.stories.ts
+++ b/apps/pie-storybook/stories/pie-card.stories.ts
@@ -2,6 +2,7 @@ import { nothing } from 'lit';
import { html } from 'lit/static-html.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { type Meta } from '@storybook/web-components';
+import { action } from '@storybook/addon-actions';
import '@justeattakeaway/pie-card';
import {
@@ -112,6 +113,8 @@ const cardStoryMeta: CardStoryMeta = {
export default cardStoryMeta;
+const clickAction = action('clicked');
+
const Template: TemplateFunction = ({
tag,
href,
@@ -123,7 +126,10 @@ const Template: TemplateFunction = ({
variant,
padding,
isDraggable,
-}) => html`
+}) => {
+ const isButton = tag === 'button';
+
+ return html`
= ({
?disabled="${disabled}"
.aria="${aria}"
padding="${padding || nothing}"
- ?isDraggable="${isDraggable}">
+ ?isDraggable="${isDraggable}"
+ @click="${isButton ? clickAction : nothing}">
${sanitizeAndRenderHTML(slot)}
`;
+};
const createCardStory = createStory(Template, defaultArgs);
diff --git a/packages/components/pie-card/src/card.scss b/packages/components/pie-card/src/card.scss
index 56cd3b6804..409d091bda 100644
--- a/packages/components/pie-card/src/card.scss
+++ b/packages/components/pie-card/src/card.scss
@@ -39,10 +39,6 @@
outline: none;
text-decoration: none;
- &:focus-visible {
- @include p.focus;
- }
-
&.c-card--disabled {
--card-bg-color: var(--dt-color-disabled-01);
@@ -95,4 +91,8 @@
&.c-card--draggable {
@extend %has-grab-cursor;
}
+
+ &:focus-visible {
+ @include p.focus;
+ }
}
diff --git a/packages/components/pie-card/src/index.ts b/packages/components/pie-card/src/index.ts
index c03517e94e..99fa387745 100644
--- a/packages/components/pie-card/src/index.ts
+++ b/packages/components/pie-card/src/index.ts
@@ -56,6 +56,14 @@ export class PieCard extends LitElement implements CardProps {
@queryAssignedElements({ flatten: true })
private assignedElements?: HTMLElement[];
+ private onClickHandler (event: Event) {
+ if (this.disabled) {
+ // needed to intercept/prevent click events when the card is disabled.
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+
/**
* Renders the card as an anchor element.
*
@@ -63,21 +71,22 @@ export class PieCard extends LitElement implements CardProps {
*/
private renderAnchor (classes: ClassInfo): TemplateResult {
const paddingCSS = this.generatePaddingCSS();
+ const {
+ href, rel, target, disabled, aria,
+ } = this;
return html`
-
-
+
`;
}
@@ -196,10 +205,11 @@ export class PieCard extends LitElement implements CardProps {
class="${classMap(classes)}"
data-test-id="pie-card"
role="button"
- tabindex="0"
+ tabindex=${disabled ? '-1' : '0'}
aria-label=${aria?.label || nothing}
aria-disabled=${disabled ? 'true' : 'false'}
- style=${paddingCSS || ''}>
+ style=${paddingCSS || ''}
+ @click=${this.onClickHandler}>
`;
diff --git a/packages/components/pie-card/test/component/pie-card.spec.ts b/packages/components/pie-card/test/component/pie-card.spec.ts
index 498e4663a9..6f214fcdf0 100644
--- a/packages/components/pie-card/test/component/pie-card.spec.ts
+++ b/packages/components/pie-card/test/component/pie-card.spec.ts
@@ -355,5 +355,114 @@ test.describe('PieCard - Component tests', () => {
await expect(image).not.toHaveCSS('opacity', '0.5');
});
});
+
+ test.describe('when the prop `tag` is set to `button`', () => {
+ test('should set `aria-disabled` to `true` when disabled', async ({ mount, page }) => {
+ // Arrange
+ await mount(PieCard, {
+ props: {
+ tag: 'button',
+ disabled: true,
+ } as CardProps,
+ slots: {
+ default: slotContent,
+ },
+ });
+
+ // Act
+ const component = page.locator(componentSelector);
+
+ // Assert
+ await expect(component).toHaveAttribute('aria-disabled', 'true');
+ });
+
+ test('should set `tabindex` to `-1` when disabled', async ({ mount, page }) => {
+ // Arrange
+ await mount(PieCard, {
+ props: {
+ tag: 'button',
+ disabled: true,
+ } as CardProps,
+ slots: {
+ default: slotContent,
+ },
+ });
+
+ // Act
+ const component = page.locator(componentSelector);
+
+ // Assert
+ await expect(component).toHaveAttribute('tabindex', '-1');
+ });
+
+ test('should not trigger the click event when the tag prop is set to `button` and is `disabled`', async ({ mount, page }) => {
+ // Arrange
+ const messages: string[] = [];
+
+ await mount(PieCard, {
+ props: {
+ tag: 'button',
+ disabled: true,
+ } as CardProps,
+ slots: {
+ default: slotContent,
+ },
+ on: {
+ click: () => messages.push('1'),
+ },
+ });
+
+ // Act
+ const component = page.locator(componentSelector);
+ await page.evaluate(() => {
+ const card = document.querySelector('pie-card');
+ card?.shadowRoot?.querySelector('div')?.click();
+ });
+
+ // Assert
+ await expect(component).toBeDisabled();
+ expect(messages).toHaveLength(0);
+ });
+ });
+
+ test.describe('when the prop `tag` is set to `a`', () => {
+ test('should set `aria-disabled` to `true` when disabled', async ({ mount, page }) => {
+ // Arrange
+ await mount(PieCard, {
+ props: {
+ tag: 'a',
+ disabled: true,
+ } as CardProps,
+ slots: {
+ default: slotContent,
+ },
+ });
+
+ // Act
+ const component = page.locator(componentSelector);
+
+ // Assert
+ await expect(component).toHaveAttribute('aria-disabled', 'true');
+ });
+
+ test('should not set the href attribute when disabled', async ({ mount, page }) => {
+ // Arrange
+ await mount(PieCard, {
+ props: {
+ tag: 'a',
+ disabled: true,
+ } as CardProps,
+ slots: {
+ default: slotContent,
+ },
+ });
+
+ // Act
+ const component = page.locator(componentSelector);
+
+ // Assert
+ await expect(component).not.toHaveAttribute('href');
+ });
+ });
});
});