Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
51590c4
feat(select): add icon property
ozkersemih Jan 27, 2025
6c6d1a6
fix(bl-select): clone nodes
Enes5519 Jan 27, 2025
52afa4f
style(bl-select): add styles
Enes5519 Jan 27, 2025
6cefaad
feat(select): add stories
ozkersemih Jan 28, 2025
c50365e
docs(select): update storybook
ozkersemih Jan 28, 2025
a89d633
style(select): fix prettier
Enes5519 Jan 28, 2025
2d28d61
test(select): add test cases for icon
ozkersemih Jan 28, 2025
cd8b8e1
Merge branch 'select-icon' of github.com:Trendyol/baklava into select…
ozkersemih Jan 28, 2025
b884bbd
style(select): prettier
ozkersemih Jan 28, 2025
9b6a67b
test(select): add missed tests
Enes5519 Jan 28, 2025
46ffb9e
docs(select): update storybook
ozkersemih Jan 30, 2025
ba9c0ea
docs(select): update storybook
ozkersemih Jan 30, 2025
745c521
feat(select): add icon property
ozkersemih Jan 27, 2025
b96f7f7
fix(bl-select): clone nodes
Enes5519 Jan 27, 2025
f7d0651
style(bl-select): add styles
Enes5519 Jan 27, 2025
1957bb0
feat(select): add stories
ozkersemih Jan 28, 2025
401ba09
docs(select): update storybook
ozkersemih Jan 28, 2025
f299db0
test(select): add test cases for icon
ozkersemih Jan 28, 2025
74a03c8
style(select): fix prettier
Enes5519 Jan 28, 2025
e3494ee
style(select): prettier
ozkersemih Jan 28, 2025
e5927de
test(select): add missed tests
Enes5519 Jan 28, 2025
fde646b
docs(select): update storybook
ozkersemih Jan 30, 2025
fe4edb9
docs(select): update storybook
ozkersemih Jan 30, 2025
73dec7a
style: fix multiple select
Enes5519 Feb 10, 2025
0a1be62
Merge branch 'next' of github.com:Trendyol/baklava into select-icon
ozkersemih Mar 6, 2025
a0d26fa
doc(select): update label
ozkersemih Mar 6, 2025
86e950e
chore(select): fix conflict
ozkersemih Mar 6, 2025
e9e588a
Merge branch 'next' into select-icon
buseselvi Mar 13, 2025
4cc4f43
Merge branch 'next' into select-icon
buseselvi Mar 20, 2025
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
10 changes: 9 additions & 1 deletion src/components/select/bl-select.css
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,19 @@
}

.selected-options li {
display: inline;
display: flex;
align-items: center;
gap: var(--bl-size-2xs);
font-size: var(--font-size);
color: var(--text-color);
}

:host(:not([multiple])) .selected-options li {
display: inline-flex;
align-items: center;
gap: var(--bl-size-2xs);
}

.selected-options li:not(:last-child)::after {
content: ", ";
}
Expand Down
58 changes: 57 additions & 1 deletion src/components/select/bl-select.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,20 @@ export const defaultOptionsWithDisabled = [{
value: 'uk'
}]

export const optionsWithIcon = [{
label: 'Credit Card',
value: 'credit_card',
icon: 'info'
},{
label: 'Cash',
value: 'cash',
icon: 'money'
},{
label: 'Coin',
value: 'coin',
icon: 'coin'
}]

export const formSubmitter = async ({ canvasElement }) => {
const form = canvasElement?.querySelector('form');
form.addEventListener('submit', (e) => e.preventDefault());
Expand Down Expand Up @@ -118,6 +132,32 @@ export const SpecialUseCaseTemplate = () => html`<bl-select style="width: 50%" p
<bl-select-option value="3">Option 3</bl-select-option>
</bl-select>`;

export const IconWithSlotTemplate = () => html`<bl-select placeholder="bl-select" label="Choose Country">
<bl-select-option value="tr" label="Türkiye">
<div slot="icon" >
<img style="width: 1em;" src="https://www.worldometers.info//img/flags/small/tn_tu-flag.gif">
</div>
Turkiye
</bl-select-option>
<bl-select-option value="nl" label="Netherlands">
<div slot="icon">
<img style="width: 1em;" src="https://www.worldometers.info//img/flags/small/tn_nl-flag.gif">
</div>
Netherlands
</bl-select-option>
<bl-select-option value="gr" label="Germany">
<div slot="icon">
<img style="width: 1em;" src="https://www.worldometers.info//img/flags/small/tn_gm-flag.gif">
</div>
Germany
</bl-select-option>
<bl-select-option value="uk" label="United Kingdom">
<div slot="icon">
<img style="width: 1em;" src="https://www.worldometers.info//img/flags/small/tn_uk-flag.gif">
</div>
United Kingdom
</bl-select-option></bl-select>`;

export const SelectTemplate = (args) => html`<bl-select
label=${ifDefined(args.label)}
class=${ifDefined(args.class)}
Expand All @@ -139,7 +179,7 @@ export const SelectTemplate = (args) => html`<bl-select
.value=${ifDefined(args.value)}>${
(args.options || defaultOptions).map((option) => html`
<bl-select-option value=${ifDefined(option.value)} ?selected=${
( args.selected || []).includes(option.value) } ?disabled=${option.disabled}>${option.label}</bl-select-option>`
( args.selected || []).includes(option.value) } ?disabled=${option.disabled} icon=${ifDefined(option.icon)}>${option.label}</bl-select-option>`
)}
</bl-select>`

Expand Down Expand Up @@ -333,6 +373,22 @@ This is useful when you want to show a custom label for an option inside a `bl-s
</Story>
</Canvas>

## Icon
If `bl-select-option` has icon by `icon` attribute, `bl-select` would render the icon of selected option.

<Canvas>
<Story name="Select With Icons" args={{ options: optionsWithIcon, placeholder: "Choose Payment Method" }} play={selectOpener}>
{SelectTemplate.bind({})}
</Story>
</Canvas>

You can use slot icon in `bl-select-option` instead of `icon` attribute to display icon on `bl-select`.
<Canvas>
<Story name="Select With Icon Slots" args={{ options: optionsWithIcon, placeholder: "Choose Payment Method" }} play={selectOpener}>
{IconWithSlotTemplate.bind({})}
</Story>
</Canvas>

## `bl-select` Event

Select component fires `bl-select` event once selection changes. This event has a payload in the type of
Expand Down
10 changes: 10 additions & 0 deletions src/components/select/bl-select.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,16 @@ describe("bl-select", () => {
expect(selectedOptions?.textContent).contains("custom-label-1");
expect(selectedOptions?.textContent).contains("Option 3");
});
it("should render icon correctly on bl-select if an option selected that has icon", async () => {
const el = await fixture<BlSelect>(html`<bl-select>
<bl-select-option selected icon="info" value="1">Option 1</bl-select-option>
<bl-select-option value="2">Option 2</bl-select-option>
</bl-select>`);

const selectedOptions = el.shadowRoot?.querySelector<HTMLUListElement>(".selected-options");

expect(selectedOptions?.children[0].children[0].outerHTML).to.equal("<bl-icon name=\"info\"></bl-icon>");
});
it("should open select menu", async () => {
const el = await fixture<BlSelect>(html`<bl-select><bl-select-option value="1">Option 1</bl-select-option></bl-select>`);

Expand Down
10 changes: 7 additions & 3 deletions src/components/select/bl-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,9 +371,13 @@ export default class BlSelect<ValueType extends FormValue = string> extends Form

private inputTemplate() {
const inputSelectedOptions = html`<ul class="selected-options">
${this._selectedOptions.map(
item => html`<li>${item.getAttribute("label") || item.textContent}</li>`
)}
${this._selectedOptions.map(item => {
const icon = item.icon
? html`<bl-icon name=${item.icon}></bl-icon>`
: Array.from(item.querySelectorAll("[slot=icon]")).map(el => el.cloneNode(true));

return html`<li>${icon}${item.getAttribute("label") || item.textContent}</li>`;
})}
</ul>`;

const isAllSelectedDisabled =
Expand Down
8 changes: 7 additions & 1 deletion src/components/select/option/bl-select-option.css
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@

.single-option {
width: 100%;
display: block;
display: flex;
align-items: center;
cursor: pointer;
color: var(--option-color);
margin: var(--option-spacing);
Expand All @@ -43,6 +44,11 @@
outline: none;
}

:host ::slotted([slot="icon"]),
:host bl-icon {
margin-right: var(--bl-size-2xs);
}

.single-option:focus-visible::after {
content: "";
position: absolute;
Expand Down
12 changes: 12 additions & 0 deletions src/components/select/option/bl-select-option.stories.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import {
selected: {
control: 'boolean',
},
icon: {
control: 'text',
type: 'string'
}
}}
/>

Expand All @@ -28,6 +32,7 @@ export const Template = (args) => html`
value="bl"
?disabled='${args.disabled}'
?selected='${args.selected}'
icon="${ifDefined(args.icon)}"
>baklava</bl-select-option>
`

Expand All @@ -54,5 +59,12 @@ If you want to disable an option, you can use the `disabled` attribute.
</Story>
</Canvas>

## Icon
You can use baklava icons to render icon for `bl-select` and the icon of selected option seems in `bl-select`
<Canvas>
<Story name="Icon" args={{ icon: 'info' }}>
{Template.bind({})}
</Story>
</Canvas>

<ArgsTable of="bl-select-option" />
30 changes: 29 additions & 1 deletion src/components/select/option/bl-select-option.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assert, elementUpdated, fixture, expect, html, oneEvent } from "@open-wc/testing";
import { assert, elementUpdated, expect, fixture, html, oneEvent } from "@open-wc/testing";
import { sendKeys } from "@web/test-runner-commands";
import BlSelectOption from "./bl-select-option";

Expand All @@ -15,6 +15,26 @@ describe("bl-select", () => {
expect(el).shadowDom.equal(
`<div class="option-container">
<div class="focus-target single-option" role="option" aria-selected="false">
<slot name="icon">
</slot>
<slot>
</slot>
</div>
</div>
`
);
});

it("should rendered with icon", async () => {
const el = await fixture<BlSelectOption>(html`<bl-select-option icon="info"></bl-select-option>`);

expect(el).shadowDom.equal(
`<div class="option-container">
<div class="focus-target single-option" role="option" aria-selected="false">
<slot name="icon">
<bl-icon name="info">
</bl-icon>
</slot>
<slot>
</slot>
</div>
Expand All @@ -23,6 +43,14 @@ describe("bl-select", () => {
);
});

it("should rendered with custom icon", async () => {
const el = await fixture<BlSelectOption>(html`<bl-select-option><bl-icon slot="icon" name="info"></bl-icon></bl-select-option>`);

const iconSlot = el.shadowRoot!.querySelector<HTMLSlotElement>("slot[name=icon]")!;

expect(iconSlot.assignedElements()).deep.equal([el.querySelector("bl-icon")]);
});

it("should have aria-selected attribute set to true if the option is selected", async function () {
const el = await fixture<BlSelectOption>(
html`<bl-select-option value="basketball" selected>Basketball</bl-select-option>`
Expand Down
10 changes: 10 additions & 0 deletions src/components/select/option/bl-select-option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CSSResultGroup, html, LitElement } from "lit";
import { customElement, property, query, state } from "lit/decorators.js";
import { FormValue } from "@open-wc/form-helpers";
import { event, EventDispatcher } from "../../../utilities/event";
import { BaklavaIcon } from "../../icon/icon-list";
import BlSelect from "../bl-select";
import style from "./bl-select-option.css";

Expand Down Expand Up @@ -44,6 +45,12 @@ export default class BlSelectOption<ValueType extends FormValue = string> extend
@property({ type: Boolean, reflect: true })
selected = false;

/**
* Sets the name of the icon
*/
@property({ type: String })
icon?: BaklavaIcon;

@state()
multiple = false;

Expand Down Expand Up @@ -86,6 +93,8 @@ export default class BlSelectOption<ValueType extends FormValue = string> extend
private blSelect: BlSelect<ValueType> | null;

private singleOptionTemplate() {
const icon = this.icon ? html`<bl-icon name=${this.icon}></bl-icon>` : null;

return html`<div
class="single-option focus-target"
@blur=${this.blur}
Expand All @@ -94,6 +103,7 @@ export default class BlSelectOption<ValueType extends FormValue = string> extend
role="option"
aria-selected="${this.selected}"
>
<slot name="icon">${icon}</slot>
<slot></slot>
</div>`;
}
Expand Down
Loading