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 65d30c07..b8564237 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 ea719be2..b4a518ff 100644 --- a/src/components/select/test/select.test.js +++ b/src/components/select/test/select.test.js @@ -405,6 +405,21 @@ describe("LeuSelect", () => { 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 + it("closes the popup when the document is clicked outside the component", async () => { const el = await defaultFixture({ options: MUNICIPALITIES,