Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions 1st-gen/packages/action-button/src/ActionButton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,18 @@ export class ActionButton extends SizedMixin(ButtonBase, {
return [...super.styles, buttonStyles, cornerTriangleStyles];
}

/**
* @deprecated The `emphasized` property is deprecated and will be removed
* in a future release.
*/
@property({ type: Boolean, reflect: true })
public emphasized = false;

/**
* Hold affordance is not included in the initial 2nd-gen `swc-action-button`
* release. It is planned for a future phase. See the action-button migration
* plan for consumer options in the interim.
*/
@property({ type: Boolean, reflect: true, attribute: 'hold-affordance' })
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not deprecated technically. its just not included in scope of MVP but should be addressed sooner than later.

For all deprecation notices: Also we might want to have a chat in a sync about the deprecation language because these are a bit confusing to me since there wont be a future release its removed and we are referencing gen2 components that dont exist yet

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right on! I updated it but can revise as needed. ✨

public holdAffordance = false;

Expand All @@ -74,6 +83,9 @@ export class ActionButton extends SizedMixin(ButtonBase, {
/**
* Whether an Action Button with `role='button'`
* should also be `aria-pressed='true'`
*
* @deprecated The `selected` property is deprecated and will be removed in
* a future release. Use `swc-toggle-button` for selectable button behavior.
*/
@property({ type: Boolean, reflect: true })
public selected = false;
Expand All @@ -82,6 +94,10 @@ export class ActionButton extends SizedMixin(ButtonBase, {
* Whether to automatically manage the `selected`
* attribute on interaction and whether `aria-pressed="false"`
* should be used when `selected === false`
*
* @deprecated The `toggles` property is deprecated and will be removed in
* a future release. Use `swc-toggle-button` or `swc-toggle-button-group`
* for toggle button behavior.
*/
@property({ type: Boolean, reflect: true })
public toggles = false;
Expand Down Expand Up @@ -241,9 +257,41 @@ export class ActionButton extends SizedMixin(ButtonBase, {
this.setAttribute('aria-expanded', this.selected ? 'true' : 'false');
}
}
if (changes.has('selected') && this.selected && window.__swc?.DEBUG) {
window.__swc.warn(
this,
`The "selected" attribute on <${this.localName}> is deprecated and will be removed in a future release.`,
'https://opensource.adobe.com/spectrum-web-components/components/action-button/',
{ level: 'deprecation' }
);
}
}
if (changes.has('toggles') && this.toggles && window.__swc?.DEBUG) {
window.__swc.warn(
this,
`The "toggles" attribute on <${this.localName}> is deprecated and will be removed in a future release.`,
'https://opensource.adobe.com/spectrum-web-components/components/action-button/',
{ level: 'deprecation' }
);
}
if (changes.has('emphasized') && this.emphasized && window.__swc?.DEBUG) {
window.__swc.warn(
this,
`The "emphasized" attribute on <${this.localName}> is deprecated and will be removed in a future release.`,
'https://opensource.adobe.com/spectrum-web-components/components/action-button/',
{ level: 'deprecation' }
);
}
if (changes.has('holdAffordance')) {
if (this.holdAffordance) {
if (window.__swc?.DEBUG) {
window.__swc.warn(
this,
`The "hold-affordance" attribute on <${this.localName}> is not included in the initial 2nd-gen release and will be addressed in a future phase.`,
'https://opensource.adobe.com/spectrum-web-components/components/action-button/',
{ level: 'deprecation' }
);
}
this.addEventListener(
'pointerdown',
this.handlePointerdownHoldAffordance
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import type { ElementSize } from '@spectrum-web-components/core/mixins/index.js';

// ──────────────────
// SHARED
// ──────────────────

export const ACTION_BUTTON_VALID_SIZES = [
'xs',
's',
'm',
'l',
'xl',
] as const satisfies readonly ElementSize[];

export const ACTION_BUTTON_STATIC_COLORS = ['white', 'black'] as const;

// ──────────────────
// TYPES
// ──────────────────

export type ActionButtonSize = (typeof ACTION_BUTTON_VALID_SIZES)[number];
export type ActionButtonStaticColor =
(typeof ACTION_BUTTON_STATIC_COLORS)[number];
12 changes: 12 additions & 0 deletions 2nd-gen/packages/core/components/action-button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
export * from './ActionButton.types.js';
5 changes: 4 additions & 1 deletion 2nd-gen/packages/core/mixins/sized-mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ export function SizedMixin<T extends Constructor<ReactiveElement>>(
public set size(value: ElementSize) {
const fallbackSize = noDefaultSize ? null : defaultSize;
const size = (value ? value.toLocaleLowerCase() : value) as ElementSize;
const classValidSizes = (
this.constructor as unknown as SizedElementConstructor
).VALID_SIZES;
Comment on lines +66 to +68
Copy link
Copy Markdown
Contributor

@caseyisonit caseyisonit Jun 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you share why this is needed? validSizes is passed in as an arg and this is disconnecting it from usage.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah, it's more or less because ButtonBase provides BUTTON_VALID_SIZES which doesn't have xs, preventing that size from being set. If we don't need xs we could get rid of this. ✨

const validSize = (
validSizes.includes(size) ? size : fallbackSize
classValidSizes.includes(size) ? size : fallbackSize
) as ElementSize;
if (validSize) {
this.setAttribute('size', validSize);
Expand Down
7 changes: 7 additions & 0 deletions 2nd-gen/packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
},
"type": "module",
"exports": {
"./components/action-button": {
"types": "./dist/components/action-button/index.d.ts",
"import": "./dist/components/action-button/index.js"
},
"./components/alert-banner": {
"types": "./dist/components/alert-banner/index.d.ts",
"import": "./dist/components/alert-banner/index.js"
Expand Down Expand Up @@ -165,6 +169,9 @@
},
"typesVersions": {
"*": {
"components/action-button": [
"dist/components/action-button/index.d.ts"
],
"components/alert-banner": [
"dist/components/alert-banner/index.d.ts"
],
Expand Down
149 changes: 149 additions & 0 deletions 2nd-gen/packages/swc/components/action-button/ActionButton.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

import { CSSResultArray, html, TemplateResult } from 'lit';
import { property, state } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import { ifDefined } from 'lit/directives/if-defined.js';

import {
ACTION_BUTTON_VALID_SIZES,
type ActionButtonSize,
type ActionButtonStaticColor,
} from '@spectrum-web-components/core/components/action-button';
import { ButtonBase } from '@spectrum-web-components/core/components/button';

import styles from './action-button.css';

/**
* A compact action button for toolbars, action groups, and icon-first chrome.
*
* @element swc-action-button
* @since 0.0.1
*
* @slot - Visible button label.
* @slot icon - Optional leading icon displayed before the label.
*
* @example
* <swc-action-button>Edit</swc-action-button>
*
* @example
* <swc-action-button quiet>
* <sp-icon-edit slot="icon"></sp-icon-edit>
* Edit
* </swc-action-button>
*/
export class ActionButton extends ButtonBase {
// ──────────────────────
// API OVERRIDES
// ──────────────────────

/** @internal */
static override readonly VALID_SIZES: readonly ActionButtonSize[] =
ACTION_BUTTON_VALID_SIZES;

// ───────────────────
// API ADDITIONS
// ───────────────────

/** Applies the quiet (low-emphasis) visual treatment. */
@property({ type: Boolean, reflect: true })
public quiet: boolean = false;

/** Static color treatment for display over colored or image backgrounds. */
@property({ type: String, reflect: true, attribute: 'static-color' })
public staticColor?: ActionButtonStaticColor;

// ──────────────────────────────
// RENDERING & STYLING
// ──────────────────────────────

public static override get styles(): CSSResultArray {
return [styles];
}

// Observe aria-haspopup / aria-expanded without @property so they don't
// conflict with ARIAMixin types on HTMLElement or appear in the public CEM.
static override get observedAttributes(): string[] {
return [...super.observedAttributes, 'aria-haspopup', 'aria-expanded'];
}

// Forwarded to the inner <button> for menu-trigger patterns; stripped from
// the host after reading to avoid duplicate ARIA state on both elements.
@state()
private _ariaHasPopup?: string;

@state()
private _ariaExpanded?: string;

// Guard against re-entrant attributeChangedCallback: removeAttribute fires a
// second callback with value=null; the guard prevents that from clearing the
// state we just set.
private _ariaForwardingInProgress = false;

/** @internal */
override attributeChangedCallback(
name: string,
old: string | null,
value: string | null
): void {
const isAriaPassthrough =
name === 'aria-haspopup' || name === 'aria-expanded';
if (isAriaPassthrough && this._ariaForwardingInProgress) {
return;
}
if (isAriaPassthrough) {
if (name === 'aria-haspopup') {
this._ariaHasPopup = value ?? undefined;
} else {
this._ariaExpanded = value ?? undefined;
}
if (value !== null) {
this._ariaForwardingInProgress = true;
this.removeAttribute(name);
this._ariaForwardingInProgress = false;
}
return;
}
super.attributeChangedCallback(name, old, value);
}

protected override render(): TemplateResult {
return html`
<button
class=${classMap({
'swc-ActionButton': true,
'swc-ActionButton--hasIcon': this.hasIcon,
'swc-ActionButton--iconOnly': this.hasIcon && !this.hasLabel,
'swc-ActionButton--pendingActive': this.pendingActive,
})}
type="button"
@click=${this.handleClick}
?disabled=${this.disabled}
aria-disabled=${ifDefined(
this.pending && !this.disabled ? 'true' : undefined
)}
aria-label=${ifDefined(
this.pending ? this.getPendingAccessibleName() : this.accessibleLabel
)}
aria-haspopup=${ifDefined(this._ariaHasPopup)}
aria-expanded=${ifDefined(this._ariaExpanded)}
>
<slot name="icon"></slot>
<span class="swc-ActionButton-label">
<slot></slot>
</span>
<span class="swc-ActionButton-pendingIcon" aria-hidden="true"></span>
</button>
`;
}
}
17 changes: 17 additions & 0 deletions 2nd-gen/packages/swc/components/action-button/action-button.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/

.swc-ActionButton {
display: inline-flex;
align-items: center;
justify-content: center;
}
12 changes: 12 additions & 0 deletions 2nd-gen/packages/swc/components/action-button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright 2026 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
export * from './ActionButton.js';
Loading
Loading