diff --git a/packages/components/src/components/button/button.tsx b/packages/components/src/components/button/button.tsx
index dce8c505d1..da08b11158 100644
--- a/packages/components/src/components/button/button.tsx
+++ b/packages/components/src/components/button/button.tsx
@@ -17,6 +17,7 @@ import {
Listen,
Element,
Method,
+ Watch,
} from '@stencil/core';
import classNames from 'classnames';
import { hasShadowDom, ScaleIcon, isScaleIcon } from '../../utils/utils';
@@ -40,7 +41,7 @@ export class Button {
/** (optional) Button variant */
@Prop() variant?: string = 'primary';
/** (optional) If `true`, the button is disabled */
- @Prop({ reflect: true }) disabled?: boolean = false;
+ @Prop() disabled?: boolean = false;
/** (optional) Button type */
@Prop() type?: 'reset' | 'submit' | 'button';
/** (optional) The name of the button, submitted as a pair with the button's `value` as part of the form data */
@@ -67,6 +68,11 @@ export class Button {
private focusableElement: HTMLElement;
private fallbackSubmitInputElement: HTMLInputElement;
+ @Watch('disabled')
+ onDisabledChange() {
+ this.syncDisabledAttr();
+ }
+
/**
* Prevent clicks from being emitted from the host
* when the component is `disabled`.
@@ -119,6 +125,7 @@ export class Button {
connectedCallback() {
this.setIconPositionProp();
this.appendEnterKeySubmitFallback();
+ this.syncDisabledAttr();
}
disconnectedCallback() {
@@ -226,7 +233,9 @@ export class Button {
ref={(el) => (this.focusableElement = el)}
class={this.getCssClassMap()}
onClick={this.handleClick}
- disabled={this.disabled}
+ disabled={
+ this.disabled ? true : undefined
+ } /* Use undefined to properly remove the disabled attribute when false*/
type={this.type}
part={basePart}
tabIndex={this.innerTabindex}
@@ -253,4 +262,11 @@ export class Button {
this.disabled && `button--disabled`
);
}
+ private syncDisabledAttr() {
+ if (this.disabled) {
+ this.hostElement.setAttribute('disabled', '');
+ } else {
+ this.hostElement.removeAttribute('disabled');
+ }
+ }
}
diff --git a/packages/components/src/components/date-picker/date-picker.tsx b/packages/components/src/components/date-picker/date-picker.tsx
index ce73c2046c..3bc10f91db 100644
--- a/packages/components/src/components/date-picker/date-picker.tsx
+++ b/packages/components/src/components/date-picker/date-picker.tsx
@@ -248,6 +248,17 @@ export class DatePicker {
input.setAttribute('placeholder', newValue);
}
}
+ /**
+ * Watch `localization` for changes and refresh all DOM bits we set manually
+ * (buttonLabel/title, custom heading, weekday abbreviations, "today" suffix, etc.).
+ */
+ @Watch('localization')
+ onLocalizationChange() {
+ if (this.duetInput && this.localization) {
+ (this.duetInput as any).localization = this.localization;
+ }
+ this.updateDomOnLocalizationChange();
+ }
componentWillLoad() {
if (this.popupTitle !== 'Pick a date') {
@@ -334,53 +345,10 @@ export class DatePicker {
input.setAttribute('aria-invalid', 'true');
}
- // Remove existing
with `{Month} {Year}` text
- const dialog = this.hostElement.querySelector('.duet-date__dialog');
- let duetHeadingId: string = '';
- if (dialog) {
- duetHeadingId = dialog.getAttribute('aria-labelledby');
- if (duetHeadingId) {
- const duetHeading = this.hostElement.querySelector(`#${duetHeadingId}`);
- if (duetHeading) {
- duetHeading.parentElement.removeChild(duetHeading);
- }
- }
- }
-
- // Add custom heading
- const dialogContent = this.hostElement.querySelector(
- '.duet-date__dialog-content'
- );
- if (dialogContent) {
- const calendarHeading =
- this.localization?.calendarHeading || this.popupTitle || 'Pick a date';
- const heading = document.createElement('h2');
- heading.id = duetHeadingId; // link to .duet-date__dialog[aria-labelledby]
- heading.className = 'scale-date-picker__popup-heading';
- heading.innerHTML = calendarHeading;
- dialogContent.insertBefore(heading, dialogContent.firstChild);
- }
-
- // truncate table headings to a single character
- const tableHeadings = this.hostElement.querySelectorAll(
- '.duet-date__table-header span[aria-hidden="true"]'
- );
- if (tableHeadings) {
- Array.from(tableHeadings).forEach(
- (item) => (item.innerHTML = item.innerHTML[0])
- );
- }
-
- const today = this.hostElement.querySelector(
- '.duet-date__day.is-today span.duet-date__vhidden'
- );
- if (today) {
- today.innerHTML = `${today.innerHTML}, ${
- this.localization?.today || 'today'
- }`;
- }
-
this.adjustButtonsLabelsForA11y();
+
+ // Initialize all localized bits
+ this.updateDomOnLocalizationChange();
}
componentDidRender() {
@@ -508,4 +476,86 @@ export class DatePicker {
);
}
+ private updateDomOnLocalizationChange = () => {
+ // Remove Duet’s default and ensure our custom heading exists/updates
+ const dialog = this.hostElement.querySelector('.duet-date__dialog');
+ const dialogContent = this.hostElement.querySelector(
+ '.duet-date__dialog-content'
+ );
+ if (dialog && dialogContent) {
+ const duetHeadingId = dialog.getAttribute('aria-labelledby') || '';
+ if (duetHeadingId) {
+ const duetHeading = this.hostElement.querySelector(
+ '#' + duetHeadingId
+ );
+ if (duetHeading && duetHeading.parentElement) {
+ duetHeading.parentElement.removeChild(duetHeading);
+ }
+ }
+ const calendarHeading =
+ this.localization?.calendarHeading || this.popupTitle || 'Pick a date';
+ let heading = this.hostElement.querySelector(
+ '.scale-date-picker__popup-heading'
+ );
+ if (!heading) {
+ heading = document.createElement('h2');
+ if (duetHeadingId) {
+ heading.id = duetHeadingId;
+ heading.className = 'scale-date-picker__popup-heading';
+ dialogContent.insertBefore(heading, dialogContent.firstChild);
+ }
+ }
+ heading.textContent = calendarHeading;
+ }
+
+ // Toggle button (buttonLabel / title / aria-label)
+ const toggleBtn =
+ this.hostElement.querySelector('.duet-date__toggle');
+ const btnLabel = (this.localization as any)?.buttonLabel;
+ if (toggleBtn && btnLabel) {
+ toggleBtn.setAttribute('title', btnLabel);
+ toggleBtn.setAttribute('aria-label', btnLabel);
+ }
+
+ // Truncate weekday headings to one character
+ // Update weekday headings based on current localization
+ const short =
+ (this.localization as any)?.weekdays?.shorthand || // Duet format
+ (this.localization as any)?.dayNamesShort || // alternative
+ (this.localization as any)?.dayNames?.map((d: string) => d.slice(0, 1)) ||
+ [];
+
+ // Adjust for firstDayOfWeek (Duet default = Monday = 1)
+ const start = Number.isInteger(this.firstDayOfWeek)
+ ? Number(this.firstDayOfWeek)
+ : 1;
+
+ const ordered =
+ short.length === 7
+ ? [...short.slice(start), ...short.slice(0, start)]
+ : short;
+
+ // Apply the correct short labels to the DOM
+ const tableHeadings = Array.from(
+ this.hostElement.querySelectorAll(
+ '.duet-date__table-header span[aria-hidden="true"]'
+ )
+ );
+
+ tableHeadings.forEach((el, i) => {
+ const label = ordered[i];
+ // Use the first character if full names are provided
+ el.textContent = label ? label[0] : (el.textContent || '').slice(0, 1);
+ });
+
+ // "today" visually hidden text
+ const today = this.hostElement.querySelector(
+ '.duet-date__day.is-today span.duet-date__vhidden'
+ );
+ if (today) {
+ today.innerHTML = `${today.innerHTML}, ${
+ this.localization?.today || 'today'
+ }`;
+ }
+ };
}