Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dropdown): add support for disabling dropdown items #1018

Merged
merged 3 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions src/components/dropdown/bl-dropdown.stories.mdx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { ArgsTable, Canvas, Meta, Story } from '@storybook/addon-docs';
import { userEvent } from '@storybook/testing-library';
import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import { styleMap } from 'lit/directives/style-map.js';
import { Meta, Canvas, ArgsTable, Story } from '@storybook/addon-docs';
import { userEvent } from '@storybook/testing-library';

<Meta
title="Components/Dropdown Button/Dropdown"
Expand Down Expand Up @@ -35,8 +35,8 @@ import { userEvent } from '@storybook/testing-library';

export const dropdownOpener = async ({ canvasElement }) => {
const dropdown = canvasElement?.querySelector('bl-dropdown')
if(dropdown.shadowRoot) {
const button = dropdown.shadowRoot.querySelector('bl-button')
if (dropdown.shadowRoot) {
const button = dropdown.shadowRoot.querySelector("bl-button")?.shadowRoot?.querySelector("button");
await userEvent.click(button);
}
}
Expand Down Expand Up @@ -78,6 +78,22 @@ export const IconDropdownTemplate = (args) => html`<bl-dropdown
<bl-dropdown-item>Action 5</bl-dropdown-item>
</bl-dropdown>`

export const DisabledItemDropdownTemplate = (args) => html`<bl-dropdown
variant=${ifDefined(args.variant)}
kind=${ifDefined(args.kind)}
size=${ifDefined(args.size)}
label="${ifDefined(args.label)}"
?disabled=${args.disabled}
style=${ifDefined(args.styles ? styleMap(args.styles) : undefined)}
><bl-dropdown-group caption="Caption">
<bl-dropdown-item disabled>${args.content || 'Action 1'}</bl-dropdown-item>
<bl-dropdown-item>Action 2</bl-dropdown-item>
</bl-dropdown-group>
<bl-dropdown-item>Action 3</bl-dropdown-item>
<bl-dropdown-item icon="info" disabled>Action 4</bl-dropdown-item>
<bl-dropdown-item>Action 5</bl-dropdown-item>
</bl-dropdown>`

export const Template = (args) => html`
${SingleDropdownButtonTemplate({...args})}
${SingleDropdownButtonTemplate({variant: 'secondary', ...args})}
Expand Down Expand Up @@ -148,7 +164,7 @@ If dropdown button has an action with a long text that can not fit in a single l
You can add icons to your dropdown buttons using the `icon` attribute. The icon will be displayed on the left side of the button label.

<Canvas>
<Story name="With Icons" args={{ label: 'Dropdown Button' }}>
<Story name="With Icons" args={{ label: 'Dropdown Button' }} play={dropdownOpener}>
{html`
${IconDropdownTemplate({label: 'Settings', icon: 'settings', variant: 'primary'})}
${IconDropdownTemplate({label: 'Settings', icon: 'settings', variant: 'secondary'})}
Expand All @@ -162,19 +178,29 @@ You can add icons to your dropdown buttons using the `icon` attribute. The icon
We have 2 types of disabled dropdown buttons: Disable version of Primary and Secondary buttons is the same.

<Canvas columns={1}>
<Story name="Disabling Dropdown Buttons" args={{ label: 'Dropdown Button', disabled: true }} play={dropdownOpener}>
<Story name="Disabling Dropdown Buttons" args={{ label: 'Dropdown Button', disabled: true }}>
{SizesTemplate.bind({})}
</Story>
</Canvas>

Whereas Tertiary buttons keep their transparent backgrounds.

<Canvas columns={1}>
<Story name="Disabling Tertiary Dropdown Buttons" args={{ label: 'Dropdown Button', disabled: true, variant:"tertiary" }} play={dropdownOpener}>
<Story name="Disabling Tertiary Dropdown Buttons" args={{ label: 'Dropdown Button', disabled: true, variant:"tertiary" }}>
{SizesTemplate.bind({})}
</Story>
</Canvas>

## Disabling Dropdown Items

You can disable dropdown items by setting the `disabled` attribute on the `bl-dropdown-item` element.

<Canvas>
<Story name="Disabling Dropdown Items" args={{ label: 'Dropdown Button' }} play={dropdownOpener}>
{DisabledItemDropdownTemplate.bind({})}
</Story>
</Canvas>


## Reference

Expand Down
42 changes: 37 additions & 5 deletions src/components/dropdown/bl-dropdown.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import BlDropdown from "./bl-dropdown";
import {
assert,
elementUpdated,
expect,
fixture,
html,
oneEvent,
expect,
elementUpdated,
waitUntil,
} from "@open-wc/testing";
import { sendKeys } from "@web/test-runner-commands";
import BlDropdown from "./bl-dropdown";

import type typeOfBlDropdown from "./bl-dropdown";
import BlButton from "../button/bl-button";
import "../popover/bl-popover";
import BlPopover from "../popover/bl-popover";
import type typeOfBlDropdown from "./bl-dropdown";

describe("bl-dropdown", () => {
it("is defined", () => {
Expand Down Expand Up @@ -112,7 +112,12 @@ describe("bl-dropdown", () => {
.querySelector("bl-dropdown-item")
?.shadowRoot?.querySelector("bl-button") as HTMLElement | null;

item?.click();
item?.dispatchEvent(
new CustomEvent("bl-click", {
bubbles: true,
composed: true,
})
);

setTimeout(() => {
expect(el.opened).to.false;
Expand Down Expand Up @@ -167,6 +172,33 @@ describe("bl-dropdown", () => {
expect(popover.visible).to.false;
});

it("should not close dropdown when disabled item is clicked", async () => {
const el = await fixture<typeOfBlDropdown>(html`
<bl-dropdown label="Dropdown Button">
<bl-dropdown-item disabled>dropdown-item</bl-dropdown-item>
</bl-dropdown>
`);

const buttonHost = <BlButton>el.shadowRoot?.querySelector("bl-button");
const button = buttonHost.shadowRoot?.querySelector(".button") as HTMLElement | null;
const popover = <BlPopover>el.shadowRoot?.querySelector("bl-popover");

button?.click();
expect(el.opened).to.true;
expect(popover.visible).to.true;

const item = el
.querySelector("bl-dropdown-item")
?.shadowRoot?.querySelector("bl-button") as HTMLElement | null;

item?.click();

setTimeout(() => {
expect(el.opened).to.true;
expect(popover.visible).to.true;
});
});

describe("keyboard navigation", () => {
it("should focus next action with down arrow key", async () => {
//when
Expand Down
17 changes: 13 additions & 4 deletions src/components/dropdown/item/bl-dropdown-item.stories.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';
import {
Meta,
Canvas,
ArgsTable,
Canvas,
Meta,
Story,
} from '@storybook/addon-docs';
import { html } from 'lit';
import { ifDefined } from 'lit/directives/if-defined.js';

<Meta
title="Components/Dropdown Button/Dropdown Item"
Expand All @@ -25,6 +25,7 @@ Dropdown Item component is a component should be used inside a bl-dropdown or bl
export const Template = (args) => html`
<bl-dropdown-item
icon='${ifDefined(args.icon)}'
?disabled=${args.disabled}
>Dropdown Item</bl-dropdown-item>
`

Expand All @@ -43,5 +44,13 @@ If you want to have an icon for your actions, you can use the `icon` attribute.
</Story>
</Canvas>

## Disabled
You can disable the item by setting the `disabled` attribute.
<Canvas>
<Story name="Disabled" args={{ disabled: true }}>
{Template.bind({})}
</Story>
</Canvas>


<ArgsTable of="bl-dropdown-item" />
9 changes: 7 additions & 2 deletions src/components/dropdown/item/bl-dropdown-item.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { assert, expect, fixture, html, oneEvent } from "@open-wc/testing";
import BlDropdownItem from "./bl-dropdown-item";
import { assert, fixture, html, oneEvent, expect } from "@open-wc/testing";

import type typeOfBlDropdownItem from "./bl-dropdown-item";

Expand Down Expand Up @@ -55,7 +55,12 @@ describe("bl-dropdown-item", () => {
);
const button = el.shadowRoot?.querySelector("bl-button");

setTimeout(() => button?.click());
setTimeout(() => button?.dispatchEvent(
new CustomEvent("bl-click", {
bubbles: true,
composed: true,
})
));
const event = await oneEvent(el, "bl-dropdown-item-click");

expect(el).to.exist;
Expand Down
11 changes: 9 additions & 2 deletions src/components/dropdown/item/bl-dropdown-item.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { LitElement, html, CSSResultGroup, TemplateResult } from "lit";
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, property, query } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js";
import { event, EventDispatcher } from "../../../utilities/event";
Expand Down Expand Up @@ -32,6 +32,12 @@ export default class BlDropdownItem extends LitElement {
@property({ type: String })
icon?: BaklavaIcon;

/**
* Sets item as disabled
*/
@property({ type: Boolean, reflect: true })
disabled = false;

@event("bl-dropdown-item-click") private onClick: EventDispatcher<string>;

private _handleClick() {
Expand Down Expand Up @@ -78,7 +84,8 @@ export default class BlDropdownItem extends LitElement {
kind="neutral"
icon="${ifDefined(this.icon)}"
role="menuitem"
@click="${this._handleClick}"
?disabled="${this.disabled}"
@bl-click="${this._handleClick}"
><slot></slot>
</bl-button>`;
}
Expand Down
29 changes: 17 additions & 12 deletions src/components/split-button/bl-split-button.test.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import BlSplitButton from "./bl-split-button";
import {
assert,
fixture,
html,
oneEvent,
expect,
elementUpdated,
waitUntil,
} from "@open-wc/testing";
import type typeOfBlSplitButton from "./bl-split-button";
assert,
elementUpdated,
expect,
fixture,
html,
oneEvent,
waitUntil,
} from "@open-wc/testing";
import { sendKeys } from "@web/test-runner-commands";
import BlPopover from "../popover/bl-popover";
import BlButton from "../button/bl-button";
import "../popover/bl-popover";
import BlPopover from "../popover/bl-popover";
import type typeOfBlSplitButton from "./bl-split-button";
import BlSplitButton from "./bl-split-button";

describe("bl-split-button", () => {
it("is defined", () => {
Expand Down Expand Up @@ -143,7 +143,12 @@ describe("bl-split-button", () => {
.querySelector("bl-dropdown-item")
?.shadowRoot?.querySelector("bl-button") as HTMLElement | null;

item?.click();
item?.dispatchEvent(
new CustomEvent("bl-click", {
bubbles: true,
composed: true,
})
);

setTimeout(() => {
expect(el.opened).to.false;
Expand Down
Loading