diff --git a/packages/components/src/components/input-date/input-date.e2e.ts b/packages/components/src/components/input-date/input-date.e2e.ts
index 78c28964af..3bc10af728 100644
--- a/packages/components/src/components/input-date/input-date.e2e.ts
+++ b/packages/components/src/components/input-date/input-date.e2e.ts
@@ -1,5 +1,6 @@
import { expect } from '@playwright/test';
import { test } from '@stencil/playwright';
+import type { Iso8601 } from '../../schema';
test.describe('kol-input-date', () => {
test.describe('when value is Date object', () => {
@@ -60,6 +61,153 @@ test.describe('kol-input-date', () => {
});
});
+ test.describe('Events', () => {
+ const testValues = [
+ { label: 'ISO String', value: '2020-03-03' as Iso8601 },
+ { label: 'Date Object', value: new Date('2020-03-03T03:02:01.099Z') },
+ ];
+
+ testValues.forEach(({ label, value }) => {
+ test.describe(`when initial value is a ${label}`, () => {
+ test(`should call onChange with value parameter as ${label}`, async ({ page }) => {
+ await page.setContent('');
+ const inputDate = page.locator('kol-input-date');
+ void inputDate.evaluate((element: HTMLKolInputDateElement, date) => {
+ element._value = date;
+ }, value);
+
+ const inputDateEventPromise = inputDate.evaluate((element) => {
+ return new Promise((resolve) => {
+ (element as HTMLKolSelectElement)._on = {
+ onChange: (_event, eventValue: unknown) => {
+ resolve(eventValue as Date | Iso8601);
+ },
+ };
+ });
+ });
+
+ await page.waitForChanges();
+ await page.locator('input').dispatchEvent('change');
+
+ const eventDetail = await inputDateEventPromise;
+ if (value instanceof Date) {
+ expect(eventDetail).toBeInstanceOf(Date);
+ expect((eventDetail as Date).toISOString().split('T')[0]).toBe(value.toISOString().split('T')[0]);
+ } else {
+ expect(eventDetail).toBe(value);
+ }
+ });
+
+ test(`should call onInput with value parameter as ${label}`, async ({ page }) => {
+ await page.setContent('');
+ const inputDate = page.locator('kol-input-date');
+ void inputDate.evaluate((element: HTMLKolInputDateElement, date) => {
+ element._value = date;
+ }, value);
+
+ const inputDateEventPromise = inputDate.evaluate((element) => {
+ return new Promise((resolve) => {
+ (element as HTMLKolSelectElement)._on = {
+ onInput: (_event, eventValue: unknown) => {
+ resolve(eventValue as Date | Iso8601);
+ },
+ };
+ });
+ });
+
+ await page.waitForChanges();
+ await page.locator('input').dispatchEvent('input');
+
+ const eventDetail = await inputDateEventPromise;
+ if (value instanceof Date) {
+ expect(eventDetail).toBeInstanceOf(Date);
+ expect((eventDetail as Date).toISOString().split('T')[0]).toBe(value.toISOString().split('T')[0]);
+ } else {
+ expect(eventDetail).toBe(value);
+ }
+ });
+
+ test(`should trigger custom "kol-change" DOM event with ${label} as event detail`, async ({ page }) => {
+ await page.setContent('');
+ const inputDate = page.locator('kol-input-date');
+
+ await inputDate.evaluate((element: HTMLKolInputDateElement, date) => {
+ element._value = date;
+ }, value);
+
+ const inputDateEventPromise = inputDate.evaluate((element: HTMLKolInputDateElement) => {
+ return new Promise((resolve) => {
+ element.addEventListener('kol-change', (e: Event) => {
+ const eventValue: Date | Iso8601 = (e as CustomEvent).detail;
+ resolve(eventValue);
+ });
+ });
+ });
+
+ await page.waitForChanges();
+ await page.locator('input').dispatchEvent('change');
+
+ const eventDetail = await inputDateEventPromise;
+
+ if (value instanceof Date) {
+ expect(eventDetail).toBeInstanceOf(Date);
+ expect((eventDetail as Date).toISOString().split('T')[0]).toBe(value.toISOString().split('T')[0]);
+ } else {
+ expect(eventDetail).toBe(value);
+ }
+ });
+
+ test(`should trigger custom "kol-input" DOM event with ${label} as event detail`, async ({ page }) => {
+ await page.setContent('');
+ const inputDate = page.locator('kol-input-date');
+
+ await inputDate.evaluate((element: HTMLKolInputDateElement, date) => {
+ element._value = date;
+ }, value);
+
+ const inputDateEventPromise = inputDate.evaluate((element: HTMLKolInputDateElement) => {
+ return new Promise((resolve) => {
+ element.addEventListener('kol-input', (e: Event) => {
+ const eventValue: Date | Iso8601 = (e as CustomEvent).detail;
+ resolve(eventValue);
+ });
+ });
+ });
+
+ await page.waitForChanges();
+ await page.locator('input').dispatchEvent('input');
+
+ const eventDetail = await inputDateEventPromise;
+
+ if (value instanceof Date) {
+ expect(eventDetail).toBeInstanceOf(Date);
+ expect((eventDetail as Date).toISOString().split('T')[0]).toBe(value.toISOString().split('T')[0]);
+ } else {
+ expect(eventDetail).toBe(value);
+ }
+ });
+
+ test(`should return the correct value for getValue() as ${label}`, async ({ page }) => {
+ await page.setContent('');
+ await page.locator('kol-input-date').evaluate((element: HTMLKolInputDateElement, date) => {
+ element._value = date;
+ }, value);
+
+ const getValue = await page.locator('kol-input-date').evaluate((element: HTMLKolInputDateElement) => {
+ return element.getValue();
+ });
+
+ if (value instanceof Date) {
+ expect(getValue).toBeInstanceOf(Date);
+ expect((getValue as Date).toISOString().split('T')[0]).toBe(value.toISOString().split('T')[0]);
+ } else {
+ expect(getValue).toBe(value);
+ }
+ });
+ });
+ });
+ });
+
test.describe('when min and max is set', () => {
test.describe('for Iso8601-Format', () => {
test('should set correct min and max for type date', async ({ page }) => {
@@ -202,4 +350,14 @@ test.describe('kol-input-date', () => {
});
});
});
+
+ test.describe('when initial value is null', () => {
+ test('should set value as empty string', async ({ page }) => {
+ await page.setContent('');
+ await page.locator('kol-input-date').evaluate((element: HTMLKolInputDateElement) => {
+ element._value = null;
+ });
+ await expect(page.locator('input')).toHaveValue('');
+ });
+ });
});
diff --git a/packages/components/src/components/input-date/shadow.tsx b/packages/components/src/components/input-date/shadow.tsx
index 34d316f6b7..adc37e2f8d 100644
--- a/packages/components/src/components/input-date/shadow.tsx
+++ b/packages/components/src/components/input-date/shadow.tsx
@@ -42,14 +42,16 @@ export class KolInputDate implements InputDateAPI, FocusableElement {
@Element() private readonly host?: HTMLKolInputDateElement;
private inputRef?: HTMLInputElement;
+ @State() private _initialValueType: 'Date' | 'String' | null = null;
+
private readonly catchRef = (ref?: HTMLInputElement) => {
this.inputRef = ref;
};
@Method()
// eslint-disable-next-line @typescript-eslint/require-await
- public async getValue(): Promise {
- return this.inputRef?.value;
+ public async getValue(): Promise {
+ return this.inputRef && this.remapValue(this.inputRef?.value);
}
/**
@@ -82,6 +84,31 @@ export class KolInputDate implements InputDateAPI, FocusableElement {
}
}
+ private setInitialValueType(value: Iso8601 | Date | null) {
+ if (value instanceof Date) {
+ this._initialValueType = 'Date';
+ } else if (typeof value === 'string') {
+ this._initialValueType = 'String';
+ } else {
+ this._initialValueType = null;
+ }
+ }
+ private remapValue(newValue: string): Date | string {
+ return this._initialValueType === 'Date' ? new Date(newValue) : newValue;
+ }
+
+ private readonly onChange = (event: Event) => {
+ const newValue = (event.target as HTMLInputElement).value;
+ const remappedValue = this.remapValue(newValue);
+ this.controller.onFacade.onChange(event, remappedValue);
+ };
+
+ private readonly onInput = (event: Event) => {
+ const newValue = (event.target as HTMLInputElement).value;
+ const remappedValue = this.remapValue(newValue);
+ this.controller.onFacade.onInput(event, true, remappedValue);
+ };
+
private readonly onKeyDown = (event: KeyboardEvent) => {
if (event.code === 'Enter' || event.code === 'NumpadEnter') {
propagateSubmitEventToForm({
@@ -157,6 +184,8 @@ export class KolInputDate implements InputDateAPI, FocusableElement {
value={this.state._value || undefined}
{...this.controller.onFacade}
onKeyDown={this.onKeyDown}
+ onChange={this.onChange}
+ onInput={this.onInput}
/>
@@ -455,9 +484,11 @@ export class KolInputDate implements InputDateAPI, FocusableElement {
deprecatedHint('Date type will be removed in v3. Use `Iso8601` instead.');
}
this.controller.validateValueEx(value);
+ if (value !== undefined) this.setInitialValueType(value);
}
public componentWillLoad(): void {
+ if (this._value !== undefined) this.setInitialValueType(this._value);
this._alert = this._alert === true;
this._touched = this._touched === true;
this.controller.componentWillLoad();
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index dd672e0d09..d3813d069a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -3566,6 +3566,15 @@ packages:
rollup:
optional: true
+ '@rollup/plugin-node-resolve@15.3.0':
+ resolution: {integrity: sha512-9eO5McEICxMzJpDW9OnMYSv4Sta3hmt7VtBFz5zR9273suNOydOyq/FrGeGy+KsTRFm8w0SLVhzig2ILFT63Ag==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ rollup: ^2.78.0||^3.0.0||^4.0.0
+ peerDependenciesMeta:
+ rollup:
+ optional: true
+
'@rollup/plugin-replace@5.0.7':
resolution: {integrity: sha512-PqxSfuorkHz/SPpyngLyg5GCEkOcee9M1bkxiVDr41Pd61mqP1PLOoDPbpl44SB2mQGKwV/In74gqQmGITOhEQ==}
engines: {node: '>=14.0.0'}
@@ -4072,6 +4081,9 @@ packages:
'@types/node@22.7.3':
resolution: {integrity: sha512-qXKfhXXqGTyBskvWEzJZPUxSslAiLaB6JGP1ic/XTH9ctGgzdgYguuLP1C601aRTSDNlLb0jbKqXjZ48GNraSA==}
+ '@types/node@22.7.5':
+ resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==}
+
'@types/normalize-package-data@2.4.4':
resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==}
@@ -12712,6 +12724,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@babel/helper-create-class-features-plugin@7.25.4(@babel/core@7.24.7)':
+ dependencies:
+ '@babel/core': 7.24.7
+ '@babel/helper-annotate-as-pure': 7.24.7
+ '@babel/helper-member-expression-to-functions': 7.24.8
+ '@babel/helper-optimise-call-expression': 7.24.7
+ '@babel/helper-replace-supers': 7.25.0(@babel/core@7.24.7)
+ '@babel/helper-skip-transparent-expression-wrappers': 7.24.7
+ '@babel/traverse': 7.25.6
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/helper-create-class-features-plugin@7.25.4(@babel/core@7.25.2)':
dependencies:
'@babel/core': 7.25.2
@@ -16303,6 +16328,10 @@ snapshots:
dependencies:
undici-types: 6.19.8
+ '@types/node@22.7.5':
+ dependencies:
+ undici-types: 6.19.8
+
'@types/normalize-package-data@2.4.4': {}
'@types/parse-json@4.0.2': {}
@@ -16768,7 +16797,7 @@ snapshots:
webpack: 5.95.0(@swc/core@1.5.28)(webpack-cli@5.1.4)
webpack-cli: 5.1.4(webpack@5.95.0)
- '@webpack-cli/info@1.5.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0))':
+ '@webpack-cli/info@1.5.0(webpack-cli@4.10.0)':
dependencies:
envinfo: 7.14.0
webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0)
@@ -16778,7 +16807,7 @@ snapshots:
webpack: 5.95.0(@swc/core@1.5.28)(webpack-cli@5.1.4)
webpack-cli: 5.1.4(webpack@5.95.0)
- '@webpack-cli/serve@1.7.0(webpack-cli@4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0))(webpack-dev-server@4.15.2(webpack-cli@4.10.0)(webpack@5.91.0))':
+ '@webpack-cli/serve@1.7.0(webpack-cli@4.10.0)(webpack-dev-server@4.15.2)':
dependencies:
webpack-cli: 4.10.0(webpack-dev-server@4.15.2)(webpack@5.91.0)
optionalDependencies:
@@ -20322,7 +20351,7 @@ snapshots:
init-package-json@6.0.3:
dependencies:
'@npmcli/package-json': 5.2.0
- npm-package-arg: 11.0.2
+ npm-package-arg: 11.0.3
promzard: 1.0.2
read: 3.0.1
semver: 7.6.3
@@ -21554,7 +21583,7 @@ snapshots:
libnpmaccess@8.0.6:
dependencies:
- npm-package-arg: 11.0.2
+ npm-package-arg: 11.0.3
npm-registry-fetch: 17.1.0
transitivePeerDependencies:
- supports-color
@@ -21563,7 +21592,7 @@ snapshots:
dependencies:
ci-info: 4.0.0
normalize-package-data: 6.0.2
- npm-package-arg: 11.0.2
+ npm-package-arg: 11.0.3
npm-registry-fetch: 17.1.0
proc-log: 4.2.0
semver: 7.6.3
@@ -22557,7 +22586,7 @@ snapshots:
minipass: 7.1.2
minipass-fetch: 3.0.5
minizlib: 2.1.2
- npm-package-arg: 11.0.2
+ npm-package-arg: 11.0.3
proc-log: 4.2.0
transitivePeerDependencies:
- supports-color