Skip to content
Merged
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
12 changes: 6 additions & 6 deletions .ai/skills/migration-api/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ The plan's architectural sections also govern **where each property and type lan

**Deferred items are out of scope.** The plan may explicitly defer features to follow-up tickets (e.g. form-associated behavior, cross-root ARIA). Do not implement deferred items in Phase 3 even if they appear in the API section of the plan — mark them as skipped and note the tracking ticket.

**1st-gen changes are limited to deprecation notices only.** When adding deprecation notices to 1st-gen, restrict changes to: (1) `@deprecated` JSDoc on exported types, consts, and properties; and (2) `window.__swc.warn()` calls added inside already-existing setters or other existing code paths. Do not refactor 1st-gen code structure — no new backing types, no converting simple properties to getter/setter patterns, no new private fields. TypeScript TS6385 hints that arise from deprecated types referencing each other internally are an accepted side effect and do not require structural fixes.
**For each breaking change in the migration plan, you must add both `@deprecated` JSDoc and a `window.__swc.warn()` call to the 1st-gen file.** JSDoc alone is not enough — types and IDE tooling pick it up, but consumers building against compiled output won't see it. Restrict 1st-gen changes to these two things only: do not add new backing types, do not convert plain properties to getter/setter patterns, do not add new private fields.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The previous instructions stated "When adding deprecation notices to 1st-gen, restrict changes to: (1) .....", which was really more just a guardrail in how to add notices, but it wasn't explicit about needing to add the deprecation warning. I didn't find the skill prompts I used (in the migration skill cheat sheet canvas) prompting claude to add the deprecation notice.


For where the warn goes — inside an existing setter, or in the `updated()` lifecycle method with a `changes.has()` guard and a non-default value check for a setter-less `@property` — follow the [Where to place the warning](../../../CONTRIBUTOR-DOCS/02_style-guide/02_typescript/17_debug-validation.md#where-to-place-the-warning) section of the debug and validation guide. The setter-less pattern matters here because Phase 3 does not allow converting a plain `@property` into a getter/setter just to host the warn, so `updated()` is its only home.

After writing the deprecation notices, scan every `@deprecated` tag in the 1st-gen file and confirm a `__swc.warn()` call exists for each one before closing Phase 3. TypeScript TS6385 hints that arise from deprecated types referencing each other internally are an accepted side effect and do not require structural fixes.

**`@deprecated` JSDoc message format** — the washing machine workflow Phase 3 section on "Deprecating 1st-gen type and const exports" says to document migration to class-level inference (e.g. `typeof Badge.prototype.variant`, `typeof Badge.FIXED_VALUES`) when a class static already exists in 1st-gen. Apply this as follows:

Expand All @@ -26,11 +30,7 @@ The plan's architectural sections also govern **where each property and type lan
- Do not add new class statics to 1st-gen just to provide this alternative — that is a refactor and out of scope.
- For **renames**, note the new name in the JSDoc: "...will be replaced by `newName` in a future release."

For the `window.__swc.warn()` call format and the `{ level: 'deprecation' }` option, follow the **Deprecation warnings** section of [`CONTRIBUTOR-DOCS/02_style-guide/02_typescript/17_debug-validation.md`](../../../CONTRIBUTOR-DOCS/02_style-guide/02_typescript/17_debug-validation.md). Key rules from that guide:

- Use `{ level: 'deprecation' }` instead of `{ issues: [...] }`.
- If a 1st-gen replacement exists, include `Use "newProp" instead.` at the end of the message. If there is no 1st-gen alternative, omit the "Use" clause.
- Do not reference 2nd-gen APIs as replacements in runtime warn messages.
For the `window.__swc.warn()` call format, the `{ level: 'deprecation' }` option, and the rules on referencing a replacement in the message, follow the **[Deprecation warnings](../../../CONTRIBUTOR-DOCS/02_style-guide/02_typescript/17_debug-validation.md#deprecation-warnings)** section of the debug and validation guide. Applying its replacement-reference rule to migration: reference the 1st-gen replacement if one exists, otherwise omit the "Use" clause — and never point a 1st-gen runtime warn at a 2nd-gen API, since consumers on the 1st-gen build cannot use it.

## When to use this skill

Expand Down
7 changes: 7 additions & 0 deletions .changeset/quiet-owls-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@spectrum-web-components/progress-bar': patch
---

**docs(progress-bar):** Added deprecation notices to `<sp-progress-bar>` ahead of the Spectrum 2 migration.

The `label`, `side-label`, and `progress` properties are now marked `@deprecated` and emit `window.__swc.DEBUG` warnings pointing to their `<swc-progress-bar>` replacements (`label` slot, `label-position="side"`, and `value`). Runtime behavior is unchanged when debug validation is disabled.
9 changes: 9 additions & 0 deletions .changeset/spicy-melon-parade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@adobe/spectrum-wc': minor
---

**feat(progress-bar):** Added `<swc-progress-bar>`, the Spectrum 2 migration of Progress Bar.

Renames from `<sp-progress-bar>`: `progress` becomes `value`, `side-label` becomes `label-position="side"`, the `label` string attribute becomes a `label` named slot, and `--mod-progressbar-*` custom properties become `--swc-linear-progress-*`. The default `size` is now `'m'`.

New in Spectrum 2: `min-value` / `max-value` for arbitrary numeric ranges, `static-color="black"`, `value-label`, a `formatOptions` property, `accessible-label`, and a `description` slot. `over-background` is removed in favor of `static-color="white"`, and `role="progressbar"` plus the `aria-value*` attributes now live on an internal element rather than the host. See the Progress Bar migration guide for full upgrade steps.
41 changes: 40 additions & 1 deletion 1st-gen/packages/progress-bar/src/ProgressBar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export class ProgressBar extends SizedMixin(
@property({ type: Boolean, reflect: true })
public indeterminate = false;

/**
* @deprecated The `label` string attribute will be replaced by a `label`
* named slot in a future release.
*/
@property({ type: String, reflect: true })
public label = '';

Expand Down Expand Up @@ -82,9 +86,17 @@ export class ProgressBar extends SizedMixin(
}
private _overBackground: boolean = false;

/**
* @deprecated The `side-label` attribute will be replaced by
* `label-position="side"` in a future release.
*/
@property({ type: Boolean, reflect: true, attribute: 'side-label' })
public sideLabel = false;

/**
* @deprecated The `progress` property will be replaced by `value` in a
* future release.
*/
@property({ type: Number })
public progress = 0;

Expand Down Expand Up @@ -175,6 +187,33 @@ export class ProgressBar extends SizedMixin(
}
}

if (window.__swc?.DEBUG) {
if (changes.has('label') && this.label.length > 0) {
window.__swc.warn(
this,
`The "label" attribute on <${this.localName}> has been deprecated and will be removed in a future release. Use a "label" named slot, or set "aria-label" or "aria-labelledby" on the element instead.`,
'https://opensource.adobe.com/spectrum-web-components/components/progress-bar/#accessibility',
{ level: 'deprecation' }
);
}
if (changes.has('sideLabel') && this.sideLabel) {
window.__swc.warn(
this,
`The "side-label" attribute on <${this.localName}> has been deprecated and will be removed in a future release. Use label-position="side" instead.`,
'https://opensource.adobe.com/spectrum-web-components/components/progress-bar/',
{ level: 'deprecation' }
);
}
if (changes.has('progress') && this.progress !== 0) {
window.__swc.warn(
this,
`The "progress" property on <${this.localName}> has been deprecated and will be removed in a future release. Use the "value" attribute instead.`,
'https://opensource.adobe.com/spectrum-web-components/components/progress-bar/',
{ level: 'deprecation' }
);
}
}

if (window.__swc?.DEBUG) {
if (
!this.label &&
Expand All @@ -190,7 +229,7 @@ export class ProgressBar extends SizedMixin(
type: 'accessibility',
issues: [
'value supplied to the "label" attribute, which will be displayed visually as part of the element, or',
'text content supplied directly to the <sp-progress-circle> element, or',
'text content supplied directly to the <sp-progress-bar> element, or',
'value supplied to the "aria-label" attribute, which will only be provided to screen readers, or',
'an element ID reference supplied to the "aria-labelledby" attribute, which will be provided by screen readers and will need to be managed manually by the parent application.',
],
Expand Down
48 changes: 19 additions & 29 deletions 1st-gen/packages/progress-bar/test/progress-bar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,29 +168,6 @@ describe('ProgressBar', () => {
consoleWarnStub.restore();
});

it('warns in Dev Mode when accessible attributes are not leveraged', async () => {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This test is a duplicate in the file, so I removed it

const el = await fixture<ProgressBar>(html`
<sp-progress-bar progress="50"></sp-progress-bar>
`);

await elementUpdated(el);

const spyCall = consoleWarnStub.getCall(0);
expect(
(spyCall.args[0] as string).includes('accessible'),
'confirm accessibility-centric message'
).to.be.true;
expect(
spyCall.args[spyCall.args.length - 1],
'confirm `data` shape'
).to.deep.equal({
data: {
localName: 'sp-progress-bar',
type: 'accessibility',
level: 'default',
},
});
});
it('resolves a language (en-US)', async () => {
const [languageContext] = createLanguageContext('en-US');
const test = await fixture(html`
Expand Down Expand Up @@ -270,13 +247,18 @@ describe('ProgressBar', () => {
'confirm deprecated over-background warning'
).to.be.true;

const spyCall = consoleWarnStub.getCall(0);
const spyCall = consoleWarnStub
.getCalls()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The tests were breaking because the warnStub no longer has just one warning with the introduction of the additional deprecation warnings.

.find((call: { args: unknown[] }) =>
(call.args[0] as string)?.includes('over-background')
);
expect(spyCall, 'over-background warning emitted').to.not.be.undefined;
expect(
(spyCall.args[0] as string).includes('deprecated'),
(spyCall!.args[0] as string).includes('deprecated'),
'confirm deprecated over-background warning'
).to.be.true;
expect(
spyCall.args[spyCall.args.length - 1],
spyCall!.args[spyCall!.args.length - 1],
'confirm `data` shape'
).to.deep.equal({
data: {
Expand All @@ -294,13 +276,21 @@ describe('ProgressBar', () => {
await elementUpdated(el);

expect(consoleWarnStub.called).to.be.true;
const spyCall = consoleWarnStub.getCall(0);
const spyCall = consoleWarnStub.getCalls().find(
(call: { args: unknown[] }) =>
(
call.args[call.args.length - 1] as {
data?: { type?: string };
}
)?.data?.type === 'accessibility'
);
expect(spyCall, 'accessibility warning emitted').to.not.be.undefined;
expect(
(spyCall.args[0] as string).includes('accessible'),
(spyCall!.args[0] as string).includes('accessible'),
'confirm accessibility-centric message'
).to.be.true;
expect(
spyCall.args[spyCall.args.length - 1],
spyCall!.args[spyCall!.args.length - 1],
'confirm `data` shape'
).to.deep.equal({
data: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* 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 { property } from 'lit/decorators.js';

import { SpectrumElement } from '@adobe/spectrum-wc-core/element/index.js';
import {
LINEAR_PROGRESS_VALID_SIZES,
LinearProgressMixin,
SizedMixin,
} from '@adobe/spectrum-wc-core/mixins/index.js';

/**
* A non-focusable, read-only bar that shows task progress (0–100, or a custom
* range) or an indeterminate loading animation when completion time is unknown.
* Implements the WAI-ARIA `progressbar` role on the shadow `.swc-LinearProgress`
* element (the host carries no ARIA).
*

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The a11y analysis requires role="progressbar" on the host, but this puts it on the shadow .swc-LinearProgress element. This is intentional because the host-level ARIA can't aria-labelledby/aria-describedby-reference the shadow-internal label/description ids (Sounds like ElementInternals reference-target not ready). This also matches Meter.

* @attribute {ElementSize} size - The size of the progress bar.
*/
export abstract class ProgressBarBase extends LinearProgressMixin(
SizedMixin(SpectrumElement, {
validSizes: LINEAR_PROGRESS_VALID_SIZES,
defaultSize: 'm',
})
) {
/**
* The size of the progress bar.
*
* @default m
*/
declare public size: (typeof LINEAR_PROGRESS_VALID_SIZES)[number];

// ─────────────────────────────────────────
// PROGRESS-BAR-SPECIFIC PROPERTIES
// ─────────────────────────────────────────

/**
* When true, the bar runs a looping fill animation and all four
* `aria-value*` attributes are omitted from the DOM. The visible
* value text is also omitted.
*
* @default false
*/
@property({ type: Boolean, reflect: true })
public indeterminate = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* 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.
*/

// Progress bar has no variant types — it uses a single accent fill.
// Shared linear-progress types (LinearProgressSize, LinearProgressStaticColor,
// LinearProgressLabelPosition, LinearProgressInterface) are re-exported from
// the mixin for consumer convenience.
export type {
LinearProgressInterface,
LinearProgressLabelPosition,
LinearProgressSize,
LinearProgressStaticColor,
} from '@adobe/spectrum-wc-core/mixins/index.js';
13 changes: 13 additions & 0 deletions 2nd-gen/packages/core/components/progress-bar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* 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 './ProgressBar.base.js';
export * from './ProgressBar.types.js';
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,6 @@ import {
ProgressCircleStaticColor,
} from './ProgressCircle.types.js';

/**
* @todo SWC-2037 Extract shared progress logic (ARIA, label, clamping, formatting,
* indeterminate derivation) into a `ProgressBase` mixin or abstract class in
* `core/components/progress/` so that both `ProgressCircleBase` and a future
* `ProgressBarBase` can extend it. Also add `formatOptions` support for
* progress-bar's custom value labels (e.g. "3 of 10", "45 MB / 100 MB").
*/

/**
* A progress circle component that visually represents the completion progress of a task.
* Can be used in both determinate (with specific progress value) and indeterminate (loading) states.
Expand Down
20 changes: 17 additions & 3 deletions 2nd-gen/packages/core/mixins/linear-progress-mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,20 @@ export function LinearProgressMixin<T extends Constructor<ReactiveElement>>(

private readonly _instanceId = ++nextLinearProgressId;

/**
* @internal
*
* Documentation URL used in DEBUG warnings. Derived from the custom
* element tag name so each concrete component gets an accurate link
* with no per-subclass override needed (e.g. `swc-meter` →
* `components-meter--docs`, `swc-progress-bar` →
* `components-progress-bar--docs`).
*/
protected get docsHref(): string {
const name = this.localName.replace(/^swc-/, '');
return `https://spectrum-web-components.adobe.com/?path=/docs/components-${name}--docs`;
}

/**
* @internal
*/
Expand Down Expand Up @@ -305,7 +319,7 @@ export function LinearProgressMixin<T extends Constructor<ReactiveElement>>(
window.__swc?.warn(
this,
`<${this.localName}> "value" (${value}) is outside the [${min}, ${max}] range and was clamped to ${this.clampedValue}.`,
'https://spectrum-web-components.adobe.com/?path=/docs/components-meter--docs',
this.docsHref,
{
issues: [
'set "value" within the "min-value" and "max-value" range, or',
Expand All @@ -324,12 +338,12 @@ export function LinearProgressMixin<T extends Constructor<ReactiveElement>>(
window.__swc?.warn(
this,
`<${this.localName}> requires an accessible name.`,
'https://spectrum-web-components.adobe.com/?path=/docs/components-meter--docs',
this.docsHref,
{
type: 'accessibility',
issues: [
'add visible label content via the "label" named slot, or',
'set the "accessible-label" attribute (or "accessibleLabel" property) when there is no visible label, for example a data grid of meters.',
'set the "accessible-label" attribute (or "accessibleLabel" property) when there is no visible label.',
],
}
);
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 @@ -95,6 +95,10 @@
"types": "./dist/components/popover/index.d.ts",
"import": "./dist/components/popover/index.js"
},
"./components/progress-bar": {
"types": "./dist/components/progress-bar/index.d.ts",
"import": "./dist/components/progress-bar/index.js"
},
"./components/progress-circle": {
"types": "./dist/components/progress-circle/index.d.ts",
"import": "./dist/components/progress-circle/index.js"
Expand Down Expand Up @@ -287,6 +291,9 @@
"components/meter": [
"dist/components/meter/index.d.ts"
],
"components/progress-bar": [
"dist/components/progress-bar/index.d.ts"
],
"components/progress-circle": [
"dist/components/progress-circle/index.d.ts"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ argTypes.value = {
* user actions rather than system progress. The accessible `meter` role and its
* `aria-value*` attributes live on the internal bar wrapper; the host carries no
* ARIA role.
*
* To show the progression of a system operation such as downloading or processing,
* see [Progress Bar](../?path=/docs/components-progress-bar--docs).
*/
const meta: Meta = {
title: 'Meter',
Expand Down
Loading
Loading