diff --git a/.changeset/social-guests-wish.md b/.changeset/social-guests-wish.md new file mode 100644 index 0000000000..8d413c1fb1 --- /dev/null +++ b/.changeset/social-guests-wish.md @@ -0,0 +1,24 @@ +--- +"@patternfly/elements": major +--- + +✨ Added `` replacing ``. Avatar now follows +PatternFly v6 design specs. + +```html + +``` + +**Breaking Changes from v5 ** + +- Renamed tag from `` to `` +- ✨ Added `size` attribute with `sm`, `md`, `lg`, `xl` presets +- ✨ Added `bordered` boolean attribute +- ✨ Added `color-scheme` support via `light-dark()` for placeholder graphic +- ✨ Added v6 design tokens +- CSS custom properties renamed from `--pf-v5-c-avatar--*` to `--pf-v6-c-avatar--*` +- Replaced `border` attribute with `bordered` boolean attribute +- `alt` attribute no longer defaults to "Avatar image" (defaults to empty string) +- `size` attribute no longer defaults to `sm` (defaults to `unset`, renders at default +dimensions) +- Removed `dark` attribute diff --git a/elements/pf-v5-avatar/README.md b/elements/pf-v5-avatar/README.md deleted file mode 100644 index 0ec4063598..0000000000 --- a/elements/pf-v5-avatar/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# PatternFly Elements Avatar - -`` is an element for displaying a user's avatar image. If the user in -question has provided a custom avatar, provide it and it will be displayed. - -Read more about Avatar in the [PatternFly Elements Avatar documentation](https://patternflyelements.org/components/avatar) - -## Installation - -Load `` via CDN: - -```html - -``` - -Or, if you are using [NPM](https://npm.im), install it - -```bash -npm install @patternfly/elements -``` - -Then once installed, import it to your application: - -```js -import '@patternfly/elements/pf-v5-avatar/pf-v5-avatar.js'; -``` -## Usage - -```html - -``` diff --git a/elements/pf-v5-avatar/demo/bordered.html b/elements/pf-v5-avatar/demo/bordered.html deleted file mode 100644 index 072dfd254b..0000000000 --- a/elements/pf-v5-avatar/demo/bordered.html +++ /dev/null @@ -1,50 +0,0 @@ -
-
-
Small
-
- -
-
- -
-
Medium
-
- -
-
- -
-
Large
-
- -
-
- -
-
Extra Large
-
- -
-
-
- - - - - diff --git a/elements/pf-v5-avatar/demo/dark-border.html b/elements/pf-v5-avatar/demo/dark-border.html deleted file mode 100644 index 50f110b1e1..0000000000 --- a/elements/pf-v5-avatar/demo/dark-border.html +++ /dev/null @@ -1,49 +0,0 @@ -
-
-
Small
-
- -
-
- -
-
Medium
-
- -
-
- -
-
Large
-
- -
-
- -
-
Extra Large
-
- -
-
-
- - - - diff --git a/elements/pf-v5-avatar/demo/dark-variant.html b/elements/pf-v5-avatar/demo/dark-variant.html deleted file mode 100644 index 36dbb71eb1..0000000000 --- a/elements/pf-v5-avatar/demo/dark-variant.html +++ /dev/null @@ -1,69 +0,0 @@ -
-
-
Small
-
- -
-
- -
-
Medium
-
- -
-
- -
-
Large
-
- -
-
- -
-
Extra Large
-
- -
-
-
- - - - diff --git a/elements/pf-v5-avatar/demo/index.html b/elements/pf-v5-avatar/demo/index.html deleted file mode 100644 index b0cc5bc048..0000000000 --- a/elements/pf-v5-avatar/demo/index.html +++ /dev/null @@ -1,13 +0,0 @@ -
- -
- - - - diff --git a/elements/pf-v5-avatar/demo/sizes.html b/elements/pf-v5-avatar/demo/sizes.html deleted file mode 100644 index 1d93dbab01..0000000000 --- a/elements/pf-v5-avatar/demo/sizes.html +++ /dev/null @@ -1,49 +0,0 @@ -
-
-
Small
-
- -
-
- -
-
Medium
-
- -
-
- -
-
Large
-
- -
-
- -
-
Extra Large
-
- -
-
-
- - - - diff --git a/elements/pf-v5-avatar/docs/CHANGELOG.old.md b/elements/pf-v5-avatar/docs/CHANGELOG.old.md deleted file mode 100644 index 4a4f39449e..0000000000 --- a/elements/pf-v5-avatar/docs/CHANGELOG.old.md +++ /dev/null @@ -1,52 +0,0 @@ -# @patternfly/pfe-avatar - -## 2.0.0-next.3 - -### Major Changes - -- 21f7fa8b: Redesigned `` to bring it in line with [PatternFly v4 Avatar](https://patternfly.org/v4/components/avatar) - - The new `` has three attributes: `src`, `alt` (like ``) and - `border="dark|light"` for adding a border on dark or light backgrounds. - -## 2.0.0-next.2 - -### Patch Changes - -- bfad8b4b: Updates dependencies -- Updated dependencies [bfad8b4b] - - @patternfly/pfe-core@2.0.0-next.8 - -## 2.0.0-next.1 - -### Patch Changes - -- 447b2d75: Remove `esbuild` export condition, as this anyways was a runtime error -- Updated dependencies [447b2d75] - - @patternfly/pfe-core@2.0.0-next.3 - -## 2.0.0-next.0 - -### Major Changes - -- 216beeb8: ## 🔥 Migrate to Lit - - This release migrates `` to LitElement. - - ### NEW: CSS Shadow Parts - - - Adds `canvas` and `img` CSS parts to `` - - ### Breaking Changes - - - Initial render is now [asynchronous](https://lit.dev/docs/components/lifecycle/#reactive-update-cycle). - If your code assumes that shadow DOM is ready once the element is constructed, update it to `await element.updateComplete`; - - Deprecates `pfe-avatar:connected` event. Use `await pfeAvatar.updateComplete` instead - - Deprecates `pfe-avatar:options-shown`, `pfe-avatar:option-cleared`, `pfe-avatar:search-event`, and `pfe-avatar:option-selected` events. - - See [docs](https://patternflyelements.org/components/avatar/) for more info - -### Patch Changes - -- Updated dependencies [e8788c72] - - @patternfly/pfe-core@2.0.0-next.0 diff --git a/elements/pf-v5-avatar/docs/pf-v5-avatar.md b/elements/pf-v5-avatar/docs/pf-v5-avatar.md deleted file mode 100644 index fae6c555bf..0000000000 --- a/elements/pf-v5-avatar/docs/pf-v5-avatar.md +++ /dev/null @@ -1,28 +0,0 @@ -{% renderInstallation %} {% endrenderInstallation %} - -{% renderOverview %} - -{% endrenderOverview %} - -{% band header="Usage" %} - {% htmlexample style="display: flex; gap: 1em;" %} - - - - - {% endhtmlexample %} -{% endband %} - -{% renderSlots %}{% endrenderSlots %} - -{% renderAttributes %}{% endrenderAttributes %} - -{% renderProperties %}{% endrenderProperties %} - -{% renderMethods %}{% endrenderMethods %} - -{% renderEvents %}{% endrenderEvents %} - -{% renderCssCustomProperties %}{% endrenderCssCustomProperties %} - -{% renderCssParts %}{% endrenderCssParts %} diff --git a/elements/pf-v5-avatar/docs/screenshot.png b/elements/pf-v5-avatar/docs/screenshot.png deleted file mode 100644 index eaea2ccddc..0000000000 Binary files a/elements/pf-v5-avatar/docs/screenshot.png and /dev/null differ diff --git a/elements/pf-v5-avatar/pf-v5-avatar.css b/elements/pf-v5-avatar/pf-v5-avatar.css deleted file mode 100644 index 5e36c8d8b2..0000000000 --- a/elements/pf-v5-avatar/pf-v5-avatar.css +++ /dev/null @@ -1,80 +0,0 @@ -:host { - display: inline-block; - /** Border color for avatar */ - --pf-v5-c-avatar--BorderColor: transparent; - /** Border width for avatar */ - --pf-v5-c-avatar--BorderWidth: 0; - /** Border radius for avatar */ - --pf-v5-c-avatar--BorderRadius: var(--pf-global--BorderRadius--lg, 30em); - /** Width for avatar */ - --pf-v5-c-avatar--Width: 2.25rem; - /** Height for avatar */ - --pf-v5-c-avatar--Height: 2.25rem; - /** Width for small avatar */ - --pf-v5-c-avatar--m-sm--Width: 1.5rem; - /** Height for small avatar */ - --pf-v5-c-avatar--m-sm--Height: 1.5rem; - /** Width for medium avatar */ - --pf-v5-c-avatar--m-md--Width: 2.25rem; - /** Height for medium avatar */ - --pf-v5-c-avatar--m-md--Height: 2.25rem; - /** Width for large avatar */ - --pf-v5-c-avatar--m-lg--Width: 4.5rem; - /** Height for large avatar */ - --pf-v5-c-avatar--m-lg--Height: 4.5rem; - /** Width for extra large avatar */ - --pf-v5-c-avatar--m-xl--Width: 8rem; - /** Height for extra large avatar */ - --pf-v5-c-avatar--m-xl--Height: 8rem; - --pf-v5-c-avatar--m-light--BorderColor: var(--pf-global--BorderColor--dark-100, #d2d2d2); - --pf-v5-c-avatar--m-light--BorderWidth: var(--pf-global--BorderWidth--sm, 1px); - /** Border color for dark avatar */ - --pf-v5-c-avatar--m-dark--BorderColor: var(--pf-global--palette--black-700, #4f5255); - --pf-v5-c-avatar--m-dark--BorderWidth: var(--pf-global--BorderWidth--sm, 1px); - width: var(--pf-v5-c-avatar--Width); - height: var(--pf-v5-c-avatar--Height); - border-radius: var(--pf-v5-c-avatar--BorderRadius); -} - -:host([hidden]), -[hidden] { - display: none !important; -} - -svg, -img { - display: inline; - object-fit: cover; - width: var(--pf-v5-c-avatar--Width); - height: var(--pf-v5-c-avatar--Height); - border-radius: var(--pf-v5-c-avatar--BorderRadius); - border: var(--pf-v5-c-avatar--BorderWidth) solid var(--pf-v5-c-avatar--BorderColor); -} - -:host([border]) { - --pf-v5-c-avatar--BorderWidth: var(--pf-global--BorderWidth--sm, 1px); -} - -:host([border="dark"]) { - --pf-v5-c-avatar--BorderColor: var(--pf-v5-c-avatar--m-dark--BorderColor); -} - -:host([size="sm"]) { - --pf-v5-c-avatar--Width: var(--pf-v5-c-avatar--m-sm--Width); - --pf-v5-c-avatar--Height: var(--pf-v5-c-avatar--m-sm--Height); -} - -:host([size="md"]) { - --pf-v5-c-avatar--Width: var(--pf-v5-c-avatar--m-md--Width); - --pf-v5-c-avatar--Height: var(--pf-v5-c-avatar--m-md--Height); -} - -:host([size="lg"]) { - --pf-v5-c-avatar--Width: var(--pf-v5-c-avatar--m-lg--Width); - --pf-v5-c-avatar--Height: var(--pf-v5-c-avatar--m-lg--Height); -} - -:host([size="xl"]) { - --pf-v5-c-avatar--Width: var(--pf-v5-c-avatar--m-xl--Width); - --pf-v5-c-avatar--Height: var(--pf-v5-c-avatar--m-xl--Height); -} diff --git a/elements/pf-v5-avatar/pf-v5-avatar.ts b/elements/pf-v5-avatar/pf-v5-avatar.ts deleted file mode 100644 index 27c73b2073..0000000000 --- a/elements/pf-v5-avatar/pf-v5-avatar.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { LitElement, html, type TemplateResult } from 'lit'; -import { property } from 'lit/decorators/property.js'; -import { customElement } from 'lit/decorators/custom-element.js'; - -import style from './pf-v5-avatar.css'; - -export class PfV5AvatarLoadEvent extends Event { - constructor(public originalEvent: Event) { - super('load', { bubbles: true }); - } -} - -/** - * An **avatar** is a visual used to represent a user. It may contain an image or a placeholder graphic. - * @summary For displaying a user's avatar image - * @alias Avatar - * @fires {PfV5AvatarLoadEvent} load - when the avatar loads - */ -@customElement('pf-v5-avatar') -export class PfV5Avatar extends LitElement { - static readonly styles: CSSStyleSheet[] = [style]; - - /** The URL to the user's custom avatar image. */ - @property() src?: string; - - /** The alt text for the avatar image. */ - @property({ reflect: true }) alt?: string = 'Avatar image'; - - /** Size of the Avatar */ - @property({ reflect: true }) size: 'sm' | 'md' | 'lg' | 'xl' = 'sm'; - - /** Whether to display a border around the avatar */ - @property({ reflect: true }) border?: 'light' | 'dark'; - - /** Whether or not the Avatar image is dark */ - @property({ type: Boolean, reflect: true }) dark = false; - - render(): TemplateResult<1> { - return this.src != null ? html` - ${this.alt ?? ''} - ` : this.dark ? html` - - - - - ` : html` - - - - - - `; - } - - #onLoad(event: Event) { - this.dispatchEvent(new PfV5AvatarLoadEvent(event)); - } -} - -declare global { - interface HTMLElementTagNameMap { - 'pf-v5-avatar': PfV5Avatar; - } -} diff --git a/elements/pf-v5-avatar/test/mwcz.jpg b/elements/pf-v5-avatar/test/mwcz.jpg deleted file mode 100644 index afc839dd76..0000000000 Binary files a/elements/pf-v5-avatar/test/mwcz.jpg and /dev/null differ diff --git a/elements/pf-v5-avatar/test/pf-avatar.e2e.ts b/elements/pf-v5-avatar/test/pf-avatar.e2e.ts deleted file mode 100644 index 2271f89428..0000000000 --- a/elements/pf-v5-avatar/test/pf-avatar.e2e.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { test } from '@playwright/test'; -import { PfeDemoPage } from '@patternfly/pfe-tools/test/playwright/PfeDemoPage.js'; -import { SSRPage } from '@patternfly/pfe-tools/test/playwright/SSRPage.js'; - -const tagName = 'pf-v5-avatar'; - -test.describe(tagName, () => { - test('snapshot', async ({ page }) => { - const componentPage = new PfeDemoPage(page, tagName); - await componentPage.navigate(); - await componentPage.snapshot(); - }); - - test('ssr', async ({ browser }) => { - const fixture = new SSRPage({ - tagName, - browser, - demoDir: new URL('../demo/', import.meta.url), - importSpecifiers: [ - `@patternfly/elements/${tagName}/${tagName}.js`, - ], - }); - await fixture.snapshots(); - }); -}); diff --git a/elements/pf-v5-avatar/test/pf-avatar.spec.ts b/elements/pf-v5-avatar/test/pf-avatar.spec.ts deleted file mode 100644 index 8fd79928fd..0000000000 --- a/elements/pf-v5-avatar/test/pf-avatar.spec.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { html, expect, oneEvent, nextFrame } from '@open-wc/testing'; -import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js'; -import { PfV5Avatar, PfV5AvatarLoadEvent } from '@patternfly/elements/pf-v5-avatar/pf-v5-avatar.js'; - -describe('', function() { - it('imperatively instantiates', function() { - expect(document.createElement('pf-v5-avatar')).to.be.an.instanceof(PfV5Avatar); - }); - - it('should upgrade', async function() { - const el = await createFixture(html``); - expect(el, 'pf-v5-badge should be an instance of PfV5Avatar') - .to.be.an.instanceOf(customElements.get('pf-v5-avatar')) - .and - .to.be.an.instanceOf(PfV5Avatar); - }); - - describe('without src attr', function() { - let element: PfV5Avatar; - beforeEach(async function() { - element = await createFixture(html``); - await nextFrame(); - }); - it('loads default avatar', function() { - const { offsetWidth } = element; - expect(offsetWidth).to.be.greaterThan(0); - }); - }); - - describe('with a src attr', function() { - let element: PfV5Avatar; - let loaded: string | undefined; - const datauri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAB0UlEQVR4Xu3UAQ0AAAyDsM+/6QspcwAh2zXawGj64K8A8AgKoABwAzh+D1AAuAEcvwcoANwAjt8DFABuAMfvAQoAN4Dj9wAFgBvA8XuAAsAN4Pg9QAHgBnD8HqAAcAM4fg9QALgBHL8HKADcAI7fAxQAbgDH7wEKADeA4/cABYAbwPF7gALADeD4PUAB4AZw/B6gAHADOH4PUAC4ARy/BygA3ACO3wMUAG4Ax+8BCgA3gOP3AAWAG8Dxe4ACwA3g+D1AAeAGcPweoABwAzh+D1AAuAEcvwcoANwAjt8DFABuAMfvAQoAN4Dj9wAFgBvA8XuAAsAN4Pg9QAHgBnD8HqAAcAM4fg9QALgBHL8HKADcAI7fAxQAbgDH7wEKADeA4/cABYAbwPF7gALADeD4PUAB4AZw/B6gAHADOH4PUAC4ARy/BygA3ACO3wMUAG4Ax+8BCgA3gOP3AAWAG8Dxe4ACwA3g+D1AAeAGcPweoABwAzh+D1AAuAEcvwcoANwAjt8DFABuAMfvAQoAN4Dj9wAFgBvA8XuAAsAN4Pg9QAHgBnD8HqAAcAM4fg9QALgBHL8HKADcAI7fAxQAbgDH7wEKADeA4/cABYAbwPF7ADyAB6SPAIFm19U7AAAAAElFTkSuQmCC'; - const onLoad = (e: PfV5AvatarLoadEvent) => { - const paths = e.originalEvent.composedPath() as HTMLImageElement[]; - loaded = paths.find(x => x.localName === 'img')?.src; - }; - beforeEach(async function() { - element = await createFixture(html``); - setTimeout(() => element.src = datauri); - await oneEvent(element, 'load'); - }); - it('loads the image', function() { - expect(loaded).to.equal(datauri); - }); - }); -}); diff --git a/elements/pf-v6-avatar/demo/avatarImg.svg b/elements/pf-v6-avatar/demo/avatarImg.svg new file mode 100644 index 0000000000..73726f9bc5 --- /dev/null +++ b/elements/pf-v6-avatar/demo/avatarImg.svg @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/elements/pf-v6-avatar/demo/basic.html b/elements/pf-v6-avatar/demo/basic.html new file mode 100644 index 0000000000..4ca2a156ec --- /dev/null +++ b/elements/pf-v6-avatar/demo/basic.html @@ -0,0 +1,10 @@ +--- +name: Basic +description: A basic avatar displays a user image. +--- + + + diff --git a/elements/pf-v6-avatar/demo/bordered.html b/elements/pf-v6-avatar/demo/bordered.html new file mode 100644 index 0000000000..f5ab4113cb --- /dev/null +++ b/elements/pf-v6-avatar/demo/bordered.html @@ -0,0 +1,19 @@ +--- +name: Bordered +description: A bordered avatar has a thin border around the image. +--- +
+ +
+ + + + diff --git a/elements/pf-v6-avatar/demo/index.html b/elements/pf-v6-avatar/demo/index.html new file mode 100644 index 0000000000..d146cbb04f --- /dev/null +++ b/elements/pf-v6-avatar/demo/index.html @@ -0,0 +1,9 @@ +--- +name: Basic +description: A basic avatar displays a user image. +--- + + + diff --git a/elements/pf-v6-avatar/demo/size-variations.html b/elements/pf-v6-avatar/demo/size-variations.html new file mode 100644 index 0000000000..33467cde0b --- /dev/null +++ b/elements/pf-v6-avatar/demo/size-variations.html @@ -0,0 +1,63 @@ +--- +name: Size variations +description: Avatars can be displayed in four sizes. +--- +
+
+
Small
+
+ +
+
+ +
+
Medium
+
+ +
+
+ +
+
Large
+
+ +
+
+ +
+
Extra Large
+
+ +
+
+
+ + + + diff --git a/elements/pf-v6-avatar/pf-v6-avatar.css b/elements/pf-v6-avatar/pf-v6-avatar.css new file mode 100644 index 0000000000..89f9929a40 --- /dev/null +++ b/elements/pf-v6-avatar/pf-v6-avatar.css @@ -0,0 +1,84 @@ +:host { + --_width: + /** Width of the avatar */ + var(--pf-v6-c-avatar--Width, 2.25rem); + --_height: + /** Height of the avatar */ + var(--pf-v6-c-avatar--Height, 2.25rem); + --_border-radius: + /** Border radius of the avatar */ + var(--pf-v6-c-avatar--BorderRadius, var(--pf-t--global--border--radius--pill, 30em)); + --_border-color: + /** Border color of the avatar */ + var(--pf-v6-c-avatar--BorderColor, transparent); + --_border-width: + /** Border width of the avatar */ + var(--pf-v6-c-avatar--BorderWidth, 0); + --_placeholder-bg: var(--pf-t--global--background--color--200, light-dark(var(--pf-t--color--gray--10, #f2f2f2), var(--pf-t--color--gray--80, #292929))); + --_placeholder-fg: var(--pf-t--global--icon--color--subtle, light-dark(var(--pf-t--color--gray--50, #707070), var(--pf-t--color--gray--40, #a3a3a3))); + + display: inline-block; + width: var(--_width); + height: var(--_height); + border-radius: var(--_border-radius); +} + +:host([hidden]), +[hidden] { + display: none !important; +} + +:host([bordered]) { + --_border-color: + /** Border color when bordered */ + var(--pf-v6-c-avatar--m-bordered--BorderColor, var(--pf-t--global--border--color--default, #d2d2d2)); + --_border-width: + /** Border width when bordered */ + var(--pf-v6-c-avatar--m-bordered--BorderWidth, var(--pf-t--global--border--width--box--default, 1px)); +} + +:host([size="sm"]) { + --_width: + /** Width when size is sm */ + var(--pf-v6-c-avatar--m-sm--Width, 1.5rem); + --_height: + /** Height when size is sm */ + var(--pf-v6-c-avatar--m-sm--Height, 1.5rem); +} + +:host([size="md"]) { + --_width: + /** Width when size is md */ + var(--pf-v6-c-avatar--m-md--Width, 2.25rem); + --_height: + /** Height when size is md */ + var(--pf-v6-c-avatar--m-md--Height, 2.25rem); +} + +:host([size="lg"]) { + --_width: + /** Width when size is lg */ + var(--pf-v6-c-avatar--m-lg--Width, 4.5rem); + --_height: + /** Height when size is lg */ + var(--pf-v6-c-avatar--m-lg--Height, 4.5rem); +} + +:host([size="xl"]) { + --_width: + /** Width when size is xl */ + var(--pf-v6-c-avatar--m-xl--Width, 8rem); + --_height: + /** Height when size is xl */ + var(--pf-v6-c-avatar--m-xl--Height, 8rem); +} + +svg, +img { + display: block; + object-fit: cover; + width: var(--_width); + height: var(--_height); + border-radius: var(--_border-radius); + border: var(--_border-width) solid var(--_border-color); +} diff --git a/elements/pf-v6-avatar/pf-v6-avatar.ts b/elements/pf-v6-avatar/pf-v6-avatar.ts new file mode 100644 index 0000000000..01d654fa7d --- /dev/null +++ b/elements/pf-v6-avatar/pf-v6-avatar.ts @@ -0,0 +1,65 @@ +import { LitElement, html, type TemplateResult } from 'lit'; +import { property } from 'lit/decorators/property.js'; +import { customElement } from 'lit/decorators/custom-element.js'; + +import style from './pf-v6-avatar.css'; + +/** Size variants for the avatar. */ +export type AvatarSize = 'sm' | 'md' | 'lg' | 'xl'; + +export class PfV6AvatarLoadEvent extends Event { + constructor(public originalEvent: Event) { + super('load', { bubbles: true }); + } +} + +/** + * An **avatar** is a visual used to represent a user. It may contain an image + * or a placeholder graphic. + * @summary Displays a user's avatar image + * @fires {PfV6AvatarLoadEvent} load - when the avatar image loads + */ +@customElement('pf-v6-avatar') +export class PfV6Avatar extends LitElement { + static readonly styles: CSSStyleSheet[] = [style]; + + /** The URL to the user's custom avatar image. */ + @property() src?: string; + + /** The alt text for the avatar image. */ + @property({ reflect: true }) alt?: string; + + /** Size of the avatar */ + @property({ reflect: true }) size?: AvatarSize; + + /** Whether to display a border around the avatar */ + @property({ type: Boolean, reflect: true }) bordered = false; + + override render(): TemplateResult { + return this.src != null ? html` + ${this.alt ?? ''} + ` : html` + + `; + } + + #onLoad(event: Event) { + this.dispatchEvent(new PfV6AvatarLoadEvent(event)); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'pf-v6-avatar': PfV6Avatar; + } +} diff --git a/elements/pf-v6-avatar/test/pf-v6-avatar.spec.ts b/elements/pf-v6-avatar/test/pf-v6-avatar.spec.ts new file mode 100644 index 0000000000..ecd29d6988 --- /dev/null +++ b/elements/pf-v6-avatar/test/pf-v6-avatar.spec.ts @@ -0,0 +1,148 @@ +import { html, expect, oneEvent, nextFrame } from '@open-wc/testing'; +import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js'; +import { a11ySnapshot } from '@patternfly/pfe-tools/test/a11y-snapshot.js'; +import { PfV6Avatar, PfV6AvatarLoadEvent } from '@patternfly/elements/pf-v6-avatar/pf-v6-avatar.js'; + +describe('', function() { + it('imperatively instantiates', function() { + expect(document.createElement('pf-v6-avatar')).to.be.an.instanceof(PfV6Avatar); + }); + + it('should upgrade', async function() { + const el = await createFixture(html``); + expect(el, 'pf-v6-avatar should be an instance of PfV6Avatar') + .to.be.an.instanceOf(customElements.get('pf-v6-avatar')) + .and + .to.be.an.instanceOf(PfV6Avatar); + }); + + describe('without src attr', function() { + let element: PfV6Avatar; + beforeEach(async function() { + element = await createFixture(html``); + await nextFrame(); + }); + + it('displays a placeholder', function() { + const { offsetWidth } = element; + expect(offsetWidth).to.be.greaterThan(0); + }); + + it('has the default size', function() { + expect(element.offsetWidth).to.equal(36); + expect(element.offsetHeight).to.equal(36); + }); + + it('hides the placeholder from the accessibility tree', async function() { + const snapshot = await a11ySnapshot(); + expect(snapshot?.children?.find( + (child: { role: string }) => child.role === 'img' + )).to.not.be.ok; + }); + }); + + describe('with a src attr', function() { + let element: PfV6Avatar; + let loaded: string | undefined; + const datauri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAB0UlEQVR4Xu3UAQ0AAAyDsM+/6QspcwAh2zXawGj64K8A8AgKoABwAzh+D1AAuAEcvwcoANwAjt8DFABuAMfvAQoAN4Dj9wAFgBvA8XuAAsAN4Pg9QAHgBnD8HqAAcAM4fg9QALgBHL8HKADcAI7fAxQAbgDH7wEKADeA4/cABYAbwPF7gALADeD4PUAB4AZw/B6gAHADOH4PUAC4ARy/BygA3ACO3wMUAG4Ax+8BCgA3gOP3AAWAG8Dxe4ACwA3g+D1AAeAGcPweoABwAzh+D1AAuAEcvwcoANwAjt8DFABuAMfvAQoAN4Dj9wAFgBvA8XuAAsAN4Pg9QAHgBnD8HqAAcAM4fg9QALgBHL8HKADcAI7fAxQAbgDH7wEKADeA4/cABYAbwPF7gALADeD4PUAB4AZw/B6gAHADOH4PUAC4ARy/BygA3ACO3wMUAG4Ax+8BCgA3gOP3AAWAG8Dxe4ACwA3g+D1AAeAGcPweoABwAzh+D1AAuAEcvwcoANwAjt8DFABuAMfvAQoAN4Dj9wAFgBvA8XuAAsAN4Pg9QAHgBnD8HqAAcAM4fg9QALgBHL8HKADcAI7fAxQAbgDH7wEKADeA4/cABYAbwPF7ADyAB6SPAIFm19U7AAAAAElFTkSuQmCC'; + const onLoad = (e: PfV6AvatarLoadEvent) => { + const paths = e.originalEvent.composedPath() as HTMLImageElement[]; + loaded = paths.find(x => x.localName === 'img')?.src; + }; + beforeEach(async function() { + element = await createFixture(html``); + setTimeout(() => element.src = datauri); + await oneEvent(element, 'load'); + }); + + it('loads the image', function() { + expect(loaded).to.equal(datauri); + }); + + it('fires a PfV6AvatarLoadEvent', function() { + expect(loaded).to.be.ok; + }); + }); + + describe('with alt attr', function() { + let element: PfV6Avatar; + beforeEach(async function() { + element = await createFixture(html` + + `); + await oneEvent(element, 'load'); + }); + + it('passes alt text to the image', async function() { + const snapshot = await a11ySnapshot(); + expect(snapshot?.children?.find( + (child: { name: string }) => child.name === 'User avatar' + )).to.be.ok; + }); + }); + + describe('with size="md"', function() { + let element: PfV6Avatar; + beforeEach(async function() { + element = await createFixture(html``); + await nextFrame(); + }); + + it('renders at the medium size', function() { + expect(element.offsetWidth).to.equal(36); + expect(element.offsetHeight).to.equal(36); + }); + }); + + describe('with size="sm"', function() { + let element: PfV6Avatar; + beforeEach(async function() { + element = await createFixture(html``); + await nextFrame(); + }); + + it('renders at the small size', function() { + expect(element.offsetWidth).to.equal(24); + expect(element.offsetHeight).to.equal(24); + }); + }); + + describe('with size="lg"', function() { + let element: PfV6Avatar; + beforeEach(async function() { + element = await createFixture(html``); + await nextFrame(); + }); + + it('renders at the large size', function() { + expect(element.offsetWidth).to.equal(72); + expect(element.offsetHeight).to.equal(72); + }); + }); + + describe('with size="xl"', function() { + let element: PfV6Avatar; + beforeEach(async function() { + element = await createFixture(html``); + await nextFrame(); + }); + + it('renders at the extra large size', function() { + expect(element.offsetWidth).to.equal(128); + expect(element.offsetHeight).to.equal(128); + }); + }); + + describe('with bordered', function() { + let element: PfV6Avatar; + beforeEach(async function() { + element = await createFixture(html``); + await nextFrame(); + }); + + it('renders with a visible border', function() { + expect(element.offsetWidth).to.be.greaterThan(36); + }); + }); +});