diff --git a/docs/BREAKING_CHANGES.v3.md b/docs/BREAKING_CHANGES.v3.md index 5cbe824359..05b261a5dc 100644 --- a/docs/BREAKING_CHANGES.v3.md +++ b/docs/BREAKING_CHANGES.v3.md @@ -23,6 +23,18 @@ The following components have been removed: - Visually, the tooltip has been replaced by a simple label shown in parentheses after the abbreviation. - The property `_tooltipAlign` has been removed. +### kol-input-file + +- The property `_value` has been removed as it never served a purpose. Use the `getValue()` method instead to access the FileList. + +### kol-table-stateful + +- The DOM event `kol-selection-change` has been renamed to `kolSelectionChange`. + +### kol-table-stateless + +- The DOM event `kol-selection-change` has been renamed to `kolSelectionChange`. + ## `focus`-methods The public `focus`-methods have been removed from all components. Use `kolFocus` instead. diff --git a/packages/components/.gitignore b/packages/components/.gitignore index 2923bfed2c..7ace70dc01 100644 --- a/packages/components/.gitignore +++ b/packages/components/.gitignore @@ -1,4 +1,5 @@ /dist/ +/dist-e2e/ /doc/** /loader/ /public/ diff --git a/packages/components/package.json b/packages/components/package.json index eb16a19c7a..852536e86a 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -65,7 +65,7 @@ "test": "pnpm test:unit", "test:unit": "cross-env NODE_ENV=test stencil test --spec --json --outputFile dist/jest-test-results.json", "test:watch": "cross-env NODE_ENV=test stencil test --spec --watchAll", - "test:e2e": "playwright test", + "test:e2e": "cross-env E2E=1 playwright test", "postinstall": "pnpm exec playwright install", "postpack": "mv package.bak.json package.json", "prepack": "npm run build && cp package.json package.bak.json && rimraf dist/collection dist/kolibri/assets/@leanup dist/types/assets/@leanup && node scripts/anonymous.js && node scripts/minify.js", diff --git a/packages/components/playwright.config.ts b/packages/components/playwright.config.ts index dc31d041fa..84debd5bc0 100644 --- a/packages/components/playwright.config.ts +++ b/packages/components/playwright.config.ts @@ -3,7 +3,8 @@ import { createConfig, matchers } from '@stencil/playwright'; expect.extend(matchers); -const TEST_URL = 'http://localhost:3333'; +const TEST_PORT = '3333'; +const TEST_URL = `http://localhost:${TEST_PORT}`; /* See https://playwright.dev/docs/test-configuration */ export default createConfig({ @@ -30,8 +31,13 @@ export default createConfig({ use: { baseURL: TEST_URL, timezoneId: 'Europe/Berlin', + screenshot: 'only-on-failure', + trace: 'retain-on-failure', }, webServer: { url: TEST_URL, + reuseExistingServer: false, + /* The builtin Stencil server sometimes fails to serve some assets which leads to intermittent test failures. Use a more stable server (without watcher) for CI: */ + ...(process.env.CI ? { command: `stencil build --dev && mv dist-e2e/kolibri dist-e2e/build && npx serve dist-e2e -p ${TEST_PORT} -L` } : {}), }, }); diff --git a/packages/components/src/components/@deprecated/input/controller.ts b/packages/components/src/components/@deprecated/input/controller.ts index 48aa7a540e..a97afdb03e 100644 --- a/packages/components/src/components/@deprecated/input/controller.ts +++ b/packages/components/src/components/@deprecated/input/controller.ts @@ -26,14 +26,14 @@ import { validateHideLabel, validateLabelWithExpertSlot, validateMsg, + validateShortKey, validateTabIndex, validateTooltipAlign, watchBoolean, watchString, - validateShortKey, } from '../../../schema'; -import { stopPropagation, tryToDispatchKoliBriEvent } from '../../../utils/events'; +import { dispatchDomEvent, KolEvent } from '../../../utils/events'; import { ControlledInputController } from '../../input-adapter-leanup/controller'; import type { Props as AdapterProps } from '../../input-adapter-leanup/types'; @@ -161,12 +161,17 @@ export class InputController extends ControlledInputController implements Watche validateAccessAndShortKey(this.component._accessKey, this.component._shortKey); } + private emitEvent(type: KolEvent, value?: unknown): void { + if (this.host) { + dispatchDomEvent(this.host, type, value); + } + } + protected onBlur(event: Event): void { this.component._touched = true; // Event handling - stopPropagation(event); - tryToDispatchKoliBriEvent('blur', this.host); + this.emitEvent(KolEvent.blur); // Callback if (typeof this.component._on?.onBlur === 'function') { @@ -179,10 +184,12 @@ export class InputController extends ControlledInputController implements Watche * @param value - Optional value. Taken from event if not defined. */ protected onChange(event: Event, value?: StencilUnknown): void { - value = value ?? (event.target as HTMLInputElement).value; + if (typeof value === 'undefined') { + value = (event.target as HTMLInputElement).value; + } // Event handling - tryToDispatchKoliBriEvent('change', this.host, value); + this.emitEvent(KolEvent.change, value); // Callback if (typeof this.component._on?.onChange === 'function') { @@ -208,11 +215,12 @@ export class InputController extends ControlledInputController implements Watche * @param value - Optional value. Taken from event if not defined. */ protected onInput(event: Event, shouldSetFormAssociatedValue = true, value?: StencilUnknown): void { - value = value ?? (event.target as HTMLInputElement).value; + if (typeof value === 'undefined') { + value = (event.target as HTMLInputElement).value; + } // Event handling - stopPropagation(event); - tryToDispatchKoliBriEvent('input', this.host, value); + this.emitEvent(KolEvent.input, value); // Static form handling if (shouldSetFormAssociatedValue) { @@ -227,8 +235,7 @@ export class InputController extends ControlledInputController implements Watche protected onClick(event: Event): void { // Event handling - stopPropagation(event); - tryToDispatchKoliBriEvent('click', this.host); + this.emitEvent(KolEvent.click); // Callback if (typeof this.component._on?.onClick === 'function') { @@ -238,8 +245,7 @@ export class InputController extends ControlledInputController implements Watche protected onFocus(event: Event): void { // Event handling - stopPropagation(event); - tryToDispatchKoliBriEvent('focus', this.host); + this.emitEvent(KolEvent.focus); // Callback if (typeof this.component._on?.onFocus === 'function') { diff --git a/packages/components/src/components/accordion/accordion.e2e.ts b/packages/components/src/components/accordion/accordion.e2e.ts index 49cb87424e..e20af0affe 100644 --- a/packages/components/src/components/accordion/accordion.e2e.ts +++ b/packages/components/src/components/accordion/accordion.e2e.ts @@ -1,5 +1,6 @@ import { expect } from '@playwright/test'; import { test } from '@stencil/playwright'; +import { KolEvent } from '../../utils/events'; test.describe('kol-accordion', () => { test.describe('when accordion is enabled', () => { @@ -24,6 +25,32 @@ test.describe('kol-accordion', () => { await page.getByRole('button', { name: 'Accordion label' }).click(); await expect(page.locator('.collapsible__content')).toHaveAttribute('aria-hidden', 'true'); }); + + test('should emit "click" event when the title is clicked', async ({ page }) => { + const eventPromise = page.locator('kol-accordion').evaluate(async (element: HTMLKolAccordionElement, KolEvent) => { + return new Promise((resolve) => { + element.addEventListener(KolEvent.click, resolve); + }); + }, KolEvent); + await page.waitForChanges(); + await page.getByRole('button', { name: 'Accordion label' }).click(); + await expect(eventPromise).resolves.toBeTruthy(); + }); + + test('should call "onClick" callback when the title is clicked', async ({ page }) => { + const callbackPromise = page.locator('kol-accordion').evaluate(async (element: HTMLKolAccordionElement) => { + return new Promise((resolve) => { + element._on = { + onClick: (_event: MouseEvent, value?: boolean) => { + resolve(value); + }, + }; + }); + }); + await page.waitForChanges(); + await page.getByRole('button', { name: 'Accordion label' }).click(); + await expect(callbackPromise).resolves.toBe(true); + }); }); test.describe('when accordion is disabled', () => { diff --git a/packages/components/src/components/accordion/shadow.tsx b/packages/components/src/components/accordion/shadow.tsx index 6436b09caf..fd22b42a47 100644 --- a/packages/components/src/components/accordion/shadow.tsx +++ b/packages/components/src/components/accordion/shadow.tsx @@ -1,5 +1,5 @@ // https://codepen.io/mbxtr/pen/OJPOYg?html-preprocessor=haml -import { Component, h, Method, Prop, State, Watch } from '@stencil/core'; +import { Component, Element, h, Method, Prop, State, Watch } from '@stencil/core'; import type { JSX } from '@stencil/core'; import type { AccordionAPI, @@ -15,6 +15,7 @@ import { featureHint, validateAccordionCallbacks, validateDisabled, validateLabe import { nonce } from '../../utils/dev.utils'; import { watchHeadingLevel } from '../heading/validation'; import KolCollapsibleFc, { type CollapsibleProps } from '../../functional-components/Collapsible'; +import { dispatchDomEvent, KolEvent } from '../../utils/events'; featureHint(`[KolAccordion] Anfrage nach einer KolAccordionGroup bei dem immer nur ein Accordion geöffnet ist. @@ -35,6 +36,8 @@ featureHint(`[KolAccordion] Tab-Sperre des Inhalts im geschlossenen Zustand.`); shadow: true, }) export class KolAccordion implements AccordionAPI, FocusableElement { + @Element() private readonly host?: HTMLKolAccordionElement; + private readonly nonce = nonce(); private buttonWcRef?: HTMLKolButtonWcElement; @@ -58,6 +61,9 @@ export class KolAccordion implements AccordionAPI, FocusableElement { */ setTimeout(() => { this.state._on?.onClick?.(event, this._open === true); + if (this.host) { + dispatchDomEvent(this.host, KolEvent.click, this._open === true); + } }); }; diff --git a/packages/components/src/components/alert/alert.e2e.ts b/packages/components/src/components/alert/alert.e2e.ts new file mode 100644 index 0000000000..949a3cbf03 --- /dev/null +++ b/packages/components/src/components/alert/alert.e2e.ts @@ -0,0 +1,37 @@ +import { expect } from '@playwright/test'; +import { test } from '@stencil/playwright'; +import { KolEvent } from '../../utils/events'; + +test.describe('kol-alert', () => { + test.describe('Callbacks', () => { + test('should call "onClose" callback when the close button is clicked', async ({ page }) => { + await page.setContent(''); + const callbackPromise = page.locator('kol-alert').evaluate(async (element: HTMLKolAlertElement) => { + return new Promise((resolve) => { + element._on = { + onClose: (_event: Event, value?: unknown) => { + resolve(value); + }, + }; + }); + }); + await page.waitForChanges(); + await page.getByTestId('alert-close-button').click(); + await expect(callbackPromise).resolves.toBeUndefined(); + }); + }); + + test.describe('DOM events', () => { + test('should emit "close" when close button is clicked', async ({ page }) => { + await page.setContent(''); + const eventPromise = page.locator('kol-alert').evaluate(async (element: HTMLKolAlertElement, KolEvent) => { + return new Promise((resolve) => { + element.addEventListener(KolEvent.close, resolve); + }); + }, KolEvent); + await page.waitForChanges(); + await page.getByTestId('alert-close-button').click(); + await expect(eventPromise).resolves.toBeTruthy(); + }); + }); +}); diff --git a/packages/components/src/components/alert/component.tsx b/packages/components/src/components/alert/component.tsx index dfd2fc286b..4a3b71a914 100644 --- a/packages/components/src/components/alert/component.tsx +++ b/packages/components/src/components/alert/component.tsx @@ -1,9 +1,10 @@ import type { JSX } from '@stencil/core'; import { alertTypeOptions, alertVariantOptions, setState, validateHasCloser, validateLabel, watchBoolean, watchValidator } from '../../schema'; -import { Component, h, Prop, State, Watch } from '@stencil/core'; +import { Component, Element, h, Prop, State, Watch } from '@stencil/core'; import { watchHeadingLevel } from '../heading/validation'; import type { AlertAPI, AlertStates, AlertType, AlertVariant, HasCloserPropType, HeadingLevel, KoliBriAlertEventCallbacks, LabelPropType } from '../../schema'; import KolAlertFc, { type KolAlertFcProps } from '../../functional-components/Alert'; +import { dispatchDomEvent, KolEvent } from '../../utils/events'; /** * @internal @@ -14,8 +15,13 @@ import KolAlertFc, { type KolAlertFcProps } from '../../functional-components/Al shadow: false, }) export class KolAlertWc implements AlertAPI { + @Element() private readonly host?: HTMLKolAlertWcElement; + private readonly close = () => { this._on?.onClose?.(new Event('Close')); + if (this.host) { + dispatchDomEvent(this.host, KolEvent.close); + } }; private readonly handleAlertTimeout = () => { diff --git a/packages/components/src/components/badge/badge.e2e.ts b/packages/components/src/components/badge/badge.e2e.ts new file mode 100644 index 0000000000..19a3c7a67d --- /dev/null +++ b/packages/components/src/components/badge/badge.e2e.ts @@ -0,0 +1,48 @@ +import { expect } from '@playwright/test'; +import { test } from '@stencil/playwright'; +import { KolEvent } from '../../utils/events'; + +test.describe('kol-badge', () => { + test.describe('Callbacks', () => { + ['onClick', 'onMouseDown'].forEach((callbackName) => { + test(`should call ${callbackName} callback when smart button emits`, async ({ page }) => { + await page.setContent(``); + const kolBadge = page.locator('kol-badge'); + + const callbackPromise = kolBadge.evaluate((element: HTMLKolBadgeElement, callbackName) => { + return new Promise((resolve) => { + element._smartButton = { + _label: `Smart Button`, + _on: { + [callbackName]: () => { + resolve(); + }, + }, + }; + }); + }, callbackName); + await page.waitForChanges(); + + await page.locator('button').click(); + await expect(callbackPromise).resolves.toBeUndefined(); + }); + }); + }); + + test.describe('DOM events', () => { + [KolEvent.click, KolEvent.mousedown].forEach((event) => { + test(`should emit ${event} when smart button emits ${event}`, async ({ page }) => { + const BADGE_PROPS = { _label: `Smart Button` }; + await page.setContent(``); + const eventPromise = page.locator('kol-badge').evaluate(async (element, event) => { + return new Promise((resolve) => { + element.addEventListener(event, resolve); + }); + }, event); + await page.waitForChanges(); + await page.locator('button').dispatchEvent(event); + await expect(eventPromise).resolves.toBeTruthy(); + }); + }); + }); +}); diff --git a/packages/components/src/components/button-link/button-link.e2e.ts b/packages/components/src/components/button-link/button-link.e2e.ts new file mode 100644 index 0000000000..bff66a6705 --- /dev/null +++ b/packages/components/src/components/button-link/button-link.e2e.ts @@ -0,0 +1,50 @@ +import { expect } from '@playwright/test'; +import { test } from '@stencil/playwright'; +import { KolEvent } from '../../utils/events'; + +test.describe('kol-button-link', () => { + test('it renders label', async ({ page }) => { + await page.setContent(''); + const kolButton = page.locator('kol-button-link'); + await expect(kolButton).toContainText('Test ButtonLink Element'); + }); + + test.describe('Callbacks', () => { + ['onClick', 'onMouseDown'].forEach((callbackName) => { + test(`should call ${callbackName} callback when internal button emits`, async ({ page }) => { + await page.setContent(''); + const kolButton = page.locator('kol-button-link'); + + const callbackPromise = kolButton.evaluate((element: HTMLKolButtonElement, callbackName) => { + return new Promise((resolve) => { + element._on = { + [callbackName]: () => { + resolve(); + }, + }; + }); + }, callbackName); + await page.waitForChanges(); + + await page.locator('button').click(); + await expect(callbackPromise).resolves.toBeUndefined(); + }); + }); + }); + + test.describe('DOM events', () => { + [KolEvent.click, KolEvent.mousedown].forEach((event) => { + test(`should emit ${event} when internal button emits ${event}`, async ({ page }) => { + await page.setContent(''); + const eventPromise = page.locator('kol-button-link').evaluate(async (element, event) => { + return new Promise((resolve) => { + element.addEventListener(event, resolve); + }); + }, event); + await page.waitForChanges(); + await page.locator('button').dispatchEvent(event); + await expect(eventPromise).resolves.toBeTruthy(); + }); + }); + }); +}); diff --git a/packages/components/src/components/button/button.e2e.ts b/packages/components/src/components/button/button.e2e.ts index 4ef24e855d..203ba89a7d 100644 --- a/packages/components/src/components/button/button.e2e.ts +++ b/packages/components/src/components/button/button.e2e.ts @@ -1,5 +1,6 @@ import { expect } from '@playwright/test'; import { test } from '@stencil/playwright'; +import { KolEvent } from '../../utils/events'; test.describe('kol-button', () => { test('it renders label', async ({ page }) => { @@ -7,4 +8,43 @@ test.describe('kol-button', () => { const kolButton = page.locator('kol-button'); await expect(kolButton).toContainText('Test Button Element'); }); + + test.describe('Callbacks', () => { + ['onClick', 'onMouseDown'].forEach((callbackName) => { + test(`should call ${callbackName} callback when internal button emits`, async ({ page }) => { + await page.setContent(''); + const kolButton = page.locator('kol-button'); + + const callbackPromise = kolButton.evaluate((element: HTMLKolButtonElement, callbackName) => { + return new Promise((resolve) => { + element._on = { + [callbackName]: () => { + resolve(); + }, + }; + }); + }, callbackName); + await page.waitForChanges(); + + await page.locator('button').click(); + await expect(callbackPromise).resolves.toBeUndefined(); + }); + }); + }); + + test.describe('DOM events', () => { + [KolEvent.click, KolEvent.mousedown].forEach((event) => { + test(`should emit ${event} when internal button emits ${event}`, async ({ page }) => { + await page.setContent(''); + const eventPromise = page.locator('kol-button').evaluate(async (element, event) => { + return new Promise((resolve) => { + element.addEventListener(event, resolve); + }); + }, event); + await page.waitForChanges(); + await page.locator('button').dispatchEvent(event); + await expect(eventPromise).resolves.toBeTruthy(); + }); + }); + }); }); diff --git a/packages/components/src/components/button/component.tsx b/packages/components/src/components/button/component.tsx index 496b871036..be34a72421 100644 --- a/packages/components/src/components/button/component.tsx +++ b/packages/components/src/components/button/component.tsx @@ -46,7 +46,7 @@ import { import type { JSX } from '@stencil/core'; import { Component, Element, h, Host, Method, Prop, State, Watch } from '@stencil/core'; -import { stopPropagation, tryToDispatchKoliBriEvent } from '../../utils/events'; +import { dispatchDomEvent, KolEvent } from '../../utils/events'; import { nonce } from '../../utils/dev.utils'; import { propagateResetEventToForm, propagateSubmitEventToForm } from '../form/controller'; import { AssociatedInputController } from '../input-adapter-leanup/associated.controller'; @@ -90,11 +90,6 @@ export class KolButtonWc implements ButtonAPI, FocusableElement { ref: this.buttonRef, }); } else { - // Event handling - stopPropagation(event); - - tryToDispatchKoliBriEvent('click', this.host, this.state._value); - // TODO: Static form handling this.controller.setFormAssociatedValue(this.state._value); @@ -104,6 +99,17 @@ export class KolButtonWc implements ButtonAPI, FocusableElement { this.state._on?.onClick(event, this.state._value); } } + + if (this.host) { + dispatchDomEvent(this.host, KolEvent.click, this.state._value); + } + }; + + private readonly onMouseDown = (event: MouseEvent) => { + this.state?._on?.onMouseDown?.(event); + if (this.host) { + dispatchDomEvent(this.host, KolEvent.mousedown); + } }; public render(): JSX.Element { @@ -131,8 +137,8 @@ export class KolButtonWc implements ButtonAPI, FocusableElement { disabled={this.state._disabled} id={this.state._id} name={this.state._name} - {...this.state._on} onClick={this.onClick} + onMouseDown={this.onMouseDown} role={this.state._role} tabIndex={this.state._tabIndex} type={this.state._type} diff --git a/packages/components/src/components/card/card.e2e.ts b/packages/components/src/components/card/card.e2e.ts new file mode 100644 index 0000000000..f4f20d7f74 --- /dev/null +++ b/packages/components/src/components/card/card.e2e.ts @@ -0,0 +1,37 @@ +import { expect } from '@playwright/test'; +import { test } from '@stencil/playwright'; +import { KolEvent } from '../../utils/events'; + +test.describe('kol-card', () => { + test.describe('Callbacks', () => { + test('should call "onClose" callback when the close button is clicked', async ({ page }) => { + await page.setContent(''); + const callbackPromise = page.locator('kol-card').evaluate(async (element: HTMLKolCardElement) => { + return new Promise((resolve) => { + element._on = { + onClose: (_event: Event, value?: unknown) => { + resolve(value); + }, + }; + }); + }); + await page.waitForChanges(); + await page.getByTestId('card-close-button').click(); + await expect(callbackPromise).resolves.toBeUndefined(); + }); + }); + + test.describe('DOM events', () => { + test('should emit "close" when close button is clicked', async ({ page }) => { + await page.setContent(''); + const eventPromise = page.locator('kol-card').evaluate(async (element: HTMLKolCardElement, KolEvent) => { + return new Promise((resolve) => { + element.addEventListener(KolEvent.close, resolve); + }); + }, KolEvent); + await page.waitForChanges(); + await page.getByTestId('card-close-button').click(); + await expect(eventPromise).resolves.toBeTruthy(); + }); + }); +}); diff --git a/packages/components/src/components/card/shadow.tsx b/packages/components/src/components/card/shadow.tsx index d503e964f9..ccd2b5d4d9 100644 --- a/packages/components/src/components/card/shadow.tsx +++ b/packages/components/src/components/card/shadow.tsx @@ -1,4 +1,4 @@ -import { Component, h, Prop, State, Watch } from '@stencil/core'; +import { Component, Element, h, Prop, State, Watch } from '@stencil/core'; import type { JSX } from '@stencil/core'; import type { CardAPI, CardStates, HasCloserPropType, HeadingLevel, KoliBriAlertEventCallbacks, KoliBriCardEventCallbacks, LabelPropType } from '../../schema'; import { setState, validateHasCloser, validateLabel } from '../../schema'; @@ -8,6 +8,7 @@ import { watchHeadingLevel } from '../heading/validation'; import { KolButtonWcTag } from '../../core/component-names'; import { KolHeadingFc } from '../../functional-components'; +import { dispatchDomEvent, KolEvent } from '../../utils/events'; /** * @slot - Ermöglicht das Einfügen beliebigen HTML's in den Inhaltsbereich der Card. @@ -20,10 +21,15 @@ import { KolHeadingFc } from '../../functional-components'; shadow: true, }) export class KolCard implements CardAPI { + @Element() private readonly host?: HTMLKolCardElement; + private readonly close = () => { if (this._on?.onClose !== undefined) { this._on.onClose(new Event('Close')); } + if (this.host) { + dispatchDomEvent(this.host, KolEvent.close); + } }; private readonly on = { @@ -44,6 +50,7 @@ export class KolCard implements CardAPI { {this.state._hasCloser && ( - + @@ -81,7 +81,7 @@ exports[`kol-card should render with _label="Überschrift" _level=1 _hasCloser=t
- +
@@ -133,7 +133,7 @@ exports[`kol-card should render with _label="Überschrift" _level=2 _hasCloser=t
- + @@ -185,7 +185,7 @@ exports[`kol-card should render with _label="Überschrift" _level=3 _hasCloser=t
- + @@ -237,7 +237,7 @@ exports[`kol-card should render with _label="Überschrift" _level=4 _hasCloser=t
- + @@ -289,7 +289,7 @@ exports[`kol-card should render with _label="Überschrift" _level=5 _hasCloser=t
- + @@ -341,7 +341,7 @@ exports[`kol-card should render with _label="Überschrift" _level=6 _hasCloser=t
- + diff --git a/packages/components/src/components/combobox/combobox.e2e.ts b/packages/components/src/components/combobox/combobox.e2e.ts new file mode 100644 index 0000000000..82e46bced5 --- /dev/null +++ b/packages/components/src/components/combobox/combobox.e2e.ts @@ -0,0 +1,10 @@ +import { test } from '@stencil/playwright'; +import { testInputCallbacksAndEvents, testInputValueReflection } from '../../e2e'; + +const COMPONENT_NAME = 'kol-combobox'; +const TEST_VALUE = 'Hello World'; + +test.describe(COMPONENT_NAME, () => { + testInputValueReflection(COMPONENT_NAME, TEST_VALUE); + testInputCallbacksAndEvents(COMPONENT_NAME, TEST_VALUE); +}); diff --git a/packages/components/src/components/combobox/shadow.tsx b/packages/components/src/components/combobox/shadow.tsx index 6e57db76ac..6d691e485e 100644 --- a/packages/components/src/components/combobox/shadow.tsx +++ b/packages/components/src/components/combobox/shadow.tsx @@ -20,7 +20,6 @@ import type { JSX } from '@stencil/core'; import { Component, Element, Fragment, h, Host, Listen, Method, Prop, State, Watch } from '@stencil/core'; import { nonce } from '../../utils/dev.utils'; -import { stopPropagation, tryToDispatchKoliBriEvent } from '../../utils/events'; import { ComboboxController } from './controller'; import { KolIconTag, KolInputTag } from '../../core/component-names'; import { InternalUnderlinedBadgeText } from '../../functional-components'; @@ -85,6 +84,7 @@ export class KolCombobox implements ComboboxAPI { private onInput(event: Event) { const target = event.target as HTMLInputElement; this.state._value = target.value; + this._value = target.value; this.controller.onFacade.onInput(event); this.setFilteredSuggestionsByQuery(target.value); this._focusedOptionIndex = -1; @@ -470,7 +470,7 @@ export class KolCombobox implements ComboboxAPI { /** * Defines the value of the input. */ - @Prop({ mutable: true }) public _value?: string; + @Prop({ mutable: true, reflect: true }) public _value?: string; @State() public state: ComboboxStates = { _hasValue: false, @@ -625,16 +625,9 @@ export class KolCombobox implements ComboboxAPI { } private onChange(event: Event): void { - // Event handling - stopPropagation(event); - tryToDispatchKoliBriEvent('change', this.host, this.state._value); + this.controller.onFacade.onChange(event); // Static form handling this.controller.setFormAssociatedValue(this.state._value as unknown as string); - - // Callback - if (typeof this.state._on?.onChange === 'function' && !this._isOpen) { - this.state._on.onChange(event, this.state._value); - } } } diff --git a/packages/components/src/components/combobox/test/__snapshots__/snapshot.spec.tsx.snap b/packages/components/src/components/combobox/test/__snapshots__/snapshot.spec.tsx.snap index e38f058eac..f45ea2acdc 100644 --- a/packages/components/src/components/combobox/test/__snapshots__/snapshot.spec.tsx.snap +++ b/packages/components/src/components/combobox/test/__snapshots__/snapshot.spec.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`kol-combobox should render with _label="Label" _name="field" _placeholder="Hier steht ein Platzhaltertext" _value="Herr" _suggestions=["Frau","Herr","Divers"] _accessKey="V" 1`] = ` - +