Skip to content

Commit

Permalink
feat(pie-card): DSW-2426 image opacity when disabled (#1946)
Browse files Browse the repository at this point in the history
* feat(pie-card): DSW-2426 adds opacity change to image if card is disabled

* feat(pie-card): DSW-2426 fix lint error

* feat(pie-card): DSW-2426 pR comment to use propertyValues

* feat(pie-card): DSW-2426 update method changed to handle propertyValues

* feat(pie-card): DSW-2426 fix lint errors

* feat(pie-card): DSW-2426 add visual tests

* feat(pie-card): DSW-2426 fix linting

* feat(pie-card): DSW-2426 add image card sb example

* feat(pie-card): DSW-2426 pr comment

* feat(pie-card): DSW-2426 add further tests and update comments

---------

Co-authored-by: Xander Marjoram <[email protected]>
  • Loading branch information
kevinrodrigues and xander-marjoram authored Sep 26, 2024
1 parent 489ce9d commit db1d4c8
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 4 deletions.
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
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,
} 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`', () => {
test('should set the opacity to 50%', 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: 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');
});
});

0 comments on commit db1d4c8

Please sign in to comment.