From 2d469527f0af04de88e5ffe35b85f68e1aba5d81 Mon Sep 17 00:00:00 2001 From: Vibe Nuage Agent Date: Fri, 7 Aug 2026 06:32:53 +0000 Subject: [PATCH] feat(input-text): add clear button for search type (#10635) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add clear button for input-text with type='search' (no new prop needed) - Button uses kolicon-x icon with ARIA label 'Suche löschen'/'Clear search' - Button is disabled when input is empty or component is disabled - Button clears input value and keeps focus on input - Add translations for clear button label - Add sample and e2e tests Closes #10635 Co-authored-by: deleonio --- .../input-text/input-text.clear-button.e2e.ts | 50 +++++++++++++++++++ .../src/components/input-text/shadow.tsx | 30 ++++++++++- packages/components/src/locales/de.ts | 1 + packages/components/src/locales/en.ts | 1 + .../react/src/components/input-text/basic.tsx | 3 +- .../components/input-text/clear-button.tsx | 19 +++++++ 6 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 packages/components/src/components/input-text/input-text.clear-button.e2e.ts create mode 100644 packages/samples/react/src/components/input-text/clear-button.tsx diff --git a/packages/components/src/components/input-text/input-text.clear-button.e2e.ts b/packages/components/src/components/input-text/input-text.clear-button.e2e.ts new file mode 100644 index 00000000000..9d510c5a6ac --- /dev/null +++ b/packages/components/src/components/input-text/input-text.clear-button.e2e.ts @@ -0,0 +1,50 @@ +import { expect } from '@playwright/test'; +import { test } from '@stencil/playwright'; + +test.describe('kol-input-text clear button', () => { + test('should render clear button when type is search and input has value', async ({ page }) => { + await page.setContent(''); + const clearButton = page.getByTestId('kol-input-text-clear-button'); + await expect(clearButton).toBeVisible(); + }); + + test('should not render clear button when type is not search', async ({ page }) => { + await page.setContent(''); + const clearButton = page.getByTestId('kol-input-text-clear-button'); + await expect(clearButton).not.toBeVisible(); + }); + + test('should not render clear button when input is empty', async ({ page }) => { + await page.setContent(''); + const clearButton = page.getByTestId('kol-input-text-clear-button'); + await expect(clearButton).not.toBeVisible(); + }); + + test('should render clear button when input has value', async ({ page }) => { + await page.setContent(''); + const clearButton = page.getByTestId('kol-input-text-clear-button'); + await expect(clearButton).toBeVisible(); + }); + + test('should clear input value when clear button is clicked', async ({ page }) => { + await page.setContent(''); + const input = page.locator('kol-input-text input'); + const clearButton = page.getByTestId('kol-input-text-clear-button'); + + await expect(input).toHaveValue('test'); + await clearButton.click(); + await expect(input).toHaveValue(''); + }); + + test('should not render clear button when disabled', async ({ page }) => { + await page.setContent(''); + const clearButton = page.getByTestId('kol-input-text-clear-button'); + await expect(clearButton).not.toBeVisible(); + }); + + test('should have correct aria-label', async ({ page }) => { + await page.setContent(''); + const clearButton = page.getByTestId('kol-input-text-clear-button'); + await expect(clearButton).toHaveAttribute('aria-label', 'Suche löschen'); + }); +}); diff --git a/packages/components/src/components/input-text/shadow.tsx b/packages/components/src/components/input-text/shadow.tsx index 48a61346e5c..46c5a7bbc9e 100644 --- a/packages/components/src/components/input-text/shadow.tsx +++ b/packages/components/src/components/input-text/shadow.tsx @@ -1,10 +1,12 @@ -import type { JSX } from '@stencil/core'; +import type { JSX, VNode } from '@stencil/core'; import { Component, Element, h, Method, Prop, State, Watch } from '@stencil/core'; import clsx from '../../utils/clsx'; import KolFormFieldStateWrapperFc, { type FormFieldStateWrapperProps } from '../../functional-component-wrappers/FormFieldStateWrapper/FormFieldStateWrapper'; import KolInputContainerFc from '../../functional-component-wrappers/InputContainerStateWrapper/InputContainerStateWrapper'; import KolInputStateWrapperFc, { type InputStateWrapperProps } from '../../functional-component-wrappers/InputStateWrapper/InputStateWrapper'; +import KolIconButtonFc from '../../functional-components/IconButton'; +import { translate } from '../../i18n'; import type { AccessKeyPropType, AriaDetailsPropType, @@ -100,6 +102,30 @@ export class KolInputText implements ClickableElement, FocusableElement, InputTe } }; + private readonly translateClearSearch = translate('kol-clear-search'); + + private getClearButton(): VNode | null { + if (this.state._type === 'search' && !this._disabled && this.state._hasValue) { + return ( + { + this._value = ''; + this.ctaRef.el?.focus(); + }} + icon="kolicon-x" + disabled={this._disabled} + /> + ); + } + + return null; + } + /** * Returns the current value. */ @@ -208,7 +234,7 @@ export class KolInputText implements ClickableElement, FocusableElement, InputTe public render(): JSX.Element { return ( - + diff --git a/packages/components/src/locales/de.ts b/packages/components/src/locales/de.ts index 3ad822762f7..21d6093e126 100644 --- a/packages/components/src/locales/de.ts +++ b/packages/components/src/locales/de.ts @@ -57,6 +57,7 @@ export default { 'close-alert': 'Benachrichtigung schließen', 'show-password': 'einblenden', 'hide-password': 'ausblenden', +t'clear-search': 'Suche löschen', 'no-results-message': 'Keine Ergebnisse gefunden.', 'delete-selection': 'Auswahl entfernen', 'filename-text': 'Datei auswählen oder hier ablegen...', diff --git a/packages/components/src/locales/en.ts b/packages/components/src/locales/en.ts index 77cb06efacb..1b7689c32c5 100644 --- a/packages/components/src/locales/en.ts +++ b/packages/components/src/locales/en.ts @@ -57,6 +57,7 @@ export default { 'close-alert': 'Close notification', 'show-password': 'Show', 'hide-password': 'Hide', +t'clear-search': 'Clear search', 'no-results-message': 'No results found.', 'delete-selection': 'Delete selection', 'filename-text': 'Choose a file or drop it here...', diff --git a/packages/samples/react/src/components/input-text/basic.tsx b/packages/samples/react/src/components/input-text/basic.tsx index 580ae794e56..c80c7526c16 100644 --- a/packages/samples/react/src/components/input-text/basic.tsx +++ b/packages/samples/react/src/components/input-text/basic.tsx @@ -7,7 +7,7 @@ import { SampleDescription } from '../SampleDescription'; export const InputTextBasic: FC = () => ( <> -

This story showcases the most important InputText variants: default, required, validation error, disabled, read-only, and with icons.

+

This story showcases the most important InputText variants: default, required, validation error, disabled, read-only, search with clear button, and with icons.

@@ -34,6 +34,7 @@ export const InputTextBasic: FC = () => ( +
); diff --git a/packages/samples/react/src/components/input-text/clear-button.tsx b/packages/samples/react/src/components/input-text/clear-button.tsx new file mode 100644 index 00000000000..52bb18df579 --- /dev/null +++ b/packages/samples/react/src/components/input-text/clear-button.tsx @@ -0,0 +1,19 @@ +import { KolInputText } from '@public-ui/react-v19'; +import type { FC } from 'react'; +import React from 'react'; +import { SampleDescription } from '../SampleDescription'; + +export const InputTextClearButton: FC = () => ( + <> + +

This sample demonstrates the clear button for search type input fields.

+
+ +
+ + + + +
+ +);