Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(pie-card): DSW-2426 image opacity when disabled #1946

Merged
merged 15 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/mean-planets-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@justeattakeaway/pie-card": minor
---

[Added] Ability to set images to 50% opacity when disabled
kevinrodrigues marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 15 additions & 0 deletions apps/pie-storybook/stories/pie-card.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,18 @@ export const Default = createCardStory();
export const Outline = createCardStory({ variant: 'outline' });
export const Inverse = createCardStory({ variant: 'inverse' }, { bgColor: 'dark (container-dark)' });
export const OutlineInverse = createCardStory({ variant: 'outline-inverse' }, { bgColor: 'dark (container-dark)' });
export const CardWithImage = createCardStory({
...defaultArgs,
slot: `<div style="font-size: calc(var(--dt-font-body-l-size) * 1px); font-family: var(--dt-font-interactive-l-family);">
<h2 style="margin-top: 0"> Card title </h2>
<p> Card content </p>
<p style="margin-bottom: 0"> Lorem ipsum dolor sit amet
consectetur adipisicing elit.
Fugiat dolore dolorem maxime,
quod, in minima esse fugit
distinctio, officia et soluta
dicta consequuntur commodi officiis
tempora asperiores aspernatur atque quas.</p>
<img src="https://picsum.photos/200/300?image=0" alt="Sample image" />
</div>`
});
60 changes: 57 additions & 3 deletions packages/components/pie-card/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {
html, LitElement, unsafeCSS, nothing, TemplateResult,
html, LitElement, unsafeCSS, nothing, TemplateResult, type PropertyValues,
kevinrodrigues marked this conversation as resolved.
Show resolved Hide resolved
} from 'lit';
import { classMap, type ClassInfo } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';
import { property } from 'lit/decorators.js';
import { property, queryAssignedElements } from 'lit/decorators.js';
import { validPropertyValues, defineCustomElement } from '@justeattakeaway/pie-webc-core';
import styles from './card.scss?inline';
import {
Expand Down Expand Up @@ -53,6 +53,9 @@ export class PieCard extends LitElement implements CardProps {
@validPropertyValues(componentSelector, paddingValues, undefined)
public padding?: CardProps['padding'];

@queryAssignedElements({ flatten: true })
private assignedElements?: HTMLElement[];

/**
* Renders the card as an anchor element.
*
Expand Down Expand Up @@ -117,6 +120,57 @@ export class PieCard extends LitElement implements CardProps {
return `padding: ${paddingCSS}`;
}

/**
* Handles the slot change event and applies/removes opacity to images based on the `disabled` state.
*
* @private
*/
private handleSlotChange () {
this.updateImagesOpacity();
}

/**
* Updates opacity of all images (slotted and non-slotted) based on the `disabled` property.
*
* @private
*/
private updateImagesOpacity (): void {
if (this.assignedElements) {
// Handle images nested inside slotted elements
this.assignedElements.forEach((element) => {
const images = element.querySelectorAll('img');
this.applyOpacityToImages(images);
});
}

// Handle directly slotted images
const directImages = this.querySelectorAll('img');
this.applyOpacityToImages(directImages);
}

/**
* Applies or removes opacity from the given images based on the `disabled` property.
*
* @param images
* @private
*/
private applyOpacityToImages (images: NodeListOf<HTMLImageElement>): void {
images.forEach((img) => {
img.style.opacity = this.disabled ? '0.5' : '';
});
}

/**
* Observes changes in the `disabled` property and triggers the update of images' opacity.
*
* @param changedProperties
*/
updated (changedProperties: PropertyValues<this>) : void {
if (changedProperties.has('disabled')) {
this.updateImagesOpacity(); // Re-apply styles when disabled state changes
}
}

render () {
const {
variant,
Expand Down Expand Up @@ -146,7 +200,7 @@ export class PieCard extends LitElement implements CardProps {
aria-label=${aria?.label || nothing}
aria-disabled=${disabled ? 'true' : 'false'}
style=${paddingCSS || ''}>
<slot></slot>
<slot @slotchange=${this.handleSlotChange}></slot>
</div>
</div>`;
}
Expand Down
52 changes: 52 additions & 0 deletions packages/components/pie-card/test/component/pie-card.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,56 @@ test.describe('PieCard - Component tests', () => {
});
});
});

test.describe('Prop: disabled', () => {
test.describe('when an image exists and the prop `disabled` is set to `true`', () => {
kevinrodrigues marked this conversation as resolved.
Show resolved Hide resolved
test('should set the opacity to 50%', async ({ mount, page }) => {
kevinrodrigues marked this conversation as resolved.
Show resolved Hide resolved
// Arrange
const slottedImageContent = `<div data-test-id="slot-content">
<img alt="Sample Image" src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBA==" />
</div>`;

await mount(PieCard, {
props: {
disabled: true,
} as CardProps,
slots: {
default: slottedImageContent,
},
});

// Act
const component = page.locator('[data-test-id="slot-content"]');
const image = component.locator('img');

// Assert the image has the correct opacity
await expect(image).toHaveCSS('opacity', '0.5');
});
});

test.describe('when an image exists and the prop `disabled` is set to `false`', () => {
test('should not set the opacity style on the image element', async ({ mount, page }) => {
// Arrange
const slottedImageContent = `<div data-test-id="slot-content">
<img alt="Sample Image" src="data:image/gif;base64,R0lGODlhAQABAIAAAAUEBA==" />
</div>`;

await mount(PieCard, {
props: {
disabled: false,
} as CardProps,
slots: {
default: slottedImageContent,
},
});

// Act
const component = page.locator('[data-test-id="slot-content"]');
const image = component.locator('img');

// Assert the image has the correct opacity
await expect(image).not.toHaveCSS('opacity', '0.5');
});
});
});
});
56 changes: 55 additions & 1 deletion packages/components/pie-card/test/visual/pie-card.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test } from '@sand4rt/experimental-ct-web';
import { test, expect } from '@sand4rt/experimental-ct-web';
import percySnapshot from '@percy/playwright';
import type {
PropObject, WebComponentPropValues, WebComponentTestInput,
Expand Down Expand Up @@ -95,3 +95,57 @@ test.describe('PieCard - `padding` prop', async () => {
await percySnapshot(page, `PIE Card - Padding values | batch number: ${index}`, percyWidths);
}));
});

test.describe('PieCard - Disabled Prop Visual Tests', () => {
const slotContent = `
<div style="padding: 16px;">
<h2> Card title </h2>
<img src="https://picsum.photos/200/300?image=0" alt="Sample image" />
<p> Card content </p>
</div>`;

const renderTestPieCard = (disabled: boolean) => `
<pie-card ${disabled ? 'disabled' : ''}>
${slotContent}
</pie-card>`;

test('should set image opacity to 50% when disabled is true', async ({ page, mount }) => {
const testComponent = renderTestPieCard(true);

await mount(
WebComponentTestWrapper,
{
slots: {
component: testComponent.trim(),
},
},
);

const image = page.locator('img');
const opacity = await image.evaluate((img) => getComputedStyle(img).opacity);

expect(opacity).toBe('0.5');

await percySnapshot(page, 'PIE Card - Disabled State & image set to opacity of 50%');
});

test('should not set image opacity style when disabled is false', async ({ page, mount }) => {
const testComponent = renderTestPieCard(false);

await mount(
WebComponentTestWrapper,
{
slots: {
component: testComponent.trim(),
},
},
);

const image = page.locator('img');
const opacity = await image.evaluate((img) => getComputedStyle(img).opacity);

expect(opacity).not.toBe('0.5');

await percySnapshot(page, 'PIE Card - Enabled State & image opacity not set');
});
});
Loading