diff --git a/src/dev/pages/avatar/avatar.ejs b/src/dev/pages/avatar/avatar.ejs
index 77eec2c6b..1a8c646e2 100644
--- a/src/dev/pages/avatar/avatar.ejs
+++ b/src/dev/pages/avatar/avatar.ejs
@@ -16,14 +16,14 @@
w/Icon
-
+
@@ -32,7 +32,7 @@
w/Custom Slotted Content and CSS Variable
- A
+ A
diff --git a/src/dev/pages/avatar/avatar.html b/src/dev/pages/avatar/avatar.html
index 37054fba9..11282eec1 100644
--- a/src/dev/pages/avatar/avatar.html
+++ b/src/dev/pages/avatar/avatar.html
@@ -3,9 +3,7 @@
page: {
title: 'Avatar',
includePath: './pages/avatar/avatar.ejs',
- options: [
- { type: 'switch', label: 'Auto color', id: 'auto-color-checkbox' }
- ]
+ options: []
}
})
%>
diff --git a/src/dev/pages/avatar/avatar.ts b/src/dev/pages/avatar/avatar.ts
index 939fd1baa..e15f41266 100644
--- a/src/dev/pages/avatar/avatar.ts
+++ b/src/dev/pages/avatar/avatar.ts
@@ -1,15 +1,3 @@
// Components
import '@tylertech/forge/avatar';
-import type { AvatarComponent } from '@tylertech/forge/avatar';
-import type { ISwitchComponent } from '@tylertech/forge/switch';
import '$src/shared';
-
-function getAvatarElements(): NodeListOf
{
- return document.querySelectorAll('.content forge-avatar');
-}
-
-const autoColorToggle = document.querySelector('#auto-color-checkbox') as ISwitchComponent;
-autoColorToggle.addEventListener('forge-switch-change', ({ detail: selected }) => {
- const avatars = getAvatarElements();
- avatars.forEach(avatar => avatar.autoColor = selected);
-});
diff --git a/src/lib/avatar/_core.scss b/src/lib/avatar/_core.scss
new file mode 100644
index 000000000..aa0e95162
--- /dev/null
+++ b/src/lib/avatar/_core.scss
@@ -0,0 +1,38 @@
+@use '../core/styles/typography';
+@use './token-utils' as *;
+
+@forward './token-utils';
+
+@mixin host() {
+ contain: content;
+
+ display: inline-block;
+}
+
+@mixin base() {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ overflow: hidden;
+
+ transition: height #{token(transition-duration)} #{token(transition-timing)},
+ width #{token(transition-duration)} #{token(transition-timing)};
+
+ border-radius: #{token(shape)};
+ box-sizing: border-box;
+ width: #{token(size)};
+ height: #{token(size)};
+
+ background-color: #{token(background)};
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: cover;
+
+ @include typography.style(subheading2);
+ color: #{token(color)};
+}
+
+@mixin base-with-image() {
+ background-color: inherit;
+}
diff --git a/src/lib/avatar/_mixins.scss b/src/lib/avatar/_mixins.scss
deleted file mode 100644
index 4c3942877..000000000
--- a/src/lib/avatar/_mixins.scss
+++ /dev/null
@@ -1,41 +0,0 @@
-@use '@material/animation/variables' as animation-variables;
-@use '../theme';
-@use '../typography/mixins' as typography-mixins;
-@use './variables';
-
-@mixin provide-theme($theme) {
- @include theme.theme-properties(avatar, $theme, variables.$theme-values);
-}
-
-@mixin core-styles() {
- .forge-avatar {
- @include base;
- }
-}
-
-@mixin host() {
- display: inline-block;
- contain: content;
-}
-
-@mixin base() {
- @include typography-mixins.typography(title);
- @include theme.css-custom-property(font-size, --forge-avatar-font-size, variables.$font-size);
- @include theme.css-custom-property(font-weight, --forge-avatar-font-weight, variables.$font-weight);
- @include theme.css-custom-property(background-color, --forge-avatar-theme-background, variables.$background);
- @include theme.css-custom-property(height, --forge-avatar-size, variables.$size);
- @include theme.css-custom-property(width, --forge-avatar-size, variables.$size);
- @include theme.css-custom-property(color, --forge-avatar-theme-on-background, variables.$on-background);
- @include theme.css-custom-property(border-radius, --forge-avatar-radius, variables.$border-radius);
-
- display: flex;
- justify-content: center;
- align-items: center;
- box-sizing: border-box;
- background-position: center;
- background-repeat: no-repeat;
- background-size: cover;
- overflow: hidden;
- transition: height variables.$transition-duration animation-variables.$standard-curve-timing-function,
- width variables.$transition-duration animation-variables.$standard-curve-timing-function;
-}
diff --git a/src/lib/avatar/_token-utils.scss b/src/lib/avatar/_token-utils.scss
new file mode 100644
index 000000000..b7404f8c5
--- /dev/null
+++ b/src/lib/avatar/_token-utils.scss
@@ -0,0 +1,25 @@
+@use '../core/styles/tokens/avatar/tokens';
+@use '../core/styles/tokens/token-utils';
+
+$_module: avatar;
+$_tokens: tokens.$tokens;
+
+@mixin provide-theme($theme) {
+ @include token-utils.provide-theme($_module, $_tokens, $theme);
+}
+
+@function token($name, $type: token) {
+ @return token-utils.token($_module, $_tokens, $name, $type);
+}
+
+@function declare($token) {
+ @return token-utils.declare($_module, $token);
+}
+
+@mixin override($token, $token-or-value, $type: token) {
+ @include token-utils.override($_module, $_tokens, $token, $token-or-value, $type);
+}
+
+@mixin tokens($includes: null, $excludes: null) {
+ @include token-utils.tokens($_module, $_tokens, $includes, $excludes);
+}
diff --git a/src/lib/avatar/_variables.scss b/src/lib/avatar/_variables.scss
deleted file mode 100644
index 1361181ee..000000000
--- a/src/lib/avatar/_variables.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-$size: 40px !default;
-$font-size: 1rem !default;
-$font-weight: 400 !default;
-$background: none;
-$on-background: #ffffff !default;
-$transition-duration: 200ms !default;
-$border-radius: 50% !default;
-$theme-values: (
- background: $background,
- on-background: $on-background
-);
diff --git a/src/lib/avatar/avatar-adapter.ts b/src/lib/avatar/avatar-adapter.ts
index d7d3b2460..cb1a3cb07 100644
--- a/src/lib/avatar/avatar-adapter.ts
+++ b/src/lib/avatar/avatar-adapter.ts
@@ -4,7 +4,6 @@ import { IAvatarComponent } from './avatar';
import { AVATAR_CONSTANTS } from './avatar-constants';
export interface IAvatarAdapter extends IBaseAdapter {
- setBackgroundColor(color: string): void;
setBackgroundImageUrl(url: string): Promise;
removeBackgroundImage(): void;
setText(value: string): void;
@@ -24,24 +23,14 @@ export class AvatarAdapter extends BaseAdapter implements IAva
this._defaultSlot = getShadowElement(this._component, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT) as HTMLSlotElement;
}
- /**
- * Sets the `backgroundColor` style on the content element.
- * @param {string} value The background color.
- */
- public setBackgroundColor(value: string): void {
- this._root.style.backgroundColor = `var(${AVATAR_CONSTANTS.strings.BACKGROUND_VARNAME}, ${value})`;
- }
-
/**
* Sets the background image URL.
* @param url The URL.
*/
- public setBackgroundImageUrl(url: string): Promise {
- const backgroundColor = this._root.style.backgroundColor;
- // doing his before the promise so it doesn't flash a color before loading
- this._root.style.backgroundColor = 'inherit';
-
- const loadResult = new Promise(resolve => {
+ public async setBackgroundImageUrl(url: string): Promise {
+ // Set before loading image to prevent a flash of background color
+ this._root.classList.add('forge-avatar--image');
+ return new Promise(resolve => {
const image = new Image();
image.onload = () => {
this._root.style.backgroundImage = `url(${image.src})`;
@@ -49,14 +38,12 @@ export class AvatarAdapter extends BaseAdapter implements IAva
};
image.onerror = () => {
- this._root.style.backgroundColor = backgroundColor;
+ this._root.classList.remove('forge-avatar--image');
resolve(false);
};
image.src = url;
});
-
- return loadResult;
}
/**
@@ -64,6 +51,7 @@ export class AvatarAdapter extends BaseAdapter implements IAva
*/
public removeBackgroundImage(): void {
this._root.style.removeProperty('background-image');
+ this._root.classList.remove('forge-avatar--image');
}
/**
diff --git a/src/lib/avatar/avatar-component-delegate.ts b/src/lib/avatar/avatar-component-delegate.ts
deleted file mode 100644
index 5b99a94bb..000000000
--- a/src/lib/avatar/avatar-component-delegate.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { BaseComponentDelegate, IBaseComponentDelegateConfig, IBaseComponentDelegateOptions } from '../core/delegates/base-component-delegate';
-import { IAvatarComponent } from './avatar';
-import { AVATAR_CONSTANTS } from './avatar-constants';
-
-export type AvatarComponentDelegateProps = Partial;
-export interface IAvatarComponentDelegateOptions extends IBaseComponentDelegateOptions {}
-export interface IAvatarComponentDelegateConfig extends IBaseComponentDelegateConfig {}
-
-export class AvatarComponentDelegate extends BaseComponentDelegate {
- constructor(config?: IAvatarComponentDelegateConfig) {
- super(config);
- }
-
- protected _build(): IAvatarComponent {
- return document.createElement(AVATAR_CONSTANTS.elementName);
- }
-}
diff --git a/src/lib/avatar/avatar-constants.ts b/src/lib/avatar/avatar-constants.ts
index 764b745df..58b4aa1eb 100644
--- a/src/lib/avatar/avatar-constants.ts
+++ b/src/lib/avatar/avatar-constants.ts
@@ -6,8 +6,7 @@ const elementName: keyof HTMLElementTagNameMap = `${COMPONENT_NAME_PREFIX}avatar
const attributes = {
IMAGE_URL: 'image-url',
TEXT: 'text',
- LETTER_COUNT: 'letter-count',
- AUTO_COLOR: 'auto-color'
+ LETTER_COUNT: 'letter-count'
};
const numbers = {
@@ -21,7 +20,7 @@ const selectors = {
const strings = {
DEFAULT_COLOR: COLOR_CONSTANTS.themeColors.tertiary,
- BACKGROUND_VARNAME: '--forge-avatar-theme-background'
+ BACKGROUND_VARNAME: '--forge-avatar-background'
};
export const AVATAR_CONSTANTS = {
diff --git a/src/lib/avatar/avatar-foundation.ts b/src/lib/avatar/avatar-foundation.ts
index d3f4801e7..46be957ea 100644
--- a/src/lib/avatar/avatar-foundation.ts
+++ b/src/lib/avatar/avatar-foundation.ts
@@ -1,15 +1,11 @@
import { ICustomElementFoundation, isDefined, isString } from '@tylertech/forge-core';
-import { getTextColor } from '../utils/color-utils';
import { IAvatarAdapter } from './avatar-adapter';
import { AVATAR_CONSTANTS } from './avatar-constants';
-
-
export interface IAvatarFoundation extends ICustomElementFoundation {
imageUrl: string;
text: string;
letterCount: number;
- autoColor: boolean;
}
/**
@@ -19,7 +15,6 @@ export class AvatarFoundation implements IAvatarFoundation {
private _imageUrl: string;
private _text = '';
private _letterCount = AVATAR_CONSTANTS.numbers.DEFAULT_LETTER_COUNT;
- private _autoColor = false;
private _initialized = false;
constructor(private _adapter: IAvatarAdapter) {}
@@ -56,9 +51,6 @@ export class AvatarFoundation implements IAvatarFoundation {
} else {
this._adapter.clearText();
}
-
- const color = this._autoColor ? getTextColor(data) : AVATAR_CONSTANTS.strings.DEFAULT_COLOR;
- this._adapter.setBackgroundColor(color);
}
/**
@@ -122,16 +114,4 @@ export class AvatarFoundation implements IAvatarFoundation {
}
}
}
-
- /** Controls whether the background color set automatically based on the text value. Does not have any effect when an image URL is specified. */
- public get autoColor(): boolean {
- return this._autoColor;
- }
- public set autoColor(value: boolean) {
- if (this._autoColor !== value) {
- this._autoColor = value;
- this._setText();
- this._adapter.setHostAttribute(AVATAR_CONSTANTS.attributes.AUTO_COLOR, isDefined(this._autoColor) ? this._autoColor.toString() : '');
- }
- }
}
diff --git a/src/lib/avatar/avatar.scss b/src/lib/avatar/avatar.scss
index bef4cc3c9..6137b1f8b 100644
--- a/src/lib/avatar/avatar.scss
+++ b/src/lib/avatar/avatar.scss
@@ -1,11 +1,29 @@
-@use './mixins';
+@use './core' as *;
+
+//
+// Host
+//
:host {
- @include mixins.host;
+ @include host;
}
:host([hidden]) {
display: none;
}
-@include mixins.core-styles;
+//
+// Base
+//
+
+.forge-avatar {
+ @include tokens;
+}
+
+.forge-avatar {
+ @include base;
+
+ &--image {
+ @include base-with-image;
+ }
+}
diff --git a/src/lib/avatar/avatar.test.ts b/src/lib/avatar/avatar.test.ts
new file mode 100644
index 000000000..ce602bcb2
--- /dev/null
+++ b/src/lib/avatar/avatar.test.ts
@@ -0,0 +1,122 @@
+import { expect } from '@esm-bundle/chai';
+import { fixture, html } from '@open-wc/testing';
+import { timer } from '@tylertech/forge-testing';
+import { getShadowElement } from '@tylertech/forge-core';
+import { IAvatarComponent } from './avatar';
+import { AVATAR_CONSTANTS } from './avatar-constants';
+
+import './avatar'
+
+describe('Avatar', () => {
+ it('should initialize', async () => {
+ const el = await fixture(html``);
+
+ expect(el.shadowRoot).not.to.be.null;
+ });
+
+ it('should should be accessible', async () => {
+ const el = await fixture(html``);
+
+ await expect(el).to.be.accessible();
+ });
+
+ it('should set slot text content to first characters of text attribute', async () => {
+ const el = await fixture(html``);
+
+ expect(el.text).to.equal('Tyler Forge');
+ expect(getDefaultSlotEl(el).textContent).to.equal('TF');
+ });
+
+ it('should set restrict letter count when attribute set', async () => {
+ const el = await fixture(html``);
+
+ el.setAttribute(AVATAR_CONSTANTS.attributes.LETTER_COUNT, '1');
+
+ expect(getDefaultSlotEl(el).textContent).to.equal('T');
+ });
+
+ it('should update the letter count attribute when the property is set ', async () => {
+ const el = await fixture(html``);
+
+ el.letterCount = 3;
+
+ expect(el.getAttribute(AVATAR_CONSTANTS.attributes.LETTER_COUNT)).to.equal('3');
+ });
+
+ it('should change background image when set via attribute', async () => {
+ const el = await fixture(html``);
+
+ const url = 'https://empower.tylertech.com/rs/015-NUU-525/images/tyler-logo-color.svg';
+ el.setAttribute(AVATAR_CONSTANTS.attributes.IMAGE_URL, url);
+ const root = getRootEl(el);
+ // Give enough time for the image to load
+ await timer(1800);
+
+ expect(root.hasAttribute('style')).to.be.true;
+ expect(root.style.backgroundImage).to.equal(`url("${url}")`);
+ });
+
+ it('should change text when set via attribute', async () => {
+ const el = await fixture(html``);
+
+ el.setAttribute(AVATAR_CONSTANTS.attributes.TEXT, 'New Value');
+
+ expect(getDefaultSlotEl(el).textContent).to.equal('NV');
+ });
+
+ it('should render text when image url fails to load an image with 500 error', async () => {
+ const el = await fixture(html``);
+ const root = getRootEl(el);
+
+ await timer(300);
+
+ expect(getDefaultSlotEl(el).textContent).to.equal('IU');
+ expect(root.hasAttribute('style')).to.be.false;
+ });
+
+ it('should render text when image url fails to load an image with 404 error', async () => {
+ const el = await fixture(html``);
+ const root = getRootEl(el);
+
+ await timer(300);
+
+ expect(getDefaultSlotEl(el).textContent).to.equal('IU');
+ expect(root.hasAttribute('style')).to.be.false;
+ });
+
+ it('should have proper default values', async () => {
+ const el = await fixture(html``);
+
+ expect(el.text).to.equal('');
+ expect(el.letterCount).to.equal(AVATAR_CONSTANTS.numbers.DEFAULT_LETTER_COUNT);
+ expect(el.imageUrl).to.be.undefined;
+ });
+
+ it('should have not have any content by default', async () => {
+ const el = await fixture(html``);
+
+ expect(getDefaultSlotEl(el).textContent).to.equal('');
+ });
+
+ it('should render slotted content in place of text content', async () => {
+ const el = await fixture(html`Slotted Content`);
+ const defaultSlot = getDefaultSlotEl(el);
+
+ expect(defaultSlot.textContent).to.equal('AT');
+ expect(defaultSlot.assignedNodes().length).to.equal(1);
+ });
+
+ it('should render first character of each word when letter count is greater than number of words', async () => {
+ const el = await fixture(html`Slotted Content`);
+ const defaultSlot = getDefaultSlotEl(el);
+
+ expect(defaultSlot.textContent).to.equal('SLSWS');
+ });
+
+ function getRootEl(el: IAvatarComponent): HTMLElement {
+ return el.shadowRoot?.firstElementChild as HTMLElement;
+ }
+ function getDefaultSlotEl(el: IAvatarComponent): HTMLSlotElement {
+ return getShadowElement(el, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT) as HTMLSlotElement;
+ }
+});
\ No newline at end of file
diff --git a/src/lib/avatar/avatar.ts b/src/lib/avatar/avatar.ts
index a4cfd64fc..e5aa737b7 100644
--- a/src/lib/avatar/avatar.ts
+++ b/src/lib/avatar/avatar.ts
@@ -1,4 +1,4 @@
-import { CustomElement, attachShadowTemplate, coerceNumber, coerceBoolean, FoundationProperty } from '@tylertech/forge-core';
+import { CustomElement, attachShadowTemplate, coerceNumber, FoundationProperty } from '@tylertech/forge-core';
import { AvatarAdapter } from './avatar-adapter';
import { AvatarFoundation } from './avatar-foundation';
@@ -12,7 +12,6 @@ export interface IAvatarComponent extends IBaseComponent {
imageUrl: string;
text: string;
letterCount: number;
- autoColor: boolean;
}
declare global {
@@ -22,9 +21,33 @@ declare global {
}
/**
- * The custom element class behind the `` element.
- *
* @tag forge-avatar
+ *
+ * @summary Avatars represent an entity via text or image.
+ *
+ * @description
+ * The avatar component allows you to provide text or images to display that represent an entity. By default, the
+ * avatar will display textual content as single characters (character count is configurable), or display an image or
+ * icon based on the URL provided to it.
+ *
+ * @property {string} text - The text to display in the avatar.
+ * @property {number} letterCount - Controls the number of letters to display from the text. By default the text is split on spaces and the first character of each word is used.
+ * @property {string} imageUrl - The background image URL to use.
+ *
+ * @attribute {string} text - The text to display in the avatar.
+ * @attribute {number} letter-count - Controls the number of letters to display from the text. By default the text is split on spaces and the first character of each word is used.
+ * @attribute {string} image-url - The background image URL to use.
+ *
+ * @cssproperty --forge-avatar-background - The background color of the avatar.
+ * @cssproperty --forge-avatar-shape - The border radius of the avatar, defaults to 50%.
+ * @cssproperty --forge-avatar-color - The text color of the avatar.
+ * @cssproperty --forge-avatar-size - The height and width of the avatar.
+ * @cssproperty --forge-avatar-transition-duration - The transition duration for animations.
+ * @cssproperty --forge-avatar-transition-timing - The transition timing function for animations.
+ *
+ * @csspart root - The root container element.
+ *
+ * @slot - The default/unnamed slot for avatar content if not provided via text/imageUrl.
*/
@CustomElement({
name: AVATAR_CONSTANTS.elementName
@@ -34,8 +57,7 @@ export class AvatarComponent extends BaseComponent implements IAvatarComponent {
return [
AVATAR_CONSTANTS.attributes.TEXT,
AVATAR_CONSTANTS.attributes.LETTER_COUNT,
- AVATAR_CONSTANTS.attributes.IMAGE_URL,
- AVATAR_CONSTANTS.attributes.AUTO_COLOR
+ AVATAR_CONSTANTS.attributes.IMAGE_URL
];
}
@@ -66,13 +88,10 @@ export class AvatarComponent extends BaseComponent implements IAvatarComponent {
case AVATAR_CONSTANTS.attributes.IMAGE_URL:
this.imageUrl = newValue;
break;
- case AVATAR_CONSTANTS.attributes.AUTO_COLOR:
- this.autoColor = coerceBoolean(newValue);
- break;
}
}
- /** Gets/sets the text to display. */
+ /** The text to display in the avatar. */
@FoundationProperty()
public declare text: string;
@@ -80,11 +99,7 @@ export class AvatarComponent extends BaseComponent implements IAvatarComponent {
@FoundationProperty()
public declare letterCount: number;
- /** Sets the background image URL to use. */
+ /** The background image URL to use. */
@FoundationProperty()
public declare imageUrl: string;
-
- /** Controls whether the background color is set automatically based on the text value. Does not have any effect when an image URL is specified. */
- @FoundationProperty()
- public declare autoColor: boolean;
}
diff --git a/src/lib/avatar/index.ts b/src/lib/avatar/index.ts
index 9ddf95094..8e9b8b867 100644
--- a/src/lib/avatar/index.ts
+++ b/src/lib/avatar/index.ts
@@ -6,7 +6,6 @@ export * from './avatar-adapter';
export * from './avatar-constants';
export * from './avatar-foundation';
export * from './avatar';
-export * from './avatar-component-delegate';
export function defineAvatarComponent(): void {
defineCustomElement(AvatarComponent);
diff --git a/src/lib/core/styles/tokens/avatar/_tokens.scss b/src/lib/core/styles/tokens/avatar/_tokens.scss
new file mode 100644
index 000000000..aca79e869
--- /dev/null
+++ b/src/lib/core/styles/tokens/avatar/_tokens.scss
@@ -0,0 +1,18 @@
+@use 'sass:map';
+@use '../../animation';
+@use '../../shape';
+@use '../../theme';
+@use '../../utils';
+
+$tokens: (
+ size: utils.module-val(avatar, size, 40px),
+ background: utils.module-val(avatar, background, theme.variable(tertiary)),
+ color: utils.module-val(avatar, color, theme.variable(on-tertiary)),
+ transition-duration: utils.module-val(avatar, transition-duration, animation.variable(duration-short4)),
+ transition-timing: utils.module-val(avatar, transition-timing, animation.variable(easing-standard)),
+ shape: utils.module-val(avatar, shape, shape.variable(round))
+) !default;
+
+@function get($key) {
+ @return map.get($tokens, $key);
+}
diff --git a/src/stories/src/components/avatar/avatar-args.ts b/src/stories/src/components/avatar/avatar-args.ts
index 282c7c2f9..b2770bad4 100644
--- a/src/stories/src/components/avatar/avatar-args.ts
+++ b/src/stories/src/components/avatar/avatar-args.ts
@@ -1,5 +1,4 @@
export interface IAvatarProps {
- autoColor: boolean;
imageUrl: string;
useIcon: boolean;
letterCount: number;
@@ -7,12 +6,6 @@ export interface IAvatarProps {
}
export const argTypes = {
- autoColor: {
- control: 'boolean',
- table: {
- category: 'Properties'
- }
- },
imageUrl: {
control: 'text',
table: {
diff --git a/src/stories/src/components/avatar/avatar.mdx b/src/stories/src/components/avatar/avatar.mdx
index f06fc3a33..0020e0a7f 100644
--- a/src/stories/src/components/avatar/avatar.mdx
+++ b/src/stories/src/components/avatar/avatar.mdx
@@ -49,12 +49,6 @@ When using with an image, the avatar will apply your image as that background of
## Properties/Attributes
-
-
-Controls whether the background color is set automatically based on the text value. Does not have any effect when an image URL is specified.
-
-
-
Sets the background image URL to use in place of the text representation.
@@ -103,12 +97,12 @@ Gets/set the text to display as characters. If spaces exist, the first letter of
| Name | Description
| :-----------------------------------------------| :--------------------
-| `--forge-avatar-theme-background` | Controls the `background-color` style.
-| `--forge-avatar-font-size` | Controls the `font-size` style.
-| `--forge-avatar-font-weight` | Controls the `font-weight` style.
-| `--forge-avatar-theme-on-background` | Controls the `color` of the font.
-| `--forge-avatar-radius` | Controls the `border-radius` style. Can be useful for custom avatar shapes.
-| `--forge-avatar-size` | Controls the `height` and `width` styles together.
+| `--forge-avatar-background` | Controls the `background-color` style.
+| `--forge-avatar-color` | Controls the `color` of the font.
+| `--forge-avatar-shape` | Controls the `border-radius` style. Can be useful for custom avatar shapes.
+| `--forge-avatar-size` | Controls the `height` and `width` styles together.
+| `--forge-avatar-transition-duration` | The transition duration for animations.
+| `--forge-avatar-transition-timing` | The transition timing function for animations.
diff --git a/src/stories/src/components/avatar/avatar.stories.tsx b/src/stories/src/components/avatar/avatar.stories.tsx
index cda465b61..437865cad 100644
--- a/src/stories/src/components/avatar/avatar.stories.tsx
+++ b/src/stories/src/components/avatar/avatar.stories.tsx
@@ -19,7 +19,6 @@ export default {
} as Meta;
export const Default: Story = ({
- autoColor = false,
imageUrl = '',
useIcon = false,
letterCount = 2,
@@ -31,7 +30,6 @@ export const Default: Story = ({
return (
@@ -40,7 +38,6 @@ export const Default: Story = ({
);
};
Default.args = {
- autoColor: false,
imageUrl: '',
useIcon: false,
letterCount: 2,
diff --git a/src/test/spec/avatar/avatar.spec.ts b/src/test/spec/avatar/avatar.spec.ts
deleted file mode 100644
index 666176771..000000000
--- a/src/test/spec/avatar/avatar.spec.ts
+++ /dev/null
@@ -1,204 +0,0 @@
-import { IAvatarComponent, AVATAR_CONSTANTS, defineAvatarComponent } from '@tylertech/forge/avatar';
-import { tick, appendElement, timer } from '@tylertech/forge-testing';
-import { removeElement, getShadowElement } from '@tylertech/forge-core';
-
-const DEFAULT_TEXT = 'Tom Brady';
-const DEFAULT_LETTER_COUNT = 1;
-
-interface ITestContext {
- context: ITestAvatarContext;
-}
-
-interface ITestAvatarContext {
- component: IAvatarComponent;
- destroy(): void;
-}
-
-describe('AvatarComponent', function(this: ITestContext) {
- beforeAll(function(this: ITestContext) {
- defineAvatarComponent();
- });
-
- afterEach(function(this: ITestContext) {
- this.context.destroy();
- });
-
- describe('with default attribute values', function(this: ITestContext) {
- it('should set slot text to first character of text', function(this: ITestContext) {
- this.context = setupTestContext(true);
- const defaultSlot = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT);
- expect(defaultSlot.textContent).toBe('T');
- });
-
- it('should change background image when set via attribute', async function(this: ITestContext) {
- this.context = setupTestContext(true);
- await tick();
-
- const url = 'https://empower.tylertech.com/rs/015-NUU-525/images/tyler-logo-color.svg';
- this.context.component.setAttribute(AVATAR_CONSTANTS.attributes.IMAGE_URL, url);
- const root = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.ROOT);
- // Give enough time for the image to load
- await timer(2000);
- expect(root.hasAttribute('style')).toBe(true);
- expect(root.getAttribute('style')).toContain('background-image');
- expect(root.getAttribute('style')).toContain(url);
- });
-
- it('should change text content when set via attribute', async function(this: ITestContext) {
- this.context = setupTestContext(true);
- await tick();
- this.context.component.setAttribute(AVATAR_CONSTANTS.attributes.TEXT, 'New Text');
- const defaultSlot = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT);
- expect(defaultSlot.textContent).toBe('N');
- });
-
- it('should change text content when letter count is set via attribute', async function(this: ITestContext) {
- this.context = setupTestContext(true);
- await tick();
- this.context.component.setAttribute(AVATAR_CONSTANTS.attributes.LETTER_COUNT, '3');
- const defaultSlot = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT);
- expect(defaultSlot.textContent).toBe('TB');
- });
- });
-
- describe('without default values', function(this: ITestContext) {
- it('should have proper default values', function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- expect(this.context.component.text).toBe('');
- expect(this.context.component.letterCount).toBe(AVATAR_CONSTANTS.numbers.DEFAULT_LETTER_COUNT);
- expect(this.context.component.imageUrl).toBeUndefined();
- expect(this.context.component.autoColor).toBeFalse();
- });
-
- it('should have not have any content by default', function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- const defaultSlot = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT);
- expect(defaultSlot.textContent).toBe('');
- });
-
- it('should not set background-color when text is not set', async function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- const root = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.ROOT);
- expect(root.style.backgroundColor).toBe(`var(${AVATAR_CONSTANTS.strings.BACKGROUND_VARNAME}, ${AVATAR_CONSTANTS.strings.DEFAULT_COLOR})`);
- });
-
- it('should set text content properly using default letter count', function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- const defaultSlot = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT);
- this.context.component.text = DEFAULT_TEXT;
- expect(defaultSlot.innerText.length).toBe(AVATAR_CONSTANTS.numbers.DEFAULT_LETTER_COUNT);
- expect(defaultSlot.textContent).toBe('TB');
- });
-
- it('should set background-color when text is set', function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- const root = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.ROOT);
- this.context.component.text = DEFAULT_TEXT;
-
- expect(root.hasAttribute('style')).toBe(true);
- expect(root.getAttribute('style')).toContain('background-color');
- expect(root.style.backgroundColor).not.toBeNull();
- expect(root.style.backgroundColor).not.toBe('');
- expect(root.style.backgroundColor!.length).toBeGreaterThan(0);
- });
-
- it('should set background-color when text is set and autoColor is false', function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- this.context.component.autoColor = false;
-
- const root = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.ROOT);
- this.context.component.text = DEFAULT_TEXT;
-
- expect(root.style.backgroundColor).toBe(`var(${AVATAR_CONSTANTS.strings.BACKGROUND_VARNAME}, ${AVATAR_CONSTANTS.strings.DEFAULT_COLOR})`);
- });
-
- it('should render slotted content in place of text content', function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- const defaultSlot = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT) as HTMLSlotElement;
-
- this.context.component.text = DEFAULT_TEXT;
-
- const node = document.createTextNode('Test Text');
- this.context.component.appendChild(node);
-
- expect(defaultSlot.textContent).toBe('TB');
- expect(defaultSlot.assignedNodes().length).toBe(1);
- });
-
- xit('should render background-image when image URL is set', async function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- const url = 'https://empower.tylertech.com/rs/015-NUU-525/images/tyler-logo-color.svg';
- this.context.component.imageUrl = url;
-
- const root = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.ROOT);
-
- // Give enough time for the image to load
- await timer(2000);
-
- expect(root.hasAttribute('style')).toBe(true);
- expect(root.getAttribute('style')).toContain('background-image');
- expect(root.getAttribute('style')).toContain(url);
- });
-
- it('should update text content when letter count changes', async function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- const defaultSlot = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT) as HTMLSlotElement;
- this.context.component.text = DEFAULT_TEXT;
- await tick();
- expect(defaultSlot.textContent).toBe('TB');
- await tick();
- this.context.component.letterCount = 1;
- expect(defaultSlot.textContent).toBe('T');
- });
-
- it('should render first character of each word when letter count is greater than number of words', function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- const defaultSlot = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT) as HTMLSlotElement;
- this.context.component.text = 'some long string with spaces';
- this.context.component.letterCount = 6; // One greater than the number of words in the string
- expect(defaultSlot.textContent).toBe('SLSWS');
- });
-
- it('should render text when image url fails to load an image with 500 error', async function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- const defaultSlot = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT) as HTMLSlotElement;
-
- this.context.component.text = 'Invalid Url';
- // Url that will return a 500 error
- this.context.component.imageUrl = 'https://httpstat.us/500';
- await timer(300);
- expect(defaultSlot.textContent).toBe('IU');
- });
-
- it('should render text when image url fails to load an image', async function(this: ITestContext) {
- this.context = setupTestContext(true, false);
- const defaultSlot = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.DEFAULT_SLOT) as HTMLSlotElement;
- const root = getShadowElement(this.context.component, AVATAR_CONSTANTS.selectors.ROOT);
-
- this.context.component.text = 'Invalid Url';
- // Url that will return a 404 error
- this.context.component.imageUrl = 'https://httpstat.us/404';
- await timer(300);
- expect(defaultSlot.textContent).toBe('IU');
- expect(root.getAttribute('style')).not.toContain('background-image');
- });
- });
-
- function setupTestContext(append = false, useDefaultValues = true): ITestAvatarContext {
- const fixture = document.createElement('div');
- fixture.id = 'avatar-test-fixture';
- const component = document.createElement(AVATAR_CONSTANTS.elementName) as IAvatarComponent;
- if (useDefaultValues) {
- component.text = DEFAULT_TEXT;
- component.letterCount = DEFAULT_LETTER_COUNT;
- }
- fixture.appendChild(component);
- if (append) {
- document.body.appendChild(fixture);
- }
- return {
- component,
- destroy: () => removeElement(fixture)
- };
- }
-});