From b288e306fc57250d5985b8cba79b74bd12ab5660 Mon Sep 17 00:00:00 2001 From: Ricardo Hauschild Date: Thu, 5 Mar 2026 08:56:41 +0100 Subject: [PATCH 1/9] feat: textarea/Datepicker updates --- .../components/date-picker/date-picker.css | 13 +++ .../components/date-picker/date-picker.tsx | 4 + .../src/components/textarea/textarea.css | 14 +++ .../src/components/textarea/textarea.e2e.ts | 17 ++++ .../src/components/textarea/textarea.spec.ts | 20 +++++ .../src/components/textarea/textarea.tsx | 3 + packages/components/src/html/date-picker.html | 33 +++++++ packages/components/src/html/textarea.html | 88 +++++++++++++++++++ 8 files changed, 192 insertions(+) create mode 100644 packages/components/src/html/textarea.html diff --git a/packages/components/src/components/date-picker/date-picker.css b/packages/components/src/components/date-picker/date-picker.css index c03c70d843..718cd7de69 100644 --- a/packages/components/src/components/date-picker/date-picker.css +++ b/packages/components/src/components/date-picker/date-picker.css @@ -148,6 +148,19 @@ duet-date-picker .duet-date__toggle:active scale-icon-content-calendar { overflow: hidden; } +/* visually hidden label via descendant selector (getCssClassMap) */ +.scale-date-picker--hide-label .date-picker__label { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; + white-space: nowrap; +} + duet-date-picker .duet-date__input::placeholder { visibility: hidden; color: transparent; diff --git a/packages/components/src/components/date-picker/date-picker.tsx b/packages/components/src/components/date-picker/date-picker.tsx index f78d6e0ee9..af103f97b1 100644 --- a/packages/components/src/components/date-picker/date-picker.tsx +++ b/packages/components/src/components/date-picker/date-picker.tsx @@ -140,6 +140,9 @@ export class DatePicker { /** (optional) Label */ @Prop() label: string = ''; + /** (optional) Visually hide the label */ + @Prop() hideLabel?: boolean; + /** (optional) Injected CSS styles */ @Prop() styles?: string; @@ -416,6 +419,7 @@ export class DatePicker { From b7b7362297d4e2648cfe0028dc37383a93e1d76a Mon Sep 17 00:00:00 2001 From: Ricardo Hauschild Date: Wed, 11 Mar 2026 14:22:24 +0100 Subject: [PATCH 6/9] chore: update based on review comments --- .../components/date-picker/date-picker.tsx | 2 +- .../src/components/date-picker/readme.md | 49 +++++++++--------- .../src/components/textarea/readme.md | 51 ++++++++++--------- .../src/components/textarea/textarea.e2e.ts | 6 +-- .../src/components/textarea/textarea.spec.ts | 13 +++-- .../src/components/textarea/textarea.tsx | 6 +-- packages/components/src/html/date-picker.html | 4 +- packages/components/src/html/textarea.html | 4 +- 8 files changed, 67 insertions(+), 68 deletions(-) diff --git a/packages/components/src/components/date-picker/date-picker.tsx b/packages/components/src/components/date-picker/date-picker.tsx index 4da3fc6940..914685622a 100644 --- a/packages/components/src/components/date-picker/date-picker.tsx +++ b/packages/components/src/components/date-picker/date-picker.tsx @@ -419,7 +419,7 @@ export class DatePicker {
**[DEPRECATED]** in v3 in favor of localization.calendarHeading

| `string` | `'Pick a date'` | -| `required` | `required` | Should the input be marked as required? | `boolean` | `false` | -| `size` | `size` | **[DEPRECATED]**

| `string` | `undefined` | -| `status` | `status` | **[DEPRECATED]** - invalid should replace status

| `string` | `''` | -| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` | -| `value` | `value` | Date value. Must be in IS0-8601 format: YYYY-MM-DD. | `string` | `''` | -| `variant` | `variant` | | `"danger" \| "informational" \| "success" \| "warning"` | `'informational'` | +| Property | Attribute | Description | Type | Default | +| ------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ----------------- | +| `ariaDetailsId` | `aria-details-id` | (optional) id or space separated list of ids of elements that provide or link to additional related information. | `string` | `undefined` | +| `dateAdapter` | `date-adapter` | Date adapter, for custom parsing/formatting. Must be object with a `parse` function which accepts a `string` and returns a `Date`, and a `format` function which accepts a `Date` and returns a `string`. Default is IS0-8601 parsing and formatting. | `any` | `undefined` | +| `direction` | `direction` | Forces the opening direction of the calendar modal to be always left or right. This setting can be useful when the input is smaller than the opening date picker would be as by default the picker always opens towards right. | `"left" \| "right"` | `'right'` | +| `disabled` | `disabled` | Makes the date picker input component disabled. This prevents users from being able to interact with the input, and conveys its inactive state to assistive technologies. | `boolean` | `false` | +| `firstDayOfWeek` | `first-day-of-week` | Which day is considered first day of the week? `0` for Sunday, `1` for Monday, etc. Default is Monday. | `any` | `undefined` | +| `helperText` | `helper-text` | (optional) Helper text | `string` | `''` | +| `hideLabelVisually` | `hide-label-visually` | (optional) Visually hide the label | `boolean` | `undefined` | +| `identifier` | `identifier` | Adds a unique identifier for the date picker input. Use this instead of html `id` attribute. | `string` | `undefined` | +| `innerRole` | `inner-role` | Defines a specific role attribute for the date picker input. | `string` | `undefined` | +| `invalid` | `invalid` | (optional) invalid status | `boolean` | `undefined` | +| `label` | `label` | (optional) Label | `string` | `''` | +| `localization` | `localization` | Button labels, day names, month names, etc, used for localization. Default is English. | `DuetLocalizedText & { today: string; }` | `undefined` | +| `max` | `max` | Maximum date allowed to be picked. Must be in IS0-8601 format: YYYY-MM-DD. This setting can be used alone or together with the min property. | `string` | `''` | +| `min` | `min` | Minimum date allowed to be picked. Must be in IS0-8601 format: YYYY-MM-DD. This setting can be used alone or together with the max property. | `string` | `''` | +| `name` | `name` | Name of the date picker input. | `string` | `'date'` | +| `placeholder` | `placeholder` | (optional) Input place holder | `string` | `''` | +| `popupTitle` | `popup-title` | **[DEPRECATED]** in v3 in favor of localization.calendarHeading

| `string` | `'Pick a date'` | +| `required` | `required` | Should the input be marked as required? | `boolean` | `false` | +| `size` | `size` | **[DEPRECATED]**

| `string` | `undefined` | +| `status` | `status` | **[DEPRECATED]** - invalid should replace status

| `string` | `''` | +| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` | +| `value` | `value` | Date value. Must be in IS0-8601 format: YYYY-MM-DD. | `string` | `''` | +| `variant` | `variant` | | `"danger" \| "informational" \| "success" \| "warning"` | `'informational'` | ## Events diff --git a/packages/components/src/components/textarea/readme.md b/packages/components/src/components/textarea/readme.md index 5f79762783..f1dedd5066 100644 --- a/packages/components/src/components/textarea/readme.md +++ b/packages/components/src/components/textarea/readme.md @@ -7,31 +7,32 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ---------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ----------------- | -| `ariaDetailsId` | `aria-details-id` | (optional) id or space separated list of ids of elements that provide or link to additional related information. | `string` | `undefined` | -| `cols` | `cols` | (optional) textarea column | `number` | `undefined` | -| `counter` | `counter` | (optional) Input counter | `boolean` | `undefined` | -| `dataQa` | `data-qa` | (optional) data-qa attribute for e2e testing | `string` | `undefined` | -| `disabled` | `disabled` | (optional) Input disabled | `boolean` | `undefined` | -| `helperText` | `helper-text` | (optional) Input helper text | `string` | `''` | -| `inputAutofocus` | `input-autofocus` | (optional) the input should automatically get focus when the page loads. | `boolean` | `undefined` | -| `inputId` | `input-id` | (optional) Input checkbox id | `string` | `undefined` | -| `invalid` | `invalid` | (optional) Input status | `boolean` | `false` | -| `label` | `label` | (optional) Input label | `string` | `''` | -| `maxLength` | `max-length` | (optional) Input max length | `number` | `undefined` | -| `minLength` | `min-length` | (optional) Input min length | `number` | `undefined` | -| `name` | `name` | (optional) Input name | `string` | `''` | -| `placeholder` | `placeholder` | (optional) Input placeHolder | `string` | `''` | -| `readonly` | `readonly` | (optional) Input readonly | `boolean` | `undefined` | -| `required` | `required` | (optional) Input required | `boolean` | `undefined` | -| `resize` | `resize` | (optional) textarea resize | `"horizontal" \| "none" \| "unset" \| "vertical"` | `undefined` | -| `rows` | `rows` | (optional) textarea row | `number` | `undefined` | -| `status` | `status` | **[DEPRECATED]** - invalid should replace status

| `string` | `''` | -| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` | -| `transparent` | `transparent` | (optional) input background transparent | `boolean` | `undefined` | -| `value` | `value` | (optional) Input value | `number \| string` | `''` | -| `variant` | `variant` | (optional) Variant | `"danger" \| "informational" \| "success" \| "warning"` | `'informational'` | +| Property | Attribute | Description | Type | Default | +| ------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ----------------- | +| `ariaDetailsId` | `aria-details-id` | (optional) id or space separated list of ids of elements that provide or link to additional related information. | `string` | `undefined` | +| `cols` | `cols` | (optional) textarea column | `number` | `undefined` | +| `counter` | `counter` | (optional) Input counter | `boolean` | `undefined` | +| `dataQa` | `data-qa` | (optional) data-qa attribute for e2e testing | `string` | `undefined` | +| `disabled` | `disabled` | (optional) Input disabled | `boolean` | `undefined` | +| `helperText` | `helper-text` | (optional) Input helper text | `string` | `''` | +| `hideLabelVisually` | `hide-label-visually` | (optional) Visually hide the label | `boolean` | `undefined` | +| `inputAutofocus` | `input-autofocus` | (optional) the input should automatically get focus when the page loads. | `boolean` | `undefined` | +| `inputId` | `input-id` | (optional) Input checkbox id | `string` | `undefined` | +| `invalid` | `invalid` | (optional) Input status | `boolean` | `false` | +| `label` | `label` | (optional) Input label | `string` | `''` | +| `maxLength` | `max-length` | (optional) Input max length | `number` | `undefined` | +| `minLength` | `min-length` | (optional) Input min length | `number` | `undefined` | +| `name` | `name` | (optional) Input name | `string` | `''` | +| `placeholder` | `placeholder` | (optional) Input placeHolder | `string` | `''` | +| `readonly` | `readonly` | (optional) Input readonly | `boolean` | `undefined` | +| `required` | `required` | (optional) Input required | `boolean` | `undefined` | +| `resize` | `resize` | (optional) textarea resize | `"horizontal" \| "none" \| "unset" \| "vertical"` | `undefined` | +| `rows` | `rows` | (optional) textarea row | `number` | `undefined` | +| `status` | `status` | **[DEPRECATED]** - invalid should replace status

| `string` | `''` | +| `styles` | `styles` | (optional) Injected CSS styles | `string` | `undefined` | +| `transparent` | `transparent` | (optional) input background transparent | `boolean` | `undefined` | +| `value` | `value` | (optional) Input value | `number \| string` | `''` | +| `variant` | `variant` | (optional) Variant | `"danger" \| "informational" \| "success" \| "warning"` | `'informational'` | ## Events diff --git a/packages/components/src/components/textarea/textarea.e2e.ts b/packages/components/src/components/textarea/textarea.e2e.ts index 2b6e79bb70..39edc017c4 100644 --- a/packages/components/src/components/textarea/textarea.e2e.ts +++ b/packages/components/src/components/textarea/textarea.e2e.ts @@ -19,17 +19,17 @@ describe('scale-textarea', () => { expect(element).toHaveClass('hydrated'); }); - it('should have textarea--hide-label class when hide-label is set', async () => { + it('should have textarea--hide-label class when hide-label-visually is set', async () => { const page = await newE2EPage(); await page.setContent( - '' + '' ); const wrapper = await page.find('scale-textarea .textarea'); expect(wrapper).toHaveClass('textarea--hide-label'); }); // optional - it('should NOT have textarea--hide-label class when hide-label is not set', async () => { + it('should NOT have textarea--hide-label class when hide-label-visually is not set', async () => { const page = await newE2EPage(); await page.setContent(''); const wrapper = await page.find('scale-textarea .textarea'); diff --git a/packages/components/src/components/textarea/textarea.spec.ts b/packages/components/src/components/textarea/textarea.spec.ts index 3e8ac153a7..173698418f 100644 --- a/packages/components/src/components/textarea/textarea.spec.ts +++ b/packages/components/src/components/textarea/textarea.spec.ts @@ -44,13 +44,13 @@ describe('Textarea', () => { expect(page.root).toMatchSnapshot(); }); - it('should match snapshot -> hide-label', async () => { + it('should match snapshot -> hide-label-visually', async () => { const page = await newSpecPage({ components: [Textarea], html: ` `, }); @@ -80,7 +80,7 @@ describe('Textarea', () => { element.value = 'value'; expect(element.getCssClassMap()).toContain('animated'); - element.hideLabel = true; + element.hideLabelVisually = true; expect(element.getCssClassMap()).toContain('textarea--hide-label'); }); }); @@ -96,8 +96,7 @@ describe('Textarea', () => { expect(page.rootInstance.invalid).toBe(false); expect(page.rootInstance.placeholder).toBe(''); expect(page.rootInstance.value).toBe(''); - - expect(page.rootInstance.hideLabel).toBeUndefined(); + expect(page.rootInstance.hideLabelVisually).toBeUndefined(); }); it('check props being set', async () => { @@ -120,7 +119,7 @@ describe('Textarea', () => { page.root.value = 'value'; page.root.inputId = 'inputId'; page.root.styles = 'background : red'; - page.root.hideLabel = true; + page.root.hideLabelVisually = true; await page.waitForChanges(); expect(page.rootInstance.name).toBe('name'); expect(page.rootInstance.label).toBe('label'); @@ -137,7 +136,7 @@ describe('Textarea', () => { expect(page.rootInstance.transparent).toBe(true); expect(page.rootInstance.inputId).toBe('inputId'); expect(page.rootInstance.styles).toBe('background : red'); - expect(page.rootInstance.hideLabel).toBe(true); + expect(page.rootInstance.hideLabelVisually).toBe(true); }); }); describe('functions', () => { diff --git a/packages/components/src/components/textarea/textarea.tsx b/packages/components/src/components/textarea/textarea.tsx index 1c2aabef76..32ee9a0412 100644 --- a/packages/components/src/components/textarea/textarea.tsx +++ b/packages/components/src/components/textarea/textarea.tsx @@ -40,7 +40,7 @@ export class Textarea { /** (optional) Input label */ @Prop() label: string = ''; /** (optional) Visually hide the label */ - @Prop() hideLabel?: boolean; + @Prop() hideLabelVisually?: boolean; /** (optional) textarea row */ @Prop() rows?: number; /** (optional) textarea column */ @@ -168,7 +168,7 @@ export class Textarea { this.status === 'error' || this.invalid ? { 'aria-invalid': 'true' } : {}; const helperTextId = `helper-message-${this.internalId}`; const describedBy = this.helperText ? helperTextId : this.ariaDetailsId; - const ariaDescribedByAttr = { 'aria-describedBy': describedBy }; + const ariaDescribedByAttr = { 'aria-describedby': describedBy }; const readonlyAttr = this.readonly ? { readonly: 'readonly' } : {}; return ( @@ -244,7 +244,7 @@ export class Textarea { getCssClassMap() { return classNames( 'textarea', - this.hideLabel && 'textarea--hide-label', + this.hideLabelVisually && 'textarea--hide-label', this.hasFocus && 'textarea--has-focus', this.resize && `textarea--resize-${this.resize}`, this.disabled && `textarea--disabled`, diff --git a/packages/components/src/html/date-picker.html b/packages/components/src/html/date-picker.html index 520f5a7522..c81e22d594 100644 --- a/packages/components/src/html/date-picker.html +++ b/packages/components/src/html/date-picker.html @@ -62,7 +62,7 @@

Hidden Label

name="date-hidden-label" value="2021-03-24" placeholder="DD-MM-YYYY" - hide-label + hide-label-visually >
@@ -151,7 +151,6 @@

Hidden Label

picker.setFocus(); }); - // Listen for when date is selected picker.addEventListener('scale-change', function (e) { console.log('scale-change selected date', e.detail.valueAsDate); }); @@ -163,7 +162,6 @@

Hidden Label

console.log('scale-blur', e); }); - // optional: apply same adapter/localization to hidden-label example const hiddenPicker = document.getElementById('picker-hidden-label'); hiddenPicker.dateAdapter = picker.dateAdapter; hiddenPicker.localization = picker.localization; diff --git a/packages/components/src/html/textarea.html b/packages/components/src/html/textarea.html index 2ccec39937..b24c2f5ccb 100644 --- a/packages/components/src/html/textarea.html +++ b/packages/components/src/html/textarea.html @@ -54,7 +54,7 @@

Textarea – Hidden Label

placeholder="Type here..." helper-text="Label is visually hidden but available for screen readers" aria-details-id="ta-extra" - hide-label + hide-label-visually rows="4" > @@ -85,4 +85,4 @@

Textarea – Hidden Label

}); - \ No newline at end of file + From 2d9c0b59c92460c39858620d9122622ce615bcf6 Mon Sep 17 00:00:00 2001 From: Ricardo Hauschild Date: Thu, 12 Mar 2026 13:00:15 +0100 Subject: [PATCH 7/9] chore: remove redundancy --- .../src/components/date-picker/date-picker.css | 13 ------------- .../components/src/components/textarea/textarea.css | 13 ------------- 2 files changed, 26 deletions(-) diff --git a/packages/components/src/components/date-picker/date-picker.css b/packages/components/src/components/date-picker/date-picker.css index 3c9089cc11..1646bc0874 100644 --- a/packages/components/src/components/date-picker/date-picker.css +++ b/packages/components/src/components/date-picker/date-picker.css @@ -148,19 +148,6 @@ duet-date-picker .duet-date__toggle:active scale-icon-content-calendar { overflow: hidden; } -/* visually hidden label via descendant selector (getCssClassMap) */ -.scale-date-picker--hide-label .date-picker__label { - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; - white-space: nowrap; -} - /* when the label is visually hidden, reduce top padding on the input to remove extra space for the floating label */ .scale-date-picker--hide-label duet-date-picker .duet-date__input { padding-top: 0.5rem; diff --git a/packages/components/src/components/textarea/textarea.css b/packages/components/src/components/textarea/textarea.css index 93c1e761cd..0f8c645d2e 100644 --- a/packages/components/src/components/textarea/textarea.css +++ b/packages/components/src/components/textarea/textarea.css @@ -160,19 +160,6 @@ scale-textarea { ); } -/* visually hidden label via descendant selector (getCssClassMap) */ -.textarea--hide-label .textarea__label { - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; - white-space: nowrap; -} - .textarea--hide-label .textarea__wrapper .textarea__control { margin-top: 0; } From e00a489bee67b54959dd3c045c5d312302675f20 Mon Sep 17 00:00:00 2001 From: Ricardo-Tele Date: Wed, 8 Apr 2026 11:33:13 +0200 Subject: [PATCH 8/9] Update packages/components/src/components/date-picker/date-picker.css Co-authored-by: Amir Baghdoust --- .../components/src/components/date-picker/date-picker.css | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/components/src/components/date-picker/date-picker.css b/packages/components/src/components/date-picker/date-picker.css index 1646bc0874..c63035d0a4 100644 --- a/packages/components/src/components/date-picker/date-picker.css +++ b/packages/components/src/components/date-picker/date-picker.css @@ -148,9 +148,12 @@ duet-date-picker .duet-date__toggle:active scale-icon-content-calendar { overflow: hidden; } -/* when the label is visually hidden, reduce top padding on the input to remove extra space for the floating label */ +.scale-date-picker--hide-label .date-picker__label { + visibility: hidden; +} + .scale-date-picker--hide-label duet-date-picker .duet-date__input { - padding-top: 0.5rem; + padding-top: var(--telekom-spacing-composition-space-04); } duet-date-picker .duet-date__input::placeholder { visibility: hidden; From f6976d39ec9ec1f9a58f88a2c4791390748e60d0 Mon Sep 17 00:00:00 2001 From: Amir Baghdoust Date: Wed, 8 Apr 2026 22:09:06 +0200 Subject: [PATCH 9/9] fix(textarea): adds css for hiding label visibility when textarea is hidden --- packages/components/src/components/textarea/textarea.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/components/src/components/textarea/textarea.css b/packages/components/src/components/textarea/textarea.css index 0f8c645d2e..d6aac0bdc9 100644 --- a/packages/components/src/components/textarea/textarea.css +++ b/packages/components/src/components/textarea/textarea.css @@ -160,8 +160,12 @@ scale-textarea { ); } +.textarea--hide-label .textarea__label { + visibility: hidden; +} + .textarea--hide-label .textarea__wrapper .textarea__control { - margin-top: 0; + padding-top: var(--telekom-spacing-composition-space-02); } .textarea--status-error .textarea__wrapper { border: var(--border-error);