Skip to content

Commit 7898655

Browse files
feat(status-light)!: migrate status-light to s2 styles and second-gen architecture (#5800)
- create shared types file - import types into core status-light - imports new types in status light base - adds the api overrides for color and semantic variants - adds documentation - ensures the disabled state is not supported in S2 and throws a warning - ensures the disabled property still adds the aria-disabled attribute for S1 - removes disabled logic from base class - ensures the variants values are validated and throws a warning if not * refactor(status-light): status light second-gen - imports new S2 types for colors and semantic variants - adds the api overrides - adds documentation and example usage - refactors template render to use CSS classes and classMap() * refactor(status-light): status light first-gen - imports new S1 types for colors and semantic variants - adds the api overrides and extra disabled property - adds documentation, deprecation notices, and example usage - adds disabled logic to first-gen subclass * feat(status-light): add s2 styles * feat(status-light): add second-gen stories * feat(statuslight): add sizeMap decorator * test(status-light): add dev mode warning tests in first-gen * test(statuslight): add second-gen test file
1 parent ebfe0dd commit 7898655

File tree

9 files changed

+951
-390
lines changed

9 files changed

+951
-390
lines changed

first-gen/packages/status-light/src/StatusLight.ts

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,100 @@
1313
import {
1414
CSSResultArray,
1515
html,
16+
PropertyValues,
1617
TemplateResult,
1718
} from '@spectrum-web-components/base';
18-
import { StatusLightBase } from '@swc/core/components/status-light';
19+
20+
import { property } from '@spectrum-web-components/base/src/decorators.js';
21+
22+
import {
23+
STATUSLIGHT_VARIANTS_COLOR_S1,
24+
STATUSLIGHT_VARIANTS_S1,
25+
STATUSLIGHT_VARIANTS_SEMANTIC_S1,
26+
StatusLightBase,
27+
type StatusLightVariantS1,
28+
} from '@swc/core/components/status-light';
29+
1930
import statusLightStyles from './status-light.css.js';
2031

32+
/**
33+
* @deprecated The `STATUSLIGHT_VARIANTS` export is deprecated and will be removed
34+
* in a future release. If needed, you can access the internal
35+
* `StatusLight.VARIANTS` property from the constructor.
36+
*/
37+
export const STATUSLIGHT_VARIANTS = STATUSLIGHT_VARIANTS_S1;
38+
39+
/**
40+
* @deprecated The `StatusLightVariant` type export is deprecated and will be removed
41+
* in a future release. If needed, you can infer this type from the `StatusLight`
42+
* prototype as follows: `typeof StatusLight.prototype.variant`
43+
*/
44+
export type StatusLightVariant = StatusLightVariantS1;
45+
2146
/**
2247
* @element sp-status-light
2348
*
2449
* @slot - text label of the Status Light
2550
*/
2651
export class StatusLight extends StatusLightBase {
52+
// ────────────────────
53+
// API OVERRIDES
54+
// ────────────────────
55+
56+
/**
57+
* @internal
58+
*/
59+
static override readonly VARIANTS_COLOR = STATUSLIGHT_VARIANTS_COLOR_S1;
60+
61+
/**
62+
* @internal
63+
*/
64+
static override readonly VARIANTS_SEMANTIC =
65+
STATUSLIGHT_VARIANTS_SEMANTIC_S1;
66+
67+
/**
68+
* @internal
69+
*/
70+
static override readonly VARIANTS = STATUSLIGHT_VARIANTS_S1;
71+
72+
/**
73+
* The variant of the status light.
74+
*/
75+
@property({ type: String, reflect: true })
76+
public override variant: StatusLightVariantS1 = 'info';
77+
78+
// ───────────────────────
79+
// API ADDITIONS
80+
// ───────────────────────
81+
82+
/**
83+
* @deprecated The `disabled` property is is deprecated and will be removed
84+
* in a future release.
85+
*
86+
* A status light in a disabled state shows that a status exists, but is not available in that circumstance. This can be used to maintain layout continuity and communicate that a status may become available later.
87+
*
88+
*/
89+
@property({ type: Boolean, reflect: true })
90+
public disabled = false;
91+
92+
// ──────────────────────
93+
// IMPLEMENTATION
94+
// ──────────────────────
95+
96+
protected override updated(changes: PropertyValues): void {
97+
super.updated(changes);
98+
if (changes.has('disabled')) {
99+
if (this.disabled) {
100+
this.setAttribute('aria-disabled', 'true');
101+
} else {
102+
this.removeAttribute('aria-disabled');
103+
}
104+
}
105+
}
106+
// ──────────────────────────────
107+
// RENDERING & STYLING
108+
// ──────────────────────────────
109+
27110
public static override get styles(): CSSResultArray {
28111
return [statusLightStyles];
29112
}

first-gen/packages/status-light/test/status-light.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import '@spectrum-web-components/status-light/sp-status-light.js';
1313
import { StatusLight } from '@spectrum-web-components/status-light';
1414
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
15+
import { spy } from 'sinon';
1516

1617
describe('Status Light', () => {
1718
it('loads correctly', async () => {
@@ -41,4 +42,41 @@ describe('Status Light', () => {
4142
expect(el.hasAttribute('aria-disabled')).to.be.true;
4243
expect(el.getAttribute('aria-disabled')).to.equal('true');
4344
});
45+
46+
describe('dev mode warnings', () => {
47+
let warningMessage: typeof window.__swc.warn;
48+
49+
beforeEach(() => {
50+
// Create __swc if it doesn't exist
51+
window.__swc = window.__swc || { warn: () => {} };
52+
// Store original warn function
53+
warningMessage = window.__swc.warn;
54+
// Reset issued warnings to avoid dedupe interference
55+
window.__swc.issuedWarnings = new Set();
56+
// Enable debug guard
57+
window.__swc.DEBUG = true;
58+
});
59+
60+
afterEach(() => {
61+
// Restore original warn function
62+
window.__swc.warn = warningMessage;
63+
});
64+
65+
it('warns when unsupported variant is used (brown)', async () => {
66+
const warnSpy = spy();
67+
window.__swc.warn = warnSpy as unknown as typeof window.__swc.warn;
68+
69+
const el = await fixture<StatusLight>(html`
70+
<sp-status-light variant="brown"></sp-status-light>
71+
`);
72+
73+
await elementUpdated(el);
74+
75+
expect(warnSpy.called).to.be.true;
76+
expect(warnSpy.firstCall.args[0]).to.equal(el);
77+
expect(warnSpy.firstCall.args[1]).to.equal(
78+
`<${el.localName}> element expects the "variant" attribute to be one of the following:`
79+
);
80+
});
81+
});
4482
});

second-gen/packages/core/components/status-light/StatusLight.base.ts

Lines changed: 83 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,52 +9,108 @@
99
* OF ANY KIND, either express or implied. See the License for the specific language
1010
* governing permissions and limitations under the License.
1111
*/
12-
1312
import { PropertyValues } from 'lit';
1413
import { property } from 'lit/decorators.js';
1514

1615
import { SizedMixin, SpectrumElement } from '@swc/core/shared/base';
1716

17+
import { type StatusLightVariant } from './StatusLight.types';
18+
1819
/**
19-
* @element sp-status-light
20+
* A status light is a great way to convey semantic meaning and the condition of an entity, such as statuses and categories. It provides visual indicators through colored dots accompanied by descriptive text.
2021
*
21-
* @slot - text label of the Status Light
22+
* @slot - The text label of the status light.
2223
*/
2324
export abstract class StatusLightBase extends SizedMixin(SpectrumElement, {
2425
noDefaultSize: true,
2526
}) {
27+
// ─────────────────────────
28+
// API TO OVERRIDE
29+
// ─────────────────────────
30+
31+
/**
32+
* @internal
33+
*
34+
* A readonly array of the valid color variants for the status light.
35+
*
36+
* This is an actual internal property, intended not for customer use
37+
* but for use in internal validation logic, stories, tests, etc.
38+
*
39+
* Because S1 and S2 support different color variants, the value of this
40+
* property must be set in each subclass.
41+
*/
42+
static readonly VARIANTS_COLOR: readonly string[];
43+
2644
/**
27-
* A status light in a disabled state shows that a status exists, but is not available in that circumstance. This can be used to maintain layout continuity and communicate that a status may become available later.
45+
* @internal
46+
*
47+
* A readonly array of the valid semantic variants for the status light.
48+
*
49+
* This is an actual internal property, intended not for customer use
50+
* but for use in internal validation logic, stories, tests, etc.
51+
*
52+
* Because S1 and S2 support different semantic variants, the value of this
53+
* property must be set in each subclass.
2854
*/
29-
@property({ type: Boolean, reflect: true })
30-
public disabled = false;
55+
static readonly VARIANTS_SEMANTIC: readonly string[];
3156

3257
/**
33-
* The visual variant to apply to this status light.
58+
* @internal
59+
*
60+
* A readonly array of all valid variants for the status light.
61+
*
62+
* This is an actual internal property, intended not for customer use
63+
* but for use in internal validation logic, stories, tests, etc.
64+
*
65+
* Because S1 and S2 support different variants, the value of this
66+
* property must be set in each subclass.
3467
*/
35-
@property({ reflect: true })
36-
public variant:
37-
| 'negative'
38-
| 'notice'
39-
| 'positive'
40-
| 'info'
41-
| 'neutral'
42-
| 'yellow'
43-
| 'fuchsia'
44-
| 'indigo'
45-
| 'seafoam'
46-
| 'chartreuse'
47-
| 'magenta'
48-
| 'celery'
49-
| 'purple' = 'info';
68+
static readonly VARIANTS: readonly string[];
69+
70+
/**
71+
* @internal
72+
*
73+
* The variant of the status light.
74+
*
75+
* This is a public property, but its valid values vary between S1 and S2,
76+
* so the property (and its docs) need to be redefined in each subclass.
77+
*
78+
* The type declared here is a union of the valid values for S1 and S2,
79+
* and should be narrowed in each subclass.
80+
*/
81+
@property({ type: String, reflect: true })
82+
public variant: StatusLightVariant = 'info';
83+
84+
// ──────────────────────
85+
// IMPLEMENTATION
86+
// ──────────────────────
5087

5188
protected override updated(changes: PropertyValues): void {
5289
super.updated(changes);
53-
if (changes.has('disabled')) {
54-
if (this.disabled) {
55-
this.setAttribute('aria-disabled', 'true');
56-
} else {
57-
this.removeAttribute('aria-disabled');
90+
if (window.__swc?.DEBUG) {
91+
const constructor = this.constructor as typeof StatusLightBase;
92+
if (!constructor.VARIANTS.includes(this.variant)) {
93+
window.__swc.warn(
94+
this,
95+
`<${this.localName}> element expects the "variant" attribute to be one of the following:`,
96+
'https://opensource.adobe.com/spectrum-web-components/components/status-light/#variants',
97+
{
98+
issues: [...constructor.VARIANTS],
99+
}
100+
);
101+
}
102+
// Check disabled property if it exists (S1 only)
103+
if (this.hasAttribute('disabled') && !('disabled' in this)) {
104+
window.__swc.warn(
105+
this,
106+
`<${this.localName}> element does not support the disabled state.`,
107+
'https://opensource.adobe.com/spectrum-web-components/components/status-light/#states',
108+
{
109+
issues: [
110+
'disabled is not a supported property in Spectrum 2',
111+
],
112+
}
113+
);
58114
}
59115
}
60116
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* Copyright 2025 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
/*
14+
* @todo The S1 types can be removed once we are no longer maintaining 1st-gen.
15+
*/
16+
17+
export const STATUSLIGHT_VARIANTS_SEMANTIC = [
18+
'neutral',
19+
'info',
20+
'positive',
21+
'negative',
22+
'notice',
23+
] as const;
24+
25+
export const STATUSLIGHT_VARIANTS_SEMANTIC_S1 = [
26+
...STATUSLIGHT_VARIANTS_SEMANTIC,
27+
'accent',
28+
] as const;
29+
30+
export const STATUSLIGHT_VARIANTS_SEMANTIC_S2 = [
31+
...STATUSLIGHT_VARIANTS_SEMANTIC,
32+
] as const;
33+
34+
export const STATUSLIGHT_VARIANTS_COLOR_S1 = [
35+
'fuchsia',
36+
'indigo',
37+
'magenta',
38+
'purple',
39+
'seafoam',
40+
'yellow',
41+
'chartreuse',
42+
'celery',
43+
'cyan',
44+
] as const;
45+
46+
export const STATUSLIGHT_VARIANTS_COLOR_S2 = [
47+
...STATUSLIGHT_VARIANTS_COLOR_S1,
48+
'pink',
49+
'turquoise',
50+
'brown',
51+
'cinnamon',
52+
'silver',
53+
] as const;
54+
55+
export const STATUSLIGHT_VARIANTS_S1 = [
56+
...STATUSLIGHT_VARIANTS_SEMANTIC_S1,
57+
...STATUSLIGHT_VARIANTS_COLOR_S1,
58+
] as const;
59+
60+
export const STATUSLIGHT_VARIANTS_S2 = [
61+
...STATUSLIGHT_VARIANTS_SEMANTIC_S2,
62+
...STATUSLIGHT_VARIANTS_COLOR_S2,
63+
] as const;
64+
65+
export type StatusLightVariantS1 = (typeof STATUSLIGHT_VARIANTS_S1)[number];
66+
export type StatusLightVariantS2 = (typeof STATUSLIGHT_VARIANTS_S2)[number];
67+
export type StatusLightVariant = StatusLightVariantS1 | StatusLightVariantS2;

second-gen/packages/core/components/status-light/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010
* governing permissions and limitations under the License.
1111
*/
1212
export * from './StatusLight.base';
13+
export * from './StatusLight.types';

0 commit comments

Comments
 (0)