diff --git a/package-lock.json b/package-lock.json
index 49f572437..5dcf480b8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,8 +15,6 @@
"@material/card": "^14.0.0",
"@material/checkbox": "^14.0.0",
"@material/chips": "^14.0.0",
- "@material/density": "^14.0.0",
- "@material/dom": "^14.0.0",
"@material/drawer": "^14.0.0",
"@material/elevation": "^14.0.0",
"@material/feature-targeting": "^14.0.0",
@@ -27,7 +25,6 @@
"@material/select": "^14.0.0",
"@material/shape": "^14.0.0",
"@material/theme": "^14.0.0",
- "@material/touch-target": "^14.0.0",
"@material/typography": "^14.0.0",
"@tylertech/forge-core": "^2.3.0",
"@tylertech/tyler-icons": "^1.12.0",
diff --git a/package.json b/package.json
index fd63d4481..3a6a96d85 100644
--- a/package.json
+++ b/package.json
@@ -42,8 +42,6 @@
"@material/card": "^14.0.0",
"@material/checkbox": "^14.0.0",
"@material/chips": "^14.0.0",
- "@material/density": "^14.0.0",
- "@material/dom": "^14.0.0",
"@material/drawer": "^14.0.0",
"@material/elevation": "^14.0.0",
"@material/feature-targeting": "^14.0.0",
@@ -54,7 +52,6 @@
"@material/select": "^14.0.0",
"@material/shape": "^14.0.0",
"@material/theme": "^14.0.0",
- "@material/touch-target": "^14.0.0",
"@material/typography": "^14.0.0",
"@tylertech/forge-core": "^2.3.0",
"@tylertech/tyler-icons": "^1.12.0",
diff --git a/src/dev/pages/button-toggle/button-toggle.ejs b/src/dev/pages/button-toggle/button-toggle.ejs
index 0f427f013..a1e430756 100644
--- a/src/dev/pages/button-toggle/button-toggle.ejs
+++ b/src/dev/pages/button-toggle/button-toggle.ejs
@@ -126,6 +126,25 @@
+
+
+
+ Legacy (custom)
+
+
+
+ By email
+
+
+
+ By mail
+
+
+
+ By phone
+
+
+
diff --git a/src/dev/pages/button-toggle/button-toggle.html b/src/dev/pages/button-toggle/button-toggle.html
index 83e3bacc0..cd3e02041 100644
--- a/src/dev/pages/button-toggle/button-toggle.html
+++ b/src/dev/pages/button-toggle/button-toggle.html
@@ -8,7 +8,7 @@
type: 'select',
label: 'Theme',
id: 'opt-theme',
- defaultValue: 'primary',
+ defaultValue: 'tertiary',
options: [
{ label: 'Primary', value: 'primary' },
{ label: 'Secondary', value: 'secondary' },
diff --git a/src/dev/pages/button-toggle/button-toggle.scss b/src/dev/pages/button-toggle/button-toggle.scss
index 6d4cc2d63..d5c7aaa3b 100644
--- a/src/dev/pages/button-toggle/button-toggle.scss
+++ b/src/dev/pages/button-toggle/button-toggle.scss
@@ -1,4 +1,6 @@
@use '../../../lib/button-toggle/button-toggle-group';
+@use '../../../lib/button-toggle/button-toggle';
+@use '../../../lib/core/styles/theme';
#toolbar-card {
display: inline-block;
@@ -16,4 +18,39 @@
height: auto;
margin-block: 4px;
}
+}
+
+.legacy-button-toggle-group {
+ @include button-toggle-group.provide-theme((
+ gap: 1px,
+ padding: 0
+ ));
+
+ forge-button-toggle {
+ @include button-toggle.provide-theme((
+ selected-color: theme.variable(on-primary),
+ selected-background: theme.variable(primary),
+ focus-indicator-offset: 2px
+ ));
+
+ &:first-of-type {
+ @include button-toggle.provide-theme((
+ shape-start-end: 0,
+ shape-end-end: 0
+ ));
+ }
+
+ &:not(:first-of-type):not(:last-of-type) {
+ @include button-toggle.provide-theme((
+ shape: 0
+ ));
+ }
+
+ &:last-of-type {
+ @include button-toggle.provide-theme((
+ shape-start-start: 0,
+ shape-end-start: 0
+ ));
+ }
+ }
}
\ No newline at end of file
diff --git a/src/dev/pages/theme/theme.ts b/src/dev/pages/theme/theme.ts
index ef8623838..ad9ba5b50 100644
--- a/src/dev/pages/theme/theme.ts
+++ b/src/dev/pages/theme/theme.ts
@@ -8,7 +8,9 @@ interface ISwatchGroup {
interface ISwatch {
text?: string;
- background: string;
+ background?: string;
+ border?: string;
+ noBorder?: boolean;
foreground?: string;
}
@@ -105,17 +107,19 @@ const SWATCH_GROUPS: ISwatchGroup[] = [
{
header: 'Text',
swatches: [
- { text: 'High', background: 'text-high', foreground: 'text-high-inverse' },
- { text: 'Medium', background: 'text-medium', foreground: 'text-high-inverse' },
- { text: 'Low', background: 'text-low', foreground: 'text-high' },
- { text: 'Lowest', background: 'text-lowest', foreground: 'text-high' }
+ { text: 'High (87%)', foreground: 'text-high', noBorder: true },
+ { text: 'Medium (60%)', foreground: 'text-medium', noBorder: true },
+ { text: 'Low (38%)', foreground: 'text-low', noBorder: true },
+ { text: 'Lowest (12%)', foreground: 'text-lowest', noBorder: true }
]
},
{
header: 'Utilities',
swatches: [
- { text: 'Outline', background: 'outline', foreground: 'text-high' },
- { text: 'Outline (high)', background: 'outline-high', foreground: 'text-high-inverse' }
+ { text: 'Outline (high)', border: 'outline-high' },
+ { text: 'Outline (medium)', border: 'outline-medium' },
+ { text: 'Outline (low)', border: 'outline-low' },
+ { text: 'Outline', border: 'outline' }
]
}
];
@@ -148,7 +152,16 @@ function createSwatch(config: ISwatch): HTMLElement {
if (config.text) {
swatch.textContent = config.text;
}
- swatch.style.setProperty('background-color', `var(--forge-theme-${config.background})`);
+
+ if (config.border && !config.noBorder) {
+ swatch.style.setProperty('border-color', `var(--forge-theme-${config.border})`);
+ } else if (config.noBorder) {
+ swatch.style.setProperty('border', 'none');
+ }
+
+ if (config.background) {
+ swatch.style.setProperty('background-color', `var(--forge-theme-${config.background})`);
+ }
if (config.foreground) {
swatch.style.setProperty('color', `var(--forge-theme-${config.foreground})`);
diff --git a/src/lib/button-toggle/button-toggle-group/button-toggle-group-constants.ts b/src/lib/button-toggle/button-toggle-group/button-toggle-group-constants.ts
index 8f975d3cb..2fe3a5b88 100644
--- a/src/lib/button-toggle/button-toggle-group/button-toggle-group-constants.ts
+++ b/src/lib/button-toggle/button-toggle-group/button-toggle-group-constants.ts
@@ -1,6 +1,4 @@
import { COMPONENT_NAME_PREFIX, Theme } from '../../constants';
-import { supportsElementInternalsAria } from '../../core';
-import { ARIAAttribute } from '../../core/utils/a11y-utils';
const elementName: keyof HTMLElementTagNameMap = `${COMPONENT_NAME_PREFIX}button-toggle-group`;
@@ -18,8 +16,6 @@ const observedAttributes = {
THEME: 'theme'
};
-const observedAriaAttributes: ARIAAttribute[] = supportsElementInternalsAria() ? [] : ['role', 'aria-label'];
-
const attributes = {
...observedAttributes
};
@@ -40,7 +36,6 @@ const events = {
export const BUTTON_TOGGLE_GROUP_CONSTANTS = {
elementName,
observedAttributes,
- observedAriaAttributes,
attributes,
classes,
selectors,
diff --git a/src/lib/button-toggle/button-toggle-group/button-toggle-group-foundation.ts b/src/lib/button-toggle/button-toggle-group/button-toggle-group-foundation.ts
index 0d55db642..6d25026dc 100644
--- a/src/lib/button-toggle/button-toggle-group/button-toggle-group-foundation.ts
+++ b/src/lib/button-toggle/button-toggle-group/button-toggle-group-foundation.ts
@@ -29,7 +29,7 @@ export class ButtonToggleGroupFoundation implements IButtonToggleGroupFoundation
private _disabled = false;
private _readonly = false;
private _required = false;
- private _theme: ButtonToggleGroupTheme = 'primary';
+ private _theme: ButtonToggleGroupTheme = 'tertiary';
private _selectListener: (evt: CustomEvent) => void;
private _slotListener: () => void;
@@ -233,7 +233,7 @@ export class ButtonToggleGroupFoundation implements IButtonToggleGroupFoundation
public set theme(value: ButtonToggleGroupTheme) {
if (this._theme !== value) {
this._theme = value;
- this._adapter.toggleHostAttribute(BUTTON_TOGGLE_GROUP_CONSTANTS.attributes.THEME, this._theme !== 'primary', this._theme);
+ this._adapter.toggleHostAttribute(BUTTON_TOGGLE_GROUP_CONSTANTS.attributes.THEME, this._theme !== 'tertiary', this._theme);
}
}
}
diff --git a/src/lib/button-toggle/button-toggle-group/button-toggle-group.scss b/src/lib/button-toggle/button-toggle-group/button-toggle-group.scss
index fa1f9fcff..30dc3f828 100644
--- a/src/lib/button-toggle/button-toggle-group/button-toggle-group.scss
+++ b/src/lib/button-toggle/button-toggle-group/button-toggle-group.scss
@@ -116,15 +116,15 @@ $_host-tokens: [display];
::slotted(forge-button-toggle) {
@include button-toggle.provide-theme((
selected-background: #{theme.variable(#{$theme}-container-low)},
- selected-color: #{theme.variable($theme)},
+ selected-color: #{theme.variable(on-#{$theme}-container-low)},
focus-indicator-color: #{theme.variable($theme)}
));
}
}
}
+@include theme(primary);
@include theme(secondary);
-@include theme(tertiary);
@include theme(success);
@include theme(error);
@include theme(warning);
diff --git a/src/lib/button-toggle/button-toggle-group/button-toggle-group.ts b/src/lib/button-toggle/button-toggle-group/button-toggle-group.ts
index b37efe659..f409d66aa 100644
--- a/src/lib/button-toggle/button-toggle-group/button-toggle-group.ts
+++ b/src/lib/button-toggle/button-toggle-group/button-toggle-group.ts
@@ -8,7 +8,7 @@ import { ButtonToggleComponent } from '../button-toggle/button-toggle';
import { ButtonToggleGroupAdapter } from './button-toggle-group-adapter';
import { ButtonToggleGroupTheme, BUTTON_TOGGLE_GROUP_CONSTANTS, IButtonToggleGroupChangeEventData } from './button-toggle-group-constants';
import { ButtonToggleGroupFoundation } from './button-toggle-group-foundation';
-import { getFormState, getFormValue, inputType, observedDefaultAriaAttributes, setDefaultAria } from '../../constants';
+import { getFormState, getFormValue, inputType, setDefaultAria } from '../../constants';
import { FormValue, FormRestoreState, FormRestoreReason } from '../../core/utils/form-utils';
import { IWithDefaultAria, WithDefaultAria } from '../../core/mixins/internals/with-default-aria';
@@ -100,14 +100,9 @@ const BaseButtonToggleGroupClass = WithLabelAwareness(WithFormAssociation(WithFo
})
export class ButtonToggleGroupComponent extends BaseButtonToggleGroupClass implements IButtonToggleGroupComponent {
public static get observedAttributes(): string[] {
- return [
- ...Object.values(BUTTON_TOGGLE_GROUP_CONSTANTS.observedAttributes),
- ...Object.values(BUTTON_TOGGLE_GROUP_CONSTANTS.observedAriaAttributes)
- ];
+ return Object.values(BUTTON_TOGGLE_GROUP_CONSTANTS.observedAttributes);
}
- public readonly [observedDefaultAriaAttributes] = BUTTON_TOGGLE_GROUP_CONSTANTS.observedAriaAttributes;
-
private _foundation: ButtonToggleGroupFoundation;
constructor() {
@@ -118,7 +113,7 @@ export class ButtonToggleGroupComponent extends BaseButtonToggleGroupClass imple
}
public connectedCallback(): void {
- this[setDefaultAria]({ role: 'group' });
+ this[setDefaultAria]({ role: 'group' }, { setAttribute: !this.hasAttribute('role') });
this._foundation.initialize();
}
diff --git a/src/lib/button-toggle/button-toggle.test.ts b/src/lib/button-toggle/button-toggle.test.ts
index e2abbde60..983cdc09a 100644
--- a/src/lib/button-toggle/button-toggle.test.ts
+++ b/src/lib/button-toggle/button-toggle.test.ts
@@ -23,12 +23,12 @@ describe('Button Toggle', () => {
expect(harness.buttonToggles.every(toggle => toggle.getAttribute('aria-pressed') === 'false')).to.be.true;
});
- ['primary', 'secondary', 'danger', 'success', 'warning', 'info'].forEach((theme: ButtonToggleGroupTheme) => {
+ ['primary', 'secondary', 'tertiary', 'danger', 'success', 'warning', 'info'].forEach((theme: ButtonToggleGroupTheme) => {
it(`should be accessible with selected values for theme ${theme}`, async () => {
const harness = await createFixture({ theme, value: 'two' });
- // Primary is the default
- if (theme !== 'primary') {
+ // tertiary is the default
+ if (theme !== 'tertiary') {
expect(harness.element.getAttribute(BUTTON_TOGGLE_GROUP_CONSTANTS.attributes.THEME)).to.equal(theme);
}
diff --git a/src/lib/button-toggle/button-toggle/button-toggle-adapter.ts b/src/lib/button-toggle/button-toggle/button-toggle-adapter.ts
index 0be26ab4d..49391ceab 100644
--- a/src/lib/button-toggle/button-toggle/button-toggle-adapter.ts
+++ b/src/lib/button-toggle/button-toggle/button-toggle-adapter.ts
@@ -25,8 +25,8 @@ export class ButtonToggleAdapter extends BaseAdapter imp
}
public initialize(): void {
+ this._component[setDefaultAria]({ role: 'button' }, { setAttribute: !this._component.hasAttribute('role') });
this._component[setDefaultAria]({
- role: 'button',
ariaPressed: `${!!this._component.selected}`,
ariaDisabled: `${!!this._component.disabled}`
});
@@ -34,11 +34,11 @@ export class ButtonToggleAdapter extends BaseAdapter imp
}
public setSelected(value: boolean): void {
- this._component[setDefaultAria]({ ariaPressed: `${!!value}` }, { overwrite: true });
+ this._component[setDefaultAria]({ ariaPressed: `${!!value}` });
}
public setDisabled(value: boolean): void {
- this._component[setDefaultAria]({ ariaDisabled: `${!!value}` }, { overwrite: true });
+ this._component[setDefaultAria]({ ariaDisabled: `${!!value}` });
this._component[isFocusable] = !value;
this._stateLayerElement.disabled = value;
}
diff --git a/src/lib/button-toggle/button-toggle/button-toggle-constants.ts b/src/lib/button-toggle/button-toggle/button-toggle-constants.ts
index dd8c22238..bfb04f183 100644
--- a/src/lib/button-toggle/button-toggle/button-toggle-constants.ts
+++ b/src/lib/button-toggle/button-toggle/button-toggle-constants.ts
@@ -1,6 +1,4 @@
import { COMPONENT_NAME_PREFIX } from '../../constants';
-import { supportsElementInternalsAria } from '../../core';
-import { ARIAAttribute } from '../../core/utils/a11y-utils';
const elementName: keyof HTMLElementTagNameMap = `${COMPONENT_NAME_PREFIX}button-toggle`;
@@ -12,8 +10,6 @@ const observedAttributes = {
TABINDEX: 'tabindex' // Need this to support the focusable mixin
};
-const observedAriaAttributes: ARIAAttribute[] = supportsElementInternalsAria() ? [] : ['role', 'aria-pressed', 'aria-disabled'];
-
const attributes = {
...observedAttributes
};
@@ -25,7 +21,6 @@ const events = {
export const BUTTON_TOGGLE_CONSTANTS = {
elementName,
observedAttributes,
- observedAriaAttributes,
attributes,
events
};
diff --git a/src/lib/button-toggle/button-toggle/button-toggle.scss b/src/lib/button-toggle/button-toggle/button-toggle.scss
index d1c8399dd..3c6d6bcff 100644
--- a/src/lib/button-toggle/button-toggle/button-toggle.scss
+++ b/src/lib/button-toggle/button-toggle/button-toggle.scss
@@ -39,8 +39,13 @@ $_host-tokens: [display];
forge-focus-indicator {
@include focus-indicator.provide-theme((
- outward-offset: 0px, // Requires unit
- color: #{token(focus-indicator-color)}
+ outward-offset: #{token(focus-indicator-offset)},
+ color: #{token(focus-indicator-color)},
+ shape: #{token(shape)},
+ shape-start-start: #{token(shape-start-start)},
+ shape-start-end: #{token(shape-start-end)},
+ shape-end-start: #{token(shape-end-start)},
+ shape-end-end: #{token(shape-end-end)},
));
}
}
diff --git a/src/lib/button-toggle/button-toggle/button-toggle.ts b/src/lib/button-toggle/button-toggle/button-toggle.ts
index 500d39308..726ae4d8d 100644
--- a/src/lib/button-toggle/button-toggle/button-toggle.ts
+++ b/src/lib/button-toggle/button-toggle/button-toggle.ts
@@ -1,5 +1,5 @@
import { attachShadowTemplate, coerceBoolean, CustomElement, FoundationProperty } from '@tylertech/forge-core';
-import { ExperimentalFocusOptions, observedDefaultAriaAttributes } from '../../constants';
+import { ExperimentalFocusOptions } from '../../constants';
import { IWithFocusable, WithFocusable } from '../../core/mixins/focus/with-focusable';
import { IWithElementInternals, WithElementInternals } from '../../core/mixins/internals/with-element-internals';
import { FocusIndicatorComponent } from '../../focus-indicator';
@@ -95,14 +95,9 @@ const BaseButtonToggleClass = WithDefaultAria(WithElementInternals(WithFocusable
})
export class ButtonToggleComponent extends BaseButtonToggleClass implements IButtonToggleComponent {
public static get observedAttributes(): string[] {
- return [
- ...Object.values(BUTTON_TOGGLE_CONSTANTS.observedAttributes),
- ...Object.values(BUTTON_TOGGLE_CONSTANTS.observedAriaAttributes)
- ];
+ return Object.values(BUTTON_TOGGLE_CONSTANTS.observedAttributes);
}
- public readonly [observedDefaultAriaAttributes] = BUTTON_TOGGLE_CONSTANTS.observedAriaAttributes;
-
private _foundation: ButtonToggleFoundation;
constructor() {
diff --git a/src/lib/button/base/base-button-adapter.ts b/src/lib/button/base/base-button-adapter.ts
index 2c5a1be59..ec1fc23df 100644
--- a/src/lib/button/base/base-button-adapter.ts
+++ b/src/lib/button/base/base-button-adapter.ts
@@ -6,7 +6,7 @@ import { IStateLayerComponent, STATE_LAYER_CONSTANTS } from '../../state-layer';
import { IBaseButton } from './base-button';
import { BASE_BUTTON_CONSTANTS } from './base-button-constants';
import { BUTTON_FORM_ATTRIBUTES, cloneAttributes } from '../../core/utils/reflect-utils';
-import { internals, isFocusable } from '../../constants';
+import { internals, isFocusable, setDefaultAria } from '../../constants';
import { supportsPopover } from '../../core/utils/feature-detection';
// TODO: remove this augmentation when the TypeScript version is upgraded for latest DOM typings
@@ -34,7 +34,6 @@ export interface IBaseButtonAdapter extends IBaseAdapter {
managePopover(): boolean;
toggleDefaultPopoverIcon(value: boolean): void;
animateStateLayer(): void;
- proxyLabel(value: string | null): void;
}
export abstract class BaseButtonAdapter extends BaseAdapter implements IBaseButtonAdapter {
@@ -44,8 +43,6 @@ export abstract class BaseButtonAdapter extends BaseAdapter impleme
protected _stateLayerElement: IStateLayerComponent;
protected _endSlotElement: HTMLSlotElement;
- private _labelAwareText?: string;
-
constructor(component: IBaseButton) {
super(component);
this._rootElement = getShadowElement(this._component, BASE_BUTTON_CONSTANTS.selectors.ROOT) as HTMLButtonElement;
@@ -101,7 +98,7 @@ export abstract class BaseButtonAdapter extends BaseAdapter impleme
}
this._component[isFocusable] = !value;
- toggleAttribute(this._component, value, 'aria-disabled', 'true');
+ this._component[setDefaultAria]({ ariaDisabled: value ? 'true' : null });
}
public clickAnchor(): void {
@@ -246,12 +243,6 @@ export abstract class BaseButtonAdapter extends BaseAdapter impleme
this._stateLayerElement?.playAnimation();
}
- public proxyLabel(value: string | null): void {
- this._labelAwareText = value ?? undefined;
- const hasAriaLabel = this._component.hasAttribute('aria-label') || !!this._labelAwareText;
- toggleAttribute(this._component, hasAriaLabel, 'aria-label', this._component.getAttribute('aria-label') ?? this._labelAwareText);
- }
-
private _locatePopoverTargetElement(): TempHTMLElementWithPopover | null {
let popoverElement = (this._component as TempHTMLElementWithPopover).popoverTargetElement ?? null;
@@ -270,13 +261,8 @@ export abstract class BaseButtonAdapter extends BaseAdapter impleme
private _applyHostSemantics(): void {
const role = this._component.getAttribute('role');
-
- // Allow user provided roles to override our default role
- if (!role || ['button', 'link'].includes(role)) {
- // Set default role based on the existence of an anchor element
- this._component.role = this._anchorElement ? 'link' : 'button';
- }
-
+ const setAttribute = !role || ['button', 'link'].includes(role);
+ this._component[setDefaultAria]({ role: this._anchorElement ? 'link' : 'button' }, { setAttribute });
this._component[isFocusable] = !!this._anchorElement || !this._component.disabled;
}
diff --git a/src/lib/button/base/base-button-constants.ts b/src/lib/button/base/base-button-constants.ts
index 3d2fdaee6..c1ac6e8b8 100644
--- a/src/lib/button/base/base-button-constants.ts
+++ b/src/lib/button/base/base-button-constants.ts
@@ -34,5 +34,4 @@ export const BASE_BUTTON_CONSTANTS = {
export type ButtonType = 'button' | 'submit' | 'reset';
export type ButtonTarget = '_blank' | '_self' | '_parent' | '_top';
-
export type ButtonClickOptions = { animateStateLayer?: boolean };
diff --git a/src/lib/button/base/base-button-foundation.ts b/src/lib/button/base/base-button-foundation.ts
index c9fd86130..ecab8f769 100644
--- a/src/lib/button/base/base-button-foundation.ts
+++ b/src/lib/button/base/base-button-foundation.ts
@@ -15,7 +15,6 @@ export interface IBaseButtonFoundation extends ICustomElementFoundation {
dense: boolean;
click(options: ButtonClickOptions): void;
focus(options?: ExperimentalFocusOptions): void;
- proxyLabel(label: string | null): void;
}
export abstract class BaseButtonFoundation implements IBaseButtonFoundation {
@@ -81,10 +80,6 @@ export abstract class BaseButtonFoundation impleme
}
}
- public proxyLabel(label: string | null): void {
- this._adapter.proxyLabel(label);
- }
-
protected async _onClick(evt: MouseEvent): Promise {
const isFormType = this._type === 'submit' || this._type === 'reset';
diff --git a/src/lib/button/base/base-button.ts b/src/lib/button/base/base-button.ts
index aff892fce..68f0af1dd 100644
--- a/src/lib/button/base/base-button.ts
+++ b/src/lib/button/base/base-button.ts
@@ -3,14 +3,15 @@ import { tylIconArrowDropDown } from '@tylertech/tyler-icons/standard';
import { IconRegistry } from '../../icon/icon-registry';
import { WithFocusable, IWithFocusable } from '../../core/mixins/focus/with-focusable';
import { BaseComponent } from '../../core/base/base-component';
-import { ExperimentalFocusOptions, internals } from '../../constants';
+import { ExperimentalFocusOptions, internals, setDefaultAria } from '../../constants';
import { IBaseButtonAdapter } from './base-button-adapter';
import { BASE_BUTTON_CONSTANTS, ButtonType } from './base-button-constants';
import { BaseButtonFoundation } from './base-button-foundation';
import { WithLabelAwareness, IWithLabelAwareness } from '../../core/mixins/label/with-label-aware';
import { IWithElementInternals, WithElementInternals } from '../../core/mixins/internals/with-element-internals';
+import { IWithDefaultAria, WithDefaultAria } from '../../core/mixins/internals/with-default-aria';
-export interface IBaseButton extends IWithFocusable, IWithLabelAwareness, IWithElementInternals {
+export interface IBaseButton extends IWithFocusable, IWithLabelAwareness, IWithElementInternals, IWithDefaultAria {
type: ButtonType;
disabled: boolean;
popoverIcon: boolean;
@@ -26,7 +27,7 @@ export interface IBaseButton extends IWithFocusable, IWithLabelAwareness, IWithE
focus(options?: ExperimentalFocusOptions): void;
}
-const BaseButtonClass = WithElementInternals(WithLabelAwareness(WithFocusable(BaseComponent)));
+const BaseButtonClass = WithDefaultAria(WithElementInternals(WithLabelAwareness(WithFocusable(BaseComponent))));
export abstract class BaseButton> extends BaseButtonClass implements IBaseButton {
public static readonly formAssociated = true;
@@ -81,7 +82,7 @@ export abstract class BaseButton {
expect(determinateProgressElement.getAttribute('stroke-dashoffset')).to.equal('100');
});
+ it('should remove aria-valuenow when indeterminate after determinate', async () => {
+ const el = await fixture(html``);
+
+ expect(el.hasAttribute('aria-valuenow')).to.be.true;
+
+ el.determinate = false;
+
+ expect(el.hasAttribute('aria-valuenow')).to.be.false;
+ });
+
it('should set progress', async () => {
const el = await fixture(html``);
diff --git a/src/lib/circular-progress/circular-progress.ts b/src/lib/circular-progress/circular-progress.ts
index 6c471a74a..6a362be8a 100644
--- a/src/lib/circular-progress/circular-progress.ts
+++ b/src/lib/circular-progress/circular-progress.ts
@@ -3,12 +3,14 @@ import { CustomElement, attachShadowTemplate, FoundationProperty, coerceBoolean,
import { CircularProgressAdapter } from './circular-progress-adapter';
import { CircularProgressFoundation } from './circular-progress-foundation';
import { CircularProgressTheme, CIRCULAR_PROGRESS_CONSTANTS } from './circular-progress-constants';
-import { BaseComponent, IBaseComponent } from '../core/base/base-component';
+import { BaseComponent } from '../core/base/base-component';
+import { IWithElementInternals, WithElementInternals } from '../core/mixins/internals/with-element-internals';
+import { IWithDefaultAria, WithDefaultAria } from '../core/mixins/internals/with-default-aria';
import template from './circular-progress.html';
import styles from './circular-progress.scss';
-export interface ICircularProgressComponent extends IBaseComponent {
+export interface ICircularProgressComponent extends IWithElementInternals, IWithDefaultAria {
determinate: boolean;
progress: number;
theme: CircularProgressTheme;
@@ -58,7 +60,7 @@ declare global {
@CustomElement({
name: CIRCULAR_PROGRESS_CONSTANTS.elementName
})
-export class CircularProgressComponent extends BaseComponent implements ICircularProgressComponent {
+export class CircularProgressComponent extends WithDefaultAria(WithElementInternals(BaseComponent)) implements ICircularProgressComponent {
public static get observedAttributes(): string[] {
return [
CIRCULAR_PROGRESS_CONSTANTS.attributes.DETERMINATE,
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
index 82bd261f9..0c1a48698 100644
--- a/src/lib/constants.ts
+++ b/src/lib/constants.ts
@@ -27,9 +27,6 @@ export const isFocusable = Symbol('isFocusable');
* supported or sprouts attributes if not. */
export const setDefaultAria = Symbol('setDefaultAria');
-/** A property symbol that is used to define default ARIA attributes that will be observed. */
-export const observedDefaultAriaAttributes = Symbol('observedDefaultAriaAttributes');
-
export type Theme = 'primary' | 'secondary' | 'tertiary' | 'success' | 'warning' | 'error' | 'info';
export type Density = 'small' | 'medium' | 'large';
diff --git a/src/lib/core/mixins/internals/with-default-aria.ts b/src/lib/core/mixins/internals/with-default-aria.ts
index b4c92c6be..675c3e41a 100644
--- a/src/lib/core/mixins/internals/with-default-aria.ts
+++ b/src/lib/core/mixins/internals/with-default-aria.ts
@@ -1,13 +1,5 @@
-import { internals, MixinBase, setDefaultAria, AbstractConstructor, observedDefaultAriaAttributes } from '../../../constants';
-import {
- ARIAAttribute,
- ariaAttributeToProperty,
- ARIAMixinStrict,
- DefaultAriaOptions,
- restoreDefaultAria as restoreDefaultAriaUtil,
- setDefaultAria as setDefaultAriaUtil
-} from '../../utils/a11y-utils';
-import { supportsElementInternalsAria } from '../../utils/feature-detection';
+import { internals, MixinBase, setDefaultAria, AbstractConstructor } from '../../../constants';
+import { ARIAMixinStrict, setDefaultAria as setDefaultAriaUtil, DefaultAriaOptions } from '../../utils/a11y-utils';
import { IBaseComponent } from '../../base/base-component';
/**
@@ -26,8 +18,6 @@ export interface IWithDefaultAria extends IBaseComponent {
}
export declare abstract class WithDefaultAriaContract {
- public abstract readonly [observedDefaultAriaAttributes]: ARIAAttribute[];
- public attributeChangedCallback(name: string, oldValue: string, newValue: string): void;
public [setDefaultAria](properties: Partial, options?: DefaultAriaOptions): void;
}
@@ -35,32 +25,11 @@ export declare abstract class WithDefaultAriaContract {
* Mixes in Element Internals functionality into a base component.
*
* @param base The base component to mix into.
- * @param observedAria An array of ARIA attributes to observe for changes. If an observed attribute
- * is removed, the default ARIA will be restored. This is only used if Element Internals is not
- * supported.
* @returns The mixed-in base component.
*/
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
export function WithDefaultAria(base: TBase) {
abstract class DefaultAria extends base implements IWithDefaultAria {
- public abstract readonly [observedDefaultAriaAttributes]: ARIAAttribute[];
-
- public override attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
- super.attributeChangedCallback?.(name, oldValue, newValue);
-
- // If Element Internals is supported our default ARIA is never set as an attribute, so
- // there's nothing to do here.
- if (!this[observedDefaultAriaAttributes] || supportsElementInternalsAria()) {
- return;
- }
-
- // If the observed attribute is removed, restore the default ARIA.
- if (this[observedDefaultAriaAttributes].includes(name as ARIAAttribute) && !newValue) {
- const ariaPropertyName = ariaAttributeToProperty(name as ARIAAttribute);
- restoreDefaultAriaUtil(this, ariaPropertyName);
- }
- }
-
public [setDefaultAria](properties: Partial, options?: DefaultAriaOptions): void {
setDefaultAriaUtil(this, this[internals], properties, options);
}
diff --git a/src/lib/core/styles/tokens/button-toggle/button-toggle-group/_tokens.scss b/src/lib/core/styles/tokens/button-toggle/button-toggle-group/_tokens.scss
index ef3085be7..3cdfd7869 100644
--- a/src/lib/core/styles/tokens/button-toggle/button-toggle-group/_tokens.scss
+++ b/src/lib/core/styles/tokens/button-toggle/button-toggle-group/_tokens.scss
@@ -23,8 +23,8 @@ $tokens: (
// Outline
outline-width: utils.module-val(button-toggle-group, outline-width, border.variable(thin)),
outline-style: utils.module-val(button-toggle-group, outline-style, solid),
- outline-color: utils.module-val(button-toggle-group, outline-color, theme.variable(outline-medium)),
- outline-color-active: utils.module-val(button-toggle-group, outline-color-active, theme.variable(outline-high)),
+ outline-color: utils.module-val(button-toggle-group, outline-color, theme.variable(outline-low)),
+ outline-color-active: utils.module-val(button-toggle-group, outline-color-active, theme.variable(outline-medium)),
// Shape
shape: utils.module-val(button-toggle-group, shape, shape.variable(medium)),
diff --git a/src/lib/core/styles/tokens/button-toggle/button-toggle/_tokens.scss b/src/lib/core/styles/tokens/button-toggle/button-toggle/_tokens.scss
index f12caa7d7..8bb8d745d 100644
--- a/src/lib/core/styles/tokens/button-toggle/button-toggle/_tokens.scss
+++ b/src/lib/core/styles/tokens/button-toggle/button-toggle/_tokens.scss
@@ -26,7 +26,7 @@ $tokens: (
// Border
border-width: utils.module-val(button-toggle, border-width, medium),
border-style: utils.module-val(button-toggle, border-style, none),
- border-color: utils.module-val(button-toggle, border-color, theme.variable(text-medium)),
+ border-color: utils.module-val(button-toggle, border-color, theme.variable(outline-medium)),
// Shape
shape: utils.module-val(button-toggle, shape, shape.variable(small)),
@@ -36,8 +36,8 @@ $tokens: (
shape-end-end: utils.module-ref(button-toggle, shape-end-end, shape),
// Selected
- selected-background: utils.module-val(button-toggle, selected-background, theme.variable(primary-container-low)),
- selected-color: utils.module-val(button-toggle, selected-color, theme.variable(primary)),
+ selected-background: utils.module-val(button-toggle, selected-background, theme.variable(tertiary-container-low)),
+ selected-color: utils.module-val(button-toggle, selected-color, theme.variable(on-tertiary-container-low)),
selected-disabled-background: utils.module-val(button-toggle, selected-disabled-background, theme.variable(surface-container)),
// Dense
@@ -54,7 +54,8 @@ $tokens: (
transition-timing: utils.module-val(button-toggle, transition-timing, animation.variable(easing-standard)),
// Focus indicator
- focus-indicator-color: utils.module-val(button-toggle, focus-indicator-color, theme.variable(primary))
+ focus-indicator-color: utils.module-val(button-toggle, focus-indicator-color, theme.variable(primary)),
+ focus-indicator-offset: utils.module-val(button-toggle, focus-indicator-offset, 0px) // Requires unit
) !default;
@function get($key) {
diff --git a/src/lib/core/styles/tokens/theme/_token-utils.scss b/src/lib/core/styles/tokens/theme/_token-utils.scss
index 745852668..e5fb2bf50 100644
--- a/src/lib/core/styles/tokens/theme/_token-utils.scss
+++ b/src/lib/core/styles/tokens/theme/_token-utils.scss
@@ -18,10 +18,10 @@
$surface-tone: color-utils.tone($surface);
// The container colors are the provided color mixed with the surface color at lower emphasis levels
- $container-minimum: theme-utils.hexify($color, $surface, color-emphasis.value(if($surface-tone == 'light', minimum, lowest)));
- $container-low: theme-utils.hexify($color, $surface, color-emphasis.value(if($surface-tone == 'light', lower, low)));
- $container: theme-utils.hexify($color, $surface, color-emphasis.value(if($surface-tone == 'light', low, medium-low)));
- $container-high: theme-utils.hexify($color, $surface, color-emphasis.value(if($surface-tone == 'light', medium-low, medium)));
+ $container-high: theme-utils.hexify($color, $surface, color-emphasis.value(medium-low));
+ $container: theme-utils.hexify($color, $surface, color-emphasis.value(low));
+ $container-low: theme-utils.hexify($color, $surface, color-emphasis.value(lower));
+ $container-minimum: theme-utils.hexify($color, $surface, color-emphasis.value(minimum));
// The on-color is the contrast color against the provided color
$on-color: theme-utils.contrast($color);
@@ -29,10 +29,28 @@
// The on-container colors are the contrast color for the provided color mixed with the
// container color at a lower emphasis to let the contrast color bleed through for
// increased contrast against the lower emphasis container color
- $on-container-minimum: theme-utils.hexify($color, theme-utils.contrast($container-minimum), color-emphasis.value(medium));
- $on-container-low: theme-utils.hexify($color, theme-utils.contrast($container-low), color-emphasis.value(medium));
- $on-container: theme-utils.hexify($color, theme-utils.contrast($container), color-emphasis.value(medium));
$on-container-high: theme-utils.contrast($container-high);
+ $on-container: theme-utils.hexify($color, theme-utils.contrast($container), color-emphasis.value(medium));
+ $on-container-low: theme-utils.hexify($color, theme-utils.contrast($container-low), color-emphasis.value(medium));
+ $on-container-minimum: theme-utils.hexify($color, theme-utils.contrast($container-minimum), color-emphasis.value(medium));
+
+ // Compute contrast ratio for foreground colors against their corresponding background
+ $minimum-ratio: 4.5;
+ @if color-utils.contrast-ratio($color, $on-color) < $minimum-ratio {
+ @warn 'The contrast ratio between "#{$name}" and "on-#{$name}" is less than 4.5:1.';
+ }
+ @if color-utils.contrast-ratio($container, $on-container) < $minimum-ratio {
+ @warn 'The contrast ratio between "#{$name}-container" and "on-#{$name}-container" is less than 4.5:1.';
+ }
+ @if color-utils.contrast-ratio($container-high, $on-container-high) < $minimum-ratio {
+ @warn 'The contrast ratio between "#{$name}-container-high" and "on-#{$name}-container-high" is less than 4.5:1.';
+ }
+ @if color-utils.contrast-ratio($container-low, $on-container-low) < $minimum-ratio {
+ @warn 'The contrast ratio between "#{$name}-container-low" and "on-#{$name}-container-low" is less than 4.5:1.';
+ }
+ @if color-utils.contrast-ratio($container-minimum, $on-container-minimum) < $minimum-ratio {
+ @warn 'The contrast ratio between "#{$name}-container-minimum" and "on-#{$name}-container-minimum" is less than 4.5:1.';
+ }
@return (
#{$name}: $color,
diff --git a/src/lib/core/styles/tokens/theme/_tokens.text.scss b/src/lib/core/styles/tokens/theme/_tokens.text.scss
index cf166b837..3d1f78477 100644
--- a/src/lib/core/styles/tokens/theme/_tokens.text.scss
+++ b/src/lib/core/styles/tokens/theme/_tokens.text.scss
@@ -17,12 +17,12 @@
@return (
text-high: theme-utils.emphasized($text, color-emphasis.value(highest)),
text-high-inverse: theme-utils.emphasized($text-inverse, color-emphasis.value(highest)),
- text-medium: theme-utils.emphasized($text, color-emphasis.value(medium)),
- text-medium-inverse: theme-utils.emphasized($text-inverse, color-emphasis.value(medium)),
+ text-medium: theme-utils.emphasized($text, color-emphasis.value(medium-high)),
+ text-medium-inverse: theme-utils.emphasized($text-inverse, color-emphasis.value(medium-high)),
text-low: theme-utils.emphasized($text, color-emphasis.value(medium-low)),
text-low-inverse: theme-utils.emphasized($text-inverse, color-emphasis.value(medium-low)),
- text-lowest: theme-utils.emphasized($text, color-emphasis.value(lowest)),
- text-lowest-inverse: theme-utils.emphasized($text-inverse, color-emphasis.value(lowest)),
+ text-lowest: theme-utils.emphasized($text, color-emphasis.value(lower)),
+ text-lowest-inverse: theme-utils.emphasized($text-inverse, color-emphasis.value(lower)),
);
}
diff --git a/src/lib/core/utils/a11y-utils.ts b/src/lib/core/utils/a11y-utils.ts
index 1de284cae..f752c5b2a 100644
--- a/src/lib/core/utils/a11y-utils.ts
+++ b/src/lib/core/utils/a11y-utils.ts
@@ -66,7 +66,7 @@ export const ARIA_PROPERTIES: ARIAProperty[] = [
/**
* An object mapping all ARIA attributes to their corresponding properties.
*
- * This is required because the proeprty name cannot be reliably inferred from the attribute name.
+ * This is required because the property name cannot be reliably inferred from the attribute name.
*/
const ARIA_ATTRIBUTES_TO_PROPERTIES: Record = {
'aria-atomic': 'ariaAtomic',
@@ -209,12 +209,9 @@ export function ariaAttributeToProperty(attribut
return ARIA_ATTRIBUTES_TO_PROPERTIES[attribute] as ARIAProperty;
}
-// TODO: deprecate and remove `setupDefaultAria` and related functions when ARIA in
-// ElementInternals is widely supported in all major browsers.
-
-export interface DefaultAriaOptions {
- overwrite?: boolean;
-}
+export type DefaultAriaOptions = {
+ setAttribute?: boolean;
+};
/**
* Applies default ARIA to an element through ElementInternals if supported. Otherwise, ARIA
@@ -252,75 +249,16 @@ export function setDefaultAria(
element: HTMLElement,
internals: ElementInternals,
properties: Partial,
- options?: DefaultAriaOptions
+ { setAttribute }: DefaultAriaOptions = { setAttribute: true }
): void {
- if (supportsElementInternalsAria()) {
- Object.entries(properties).forEach(([key, value]) => {
- internals[key as ARIAProperty] = value;
- });
- return;
- }
-
Object.entries(properties).forEach(([key, value]) => {
- const ariaAttribute = ariaPropertyToAttribute(key as ARIAProperty);
-
- if (options?.overwrite || !element.hasAttribute(ariaAttribute)) {
- toggleAttribute(element, value != null, ariaPropertyToAttribute(key as ARIAProperty), value?.toString());
+ if (supportsElementInternalsAria()) {
+ internals[key as ARIAProperty] = value;
}
- if (value != null) {
- storeDefaultAria(element, key as ARIAProperty, value);
- } else {
- removeDefaultAria(element, key as ARIAProperty);
+
+ const attribute = ariaPropertyToAttribute(key as ARIAProperty);
+ if (setAttribute || !element.hasAttribute(attribute)) {
+ toggleAttribute(element, value != null, attribute, value as string);
}
});
}
-
-/**
- * Sets a `*Default` property on an element to backup the default value of an ARIA attribute.
- *
- * @param element The element to store the default ARIA value on.
- * @param property The ARIA mixin property.
- * @param value The default value to store.
- */
-export function storeDefaultAria(element: HTMLElement, property: T, value: ARIAMixinStrict[T]): void {
- element[getDefaultAriaPropertyName(property)] = value;
-}
-
-/**
- * Removes a `*Default` property from an element.
- *
- * @param element The element to remove the default ARIA value from.
- * @param property The ARIA mixin property.
- */
-export function removeDefaultAria(element: HTMLElement, property: T): void {
- delete element[getDefaultAriaPropertyName(property)];
-}
-
-/**
- * Gets a default ARIA value from an element's `*Default` property.
- *
- * @param element The element to retrieve the default ARIA value from.
- * @param property An ARIA mixin property.
- * @returns The value of the default ARIA attribute, or null if it does not exist.
- */
-export function retrieveDefaultAria(element: HTMLElement, property: T): ARIAMixinStrict[T] | null {
- const value = element[getDefaultAriaPropertyName(property)];
- return !!value ? value as ARIAMixinStrict[T] : null;
-}
-
-/**
- * Restores the given ARIA attribute of an element to its default value if it exists.
- *
- * @param element The element to restore the ARIA attribute on.
- * @param property The ARIA mixin property.
- */
-export function restoreDefaultAria(element: HTMLElement, property: T): void {
- const defaultValue = retrieveDefaultAria(element, property);
- if (defaultValue !== null) {
- toggleAttribute(element, true, ariaPropertyToAttribute(property), defaultValue.toString());
- }
-}
-
-function getDefaultAriaPropertyName(property: T): string {
- return `_forge_${property}Default`;
-}
diff --git a/src/lib/core/utils/feature-detection.ts b/src/lib/core/utils/feature-detection.ts
index dca97ddf5..6badeddc4 100644
--- a/src/lib/core/utils/feature-detection.ts
+++ b/src/lib/core/utils/feature-detection.ts
@@ -14,7 +14,5 @@ export function supportsPopover(): boolean {
* @returns {boolean}
*/
export function supportsElementInternalsAria(): boolean {
- // TODO: return whether the user agent actually supports this when tooling is able to detect it
- // return ElementInternals.prototype.hasOwnProperty('role');
- return false;
+ return ElementInternals.prototype.hasOwnProperty('role');
}
diff --git a/src/lib/expansion-panel/expansion-panel-adapter.ts b/src/lib/expansion-panel/expansion-panel-adapter.ts
index c6377fb9e..189e29009 100644
--- a/src/lib/expansion-panel/expansion-panel-adapter.ts
+++ b/src/lib/expansion-panel/expansion-panel-adapter.ts
@@ -51,8 +51,6 @@ export class ExpansionPanelAdapter extends BaseAdapter
openIconElement.open = true;
}
}
-
- this._headerElement.setAttribute('aria-expanded', open ? 'true' : 'false');
}
public setHeaderVisibility(visible: boolean): void {
@@ -125,7 +123,6 @@ export class ExpansionPanelAdapter extends BaseAdapter
this._contentElement.style.height = `${this._contentElement.scrollHeight}px`;
}
this._contentElement.style.opacity = '1';
- this._headerElement.setAttribute('aria-expanded', 'true');
if (openIconElement) {
openIconElement.open = true;
}
@@ -136,7 +133,6 @@ export class ExpansionPanelAdapter extends BaseAdapter
this._contentElement.style.height = '0px';
}
this._contentElement.style.opacity = '0';
- this._headerElement.setAttribute('aria-expanded', 'false');
if (openIconElement) {
openIconElement.open = false;
}
@@ -158,7 +154,6 @@ export class ExpansionPanelAdapter extends BaseAdapter
}
this._contentElement.style.removeProperty('visibility');
this._contentElement.style.removeProperty('opacity');
- this._headerElement.setAttribute('aria-expanded', 'true');
if (openIconElement) {
openIconElement.open = true;
}
@@ -170,7 +165,6 @@ export class ExpansionPanelAdapter extends BaseAdapter
}
this._contentElement.style.opacity = '0';
this._contentElement.style.visibility = 'hidden';
- this._headerElement.setAttribute('aria-expanded', 'false');
if (openIconElement) {
openIconElement.open = false;
}
diff --git a/src/lib/linear-progress/linear-progress-adapter.ts b/src/lib/linear-progress/linear-progress-adapter.ts
index 23cfafa8d..aa2ebcd6a 100644
--- a/src/lib/linear-progress/linear-progress-adapter.ts
+++ b/src/lib/linear-progress/linear-progress-adapter.ts
@@ -1,4 +1,5 @@
import { getShadowElement } from '@tylertech/forge-core';
+import { setDefaultAria } from '../constants';
import { BaseAdapter, IBaseAdapter } from '../core/base/base-adapter';
import { ILinearProgressComponent } from './linear-progress';
import { LINEAR_PROGRESS_CONSTANTS } from './linear-progress-constants';
@@ -23,20 +24,19 @@ export class LinearProgressAdapter extends BaseAdapter
}
public initialize(): void {
- if (!this._component.hasAttribute('role')) {
- this._component.setAttribute('role', 'progressbar');
- }
- if (!this._component.hasAttribute('aria-valuemin')) {
- this._component.setAttribute('aria-valuemin', '0');
- }
- if (!this._component.hasAttribute('aria-valuemax')) {
- this._component.setAttribute('aria-valuemax', '1');
- }
+ this._component[setDefaultAria]({
+ role: 'progressbar',
+ ariaValueMin: '0',
+ ariaValueMax: '1',
+ ariaValueNow: this._component.determinate ? `${this._component.progress}` : null
+ });
}
public setDeterminate(value: boolean): void {
- if (!value) {
- this._component.removeAttribute('aria-valuenow');
+ this._component[setDefaultAria]({ ariaValueNow: value ? `${this._component.progress}` : null });
+ if (value) {
+ this.setProgress(this._component.progress);
+ } else {
this._progressElement.style.transform = '';
this._bufferElement.style.transform = '';
}
@@ -44,7 +44,7 @@ export class LinearProgressAdapter extends BaseAdapter
}
public setProgress(value: number): void {
- this._component.setAttribute('aria-valuenow', `${value}`);
+ this._component[setDefaultAria]({ ariaValueNow: `${value}` });
this._progressElement.style.transform = `scaleX(${value * 100}%)`;
}
diff --git a/src/lib/linear-progress/linear-progress-constants.ts b/src/lib/linear-progress/linear-progress-constants.ts
index 5b8574e95..34303bb60 100644
--- a/src/lib/linear-progress/linear-progress-constants.ts
+++ b/src/lib/linear-progress/linear-progress-constants.ts
@@ -12,18 +12,23 @@ const selectors = {
BUFFER: '.buffer-bar'
};
-const attributes = {
+const observedAttributes = {
DETERMINATE: 'determinate',
PROGRESS: 'progress',
BUFFER: 'buffer',
THEME: 'theme'
};
+const attributes = {
+ ...observedAttributes
+};
+
export const LINEAR_PROGRESS_CONSTANTS = {
elementName,
classes,
selectors,
- attributes
+ attributes,
+ observedAttributes
};
export type LinearProgressTheme = Theme;
diff --git a/src/lib/linear-progress/linear-progress-foundation.ts b/src/lib/linear-progress/linear-progress-foundation.ts
index 50f2aa0c4..8585ebeab 100644
--- a/src/lib/linear-progress/linear-progress-foundation.ts
+++ b/src/lib/linear-progress/linear-progress-foundation.ts
@@ -25,6 +25,7 @@ export class LinearProgressFoundation {
return this._determinate;
}
public set determinate(value: boolean) {
+ value = Boolean(value);
if (this._determinate !== value) {
this._determinate = value;
this._adapter.setBuffer(this._determinate ? this._buffer : 1);
@@ -43,7 +44,7 @@ export class LinearProgressFoundation {
if (this._determinate) {
this._adapter.setProgress(this._progress);
}
- this._adapter.setHostAttribute(LINEAR_PROGRESS_CONSTANTS.attributes.PROGRESS, this._progress.toString());
+ this._adapter.setHostAttribute(LINEAR_PROGRESS_CONSTANTS.attributes.PROGRESS, String(this._progress));
}
}
diff --git a/src/lib/linear-progress/linear-progress.ts b/src/lib/linear-progress/linear-progress.ts
index 237eef778..994c816dc 100644
--- a/src/lib/linear-progress/linear-progress.ts
+++ b/src/lib/linear-progress/linear-progress.ts
@@ -1,5 +1,7 @@
import { attachShadowTemplate, coerceBoolean, coerceNumber, CustomElement, FoundationProperty } from '@tylertech/forge-core';
-import { BaseComponent, IBaseComponent } from '../core/base/base-component';
+import { BaseComponent } from '../core/base/base-component';
+import { IWithDefaultAria, WithDefaultAria } from '../core/mixins/internals/with-default-aria';
+import { IWithElementInternals, WithElementInternals } from '../core/mixins/internals/with-element-internals';
import { LinearProgressAdapter } from './linear-progress-adapter';
import { LinearProgressTheme, LINEAR_PROGRESS_CONSTANTS } from './linear-progress-constants';
import { LinearProgressFoundation } from './linear-progress-foundation';
@@ -7,7 +9,7 @@ import { LinearProgressFoundation } from './linear-progress-foundation';
import template from './linear-progress.html';
import styles from './linear-progress.scss';
-export interface ILinearProgressComponent extends IBaseComponent {
+export interface ILinearProgressComponent extends IWithElementInternals, IWithDefaultAria {
determinate: boolean;
progress: number;
buffer: number;
@@ -57,14 +59,9 @@ declare global {
@CustomElement({
name: LINEAR_PROGRESS_CONSTANTS.elementName
})
-export class LinearProgressComponent extends BaseComponent implements ILinearProgressComponent {
+export class LinearProgressComponent extends WithElementInternals(WithDefaultAria(BaseComponent)) implements ILinearProgressComponent {
public static get observedAttributes(): string[] {
- return [
- LINEAR_PROGRESS_CONSTANTS.attributes.DETERMINATE,
- LINEAR_PROGRESS_CONSTANTS.attributes.PROGRESS,
- LINEAR_PROGRESS_CONSTANTS.attributes.BUFFER,
- LINEAR_PROGRESS_CONSTANTS.attributes.THEME
- ];
+ return Object.values(LINEAR_PROGRESS_CONSTANTS.observedAttributes);
}
private _foundation: LinearProgressFoundation;
@@ -81,16 +78,16 @@ export class LinearProgressComponent extends BaseComponent implements ILinearPro
public attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
switch (name) {
- case LINEAR_PROGRESS_CONSTANTS.attributes.DETERMINATE:
+ case LINEAR_PROGRESS_CONSTANTS.observedAttributes.DETERMINATE:
this.determinate = coerceBoolean(newValue);
break;
- case LINEAR_PROGRESS_CONSTANTS.attributes.PROGRESS:
+ case LINEAR_PROGRESS_CONSTANTS.observedAttributes.PROGRESS:
this.progress = coerceNumber(newValue);
break;
- case LINEAR_PROGRESS_CONSTANTS.attributes.BUFFER:
+ case LINEAR_PROGRESS_CONSTANTS.observedAttributes.BUFFER:
this.buffer = coerceNumber(newValue);
break;
- case LINEAR_PROGRESS_CONSTANTS.attributes.THEME:
+ case LINEAR_PROGRESS_CONSTANTS.observedAttributes.THEME:
this.theme = newValue as LinearProgressTheme;
break;
}
diff --git a/src/lib/radio/core/radio-group-manager.ts b/src/lib/radio/core/radio-group-manager.ts
index 188f1ca7b..9d70ca3a0 100644
--- a/src/lib/radio/core/radio-group-manager.ts
+++ b/src/lib/radio/core/radio-group-manager.ts
@@ -59,7 +59,7 @@ export class RadioGroupManager {
checked: radio.checked
});
radio[internals].setValidity({ valueMissing: invalid }, validationMessage);
- radio[setDefaultAria]({ 'ariaInvalid': invalid ? 'true' : 'false' }, { overwrite: true });
+ radio[setDefaultAria]({ 'ariaInvalid': invalid ? 'true' : 'false' });
});
}
diff --git a/src/lib/radio/radio-group/radio-group-adapter.ts b/src/lib/radio/radio-group/radio-group-adapter.ts
index 5d718e50e..26c1c575c 100644
--- a/src/lib/radio/radio-group/radio-group-adapter.ts
+++ b/src/lib/radio/radio-group/radio-group-adapter.ts
@@ -20,9 +20,7 @@ export class RadioGroupAdapter extends BaseAdapter impleme
}
public setDisabled(value: boolean): void {
- this._component[setDefaultAria]({
- ariaDisabled: value ? 'true' : null
- }, { overwrite: true });
+ this._component[setDefaultAria]({ ariaDisabled: value ? 'true' : null });
this._disableRadios(value);
}
diff --git a/src/lib/radio/radio-group/radio-group-constants.ts b/src/lib/radio/radio-group/radio-group-constants.ts
index 349f9dbf0..f6ab62267 100644
--- a/src/lib/radio/radio-group/radio-group-constants.ts
+++ b/src/lib/radio/radio-group/radio-group-constants.ts
@@ -1,6 +1,4 @@
import { COMPONENT_NAME_PREFIX } from '../../constants';
-import { ARIAAttribute } from '../../core/utils/a11y-utils';
-import { supportsElementInternalsAria } from '../../core/utils/feature-detection';
const elementName: keyof HTMLElementTagNameMap = `${COMPONENT_NAME_PREFIX}radio-group`;
@@ -8,12 +6,7 @@ const attributes = {
DISABLED: 'disabled'
};
-const observedAriaAttributes: ARIAAttribute[] = supportsElementInternalsAria()
- ? []
- : ['role', 'aria-disabled', 'aria-label'];
-
export const RADIO_GROUP_CONSTANTS = {
elementName,
- attributes,
- observedAriaAttributes
+ attributes
};
diff --git a/src/lib/radio/radio-group/radio-group.ts b/src/lib/radio/radio-group/radio-group.ts
index b1012196c..38cf2b0b9 100644
--- a/src/lib/radio/radio-group/radio-group.ts
+++ b/src/lib/radio/radio-group/radio-group.ts
@@ -1,5 +1,5 @@
import { CustomElement, FoundationProperty, attachShadowTemplate, coerceBoolean, toggleAttribute } from '@tylertech/forge-core';
-import { internals, observedDefaultAriaAttributes, setDefaultAria } from '../../constants';
+import { internals, setDefaultAria } from '../../constants';
import { BaseComponent } from '../../core/base/base-component';
import { IWithDefaultAria, WithDefaultAria } from '../../core/mixins/internals/with-default-aria';
import { IWithElementInternals, WithElementInternals } from '../../core/mixins/internals/with-element-internals';
@@ -41,8 +41,6 @@ const BaseRadioGroupClass = WithLabelAwareness(WithDefaultAria(WithElementIntern
export class RadioGroupComponent extends BaseRadioGroupClass implements IRadioGroupComponent {
public static readonly formAssociated = true;
- public readonly [observedDefaultAriaAttributes] = RADIO_GROUP_CONSTANTS.observedAriaAttributes;
-
public get form(): HTMLFormElement | null {
return this[internals].form;
}
@@ -80,13 +78,12 @@ export class RadioGroupComponent extends BaseRadioGroupClass implements IRadioGr
this._foundation.initialize();
}
- public override attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
+ public attributeChangedCallback(name: string, oldValue: string, newValue: string): void {
switch (name) {
case RADIO_GROUP_CONSTANTS.attributes.DISABLED:
this.disabled = coerceBoolean(newValue);
break;
}
- super.attributeChangedCallback(name, oldValue, newValue);
}
public formDisabledCallback(isDisabled: boolean): void {
diff --git a/src/lib/radio/radio/radio-adapter.ts b/src/lib/radio/radio/radio-adapter.ts
index fe7847d9a..83282d118 100644
--- a/src/lib/radio/radio/radio-adapter.ts
+++ b/src/lib/radio/radio/radio-adapter.ts
@@ -32,9 +32,7 @@ export class RadioAdapter extends BaseAdapter implements IRadio
}
public setChecked(checked: boolean, value: string): void {
- this._component[setDefaultAria]({
- ariaChecked: checked ? 'true' : 'false'
- }, { overwrite: true });
+ this._component[setDefaultAria]({ ariaChecked: checked ? 'true' : 'false' });
if (checked) {
RadioGroupManager.setSelectedRadioInGroup(this._component);
}
@@ -60,9 +58,7 @@ export class RadioAdapter extends BaseAdapter implements IRadio
return false;
}
- this._component[setDefaultAria]({
- ariaDisabled: value ? 'true' : 'false'
- }, { overwrite: true });
+ this._component[setDefaultAria]({ ariaDisabled: `${!!value}` });
this._component[isFocusable] = !value;
return true;
}
@@ -70,14 +66,12 @@ export class RadioAdapter extends BaseAdapter implements IRadio
public setRequired(value: boolean): void {
this._component[setDefaultAria]({
ariaRequired: value ? 'true' : 'false'
- }, { overwrite: true });
+ }, { setAttribute: true });
RadioGroupManager.setRadioGroupValidity(this._component);
}
public setReadonly(value: boolean): void {
- this._component[setDefaultAria]({
- ariaDisabled: value ? 'true' : 'false'
- }, { overwrite: true });
+ this._component[setDefaultAria]({ ariaDisabled: `${!!value}` });
}
public disableStateLayer(value: boolean): void {
diff --git a/src/lib/radio/radio/radio-constants.ts b/src/lib/radio/radio/radio-constants.ts
index 67326316b..0736b2760 100644
--- a/src/lib/radio/radio/radio-constants.ts
+++ b/src/lib/radio/radio/radio-constants.ts
@@ -1,6 +1,4 @@
import { COMPONENT_NAME_PREFIX } from '../../constants';
-import { ARIAAttribute } from '../../core/utils/a11y-utils';
-import { supportsElementInternalsAria } from '../../core/utils/feature-detection';
const elementName: keyof HTMLElementTagNameMap = `${COMPONENT_NAME_PREFIX}radio`;
@@ -27,24 +25,11 @@ const events = {
INPUT: 'input'
};
-const observedAriaAttributes: ARIAAttribute[] = supportsElementInternalsAria()
- ? []
- : [
- 'role',
- 'aria-checked',
- 'aria-disabled',
- 'aria-invalid',
- 'aria-label',
- 'aria-readonly',
- 'aria-required'
- ];
-
export const RADIO_CONSTANTS = {
elementName,
attributes,
selectors,
- events,
- observedAriaAttributes
+ events
};
/**
diff --git a/src/lib/radio/radio/radio.ts b/src/lib/radio/radio/radio.ts
index 1f523caa7..fc10edaa8 100644
--- a/src/lib/radio/radio/radio.ts
+++ b/src/lib/radio/radio/radio.ts
@@ -1,5 +1,5 @@
import { CustomElement, FoundationProperty, attachShadowTemplate, coerceBoolean } from '@tylertech/forge-core';
-import { getFormState, getFormValue, inputType, internals, observedDefaultAriaAttributes, setDefaultAria } from '../../constants';
+import { getFormState, getFormValue, inputType, internals, setDefaultAria } from '../../constants';
import { BaseComponent } from '../../core/base/base-component';
import { IWithFocusable, WithFocusable } from '../../core/mixins/focus/with-focusable';
import { IWithElementInternals, WithElementInternals } from '../../core/mixins/internals/with-element-internals';
@@ -116,13 +116,10 @@ export class RadioComponent extends BaseRadioClass implements IRadioComponent {
RADIO_CONSTANTS.attributes.REQUIRED,
RADIO_CONSTANTS.attributes.READONLY,
RADIO_CONSTANTS.attributes.LABEL_POSITION,
- RADIO_CONSTANTS.attributes.TABINDEX,
- ...RADIO_CONSTANTS.observedAriaAttributes
+ RADIO_CONSTANTS.attributes.TABINDEX
];
}
- public readonly [observedDefaultAriaAttributes] = RADIO_CONSTANTS.observedAriaAttributes;
-
private _foundation: RadioFoundation;
constructor() {
@@ -195,9 +192,7 @@ export class RadioComponent extends BaseRadioClass implements IRadioComponent {
}
public labelChangedCallback(value: string | null): void {
- this[setDefaultAria]({
- ariaLabel: value ?? undefined
- });
+ this[setDefaultAria]({ ariaLabel: value });
}
@FoundationProperty()