diff --git a/src/elements/tag/tag-group/__snapshots__/tag-group.spec.snap.js b/src/elements/tag/tag-group/__snapshots__/tag-group.spec.snap.js index dd504aaae8..fbd46f618f 100644 --- a/src/elements/tag/tag-group/__snapshots__/tag-group.spec.snap.js +++ b/src/elements/tag/tag-group/__snapshots__/tag-group.spec.snap.js @@ -2,7 +2,10 @@ export const snapshots = {}; snapshots["sbb-tag-group renders - Dom"] = -` +` ``` +## Style + +The component has a `size` property which can be used to change the size of all the inner `sbb-tag`. +Two values are available, `s` and `m`, which is the default. + +```html + + All + Phones + Computer + + + + All + Phones + Computer + +``` + ## Interactions ### Exclusive selection vs. multiple selection @@ -88,6 +107,7 @@ that communicates the collective meaning of all `sbb-tag`s. | ------------------------ | -------------------------- | ------- | ---------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `listAccessibilityLabel` | `list-accessibility-label` | public | `string \| undefined` | | This will be forwarded as aria-label to the inner list. | | `multiple` | `multiple` | public | `boolean` | `false` | If set multiple to false, the selection is exclusive and the value is a string (or null). If set multiple to true, the selection can have multiple values and therefore value is an array. Changing multiple during run time is not supported. | +| `size` | `size` | public | `SbbTagSize` | `'m'` | Tag group size. | | `tags` | - | public | `SbbTagElement[]` | | The child instances of sbb-tag as an array. | | `value` | `value` | public | `string \| string[] \| null` | `null` | Value of the sbb-tag-group. If set multiple to false, the value is a string (or null). If set multiple to true, the value is an array. | diff --git a/src/elements/tag/tag-group/tag-group.stories.ts b/src/elements/tag/tag-group/tag-group.stories.ts index abcc3246da..ec76dad115 100644 --- a/src/elements/tag/tag-group/tag-group.stories.ts +++ b/src/elements/tag/tag-group/tag-group.stories.ts @@ -61,12 +61,20 @@ const numberOfTagsInGroup: InputType = { }, }; +const size: InputType = { + control: { + type: 'inline-radio', + }, + options: ['s', 'm'], +}; + const defaultArgTypes: ArgTypes = { multiple, value, 'list-accessibility-label': listAccessibilityLabel, 'aria-label': ariaLabel, numberOfTagsInGroup, + size, }; const defaultArgs: Args = { @@ -75,6 +83,7 @@ const defaultArgs: Args = { 'list-accessibility-label': 'Select your desired filter', 'aria-label': undefined, numberOfTagsInGroup: 8, + size: size.options![1], }; const tagTemplate = (label: string, checked = false): TemplateResult => html` @@ -132,6 +141,12 @@ export const tagGroup: StoryObj = { args: { ...defaultArgs }, }; +export const tagGroupSizeS: StoryObj = { + render: TagGroupTemplate, + argTypes: defaultArgTypes, + args: { ...defaultArgs, size: size.options![0] }, +}; + export const ellipsisLabel: StoryObj = { render: TagGroupTemplateEllipsis, argTypes: defaultArgTypes, diff --git a/src/elements/tag/tag-group/tag-group.ts b/src/elements/tag/tag-group/tag-group.ts index 33e35ac12c..145737c7d1 100644 --- a/src/elements/tag/tag-group/tag-group.ts +++ b/src/elements/tag/tag-group/tag-group.ts @@ -10,7 +10,7 @@ import { customElement, property } from 'lit/decorators.js'; import { setOrRemoveAttribute } from '../../core/dom.js'; import { SbbNamedSlotListMixin, type WithListChildren } from '../../core/mixins.js'; -import type { SbbTagElement } from '../tag.js'; +import type { SbbTagElement, SbbTagSize } from '../tag.js'; import style from './tag-group.scss?lit&inline'; @@ -40,6 +40,9 @@ export class SbbTagGroupElement extends SbbNamedSlotListMixin>): void { super.willUpdate(changedProperties); + if (changedProperties.has('size')) { + this.tags.forEach((t) => t.requestUpdate?.('size')); + } + if ( (changedProperties.has('listChildren') || changedProperties.has('multiple')) && !this.multiple diff --git a/src/elements/tag/tag/__snapshots__/tag.spec.snap.js b/src/elements/tag/tag/__snapshots__/tag.spec.snap.js index 3749f3abf3..6ced1f5ab6 100644 --- a/src/elements/tag/tag/__snapshots__/tag.spec.snap.js +++ b/src/elements/tag/tag/__snapshots__/tag.spec.snap.js @@ -1,7 +1,25 @@ /* @web/test-runner snapshot v1 */ export const snapshots = {}; -snapshots["sbb-tag renders unchecked"] = +snapshots["sbb-tag renders unchecked Dom"] = +` + All + +`; +/* end snapshot sbb-tag renders unchecked Dom */ + +snapshots["sbb-tag renders unchecked ShadowDom"] = ` @@ -17,9 +35,27 @@ snapshots["sbb-tag renders unchecked"] = `; -/* end snapshot sbb-tag renders unchecked */ +/* end snapshot sbb-tag renders unchecked ShadowDom */ -snapshots["sbb-tag renders checked"] = +snapshots["sbb-tag renders checked Dom"] = +` + Info + +`; +/* end snapshot sbb-tag renders checked Dom */ + +snapshots["sbb-tag renders checked ShadowDom"] = ` @@ -35,9 +71,29 @@ snapshots["sbb-tag renders checked"] = `; -/* end snapshot sbb-tag renders checked */ +/* end snapshot sbb-tag renders checked ShadowDom */ -snapshots["sbb-tag renders disabled with icon and amount"] = +snapshots["sbb-tag renders disabled with icon and amount Dom"] = +` + Info + +`; +/* end snapshot sbb-tag renders disabled with icon and amount Dom */ + +snapshots["sbb-tag renders disabled with icon and amount ShadowDom"] = ` @@ -61,9 +117,37 @@ snapshots["sbb-tag renders disabled with icon and amount"] = `; -/* end snapshot sbb-tag renders disabled with icon and amount */ +/* end snapshot sbb-tag renders disabled with icon and amount ShadowDom */ + +snapshots["sbb-tag renders slotted icon and amount Dom"] = +` + + Info + + 123 + + +`; +/* end snapshot sbb-tag renders slotted icon and amount Dom */ -snapshots["sbb-tag renders slotted icon and amount"] = +snapshots["sbb-tag renders slotted icon and amount ShadowDom"] = ` @@ -79,7 +163,7 @@ snapshots["sbb-tag renders slotted icon and amount"] = `; -/* end snapshot sbb-tag renders slotted icon and amount */ +/* end snapshot sbb-tag renders slotted icon and amount ShadowDom */ snapshots["sbb-tag A11y tree Chrome"] = `

diff --git a/src/elements/tag/tag/readme.md b/src/elements/tag/tag/readme.md index e43ed1abe8..4fac36ac8b 100644 --- a/src/elements/tag/tag/readme.md +++ b/src/elements/tag/tag/readme.md @@ -32,6 +32,16 @@ The component can be displayed in `checked` or `disabled` state using the self-n All ``` +## Style + +The component has two sizes, named `m` (default) and `s`. The `size` property can also be set on the `sbb-tag-group` where it will be applied to all tags inside the group. + +```html +All + +All +``` + ## Events Consumers can listen to the native `change` and `input` events on the `sbb-tag`. @@ -55,6 +65,7 @@ The state is reflected via `aria-pressed` attribute. | `form` | `form` | public | `string \| undefined` | | The

element to associate the button with. | | `iconName` | `icon-name` | public | `string \| undefined` | | The icon name we want to use, choose from the small icon variants from the ui-icons category from here https://icons.app.sbb.ch. | | `name` | `name` | public | `string` | | The name of the button element. | +| `size` | `size` | public | `SbbTagSize` | `'m'` | Tag size. | | `type` | `type` | public | `SbbButtonType` | `'button'` | The type attribute to use for the button. | | `value` | `value` | public | `string` | | The value of the button element. | diff --git a/src/elements/tag/tag/tag.scss b/src/elements/tag/tag/tag.scss index 5979198216..baa78635c4 100644 --- a/src/elements/tag/tag/tag.scss +++ b/src/elements/tag/tag/tag.scss @@ -11,7 +11,7 @@ --sbb-tag-border-width: var(--sbb-border-width-1x); --sbb-tag-text-color: var(--sbb-color-charcoal); --sbb-tag-amount-color: var(--sbb-color-metal); - --sbb-tag-height: #{sbb.px-to-rem-build(36)}; + --sbb-tag-height: var(--sbb-size-element-xs); --sbb-tag-inset: 0; --sbb-tag-content-shift: translateY(0); --sbb-tag-animation-duration: var(--sbb-disable-animation-time, var(--sbb-animation-duration-2x)); @@ -19,10 +19,6 @@ --sbb-tag-padding-inline: var(--sbb-spacing-fixed-5x); --sbb-tag-gap: var(--sbb-spacing-fixed-2x); - @include sbb.mq($from: medium) { - --sbb-tag-height: #{sbb.px-to-rem-build(40)}; - } - @include sbb.if-forced-colors { --sbb-tag-background-color: Canvas !important; --sbb-tag-text-color: ButtonText; @@ -92,6 +88,11 @@ } } +:host([size='s']) { + --sbb-tag-height: var(--sbb-size-element-xxxs); + --sbb-tag-padding-inline: var(--sbb-spacing-fixed-3x); +} + .sbb-tag { @include sbb.text-xs--bold; diff --git a/src/elements/tag/tag/tag.spec.ts b/src/elements/tag/tag/tag.spec.ts index 1f9b37226a..97744ce941 100644 --- a/src/elements/tag/tag/tag.spec.ts +++ b/src/elements/tag/tag/tag.spec.ts @@ -3,74 +3,65 @@ import { html } from 'lit/static-html.js'; import { fixture, testA11yTreeSnapshot } from '../../core/testing/private.js'; +import type { SbbTagElement } from './tag.js'; import './tag.js'; import '../../icon.js'; describe(`sbb-tag`, () => { - it('renders unchecked', async () => { - const root = await fixture( - html`All`, - ); - - expect(root).dom.to.be.equal( - ` - - All - - `, - ); - await expect(root).shadowDom.to.be.equalSnapshot(); - }); + let root: SbbTagElement; + + describe('renders unchecked', async () => { + beforeEach(async () => { + root = await fixture( + html`All`, + ); + }); - it('renders checked', async () => { - const root = await fixture(html`Info`); - - expect(root).dom.to.be.equal( - ` - - Info - - `, - ); - await expect(root).shadowDom.to.be.equalSnapshot(); + it('Dom', async () => { + await expect(root).dom.to.be.equalSnapshot(); + }); + + it('ShadowDom', async () => { + await expect(root).shadowDom.to.be.equalSnapshot(); + }); }); - it('renders disabled with icon and amount', async () => { - const root = await fixture(html` - - Info - - `); + describe('renders checked', async () => { + beforeEach(async () => { + root = await fixture(html`Info`); + }); + + it('Dom', async () => { + await expect(root).dom.to.be.equalSnapshot(); + }); - expect(root).dom.to.be.equal( - ` - + it('ShadowDom', async () => { + await expect(root).shadowDom.to.be.equalSnapshot(); + }); + }); + + describe('renders disabled with icon and amount', async () => { + beforeEach(async () => { + root = await fixture(html` + Info - `, - ); - await expect(root).shadowDom.to.be.equalSnapshot(); + `); + }); + + it('Dom', async () => { + await expect(root).dom.to.be.equalSnapshot(); + }); + + it('ShadowDom', async () => { + await expect(root).shadowDom.to.be.equalSnapshot(); + }); }); - it('renders slotted icon and amount', async () => { - const root = await fixture(html` - - - Info - 123 - - `); - - expect(root).dom.to.be.equal( - ` - + describe('renders slotted icon and amount', async () => { + beforeEach(async () => { + root = await fixture(html` + - `, - ); - await expect(root).shadowDom.to.be.equalSnapshot(); + `); + }); + + it('Dom', async () => { + await expect(root).dom.to.be.equalSnapshot(); + }); + + it('ShadowDom', async () => { + await expect(root).shadowDom.to.be.equalSnapshot(); + }); }); testA11yTreeSnapshot(html`Label`); diff --git a/src/elements/tag/tag/tag.stories.ts b/src/elements/tag/tag/tag.stories.ts index a6527236b8..7cbae8a6c8 100644 --- a/src/elements/tag/tag/tag.stories.ts +++ b/src/elements/tag/tag/tag.stories.ts @@ -51,6 +51,13 @@ const ariaLabel: InputType = { }, }; +const size: InputType = { + control: { + type: 'inline-radio', + }, + options: ['s', 'm'], +}; + const defaultArgTypes: ArgTypes = { checked, disabled, @@ -59,6 +66,7 @@ const defaultArgTypes: ArgTypes = { 'icon-name': icon, amount, 'aria-label': ariaLabel, + size, }; const defaultArgs: Args = { @@ -69,6 +77,7 @@ const defaultArgs: Args = { 'icon-name': undefined, amount: undefined, 'aria-label': undefined, + size: size.options![1], }; const defaultArgsIconAndAmount: Args = { @@ -112,6 +121,12 @@ export const checkedAndDisabledTag: StoryObj = { args: { ...defaultArgs, checked: true, disabled: true }, }; +export const basicTagSizeS: StoryObj = { + render: Template, + argTypes: defaultArgTypes, + args: { ...defaultArgs, size: size.options![0] }, +}; + export const withAmount: StoryObj = { render: Template, argTypes: defaultArgTypes, diff --git a/src/elements/tag/tag/tag.ts b/src/elements/tag/tag/tag.ts index 009885bb7b..108efa77c2 100644 --- a/src/elements/tag/tag/tag.ts +++ b/src/elements/tag/tag/tag.ts @@ -7,9 +7,12 @@ import { SbbConnectedAbortController, SbbSlotStateController } from '../../core/ import { EventEmitter } from '../../core/eventing.js'; import { SbbDisabledTabIndexActionMixin } from '../../core/mixins.js'; import { SbbIconNameMixin } from '../../icon.js'; +import type { SbbTagGroupElement } from '../tag-group.js'; import style from './tag.scss?lit&inline'; +export type SbbTagSize = 's' | 'm'; + /** * It displays a selectable element which can be used as a filter. * @@ -37,6 +40,19 @@ export class SbbTagElement extends SbbIconNameMixin( /** Whether the tag is checked. */ @property({ reflect: true, type: Boolean }) public checked = false; + /** Tag size. */ + @property({ reflect: true }) + public set size(value: SbbTagSize) { + this._size = value; + } + public get size(): SbbTagSize { + return this._group?.size ?? this._size; + } + private _size: SbbTagSize = 'm'; + + /** Reference to the connected tag group. */ + private _group: SbbTagGroupElement | null = null; + /** Input event emitter */ private _input: EventEmitter = new EventEmitter(this, SbbTagElement.events.input, { bubbles: true, @@ -62,6 +78,7 @@ export class SbbTagElement extends SbbIconNameMixin( public override connectedCallback(): void { super.connectedCallback(); + this._group = this.closest('sbb-tag-group') as SbbTagGroupElement; this.addEventListener('click', () => this._handleClick(), { signal: this._abort.signal }); }