-
Notifications
You must be signed in to change notification settings - Fork 246
feat(avatar): migrate avatar component to S2 #6113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
cdransf
wants to merge
43
commits into
main
Choose a base branch
from
cdransf/avatar-migration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
f2b0881
chore(avatar): add 1st pass of migration plan
cdransf c974868
chore(avatar): iterate on component structure
cdransf 7869c9a
chore(avatar): iterate on styles
cdransf ea87867
chore(avatar): add stubs to storybook sidebar
cdransf e2c5d51
chore(avatar): improve alignment w/react spectrum + design docs
cdransf 02cee92
chore(avatar): improved storybook controls
cdransf 1994cce
fix(avatar): lint violations + storybook improvements
cdransf 6bf586a
fix(avatar): lint violations
cdransf 4f442cc
chore(avatar): revise migration plan
cdransf b94f730
chore(avatar): migrate api + typescript code
cdransf 43f999c
chore(avatar): implement a11y recommendations + aria-hidden on host e…
cdransf f8badd8
chore(avatar): migrate CSS to S2 style guide conventions
cdransf a98a220
chore(avatar): add Storybook interaction tests and Playwright ARIA sn…
cdransf 7c479da
chore(avatar): add Storybook documentation, args table defaults, and …
cdransf ce7686e
chore(avatar): create consumer-facing migration guide
cdransf 22a934e
chore(avatar): fix test failures
cdransf 25ebbaf
fix(avatar): clean up todos and other minor issues
cdransf 37f7a63
chore(avatar): adds disabled variant, tests and updates docs
cdransf 7579223
Update 2nd-gen/packages/swc/components/avatar/stories/avatar.stories.ts
cdransf 34356e9
Update 2nd-gen/packages/swc/components/avatar/stories/avatar.stories.ts
cdransf 25c5bde
Update CONTRIBUTOR-DOCS/03_project-planning/03_components/avatar/migr…
cdransf 047537f
Update CONTRIBUTOR-DOCS/03_project-planning/03_components/avatar/migr…
cdransf fb4d1c3
Update CONTRIBUTOR-DOCS/03_project-planning/03_components/avatar/migr…
cdransf 20bdcba
Update 2nd-gen/packages/core/components/avatar/Avatar.base.ts
cdransf 8d7d415
Update 2nd-gen/packages/core/components/avatar/Avatar.base.ts
cdransf 8766779
Update 2nd-gen/packages/swc/components/avatar/migration.md
cdransf a2e6a83
Update 2nd-gen/packages/swc/components/avatar/migration.md
cdransf 1127f36
Update 2nd-gen/packages/swc/components/avatar/migration.md
cdransf 266cbf3
Update 2nd-gen/packages/swc/components/avatar/migration.md
cdransf bcb8ac0
Update 2nd-gen/packages/core/components/avatar/Avatar.base.ts
cdransf d95f4e4
Update 2nd-gen/packages/core/components/avatar/Avatar.base.ts
cdransf 32a3aaa
Update 2nd-gen/packages/swc/components/avatar/stories/avatar.stories.ts
cdransf 7b1f44d
Update 2nd-gen/packages/core/components/avatar/Avatar.base.ts
cdransf 70b00d0
Update 2nd-gen/packages/swc/components/avatar/stories/avatar.stories.ts
cdransf 80635fb
Update 2nd-gen/packages/core/components/avatar/Avatar.base.ts
cdransf 5ca213c
fix(avatar): additional refactoring to properly support decorative pr…
cdransf 8c5d9e2
Update 2nd-gen/packages/swc/components/avatar/stories/avatar.stories.ts
cdransf b072066
Update 2nd-gen/packages/swc/components/avatar/stories/avatar.stories.ts
cdransf 9ff3672
Update 2nd-gen/packages/swc/components/avatar/test/avatar.test.ts
cdransf a63d98c
Update 2nd-gen/packages/swc/components/avatar/test/avatar.test.ts
cdransf 1649d38
fix(avatar): remove duplicate test expectation
cdransf 926af3d
Update 2nd-gen/packages/swc/components/avatar/avatar.css
cdransf 35af4cc
docs(avatar): remove linked variant content from accessibility analysis
cdransf File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,235 @@ | ||
| /** | ||
| * 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 { | ||
| AVATAR_DEFAULT_SIZE, | ||
| AVATAR_VALID_SIZES, | ||
| type AvatarSize, | ||
| } from './Avatar.types.js'; | ||
|
|
||
| /** | ||
| * Base class for the avatar component. | ||
| * | ||
| * Provides the core API for displaying a circular profile image. Concrete | ||
| * classes supply the stylesheet and render template. | ||
| */ | ||
| export abstract class AvatarBase extends SpectrumElement { | ||
| // ───────────────────────── | ||
| // STATIC | ||
| // ───────────────────────── | ||
|
|
||
| /** | ||
| * @internal | ||
| * | ||
| * The set of valid numeric size values for the avatar. | ||
| * | ||
| * This is an internal property not intended for consumer use, but used in | ||
| * internal validation logic, stories, and tests to keep them in sync with | ||
| * the canonical type definition in `Avatar.types.ts`. | ||
| */ | ||
| static readonly VALID_SIZES: readonly AvatarSize[] = AVATAR_VALID_SIZES; | ||
|
|
||
| // ────────────────── | ||
| // CORE API | ||
| // ────────────────── | ||
|
|
||
| /** | ||
| * URL of the profile image to display. | ||
| */ | ||
| @property({ type: String }) | ||
| public src = ''; | ||
|
|
||
| /** | ||
| * Text description of the avatar image. | ||
| * | ||
| * Becomes the `alt` attribute on the underlying `<img>` element. | ||
| * Pass `alt=""` to treat the image as decorative — the host receives | ||
| * `aria-hidden="true"` so the entire shadow tree is hidden from assistive | ||
| * technology. If omitted, the image renders with `alt=""` and a DEBUG | ||
| * warning is issued; only the warning distinguishes this from an | ||
| * intentional decorative image. | ||
| */ | ||
| @property({ type: String }) | ||
| public alt: string | undefined; | ||
|
|
||
| // ─────────────────── | ||
| // SIZE API | ||
| // ─────────────────── | ||
|
|
||
| /** | ||
| * The size of the avatar. Invalid values fall back to the default (500). | ||
| */ | ||
| @property({ type: Number, reflect: true }) | ||
| public get size(): AvatarSize { | ||
| return this._size; | ||
| } | ||
|
|
||
| public set size(value: AvatarSize) { | ||
| const validSize = (AVATAR_VALID_SIZES as readonly number[]).includes( | ||
| Number(value) | ||
| ) | ||
| ? (Number(value) as AvatarSize) | ||
| : AVATAR_DEFAULT_SIZE; | ||
|
|
||
| if (this._size === validSize) { | ||
| return; | ||
| } | ||
|
|
||
| const oldSize = this._size; | ||
| this._size = validSize; | ||
| this.requestUpdate('size', oldSize); | ||
| } | ||
|
|
||
| private _size: AvatarSize = AVATAR_DEFAULT_SIZE; | ||
|
|
||
| // ─────────────────────── | ||
| // VISUAL API | ||
| // ─────────────────────── | ||
|
|
||
| /** | ||
| * Renders a stroke (outline) around the avatar image to create visual | ||
| * separation from adjacent content. Defaults to `true` within an Avatar | ||
| * Group; set explicitly on a standalone avatar when the image border color | ||
| * matches the surrounding background. | ||
| */ | ||
| @property({ type: Boolean, reflect: true, attribute: 'show-stroke' }) | ||
| public showStroke = false; | ||
|
|
||
| /** | ||
| * Renders the avatar at reduced opacity, indicating the entity is not | ||
| * currently active or available. The avatar remains present in the layout | ||
| * and accessible to assistive technology. | ||
| */ | ||
| @property({ type: Boolean, reflect: true }) | ||
| public disabled = false; | ||
|
|
||
cdransf marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // ─────────────────────────── | ||
| // ACCESSIBILITY API | ||
| // ─────────────────────────── | ||
|
|
||
| /** | ||
| * Marks the avatar as decorative, hiding it from assistive technology. | ||
| * | ||
| * Use when the surrounding context already identifies the person (e.g., | ||
| * their name appears next to the avatar). Setting this attribute causes the | ||
| * host to receive `aria-hidden="true"`, removing it from the accessibility | ||
| * tree. Equivalent to setting `alt=""`. | ||
| */ | ||
| @property({ type: Boolean, reflect: true }) | ||
| public decorative = false; | ||
|
|
||
| // ────────────────────────────────────────── | ||
| // DEPRECATED — 1st-gen compat shims | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe I missed it, but do we have documented that we want to support shims like this? I was under the impression we were ok with breaking changes, and that things like this can live in a migration plan? |
||
| // ────────────────────────────────────────── | ||
|
|
||
| /** | ||
| * @deprecated Use `alt` instead. This shim will be removed in a future release. | ||
| * | ||
| * **Breaking change:** In 1st-gen, `label` was the primary way to provide | ||
| * alternative text for the avatar image. In 2nd-gen, use `alt` instead. | ||
| */ | ||
| public get label(): string | undefined { | ||
| return this.alt; | ||
| } | ||
|
|
||
| public set label(value: string | undefined) { | ||
| if (window.__swc?.DEBUG) { | ||
| window.__swc.warn( | ||
| this, | ||
| `The "label" property on <${this.localName}> is deprecated. Use "alt" instead.`, | ||
| 'https://opensource.adobe.com/spectrum-web-components/components/avatar/', | ||
| { type: 'api', level: 'deprecation' } | ||
| ); | ||
| } | ||
| this.alt = value; | ||
| } | ||
|
|
||
| /** | ||
| * @deprecated Use `decorative` instead. This shim will be removed in a future release. | ||
| * | ||
| * **Breaking change:** In 1st-gen, `isDecorative` was used to mark an avatar | ||
| * as decorative. In 2nd-gen, use the `decorative` attribute instead. | ||
| * | ||
| * **Note:** This shim covers the JS property API only. The `is-decorative` | ||
| * HTML attribute is not shimmed — consumers using the attribute must migrate | ||
| * to `decorative`. | ||
| */ | ||
| public get isDecorative(): boolean { | ||
| return this.decorative; | ||
| } | ||
|
|
||
| public set isDecorative(value: boolean) { | ||
| if (window.__swc?.DEBUG) { | ||
| window.__swc.warn( | ||
| this, | ||
| `The "isDecorative" property on <${this.localName}> is deprecated. Use "decorative" instead.`, | ||
| 'https://opensource.adobe.com/spectrum-web-components/components/avatar/', | ||
| { type: 'api', level: 'deprecation' } | ||
| ); | ||
| } | ||
| this.decorative = value; | ||
| } | ||
|
|
||
| // ────────────────────── | ||
| // IMPLEMENTATION | ||
| // ────────────────────── | ||
|
|
||
| protected override firstUpdated(changes: PropertyValues): void { | ||
| super.firstUpdated(changes); | ||
| if (!this.hasAttribute('size')) { | ||
| this.setAttribute('size', String(this.size)); | ||
| } | ||
| this._syncAriaHidden(); | ||
| if (window.__swc?.DEBUG) { | ||
| this._warnMissingAlt(); | ||
| } | ||
| } | ||
|
|
||
| protected override updated(changes: PropertyValues): void { | ||
| super.updated(changes); | ||
| if (changes.has('decorative')) { | ||
| this._syncAriaHidden(); | ||
| } | ||
| if (changes.has('alt') && window.__swc?.DEBUG) { | ||
| this._warnMissingAlt(); | ||
| } | ||
| } | ||
|
|
||
| private _syncAriaHidden(): void { | ||
| if (this.decorative) { | ||
| this.setAttribute('aria-hidden', 'true'); | ||
| } else { | ||
| this.removeAttribute('aria-hidden'); | ||
| } | ||
| } | ||
|
|
||
| private _warnMissingAlt(): void { | ||
| if (this.alt === undefined && !this.decorative) { | ||
| window.__swc?.warn( | ||
| this, | ||
| `<${this.localName}> is missing an \`alt\` attribute. Provide a text description or pass \`alt=""\` and mark it as \`decorative\`.`, | ||
| 'https://opensource.adobe.com/spectrum-web-components/components/avatar/#accessibility', | ||
| { | ||
| type: 'accessibility', | ||
| issues: [ | ||
| 'Provide an `alt` attribute with meaningful alternative text, or', | ||
| 'Set `alt=""` and mark the image as `decorative` (hidden from screen readers).', | ||
| ], | ||
| } | ||
| ); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| /** | ||
| * 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. | ||
| */ | ||
|
|
||
| /** | ||
| * Valid numeric size values for the Avatar component. | ||
| * | ||
| * Sizes 50–700 match 1st-gen. Sizes 800–1500 are new in Spectrum 2. | ||
| */ | ||
| export const AVATAR_VALID_SIZES = [ | ||
| 50, 75, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200, 1300, | ||
| 1400, 1500, | ||
| ] as const; | ||
|
|
||
| export type AvatarSize = (typeof AVATAR_VALID_SIZES)[number]; | ||
|
|
||
| export const AVATAR_DEFAULT_SIZE = 500 as const satisfies AvatarSize; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 './Avatar.base.js'; | ||
| export * from './Avatar.types.js'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /** | ||
| * 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 { AvatarBase } from '@spectrum-web-components/core/components/avatar'; | ||
|
|
||
| import styles from './avatar.css'; | ||
|
|
||
| /** | ||
| * A static avatar component that displays a circular user profile image. | ||
| * | ||
| * Provide `alt` with a description of the person or entity depicted. | ||
| * Pass `alt=""` to treat the image as decorative and hide it from assistive | ||
| * technology. | ||
| * | ||
| * @element swc-avatar | ||
| * | ||
| * @example | ||
| * <swc-avatar src="/path/to/image.jpg" alt="Jane Doe"></swc-avatar> | ||
| * | ||
| * @example | ||
| * <swc-avatar src="/path/to/image.jpg" alt=""></swc-avatar> | ||
| * | ||
| * @example | ||
| * <swc-avatar src="/path/to/image.jpg" alt="Jane Doe" show-stroke></swc-avatar> | ||
| */ | ||
| export class Avatar extends AvatarBase { | ||
| public static override get styles(): CSSResultArray { | ||
| return [styles]; | ||
| } | ||
|
|
||
| protected override render(): TemplateResult { | ||
| return html` | ||
| <div class="swc-Avatar"> | ||
| <img class="swc-Avatar-image" src=${this.src} alt=${this.alt ?? ''} /> | ||
| </div> | ||
| `; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm wondering about alternatives, like
hasStrokeorisOutlinedor even justoutlined? Something about "stroke" is off to me, versus outline or border.The design token ultimately uses
borderas the naming, but I know the desktop specs usestroke. If nothing else, I would align our exposed custom property name to this attribute, wherever it lands.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Badge uses
outline, so we could align with that? It would get us away fromstrokeand make it consistent with what we already have. ✨