From d849888d67b2d8259c8aa2991a1f11d83c5b3db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20B=C3=BCschlen?= Date: Wed, 17 Jul 2024 09:57:53 +0200 Subject: [PATCH] feat(menu-item): add multipleSelection property to display a check icon --- src/components/menu/MenuItem.js | 23 ++++++++++++++-- .../menu/stories/menu-item.stories.js | 26 +++++++++++++------ src/components/menu/test/menu-item.test.js | 23 ++++++++++++++++ src/components/select/Select.js | 8 +++++- src/components/select/test/select.test.js | 16 ++++++++++++ 5 files changed, 85 insertions(+), 11 deletions(-) diff --git a/src/components/menu/MenuItem.js b/src/components/menu/MenuItem.js index 412b957f..8856bafe 100644 --- a/src/components/menu/MenuItem.js +++ b/src/components/menu/MenuItem.js @@ -1,4 +1,4 @@ -import { html } from "lit" +import { html, nothing } from "lit" import { ifDefined } from "lit/directives/if-defined.js" import { LeuElement } from "../../lib/LeuElement.js" @@ -15,6 +15,7 @@ import styles from "./menu-item.css" * @tagname leu-menu-item * @slot - The label of the menu item * @property {boolean} active - Defines if the item is selected or checked + * @property {boolean} multipleSelection - If the item is part of a multiple selection. Renders a checkmark before the label when active * @property {boolean} disabled - Disables the underlying button or link * @property {string} value - The value of the item. It must not contain commas. See `getValue()` * @property {string} href - The href of the underlying link @@ -38,6 +39,11 @@ export class LeuMenuItem extends LeuElement { static properties = { active: { type: Boolean, reflect: true }, + multipleSelection: { + type: Boolean, + reflect: true, + attr: "multiple-selection", + }, disabled: { type: Boolean, reflect: true }, tabbable: { type: Boolean, reflect: true }, href: { type: String, reflect: true }, @@ -50,6 +56,7 @@ export class LeuMenuItem extends LeuElement { this.active = false this.disabled = false + this.multipleSelection = false this.value = undefined this.href = undefined this.tabbable = undefined @@ -142,9 +149,21 @@ export class LeuMenuItem extends LeuElement { ` } + _renderBeforeSlotDefault() { + if (!this.multipleSelection) { + return nothing + } + + return this.active + ? html`` + : html`` + } + render() { const content = html` - + ${this._renderBeforeSlotDefault()} ` diff --git a/src/components/menu/stories/menu-item.stories.js b/src/components/menu/stories/menu-item.stories.js index cde44096..dfae2602 100644 --- a/src/components/menu/stories/menu-item.stories.js +++ b/src/components/menu/stories/menu-item.stories.js @@ -1,4 +1,4 @@ -import { html } from "lit" +import { html, nothing } from "lit" import { ifDefined } from "lit/directives/if-defined.js" import "../leu-menu-item.js" @@ -29,14 +29,19 @@ function Template(args) { href=${ifDefined(args.href)} ?active=${args.active} ?disabled=${args.disabled} + ?multipleSelection=${args.multipleSelection} > - ${isIcon(args.before) - ? html`` - : html`${args.before}`} + ${args.before + ? isIcon(args.before) + ? html`` + : html`${args.before}` + : nothing} ${args.label} - ${isIcon(args.after) - ? html`` - : html`${args.after}`} + ${args.after + ? isIcon(args.after) + ? html`` + : html`${args.after}` + : null} ` } @@ -50,7 +55,7 @@ Active.args = { export const IconBefore = Template.bind({}) IconBefore.args = { - before: "check", + before: "download", } export const IconAfterLink = Template.bind({}) @@ -69,3 +74,8 @@ export const IconPlaceholder = Template.bind({}) IconPlaceholder.args = { before: "EMPTY", } + +export const MultipleSelection = Template.bind({}) +MultipleSelection.args = { + multipleSelection: true, +} diff --git a/src/components/menu/test/menu-item.test.js b/src/components/menu/test/menu-item.test.js index 0b3f5334..b1c3b748 100644 --- a/src/components/menu/test/menu-item.test.js +++ b/src/components/menu/test/menu-item.test.js @@ -15,6 +15,7 @@ async function defaultFixture(args = {}) { ?active=${args.active} ?disabled=${args.disabled} ?tabbable=${args.tabbable} + ?multipleSelection=${args.multipleSelection} > ${args.label} @@ -183,4 +184,26 @@ describe("LeuMenuItem", () => { expect(el.getValue()).to.equal("download-01") }) + + it("renders a palceholder icon when the menu item is part of multiple selection but not active", async () => { + const el = await defaultFixture({ + label: "Download", + multipleSelection: true, + }) + + const icon = el.shadowRoot.querySelector("leu-icon") + expect(icon).to.exist + expect(icon).to.have.attribute("name", "EMPTY") + }) + + it("renders a check icon when the menu item is part of multiple selection and is active", async () => { + const el = await defaultFixture({ + label: "Download", + multipleSelection: true, + }) + + const icon = el.shadowRoot.querySelector("leu-icon") + expect(icon).to.exist + expect(icon).to.have.attribute("name", "check") + }) }) diff --git a/src/components/select/Select.js b/src/components/select/Select.js index 76ee91f8..04250147 100644 --- a/src/components/select/Select.js +++ b/src/components/select/Select.js @@ -146,11 +146,13 @@ export class LeuSelect extends LeuElement { if ( changedProperties.has("value") || - changedProperties.has("_optionFilter") + changedProperties.has("_optionFilter") || + changedProperties.has("multiple") ) { this._updateMenuItems({ value: changedProperties.has("value"), optionFilter: changedProperties.has("_optionFilter"), + multiple: changedProperties.has("multiple"), }) } } @@ -171,6 +173,10 @@ export class LeuSelect extends LeuElement { /* eslint-disable no-param-reassign */ menuItems.forEach((menuItem) => { + if (changed.multiple) { + menuItem.multipleSelection = this.multiple + } + if (changed.optionFilter) { menuItem.hidden = this._optionFilter !== "" && diff --git a/src/components/select/test/select.test.js b/src/components/select/test/select.test.js index 596c025e..55698432 100644 --- a/src/components/select/test/select.test.js +++ b/src/components/select/test/select.test.js @@ -404,4 +404,20 @@ describe("LeuSelect", () => { const popup = el.shadowRoot.querySelector("leu-popup") expect(popup.active).to.not.be.true }) + + it("sets the multipleSelection property on the menu items when multiple selection is allowed", async () => { + const el = await defaultFixture({ + options: MUNICIPALITIES, + label: "Gemeinde", + multiple: true, + }) + + const menuItems = Array.from(el.querySelectorAll("leu-menu-item")) + expect(menuItems.every((item) => item.multipleSelection)).to.be.true + + el.multiple = false + await elementUpdated(el) + + expect(menuItems.every((item) => !item.multipleSelection)).to.be.true + }) })