From 84438a51419b659551d28968329d0aff8a95ede2 Mon Sep 17 00:00:00 2001 From: Jeff Daley Date: Mon, 6 Nov 2023 09:16:44 -0500 Subject: [PATCH] Add `large` and `xl` sizes to PersonAvatar (#397) * Tweak HDS types * Updates `get-product-id` files * Remove number type * Update `productShortName` getter * Avatar.gts * Basic ProductAvatar implementation * Add `large` size to PersonAvatar * Refactor to class-based sizing * Fix visual bugs, rename SCSS file * Refactor Thumbnail interface * Switch `size` to getters * Add data-test-identifier * Add XL to PersonAvatar --- web/app/components/person/avatar.hbs | 11 ++- web/app/components/person/avatar.ts | 9 ++- web/app/components/product/avatar.gts | 6 +- web/app/styles/hermes/avatar.scss | 4 + web/app/types/avatar-size.ts | 10 ++- .../components/person/avatar-test.ts | 76 +++++++++++++++++++ 6 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 web/tests/integration/components/person/avatar-test.ts diff --git a/web/app/components/person/avatar.hbs b/web/app/components/person/avatar.hbs index cc02f27c8..db7a59a32 100644 --- a/web/app/components/person/avatar.hbs +++ b/web/app/components/person/avatar.hbs @@ -1,4 +1,5 @@
{{else}} {{#if @imgURL}} - + {{else}} -
+
{{#if @email}} {{get-first-letter @email}} diff --git a/web/app/components/person/avatar.ts b/web/app/components/person/avatar.ts index 1b26e6be8..c9e1e358c 100644 --- a/web/app/components/person/avatar.ts +++ b/web/app/components/person/avatar.ts @@ -1,5 +1,8 @@ import Component from "@glimmer/component"; -import { HermesAvatarSize } from "hermes/types/avatar-size"; +import { + HermesBasicAvatarSize, + HermesPersonAvatarSize, +} from "hermes/types/avatar-size"; interface PersonAvatarComponentSignature { Element: HTMLDivElement; @@ -7,7 +10,7 @@ interface PersonAvatarComponentSignature { imgURL?: string | null; isLoading?: boolean; email: string; - size: `${HermesAvatarSize}`; + size?: `${HermesPersonAvatarSize}`; }; Blocks: { default: []; @@ -15,7 +18,7 @@ interface PersonAvatarComponentSignature { } export default class PersonAvatarComponent extends Component { - protected size = this.args.size ?? HermesAvatarSize.Small; + protected size = this.args.size ?? HermesBasicAvatarSize.Small; } declare module "@glint/environment-ember-loose/registry" { diff --git a/web/app/components/product/avatar.gts b/web/app/components/product/avatar.gts index 95cc16cea..5840955d0 100644 --- a/web/app/components/product/avatar.gts +++ b/web/app/components/product/avatar.gts @@ -4,13 +4,13 @@ import { inject as service } from "@ember/service"; import FlightIcon from "@hashicorp/ember-flight-icons/components/flight-icon"; import ProductAreasService from "hermes/services/product-areas"; import { assert } from "@ember/debug"; -import { HermesAvatarSize } from "hermes/types/avatar-size"; +import { HermesBasicAvatarSize } from "hermes/types/avatar-size"; interface ProductAvatarComponentSignature { Element: HTMLDivElement; Args: { product?: string; - size?: `${HermesAvatarSize}`; + size?: `${HermesBasicAvatarSize}`; }; Blocks: { default: []; @@ -27,7 +27,7 @@ export default class ProductAvatarComponent extends Component diff --git a/web/app/styles/hermes/avatar.scss b/web/app/styles/hermes/avatar.scss index b6ccbe000..be0ee5136 100644 --- a/web/app/styles/hermes/avatar.scss +++ b/web/app/styles/hermes/avatar.scss @@ -12,4 +12,8 @@ &.large { @apply h-9 w-9; } + + &.xl { + @apply h-16 w-16; + } } diff --git a/web/app/types/avatar-size.ts b/web/app/types/avatar-size.ts index 3d71ed261..6ae2aa5d0 100644 --- a/web/app/types/avatar-size.ts +++ b/web/app/types/avatar-size.ts @@ -1,5 +1,13 @@ -export enum HermesAvatarSize { +export enum HermesBasicAvatarSize { Small = "small", Medium = "medium", Large = "large", } + +export enum HermesExtendedAvatarSize { + XL = "xl", +} + +export type HermesPersonAvatarSize = + | HermesBasicAvatarSize + | HermesExtendedAvatarSize; diff --git a/web/tests/integration/components/person/avatar-test.ts b/web/tests/integration/components/person/avatar-test.ts new file mode 100644 index 000000000..024bb7a57 --- /dev/null +++ b/web/tests/integration/components/person/avatar-test.ts @@ -0,0 +1,76 @@ +import { TestContext, render } from "@ember/test-helpers"; +import { hbs } from "ember-cli-htmlbars"; +import { setupRenderingTest } from "ember-qunit"; +import { module, test } from "qunit"; + +const AVATAR = "[data-test-person-avatar]"; +const LOADING = `${AVATAR} [data-test-loading]`; +const IMAGE = `${AVATAR} [data-test-image]`; +const FALLBACK = `${AVATAR} [data-test-fallback]`; + +interface PersonAvatarTestContext extends TestContext { + isLoading?: boolean; + imgURL?: string; +} + +module("Integration | Component | person/avatar", async function (hooks) { + setupRenderingTest(hooks); + + test("it renders at different sizes", async function (this: PersonAvatarTestContext, assert) { + await render(hbs` + + + + + + `); + + assert.dom(".default").hasStyle({ width: "20px" }); + assert.dom(".default").hasStyle({ height: "20px" }); + + assert.dom(".small").hasStyle({ width: "20px" }); + assert.dom(".small").hasStyle({ height: "20px" }); + + assert.dom(".medium").hasStyle({ width: "28px" }); + assert.dom(".medium").hasStyle({ height: "28px" }); + + assert.dom(".large").hasStyle({ width: "36px" }); + assert.dom(".large").hasStyle({ height: "36px" }); + + assert.dom(".xl").hasStyle({ width: "64px" }); + assert.dom(".xl").hasStyle({ height: "64px" }); + }); + + test("it can render a loading state", async function (this: PersonAvatarTestContext, assert) { + this.set("isLoading", true); + + await render(hbs` + + `); + + assert.dom(LOADING).exists(); + assert.dom(IMAGE).doesNotExist(); + assert.dom(FALLBACK).doesNotExist(); + + this.set("isLoading", false); + + assert.dom(LOADING).doesNotExist(); + assert.dom(FALLBACK).exists(); + }); + + test("it renders an image if provided and a fallback if not", async function (this: PersonAvatarTestContext, assert) { + this.set("imgURL", "#"); + + await render(hbs` + + `); + + assert.dom(IMAGE).hasAttribute("src", "#"); + assert.dom(FALLBACK).doesNotExist(); + + this.set("imgURL", undefined); + + assert.dom(IMAGE).doesNotExist(); + assert.dom(FALLBACK).hasText("B"); + }); +});