Skip to content
Draft
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
44 changes: 42 additions & 2 deletions 1st-gen/packages/illustrated-message/src/IllustratedMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,51 @@ export class IllustratedMessage extends SpectrumElement {
return [headingStyles, bodyStyles, messageStyles];
}

/**
* @deprecated Use `<h2 slot="heading">` instead.
*/
@property()
public heading = '';
public get heading(): string {
return this._heading;
}

public set heading(value: string) {
if (window.__swc?.DEBUG && value) {
window.__swc.warn(
this,
`The "heading" property on <${this.localName}> has been deprecated and will be removed in a future release. Use <h2 slot="heading"> instead.`,
'https://opensource.adobe.com/spectrum-web-components/components/illustrated-message/',
{ level: 'deprecation' }
);
}
this._heading = value;
this.requestUpdate('heading', this._heading);
}

private _heading = '';

/**
* @deprecated Use `<span slot="description">` instead.
*/
@property()
public description = '';
public get description(): string {
return this._description;
}

public set description(value: string) {
if (window.__swc?.DEBUG && value) {
window.__swc.warn(
this,
`The "description" property on <${this.localName}> has been deprecated and will be removed in a future release. Use <span slot="description"> instead.`,
'https://opensource.adobe.com/spectrum-web-components/components/illustrated-message/',
{ level: 'deprecation' }
);
}
this._description = value;
this.requestUpdate('description', this._description);
}

private _description = '';

protected override render(): TemplateResult {
return html`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,101 @@
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import { expect, fixture, html } from '@open-wc/testing';
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
import { stub } from 'sinon';

import '@spectrum-web-components/illustrated-message/sp-illustrated-message.js';

import { IllustratedMessage } from '../';

describe('Illustrated Message', () => {
describe('dev mode', () => {
let consoleWarnStub!: ReturnType<typeof stub>;
before(() => {
window.__swc.verbose = true;
consoleWarnStub = stub(console, 'warn');
});
afterEach(() => {
consoleWarnStub.resetHistory();
});
after(() => {
window.__swc.verbose = false;
consoleWarnStub.restore();
});
it('warns when deprecated "heading" property is used', async () => {
const el = await fixture<IllustratedMessage>(html`
<sp-illustrated-message
heading="Drag and Drop Your File"
></sp-illustrated-message>
`);

await elementUpdated(el);

expect(consoleWarnStub.called).to.be.true;
const spyCall = consoleWarnStub.getCall(0);
expect(
spyCall.args[0].includes('deprecated'),
'confirm deprecation message'
).to.be.true;
expect(
spyCall.args[0].includes('heading'),
'confirm message references heading property'
).to.be.true;
expect(
spyCall.args[spyCall.args.length - 1],
'confirm `data` shape'
).to.deep.equal({
data: {
localName: 'sp-illustrated-message',
type: 'api',
level: 'deprecation',
},
});
});

it('warns when deprecated "description" property is used', async () => {
const el = await fixture<IllustratedMessage>(html`
<sp-illustrated-message
description="Additional descriptive text"
></sp-illustrated-message>
`);

await elementUpdated(el);

expect(consoleWarnStub.called).to.be.true;
const spyCall = consoleWarnStub.getCall(0);
expect(
spyCall.args[0].includes('deprecated'),
'confirm deprecation message'
).to.be.true;
expect(
spyCall.args[0].includes('description'),
'confirm message references description property'
).to.be.true;
expect(
spyCall.args[spyCall.args.length - 1],
'confirm `data` shape'
).to.deep.equal({
data: {
localName: 'sp-illustrated-message',
type: 'api',
level: 'deprecation',
},
});
});

it('does not warn when slot-based API is used', async () => {
await fixture<IllustratedMessage>(html`
<sp-illustrated-message>
<h2 slot="heading">Drag and Drop Your File</h2>
<span slot="description">Additional descriptive text</span>
</sp-illustrated-message>
`);

expect(consoleWarnStub.called).to.be.false;
});
});

it('loads', async () => {
const el = await fixture<IllustratedMessage>(html`
<sp-illustrated-message
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* 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 { PropertyValues } from 'lit';
import { property } from 'lit/decorators.js';

import { SpectrumElement } from '@spectrum-web-components/core/element/index.js';

import {
ILLUSTRATED_MESSAGE_VALID_ORIENTATIONS,
ILLUSTRATED_MESSAGE_VALID_SIZES,
type IllustratedMessageOrientation,
type IllustratedMessageSize,
} from './IllustratedMessage.types.js';

/**
* An illustrated message displays an illustration and a message, typically
* used in empty states or error pages.
*
* @slot - Decorative or informative SVG illustration
* @slot heading - The heading element, h2–h6
* @todo SWC-1943 Add slot constraints once the CEM slot constraints work is complete:
* `{required} {allowedChildren: h2, h3, h4, h5, h6} {maxChildren: 1}`
* @slot description - Supporting description text
*/
export abstract class IllustratedMessageBase extends SpectrumElement {
// ─────────────────────────
// API TO OVERRIDE
// ─────────────────────────

/**
* @internal
*/
static readonly VALID_SIZES: readonly IllustratedMessageSize[] =
ILLUSTRATED_MESSAGE_VALID_SIZES;

/**
* @internal
*/
static readonly VALID_ORIENTATIONS: readonly IllustratedMessageOrientation[] =
ILLUSTRATED_MESSAGE_VALID_ORIENTATIONS;

// ──────────────────
// SHARED API
// ──────────────────

/**
* The size of the message
*/
@property({ type: String, reflect: true })
public size: IllustratedMessageSize = 'm';

/**
* The layout orientation
*/
@property({ type: String, reflect: true })
public orientation: IllustratedMessageOrientation = 'vertical';

// ──────────────────────
// IMPLEMENTATION
// ──────────────────────

protected override updated(changedProperties: PropertyValues): void {
super.updated(changedProperties);
if (window.__swc?.DEBUG) {
if (
changedProperties.has('size') &&
!ILLUSTRATED_MESSAGE_VALID_SIZES.includes(this.size)
) {
window.__swc.warn(
this,
`<${this.localName}> received an invalid "size" value of "${this.size}". Valid values are ${ILLUSTRATED_MESSAGE_VALID_SIZES.join(', ')}.`,
'https://opensource.adobe.com/spectrum-web-components/components/illustrated-message/',
{ issues: [`size="${this.size}"`] }
);
}

if (
changedProperties.has('orientation') &&
!ILLUSTRATED_MESSAGE_VALID_ORIENTATIONS.includes(this.orientation)
) {
window.__swc.warn(
this,
`<${this.localName}> received an invalid "orientation" value of "${this.orientation}". Valid values are ${ILLUSTRATED_MESSAGE_VALID_ORIENTATIONS.join(', ')}.`,
'https://opensource.adobe.com/spectrum-web-components/components/illustrated-message/',
{ issues: [`orientation="${this.orientation}"`] }
);
}
}
}

/**
* @internal
*
* Validates that the heading slot only contains `<h2>`–`<h6>` elements.
* Rendering subclasses must wire this to the heading slot's `slotchange`
* event (e.g. `<slot name="heading" @slotchange=${this.handleHeadingSlotChange}>`)
* for the validation warning to fire.
*/
protected handleHeadingSlotChange(event: Event): void {
if (window.__swc?.DEBUG) {
const headingSlot = event.target as HTMLSlotElement;
for (const el of headingSlot.assignedElements()) {
if (!['H2', 'H3', 'H4', 'H5', 'H6'].includes(el.tagName)) {
window.__swc.warn(
this,
`<${this.localName}> heading slot received a <${el.tagName.toLowerCase()}> element. Only <h2>–<h6> elements are allowed in the heading slot.`,
'https://opensource.adobe.com/spectrum-web-components/components/illustrated-message/',
{ issues: [`heading slot: <${el.tagName.toLowerCase()}>`] }
);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* 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';

export const ILLUSTRATED_MESSAGE_VALID_SIZES = [
's',
'm',
'l',
] as const satisfies readonly ElementSize[];

export const ILLUSTRATED_MESSAGE_VALID_ORIENTATIONS = [
'vertical',
'horizontal',
] as const satisfies readonly string[];

export type IllustratedMessageSize =
(typeof ILLUSTRATED_MESSAGE_VALID_SIZES)[number];

export type IllustratedMessageOrientation =
(typeof ILLUSTRATED_MESSAGE_VALID_ORIENTATIONS)[number];
13 changes: 13 additions & 0 deletions 2nd-gen/packages/core/components/illustrated-message/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 './IllustratedMessage.base.js';
export * from './IllustratedMessage.types.js';
6 changes: 5 additions & 1 deletion 2nd-gen/packages/swc/.storybook/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,11 @@ const preview = {
'Help text',
['Rendering and styling migration analysis'],
'Illustrated message',
['Rendering and styling migration analysis'],
[
'Accessibility migration analysis',
'Migration plan',
'Rendering and styling migration analysis',
],
'Infield button',
['Rendering and styling migration analysis'],
'Infield progress circle',
Expand Down
Loading
Loading