Skip to content

Commit 57586a7

Browse files
feat(core/select): hide clear button on disabled and readonly (#1574)
Co-authored-by: Lukas Maurer <[email protected]>
1 parent 5ca2446 commit 57586a7

File tree

4 files changed

+119
-25
lines changed

4 files changed

+119
-25
lines changed

.changeset/good-mayflies-accept.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@siemens/ix': patch
3+
---
4+
5+
Hide clear button in ix-select for disabled and readonly states.

packages/core/src/components/select/select.tsx

+36-25
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ export class Select implements IxInputFieldComponent<string | string[]> {
209209
@Event() ixBlur!: EventEmitter<void>;
210210

211211
@State() dropdownShow = false;
212-
@State() selectedLabels: string[] = [];
212+
@State() selectedLabels: (string | undefined)[] = [];
213213
@State() isDropdownEmpty = false;
214214
@State() navigationItem?: DropdownItemWrapper;
215215
@State() inputFilterText = '';
@@ -302,7 +302,7 @@ export class Select implements IxInputFieldComponent<string | string[]> {
302302

303303
@Watch('dropdownShow')
304304
watchDropdownShow(show: boolean) {
305-
if (show) {
305+
if (show && this.dropdownRef) {
306306
this.arrowFocusController = new ArrowFocusController(
307307
this.visibleNonShadowItems,
308308
this.dropdownRef,
@@ -356,7 +356,7 @@ export class Select implements IxInputFieldComponent<string | string[]> {
356356
}
357357

358358
private focusDropdownItem(index: number) {
359-
this.navigationItem = null;
359+
this.navigationItem = undefined;
360360

361361
if (index < this.visibleNonShadowItems.length) {
362362
const nestedDropdownItem =
@@ -402,7 +402,7 @@ export class Select implements IxInputFieldComponent<string | string[]> {
402402
newItem.value = value;
403403
newItem.label = value;
404404

405-
this.customItemsContainerRef.appendChild(newItem);
405+
this.customItemsContainerRef?.appendChild(newItem);
406406

407407
this.clearInput();
408408
this.itemClick(value);
@@ -452,7 +452,7 @@ export class Select implements IxInputFieldComponent<string | string[]> {
452452
this.selectedLabels = this.selectedItems.map((item) => item.label);
453453

454454
if (this.selectedLabels?.length && this.isSingleMode) {
455-
this.inputValue = this.selectedLabels[0];
455+
this.inputValue = this.selectedLabels[0] ?? '';
456456
} else {
457457
this.inputValue = '';
458458
}
@@ -468,7 +468,7 @@ export class Select implements IxInputFieldComponent<string | string[]> {
468468
}
469469

470470
if (!value) {
471-
this.itemSelectionChange.emit(null);
471+
this.itemSelectionChange.emit([]);
472472
} else {
473473
this.itemSelectionChange.emit(Array.isArray(value) ? value : [value]);
474474
}
@@ -505,7 +505,7 @@ export class Select implements IxInputFieldComponent<string | string[]> {
505505
this.cleanupResources();
506506
}
507507

508-
private itemExists(item: string) {
508+
private itemExists(item: string | undefined) {
509509
return this.items.find((i) => i.label === item);
510510
}
511511

@@ -519,7 +519,7 @@ export class Select implements IxInputFieldComponent<string | string[]> {
519519
this.removeHiddenFromItems();
520520
this.isDropdownEmpty = this.isEveryDropdownItemHidden;
521521
} else {
522-
this.navigationItem = null;
522+
this.navigationItem = undefined;
523523
this.updateSelection();
524524
this.inputFilterText = '';
525525
}
@@ -551,15 +551,17 @@ export class Select implements IxInputFieldComponent<string | string[]> {
551551
return;
552552
}
553553

554+
const trimmedInput = this.inputFilterText.trim();
555+
const itemLabel = (el as HTMLIxSelectItemElement)?.label;
556+
554557
if (
555-
!this.itemExists(this.inputFilterText.trim()) &&
556-
!this.itemExists((el as HTMLIxSelectItemElement)?.label)
558+
this.editable &&
559+
!this.itemExists(trimmedInput) &&
560+
!this.itemExists(itemLabel)
557561
) {
558-
if (this.editable) {
559-
const defaultPrevented = this.emitAddItem(this.inputFilterText.trim());
560-
if (defaultPrevented) {
561-
return;
562-
}
562+
const defaultPrevented = this.emitAddItem(trimmedInput);
563+
if (defaultPrevented) {
564+
return;
563565
}
564566
}
565567

@@ -622,16 +624,14 @@ export class Select implements IxInputFieldComponent<string | string[]> {
622624

623625
if (
624626
this.isAddItemVisible() &&
625-
this.addItemRef.contains(
627+
this.addItemRef?.contains(
626628
await this.navigationItem.getDropdownItemElement()
627629
)
628630
) {
629631
if (moveUp) {
630632
this.applyFocusTo(this.visibleItems.pop());
631-
} else {
632-
if (this.visibleItems.length) {
633-
this.applyFocusTo(this.visibleItems.shift());
634-
}
633+
} else if (this.visibleItems.length) {
634+
this.applyFocusTo(this.visibleItems.shift());
635635
}
636636
return;
637637
}
@@ -687,7 +687,7 @@ export class Select implements IxInputFieldComponent<string | string[]> {
687687
}
688688

689689
private filterItemsWithTypeahead() {
690-
this.inputFilterText = this.inputRef?.value || '';
690+
this.inputFilterText = this.inputRef?.value ?? '';
691691

692692
if (this.isSingleMode && this.inputFilterText === this.selectedLabels[0]) {
693693
return;
@@ -697,7 +697,9 @@ export class Select implements IxInputFieldComponent<string | string[]> {
697697
this.items.forEach((item) => {
698698
item.classList.remove('d-none');
699699
if (
700-
!item.label.toLowerCase().includes(this.inputFilterText.toLowerCase())
700+
!item.label
701+
?.toLowerCase()
702+
.includes(this.inputFilterText.toLowerCase())
701703
) {
702704
item.classList.add('d-none');
703705
}
@@ -801,15 +803,22 @@ export class Select implements IxInputFieldComponent<string | string[]> {
801803
*/
802804
@Method()
803805
getNativeInputElement(): Promise<HTMLInputElement> {
804-
return Promise.resolve(this.inputRef);
806+
if (this.inputRef) {
807+
return Promise.resolve(this.inputRef);
808+
} else {
809+
return Promise.reject(new Error('Input element not found'));
810+
}
805811
}
806812

807813
/**
808814
* Focuses the input field
809815
*/
810816
@Method()
811817
async focusInput(): Promise<void> {
812-
return (await this.getNativeInputElement()).focus();
818+
const inputElement = await this.getNativeInputElement();
819+
if (inputElement) {
820+
inputElement.focus();
821+
}
813822
}
814823

815824
render() {
@@ -880,12 +889,14 @@ export class Select implements IxInputFieldComponent<string | string[]> {
880889
ref={(ref) => (this.inputRef = ref)}
881890
onBlur={(e) => this.onInputBlur(e)}
882891
onFocus={() => {
883-
this.navigationItem = null;
892+
this.navigationItem = undefined;
884893
}}
885894
onInput={() => this.filterItemsWithTypeahead()}
886895
onKeyDown={(e) => this.onKeyDown(e)}
887896
/>
888897
{this.allowClear &&
898+
!this.disabled &&
899+
!this.readonly &&
889900
(this.selectedLabels?.length || this.inputFilterText) ? (
890901
<ix-icon-button
891902
class="clear"

packages/core/src/components/select/test/select.ct.ts

+22
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,28 @@ test('type in a novel item name and click outside', async ({ mount, page }) => {
287287
expect(inputValue).toBe('Item 2');
288288
});
289289

290+
test('check if clear button visible in disabled', async ({ mount, page }) => {
291+
await mount(`
292+
<ix-select value="2" allow-clear>
293+
<ix-select-item value="1" label="Item 1">Test</ix-select-item>
294+
<ix-select-item value="2" label="Item 2">Test</ix-select-item>
295+
<ix-select-item value="3" label="Item 3">Test</ix-select-item>
296+
</ix-select>
297+
`);
298+
299+
const selectElement = page.locator('ix-select');
300+
await expect(selectElement).toHaveClass(/hydrated/);
301+
302+
const clearButton = page.locator('ix-icon-button.clear.btn-icon-16');
303+
await expect(clearButton).toBeVisible();
304+
305+
await selectElement.evaluate(
306+
(select: HTMLIxSelectElement) => (select.disabled = true)
307+
);
308+
309+
await expect(clearButton).not.toBeAttached();
310+
});
311+
290312
test('type in a novel item name in multiple mode, click outside', async ({
291313
mount,
292314
page,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2024 Siemens AG
3+
*
4+
* SPDX-License-Identifier: MIT
5+
*
6+
* This source code is licensed under the MIT license found in the
7+
* LICENSE file in the root directory of this source tree.
8+
*/
9+
import type { Components } from '@siemens/ix/components';
10+
import type { ArgTypes, Meta, StoryObj } from '@storybook/web-components';
11+
import { genericRender, makeArgTypes } from './utils/generic-render';
12+
import { html } from 'lit';
13+
14+
type Element = Components.IxSelect;
15+
16+
const meta = {
17+
title: 'Example/Select',
18+
tags: [],
19+
render: (args) => genericRender('ix-select', args),
20+
argTypes: makeArgTypes<Partial<ArgTypes<Element>>>('ix-select'),
21+
parameters: {
22+
design: {
23+
type: 'figma',
24+
url: 'https://www.figma.com/design/r2nqdNNXXZtPmWuVjIlM1Q/iX-Components---Brand-Dark?node-id=42365-175539&m=dev',
25+
},
26+
},
27+
} satisfies Meta<Element>;
28+
29+
export default meta;
30+
type Story = StoryObj<Element>;
31+
32+
export const Default: Story = {
33+
args: {},
34+
};
35+
36+
export const editableSelect: Story = {
37+
render: ({ value, editable, allowClear, disabled }) => {
38+
return html` <ix-select
39+
value=${value}
40+
?editable=${editable}
41+
?allow-clear=${allowClear}
42+
disabled=${disabled}
43+
>
44+
<ix-select-item label="Item 1" value="1"></ix-select-item>
45+
<ix-select-item label="Item 2" value="2"></ix-select-item>
46+
<ix-select-item label="Item 3" value="3"></ix-select-item>
47+
<ix-select-item label="Item 4" value="4"></ix-select-item>
48+
</ix-select>`;
49+
},
50+
args: {
51+
value: 'Administrator',
52+
editable: true,
53+
allowClear: true,
54+
disabled: false,
55+
},
56+
};

0 commit comments

Comments
 (0)