From db1d4c8901bb4c4b6776dd1c33b9fa149561da5c Mon Sep 17 00:00:00 2001 From: Kevin Rodrigues Date: Thu, 26 Sep 2024 15:10:44 +0100 Subject: [PATCH] feat(pie-card): DSW-2426 image opacity when disabled (#1946) * 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 --- .changeset/mean-planets-begin.md | 5 ++ .../pie-storybook/stories/pie-card.stories.ts | 15 +++++ packages/components/pie-card/src/index.ts | 60 ++++++++++++++++++- .../pie-card/test/component/pie-card.spec.ts | 52 ++++++++++++++++ .../pie-card/test/visual/pie-card.spec.ts | 56 ++++++++++++++++- 5 files changed, 184 insertions(+), 4 deletions(-) create mode 100644 .changeset/mean-planets-begin.md diff --git a/.changeset/mean-planets-begin.md b/.changeset/mean-planets-begin.md new file mode 100644 index 0000000000..b9237a8875 --- /dev/null +++ b/.changeset/mean-planets-begin.md @@ -0,0 +1,5 @@ +--- +"@justeattakeaway/pie-card": minor +--- + +[Added] Ability to set images to 50% opacity when disabled diff --git a/apps/pie-storybook/stories/pie-card.stories.ts b/apps/pie-storybook/stories/pie-card.stories.ts index 34d195e0a0..109c7592b1 100644 --- a/apps/pie-storybook/stories/pie-card.stories.ts +++ b/apps/pie-storybook/stories/pie-card.stories.ts @@ -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: `
+

Card title

+

Card content

+

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.

+ Sample image +
` +}); diff --git a/packages/components/pie-card/src/index.ts b/packages/components/pie-card/src/index.ts index 4978028d7b..c03517e94e 100644 --- a/packages/components/pie-card/src/index.ts +++ b/packages/components/pie-card/src/index.ts @@ -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 { @@ -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. * @@ -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): 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) : void { + if (changedProperties.has('disabled')) { + this.updateImagesOpacity(); // Re-apply styles when disabled state changes + } + } + render () { const { variant, @@ -146,7 +200,7 @@ export class PieCard extends LitElement implements CardProps { aria-label=${aria?.label || nothing} aria-disabled=${disabled ? 'true' : 'false'} style=${paddingCSS || ''}> - + `; } 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 f6d29f4210..498e4663a9 100644 --- a/packages/components/pie-card/test/component/pie-card.spec.ts +++ b/packages/components/pie-card/test/component/pie-card.spec.ts @@ -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 = `
+ Sample Image +
`; + + 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 = `
+ Sample Image +
`; + + 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'); + }); + }); + }); }); diff --git a/packages/components/pie-card/test/visual/pie-card.spec.ts b/packages/components/pie-card/test/visual/pie-card.spec.ts index 53237ad0a0..26928a8f3c 100644 --- a/packages/components/pie-card/test/visual/pie-card.spec.ts +++ b/packages/components/pie-card/test/visual/pie-card.spec.ts @@ -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, @@ -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 = ` +
+

Card title

+ Sample image +

Card content

+
`; + + const renderTestPieCard = (disabled: boolean) => ` + + ${slotContent} + `; + + 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'); + }); +});