diff --git a/src/dev/pages/button/button.ejs b/src/dev/pages/button/button.ejs index 8eeb17347..9cfcc5fe6 100644 --- a/src/dev/pages/button/button.ejs +++ b/src/dev/pages/button/button.ejs @@ -109,6 +109,40 @@ + +
+

Deprecated (legacy) button

+ + + + + + + + + + + + + + + + + + + + + w/anchor tag + + + +
diff --git a/src/dev/pages/button/button.scss b/src/dev/pages/button/button.scss index ebef6942a..ba6a70c8a 100644 --- a/src/dev/pages/button/button.scss +++ b/src/dev/pages/button/button.scss @@ -9,7 +9,7 @@ gap: 16px; } -forge-button:not([full-width]) { +:is(forge-button,forge-deprecated-button):not([full-width]) { width: 256px; } diff --git a/src/dev/pages/button/button.ts b/src/dev/pages/button/button.ts index 3dfe11a65..56e704e05 100644 --- a/src/dev/pages/button/button.ts +++ b/src/dev/pages/button/button.ts @@ -1,11 +1,13 @@ import '$src/shared'; import type { ISwitchComponent } from '@tylertech/forge/switch'; import type { IButtonComponent } from '@tylertech/forge/button'; +import type { IDeprecatedButtonComponent } from '@tylertech/forge/deprecated/button'; import type { ISelectComponent } from '@tylertech/forge/select'; import { IconRegistry } from '@tylertech/forge/icon'; import { tylIconFavorite, tylIconOpenInNew } from '@tylertech/tyler-icons/standard'; import { tylIconForgeLogo } from '@tylertech/tyler-icons/custom'; import '@tylertech/forge/button'; +import '@tylertech/forge/deprecated/button'; import '@tylertech/forge/label'; import './button.scss'; @@ -55,17 +57,22 @@ showDialogBtn.addEventListener('click', () => { dialog.showModal(); }); +const allDeprecatedButtons = Array.from(document.querySelectorAll('.content forge-deprecated-button')); const allButtons = Array.from(document.querySelectorAll('.content forge-button')); allButtons.forEach(btn => btn.addEventListener('click', evt => console.log('click', evt))); const disabledToggle = document.querySelector('#opt-disabled') as ISwitchComponent; disabledToggle.addEventListener('forge-switch-change', ({ detail: selected }) => { allButtons.forEach(btn => btn.toggleAttribute('disabled', selected)); + allDeprecatedButtons.forEach(btn => btn.toggleAttribute('disabled', selected)); }); const denseToggle = document.querySelector('#opt-dense') as ISwitchComponent; denseToggle.addEventListener('forge-switch-change', ({ detail: selected }) => { allButtons.forEach(btn => btn.dense = selected); + allDeprecatedButtons.forEach(btn => { + btn.type = btn.type ? btn.type.replace(/(?:-?dense)?$/, selected ? '-dense' : '') : selected ? 'dense' : ''; + }); }); const pillToggle = document.querySelector('#opt-pill') as ISwitchComponent; @@ -81,6 +88,7 @@ anchorToggle.addEventListener('forge-switch-change', ({ detail: selected }) => { const fullWidthToggle = document.querySelector('#opt-full-width') as ISwitchComponent; fullWidthToggle.addEventListener('forge-switch-change', ({ detail: selected }) => { allButtons.forEach(btn => btn.fullWidth = selected); + allDeprecatedButtons.forEach(btn => btn.toggleAttribute('full-width', selected)); }); const popoverIconToggle = document.querySelector('#opt-popover-icon') as ISwitchComponent; diff --git a/src/lib/core/styles/typography/index.scss b/src/lib/core/styles/typography/index.scss index e11ada6d5..6bf1fc258 100644 --- a/src/lib/core/styles/typography/index.scss +++ b/src/lib/core/styles/typography/index.scss @@ -92,7 +92,7 @@ @mixin anchor { a:not([forge-ignore]), .forge-typography--link { - text-decoration: underline; + text-decoration: var(--forge-typography-link-text-decoration, underline); color: theme.variable(primary); cursor: pointer; diff --git a/src/lib/deprecated/button/deprecated-button-component-delegate.ts b/src/lib/deprecated/button/deprecated-button-component-delegate.ts new file mode 100644 index 000000000..6e27bc2c4 --- /dev/null +++ b/src/lib/deprecated/button/deprecated-button-component-delegate.ts @@ -0,0 +1,49 @@ +import { BaseComponentDelegate, IBaseComponentDelegateConfig, IBaseComponentDelegateOptions } from '../../core/delegates/base-component-delegate'; +import { IDeprecatedButtonComponent } from './deprecated-button'; +import { DEPRECATED_BUTTON_CONSTANTS } from './deprecated-button-constants'; + +export type DeprecatedButtonComponentDelegateProps = Partial; +export interface IDeprecatedButtonComponentDelegateOptions extends IBaseComponentDelegateOptions { + type?: 'button' | 'submit'; + text?: string; +} +export interface IDeprecatedButtonComponentDelegateConfig extends IBaseComponentDelegateConfig {} + +export class DeprecatedButtonComponentDelegate extends BaseComponentDelegate { + private _buttonElement?: HTMLButtonElement; + + constructor(config?: IDeprecatedButtonComponentDelegateConfig) { + super(config); + } + + public override destroy(): void { + this._buttonElement = undefined; + } + + public get buttonElement(): HTMLButtonElement | undefined { + return this._buttonElement; + } + + protected _build(): IDeprecatedButtonComponent { + const component = document.createElement(DEPRECATED_BUTTON_CONSTANTS.elementName); + + this._buttonElement = document.createElement('button'); + this._buttonElement.type = this._config.options?.type || 'button'; + this._buttonElement.textContent = this._config.options?.text || ''; + component.appendChild(this._buttonElement); + + return component; + } + + public onClick(listener: (evt: MouseEvent) => void): void { + this._buttonElement?.addEventListener('click', listener); + } + + public onFocus(listener: (evt: Event) => void): void { + this._buttonElement?.addEventListener('focus', evt => listener(evt)); + } + + public onBlur(listener: (evt: Event) => void): void { + this._buttonElement?.addEventListener('blur', evt => listener(evt)); + } +} diff --git a/src/lib/deprecated/button/deprecated-button-constants.ts b/src/lib/deprecated/button/deprecated-button-constants.ts new file mode 100644 index 000000000..6c62716b9 --- /dev/null +++ b/src/lib/deprecated/button/deprecated-button-constants.ts @@ -0,0 +1,21 @@ +import { COMPONENT_NAME_PREFIX } from '../../constants'; + +const elementName: keyof HTMLElementTagNameMap = `${COMPONENT_NAME_PREFIX}deprecated-button`; + +const attributes = { + TYPE: 'type', + DISABLED: 'disabled', + FULL_WIDTH: 'full-width' +}; + +const selectors = { + BUTTON: 'button,a' +}; + +export const DEPRECATED_BUTTON_CONSTANTS = { + elementName, + attributes, + selectors +}; + +export type DeprecatedButtonType = 'text' | 'raised' | 'unelevated' | 'outlined' | 'dense'; diff --git a/src/lib/deprecated/button/deprecated-button.html b/src/lib/deprecated/button/deprecated-button.html new file mode 100644 index 000000000..0bc92866f --- /dev/null +++ b/src/lib/deprecated/button/deprecated-button.html @@ -0,0 +1,5 @@ + diff --git a/src/lib/deprecated/button/deprecated-button.scss b/src/lib/deprecated/button/deprecated-button.scss new file mode 100644 index 000000000..4f4a354c5 --- /dev/null +++ b/src/lib/deprecated/button/deprecated-button.scss @@ -0,0 +1,128 @@ +@use '../../button/core' as *; +@use '../../state-layer'; +@use '../../focus-indicator'; + +// +// Host +// + +:host { + @include tokens; +} + +:host { + @include host; + + border-radius: #{token(shape)}; +} + +:host([hidden]) { + display: none; +} + +// +// Base +// + +::slotted(:is(button,a)) { + @include base; + + --forge-icon-font-size: 1.25em; +} + +::slotted(a) { + text-decoration: none; + --forge-typography-link-text-decoration: none; +} + +forge-state-layer { + @include state-layer.provide-theme(( color: #{token(color)} )); +} + +forge-focus-indicator { + @include focus-indicator.provide-theme(( + color: #{token(primary-color)}, + outward-offset: #{token(focus-indicator-offset)} + )); +} + +// +// Types +// + +:host(:is(:not([type],[type*=text]))) { + ::slotted(:is(button,a)) { + @include text; + } +} + +:host(:is([type*=unelevated],[type*=raised])) { + ::slotted(:is(button,a)) { + @include filled; + } + + forge-state-layer { + @include state-layer.provide-theme(( color: #{token(filled-color)} )); + } +} + +:host([type*=raised]) { + ::slotted(:is(button,a)) { + @include raised; + } +} + +:host([type*=outlined]) { + ::slotted(:is(button,a)) { + @include outlined; + } +} + +// +// Full width +// + +:host([full-width]) { + width: 100%; +} + + +// +// Dense +// + +:host(:is([dense],[type*=dense])) { + ::slotted(:is(button,a)) { + @include dense; + } +} + +// +// Disabled +// + +:host([disabled]) { + @include host-disabled; + + ::slotted(button[disabled]) { + @include disabled; + } +} + +:host([type*=outlined][disabled]) { + ::slotted(button[disabled]) { + @include outlined-disabled; + } +} + +:host(:is([type*=unelevated],[type*=raised])[disabled]) { + ::slotted(button[disabled]) { + @include filled-disabled; + } +} + +:host([type*=raised][disabled]) { + ::slotted(button[disabled]) { + @include raised-disabled; + } +} diff --git a/src/lib/deprecated/button/deprecated-button.test.ts b/src/lib/deprecated/button/deprecated-button.test.ts new file mode 100644 index 000000000..f42e87660 --- /dev/null +++ b/src/lib/deprecated/button/deprecated-button.test.ts @@ -0,0 +1,358 @@ +import { expect } from '@esm-bundle/chai'; +import { spy } from 'sinon'; +import { elementUpdated, fixture, html } from '@open-wc/testing'; +import { sendMouse, sendKeys } from '@web/test-runner-commands'; +import type { IStateLayerComponent } from '../../state-layer'; +import type { IFocusIndicatorComponent } from '../../focus-indicator'; +import { DeprecatedButtonComponentDelegate } from './deprecated-button-component-delegate'; +import { DeprecatedButtonComponent, IDeprecatedButtonComponent } from './deprecated-button'; +import { DEPRECATED_BUTTON_CONSTANTS } from './deprecated-button-constants'; + +import './deprecated-button'; + +describe('Deprecated Button', () => { + it('should initialize', async () => { + const el = await fixture(html` + + + + `); + + const stateLayer = getStateLayer(el); + const focusIndicator = getFocusIndicator(el); + + expect(el.shadowRoot).not.to.be.null; + expect(stateLayer.disabled).to.be.false; + expect(focusIndicator).to.be.ok; + }); + + it('should be accessible', async () => { + const el = await fixture(html` + + + + `); + + await expect(el).to.be.accessible(); + }); + + it('should be text (undefined) type by default', async () => { + const el = await fixture(html` + + + + `); + + expect(el.type).to.be.undefined; + }); + + it('should be raised type', async () => { + const el = await fixture(html` + + + + `); + + expect(el.type).to.equal('raised'); + expect(el.getAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.TYPE)).to.equal('raised'); + await expect(el).to.be.accessible(); + }); + + it('should be outlined type', async () => { + const el = await fixture(html` + + + + `); + + expect(el.type).to.equal('outlined'); + expect(el.getAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.TYPE)).to.equal('outlined'); + await expect(el).to.be.accessible(); + }); + + it('should be flat type', async () => { + const el = await fixture(html` + + + + `); + + expect(el.type).to.equal('unelevated'); + expect(el.getAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.TYPE)).to.equal('unelevated'); + await expect(el).to.be.accessible(); + }); + + it('should use anchor element', async () => { + const el = await fixture(html` + + Anchor + + `); + + await expect(el).to.be.accessible(); + }); + + it('should set full width', async () => { + const el = await fixture(html` + + + + `); + + expect(el.fullWidth).to.be.true; + expect(el.hasAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.FULL_WIDTH)).to.be.true; + + el.fullWidth = false; + + expect(el.fullWidth).to.be.false; + expect(el.hasAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.FULL_WIDTH)).to.be.false; + }); + + it('should set disabled', async () => { + const el = await fixture(html` + + + + `); + + expect(el.disabled).to.be.true; + expect(el.hasAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.DISABLED)).to.be.true; + + el.disabled = false; + + expect(el.disabled).to.be.false; + expect(el.hasAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.DISABLED)).to.be.false; + }); + + it('should wait to initialize until child button is available', async () => { + const el = await fixture(html``); + + const stateLayer = getStateLayer(el); + const focusIndicator = getFocusIndicator(el); + + expect(stateLayer.targetElement).to.be.null; + expect(focusIndicator.targetElement).to.be.null; + + const button = document.createElement('button'); + el.appendChild(button); + + await elementUpdated(el); + + expect(stateLayer.targetElement).to.equal(button); + expect(focusIndicator.targetElement).to.equal(button); + }); + + it('should wait to initialize until child anchor is available', async () => { + const el = await fixture(html``); + + const stateLayer = getStateLayer(el); + const focusIndicator = getFocusIndicator(el); + + expect(stateLayer.targetElement).to.be.null; + expect(focusIndicator.targetElement).to.be.null; + + const anchor = document.createElement('a'); + el.appendChild(anchor); + + await elementUpdated(el); + + expect(stateLayer.targetElement).to.equal(anchor); + expect(focusIndicator.targetElement).to.equal(anchor); + }); + + it('should dynamically swap button', async () => { + const el = await fixture(html` + + + + `); + + const stateLayer = getStateLayer(el); + const focusIndicator = getFocusIndicator(el); + + expect(stateLayer.targetElement).to.be.ok; + expect(focusIndicator.targetElement).to.be.ok; + + el.querySelector('button')?.remove(); + + const anchor = document.createElement('a'); + el.appendChild(anchor); + + await elementUpdated(el); + + expect(stateLayer.targetElement).to.equal(anchor); + expect(focusIndicator.targetElement).to.equal(anchor); + }); + + it('should detect disabled state from button when initialized', async () => { + const el = await fixture(html` + + + + `); + + expect(el.disabled).to.be.true; + expect(el.hasAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.DISABLED)).to.be.true; + }); + + it('should sync disabled state from button', async () => { + const el = await fixture(html` + + + + `); + + const buttonEl = el.querySelector('button') as HTMLButtonElement; + buttonEl.disabled = true; + + await elementUpdated(el); + + expect(el.disabled).to.be.true; + expect(el.hasAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.DISABLED)).to.be.true; + }); + + it('should play state layer animation when pressing enter key', async () => { + const el = await fixture(html` + + + + `); + + const stateLayer = getStateLayer(el); + const playAnimationSpy = spy(stateLayer, 'playAnimation'); + + el.focus(); + await sendKeys({ press: 'Enter' }); + + expect(playAnimationSpy).to.be.calledOnce; + }); + + it('should play state layer animation when pressing space key', async () => { + const el = await fixture(html` + + + + `); + + const stateLayer = getStateLayer(el); + const playAnimationSpy = spy(stateLayer, 'playAnimation'); + + el.focus(); + await sendKeys({ press: ' ' }); + + expect(playAnimationSpy).to.be.calledOnce; + }); + + it('should not initialize if invalid child is element slotted in', async () => { + const el = await fixture(html` + +
Button
+
+ `); + + const stateLayer = getStateLayer(el); + const focusIndicator = getFocusIndicator(el); + + expect(stateLayer.targetElement).to.be.null; + expect(focusIndicator.targetElement).to.be.null; + }); + + describe('DeprecatedButtonComponentDelegate', () => { + it('should create button via delegate', async () => { + const delegate = new DeprecatedButtonComponentDelegate({ options: { text: 'Button' }}); + + expect(delegate.element).to.be.instanceOf(DeprecatedButtonComponent); + expect(delegate.element.innerText).to.equal('Button'); + }); + + it('should set type via delegate', async () => { + const delegate = new DeprecatedButtonComponentDelegate({ options: { type: 'button' }}); + + expect(delegate.buttonElement?.type).to.equal('button'); + }); + + it('should set type via delegate', async () => { + const delegate = new DeprecatedButtonComponentDelegate({ options: { type: 'submit' }}); + + expect(delegate.buttonElement?.type).to.equal('submit'); + }); + + it('should call click listener via delegate', async () => { + const delegate = new DeprecatedButtonComponentDelegate(); + document.body.appendChild(delegate.element); + const clickSpy = spy(); + + delegate.onClick(clickSpy); + await clickElement(delegate.buttonElement); + delegate.element.remove(); + + expect(clickSpy).to.be.have.been.calledOnce; + }); + + it('should call focus listener via delegate', async () => { + const delegate = new DeprecatedButtonComponentDelegate(); + document.body.appendChild(delegate.element); + const focusSpy = spy(); + + delegate.onFocus(focusSpy); + await clickElement(delegate.buttonElement); + delegate.element.remove(); + + expect(focusSpy).to.be.have.been.calledOnce; + }); + + it('should call blur listener via delegate', async () => { + const delegate = new DeprecatedButtonComponentDelegate(); + document.body.appendChild(delegate.element); + const blurSpy = spy(); + + delegate.onBlur(blurSpy); + delegate.buttonElement?.focus(); + await clickElement(document.body); + delegate.element.remove(); + + expect(blurSpy).to.be.have.been.calledOnce; + }); + }); + + it('should be accessible with aria-label', async () => { + const el = await fixture(html` + + + + `); + await expect(el).to.be.accessible(); + }); + + it('should be accessible with aria-labelledby', async () => { + const el = await fixture(html` +
+ + + + +
+ `); + const button = el.querySelector('forge-deprecated-button') as IDeprecatedButtonComponent; + await expect(button).to.be.accessible(); + }); + + function getStateLayer(btn: IDeprecatedButtonComponent): IStateLayerComponent { + return btn.shadowRoot?.querySelector('forge-state-layer') as IStateLayerComponent + } + + function getFocusIndicator(btn: IDeprecatedButtonComponent): IFocusIndicatorComponent { + return btn.shadowRoot?.querySelector('forge-focus-indicator') as IFocusIndicatorComponent; + } + + function clickElement(el: HTMLElement | undefined): Promise { + if (!el) { + return Promise.resolve(); + } + const { x, y, width, height } = el.getBoundingClientRect(); + return sendMouse({ type: 'click', position: [ + Math.floor(x + window.scrollX + width / 2), + Math.floor(y + window.scrollY + height / 2), + ]}); + } +}); diff --git a/src/lib/deprecated/button/deprecated-button.ts b/src/lib/deprecated/button/deprecated-button.ts new file mode 100644 index 000000000..25d7f32a5 --- /dev/null +++ b/src/lib/deprecated/button/deprecated-button.ts @@ -0,0 +1,177 @@ +import { attachShadowTemplate, coerceBoolean, CustomElement, ensureChildren, getShadowElement, toggleAttribute } from '@tylertech/forge-core'; +import { FocusIndicatorComponent, FOCUS_INDICATOR_CONSTANTS, IFocusIndicatorComponent } from '../../focus-indicator'; +import { IStateLayerComponent, StateLayerComponent, STATE_LAYER_CONSTANTS } from '../../state-layer'; +import { DeprecatedButtonType, DEPRECATED_BUTTON_CONSTANTS } from './deprecated-button-constants'; +import { BaseComponent, IBaseComponent } from '../../core/base/base-component'; + +import template from './deprecated-button.html'; +import styles from './deprecated-button.scss'; + +/** + * @deprecated Use `IButtonComponent` component instead. + */ +export interface IDeprecatedButtonComponent extends IBaseComponent { + type: string; + disabled: boolean; + fullWidth: boolean; +} + +declare global { + interface HTMLElementTagNameMap { + 'forge-deprecated-button': IDeprecatedButtonComponent; + } +} + +/** + * @tag forge-deprecated-button + * + * @deprecated Use the `` element instead. + */ +@CustomElement({ + name: DEPRECATED_BUTTON_CONSTANTS.elementName, + dependencies: [ + FocusIndicatorComponent, + StateLayerComponent + ] +}) +export class DeprecatedButtonComponent extends BaseComponent implements IDeprecatedButtonComponent { + public static get observedAttributes(): string[] { + return Object.values(DEPRECATED_BUTTON_CONSTANTS.attributes); + } + + private _slotElement: HTMLSlotElement; + private _focusIndicator: IFocusIndicatorComponent; + private _stateLayer: IStateLayerComponent; + private _buttonOrAnchorElement: HTMLButtonElement | HTMLAnchorElement | null = null; + private _type: DeprecatedButtonType; + private _disabled = false; + private _fullWidth = false; + private _buttonChangeListener = this._onButtonChange.bind(this); + private _buttonAttrMutationObserver: MutationObserver | undefined; + private _keydownListener = (evt: KeyboardEvent): void => this._onKeydown(evt); + + constructor() { + super(); + attachShadowTemplate(this, template, styles); + this._slotElement = getShadowElement(this, 'slot:not([name])') as HTMLSlotElement; + this._focusIndicator = getShadowElement(this, FOCUS_INDICATOR_CONSTANTS.elementName) as IFocusIndicatorComponent; + this._stateLayer = getShadowElement(this, STATE_LAYER_CONSTANTS.elementName) as IStateLayerComponent; + } + + public connectedCallback(): void { + this._slotElement.addEventListener('slotchange', this._buttonChangeListener); + + if (this.children.length) { + this._initialize(); + } else { + ensureChildren(this).then(() => this._initialize()); + } + } + + public disconnectedCallback(): void { + this._detachButton(); + } + + public attributeChangedCallback(name: string, oldValue: string, newValue: string): void { + switch (name) { + case DEPRECATED_BUTTON_CONSTANTS.attributes.TYPE: + this.type = newValue as DeprecatedButtonType; + return; + case DEPRECATED_BUTTON_CONSTANTS.attributes.DISABLED: + this.disabled = coerceBoolean(newValue); + return; + case DEPRECATED_BUTTON_CONSTANTS.attributes.FULL_WIDTH: + this.fullWidth = coerceBoolean(newValue); + return; + } + } + + private _onKeydown(evt: KeyboardEvent): void { + if (evt.key === 'Enter' || evt.key === ' ') { + this._stateLayer.playAnimation(); + } + } + + private _onButtonChange(): void { + this._detachButton(); + this._initialize(); + } + + private _initialize(): void { + this._buttonOrAnchorElement = this.querySelector(DEPRECATED_BUTTON_CONSTANTS.selectors.BUTTON); + if (!this._buttonOrAnchorElement) { + return; + } + + this._stateLayer.targetElement = this._buttonOrAnchorElement; + this._focusIndicator.targetElement = this._buttonOrAnchorElement; + + this._buttonOrAnchorElement.addEventListener('keydown', this._keydownListener); + + // Sync disabled state + if (this._buttonOrAnchorElement instanceof HTMLButtonElement && this._disabled && !this._buttonOrAnchorElement.disabled) { + this._buttonOrAnchorElement.disabled = true; + } else { + this.disabled = this._buttonOrAnchorElement instanceof HTMLButtonElement && this._buttonOrAnchorElement.disabled; + } + + // Listen for disabled attribute changes on the button + if (this._buttonOrAnchorElement instanceof HTMLButtonElement) { + this._buttonAttrMutationObserver = new MutationObserver(mutationList => { + if (mutationList.some(mutation => mutation.attributeName === 'disabled')) { + this._syncDisabledState(); + } + }); + this._buttonAttrMutationObserver.observe(this._buttonOrAnchorElement, { attributes: true, attributeFilter: ['disabled'] }); + } + } + + private _syncDisabledState(): void { + const isDisabled = this._buttonOrAnchorElement instanceof HTMLButtonElement && this._buttonOrAnchorElement.disabled; + toggleAttribute(this, isDisabled, DEPRECATED_BUTTON_CONSTANTS.attributes.DISABLED); + } + + private _detachButton(): void { + this._buttonOrAnchorElement?.removeEventListener('keydown', this._keydownListener); + + if (this._buttonAttrMutationObserver) { + this._buttonAttrMutationObserver.disconnect(); + this._buttonAttrMutationObserver = undefined; + } + } + + public get type(): DeprecatedButtonType { + return this._type; + } + public set type(value: DeprecatedButtonType) { + if (this._type !== value) { + this._type = value; + this.setAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.TYPE, this._type); + } + } + + public get disabled(): boolean { + return this._disabled; + } + public set disabled(value: boolean) { + if (this._disabled !== value) { + this._disabled = value; + this._buttonOrAnchorElement?.toggleAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.DISABLED, this._disabled); + this.toggleAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.DISABLED, this._disabled); + } + } + + public get fullWidth(): boolean { + return this._fullWidth; + } + public set fullWidth(value: boolean) { + if (this._fullWidth !== value) { + this._fullWidth = value; + this.toggleAttribute(DEPRECATED_BUTTON_CONSTANTS.attributes.FULL_WIDTH, this._fullWidth); + } + } + + public override focus(options?: FocusOptions | undefined): void { + this._buttonOrAnchorElement?.focus(options); + } +} diff --git a/src/lib/deprecated/button/index.ts b/src/lib/deprecated/button/index.ts new file mode 100644 index 000000000..98649b313 --- /dev/null +++ b/src/lib/deprecated/button/index.ts @@ -0,0 +1,13 @@ +import { defineCustomElement } from '@tylertech/forge-core'; +import { DeprecatedButtonComponent } from './deprecated-button'; + +export * from './deprecated-button'; +export * from './deprecated-button-component-delegate'; +export * from './deprecated-button-constants'; + +/** + * @deprecated Use `defineButtonComponent()` instead for the `` element. + */ +export function defineDeprecatedButtonComponent(): void { + defineCustomElement(DeprecatedButtonComponent); +} diff --git a/src/lib/deprecated/index.ts b/src/lib/deprecated/index.ts new file mode 100644 index 000000000..eaf5eea7f --- /dev/null +++ b/src/lib/deprecated/index.ts @@ -0,0 +1 @@ +export * from './button'; diff --git a/src/lib/index.ts b/src/lib/index.ts index 8a376cb20..0d7988c1c 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -82,6 +82,11 @@ import { ToolbarComponent } from './toolbar'; import { TooltipComponent } from './tooltip'; import { ViewComponent, ViewSwitcherComponent } from './view-switcher'; +/** + * Deprecated imports + */ +import { DeprecatedButtonComponent } from './deprecated/button'; + export * from './accordion'; export * from './app-bar'; export * from './autocomplete'; @@ -153,6 +158,11 @@ export * from './tooltip'; export * from './utils'; export * from './view-switcher'; +/** + * Deprecated exports + */ +export * from './deprecated/button'; + const CUSTOM_ELEMENTS = [ AccordionComponent, AppBarComponent, @@ -244,3 +254,15 @@ const CUSTOM_ELEMENTS = [ export function defineComponents(): void { defineCustomElements(CUSTOM_ELEMENTS); } + +/** + * Deprecated component registration + */ + +const DEPRECATED_CUSTOM_ELEMENTS = [ + DeprecatedButtonComponent +]; + +export function defineDeprecatedComponents(): void { + defineCustomElements(DEPRECATED_CUSTOM_ELEMENTS); +}