Skip to content

Commit

Permalink
feat(pie-tag): DSW-2325 add trailing icon for interactive variants (#…
Browse files Browse the repository at this point in the history
…2025)

* feat(pie-tag): DSW-2325 add trailing icon for interactive variants

* fix(pie-tag): DSW-2325 update description

* fix(pie-tag): DSW-2325 revert some changes

* fix(pie-tag): DSW-2325 update visual tests
  • Loading branch information
raoufswe authored Nov 11, 2024
1 parent 242d45b commit 50ebed5
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 29 deletions.
7 changes: 7 additions & 0 deletions .changeset/plenty-months-hope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@justeattakeaway/pie-tag": minor
"pie-storybook": minor
"pie-docs": minor
---

[Added] - Implement trailing icon for interactive variants
4 changes: 4 additions & 0 deletions apps/pie-docs/src/components/tag/code/code.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ You can customise the disabled appearance by setting the `--tag-opacity` css var
tableData: slots
} %}

## Events

The interactive tag component does not emit any custom events. In order to add event listening to it, you can treat the component like a native HTML element in your application.

### Using `pie-icons-webc` with the `pie-tag` icon slot

We recommend using `pie-icons-webc` when using the `icon` slot. Here is an example of how you would do this:
Expand Down
12 changes: 12 additions & 0 deletions apps/pie-docs/src/components/tag/code/props.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,18 @@
"type": "code",
"item": ["false"]
}
],
[
"iconPlacement",
{
"type": "code",
"item": ["\"leading\"", "\"trailing\""]
},
"Sets the position of the icon relative to the text. Leading comes before the text and trailing comes after, taking writing direction into account. To use this, you must pass an icon into the <a href=\"#slots\">icon slot</a>. Can be only used if `isInteractive` is set to true",
{
"type": "code",
"item": ["\"leading\""]
}
]
]
}
19 changes: 17 additions & 2 deletions apps/pie-storybook/stories/pie-tag.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { type Meta } from '@storybook/web-components';

import '@justeattakeaway/pie-tag';
import {
type TagProps as TagBaseProps, variants, sizes, defaultProps,
type TagProps as TagBaseProps,
variants,
sizes,
defaultProps,
iconPlacements,
} from '@justeattakeaway/pie-tag';
import '@justeattakeaway/pie-icons-webc/dist/IconHeartFilled.js';

Expand Down Expand Up @@ -55,7 +59,7 @@ const tagStoryMeta: TagStoryMeta = {
},
},
showIcon: {
description: 'Enable to see the example of Tag with icon. Available only for large tag size.',
description: '<b>**Not a component prop</b><br><br>Use the `icon` slot to pass a PIE icon component. Available only for large tag size.',
control: 'boolean',
defaultValue: {
summary: defaultArgs.showIcon,
Expand All @@ -66,6 +70,15 @@ const tagStoryMeta: TagStoryMeta = {
description: 'Content to place within the tag',
control: 'text',
},
iconPlacement: {
description: 'The placement of the icon slot such as leading or trailing. <br /><br /> Can be only used if `isInteractive` is set to true',
control: 'select',
options: iconPlacements,
defaultValue: {
summary: defaultArgs.iconPlacement,
},
if: { arg: 'isInteractive', eq: true },
},
},
args: defaultArgs,
parameters: {
Expand All @@ -86,10 +99,12 @@ const Template : TemplateFunction<TagProps> = ({
disabled,
showIcon,
slot,
iconPlacement,
}) => html`
<pie-tag
variant="${ifDefined(variant)}"
size="${ifDefined(size)}"
iconPlacement="${ifDefined(iconPlacement)}"
?isInteractive="${isInteractive}"
?isStrong="${isStrong}"
?disabled="${disabled}">
Expand Down
7 changes: 7 additions & 0 deletions packages/components/pie-tag/src/defs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { type ComponentDefaultProps } from '@justeattakeaway/pie-webc-core';

export const variants = ['neutral-alternative', 'neutral', 'outline', 'ghost', 'information', 'success', 'error', 'brand-02', 'brand-03', 'brand-04', 'brand-05', 'brand-06'] as const;
export const sizes = ['small', 'large'] as const;
export const iconPlacements = ['leading', 'trailing'] as const;

export interface TagProps {
/**
Expand Down Expand Up @@ -29,6 +30,11 @@ export interface TagProps {
* What size the tag should be.
*/
size?: typeof sizes[number];

/**
* The placement of the icon slot such as leading (default) or trailing. Available only if `isInteractive` is set to true.
*/
iconPlacement?: typeof iconPlacements[number];
}

export type DefaultProps = ComponentDefaultProps<TagProps>;
Expand All @@ -39,4 +45,5 @@ export const defaultProps: DefaultProps = {
isInteractive: false,
disabled: false,
size: 'large',
iconPlacement: 'leading',
};
60 changes: 35 additions & 25 deletions packages/components/pie-tag/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { classMap, type ClassInfo } from 'lit/directives/class-map.js';
import { validPropertyValues, defineCustomElement } from '@justeattakeaway/pie-webc-core';
import styles from './tag.scss?inline';
import {
type TagProps, variants, sizes, defaultProps,
variants,
sizes,
defaultProps,
iconPlacements,
type TagProps,
} from './defs';

// Valid values available to consumers
Expand Down Expand Up @@ -37,30 +41,9 @@ export class PieTag extends LitElement implements TagProps {
@property({ type: Boolean })
public isInteractive = defaultProps.isInteractive;

render () {
const {
disabled,
isInteractive,
isStrong,
size,
variant,
} = this;

const classes = {
'c-tag': true,
[`c-tag--${size}`]: true,
[`c-tag--${variant}`]: true,
'c-tag--disabled': disabled,
'c-tag--strong': isStrong,
'c-tag--interactive': isInteractive,
};

if (isInteractive) {
return this.renderButtonTag(classes);
}

return this.renderTag(classes);
}
@property({ type: String })
@validPropertyValues(componentSelector, iconPlacements, defaultProps.iconPlacement)
public iconPlacement = defaultProps.iconPlacement;

private renderIconSlot () {
if (this.size !== 'large') return nothing;
Expand Down Expand Up @@ -90,6 +73,33 @@ export class PieTag extends LitElement implements TagProps {
</button>`;
}

render () {
const {
disabled,
isInteractive,
isStrong,
size,
variant,
iconPlacement,
} = this;

const classes = {
'c-tag': true,
[`c-tag--${size}`]: true,
[`c-tag--${variant}`]: true,
'c-tag--disabled': disabled,
'c-tag--strong': isStrong,
'c-tag--interactive': isInteractive,
[`c-tag--icon-placement--${iconPlacement}`]: isInteractive && iconPlacement,
};

if (isInteractive) {
return this.renderButtonTag(classes);
}

return this.renderTag(classes);
}

// Renders a `CSSResult` generated from SCSS by Vite
static styles = unsafeCSS(styles);
}
Expand Down
9 changes: 9 additions & 0 deletions packages/components/pie-tag/src/tag.scss
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
--icon-size-override: 16px;

display: inline-flex;
flex-direction: row;
vertical-align: middle;
align-items: center;
justify-content: center;
Expand All @@ -80,6 +81,14 @@
}
}

&.c-tag--icon-placement--leading {
// same as default styles
}

&.c-tag--icon-placement--trailing {
flex-direction: row-reverse;
}

// Size
&.c-tag--small {
--tag-padding-block: 0;
Expand Down
10 changes: 8 additions & 2 deletions packages/components/pie-tag/test/visual/pie-tag.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,23 @@ import {
} from '@justeattakeaway/pie-webc-testing/src/helpers/components/web-component-test-wrapper/WebComponentTestWrapper.ts';
import { percyWidths } from '@justeattakeaway/pie-webc-testing/src/percy/breakpoints.ts';
import { IconHeartFilled } from '@justeattakeaway/pie-icons-webc/dist/IconHeartFilled';
import { type TagProps, sizes, variants } from '../../src/defs.ts';
import {
type TagProps, sizes, variants, iconPlacements,
} from '../../src/defs.ts';
import { PieTag } from '../../src/index.ts';

const props: PropObject<TagProps & { iconSlot: string }> = {
variant: variants,
size: sizes,
iconPlacement: iconPlacements,
isInteractive: [true, false],
isStrong: [true, false],
disabled: [true, false],
iconSlot: ['', '<icon-heart-filled slot="icon"></icon-heart-filled>'],
};

// Renders a <pie-tag> HTML string with the given prop values
const renderTestPieTag = (propVals: WebComponentPropValues) => `<pie-tag variant="${propVals.variant}" size="${propVals.size}" ${propVals.isStrong ? 'isStrong' : ''} ${propVals.disabled ? 'disabled' : ''}>${propVals.iconSlot} Hello world</pie-tag>`;
const renderTestPieTag = (propVals: WebComponentPropValues) => `<pie-tag variant="${propVals.variant}" size="${propVals.size}" iconPlacement="${propVals.iconPlacement}" ${propVals.isStrong ? 'isStrong' : ''} ${propVals.disabled ? 'disabled' : ''} ${propVals.isInteractive ? 'isInteractive' : ''}>${propVals.iconSlot} Hello world</pie-tag>`;

const componentPropsMatrix = getAllPropCombinations(props);
const componentPropsMatrixByVariant = splitCombinationsByPropertyValue(componentPropsMatrix, 'variant');
Expand All @@ -49,7 +53,9 @@ componentVariants.forEach((variant) => test(`should render all prop variations f
const propKeyValues = `
size: ${testComponent.propValues.size},
variant: ${testComponent.propValues.variant},
iconPlacement: ${testComponent.propValues.iconPlacement},
isStrong: ${testComponent.propValues.isStrong},
isInteractive: ${testComponent.propValues.isInteractive},
disabled: ${testComponent.propValues.disabled},
iconSlot: ${testComponent.propValues.iconSlot ? 'with icon' : 'no icon'}`;
const darkMode = ['neutral-alternative'].includes(variant);
Expand Down

0 comments on commit 50ebed5

Please sign in to comment.