Skip to content

Commit

Permalink
Add large and xl sizes to PersonAvatar (#397)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
jeffdaley authored Nov 6, 2023
1 parent 2557ff1 commit 84438a5
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 9 deletions.
11 changes: 9 additions & 2 deletions web/app/components/person/avatar.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<div
data-test-person-avatar
class="avatar overflow-hidden rounded-full
{{unless @isLoading 'bg-color-palette-alpha-300'}}
{{this.size}}
Expand All @@ -8,13 +9,19 @@
{{#if @isLoading}}
{{! Avatar loading affordance }}
<div
data-test-loading
class="h-full w-full animate-spin rounded-full bg-gradient-to-tr from-color-palette-neutral-50 to-color-palette-neutral-200"
></div>
{{else}}
{{#if @imgURL}}
<img src={{@imgURL}} referrerpolicy="no-referrer" class="w-full" />
<img
data-test-image
src={{@imgURL}}
referrerpolicy="no-referrer"
class="w-full"
/>
{{else}}
<div class="flex text-body-100">
<div data-test-fallback class="flex text-body-100">
{{#if @email}}
<span class="capitalize">
{{get-first-letter @email}}
Expand Down
9 changes: 6 additions & 3 deletions web/app/components/person/avatar.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
import Component from "@glimmer/component";
import { HermesAvatarSize } from "hermes/types/avatar-size";
import {
HermesBasicAvatarSize,
HermesPersonAvatarSize,
} from "hermes/types/avatar-size";

interface PersonAvatarComponentSignature {
Element: HTMLDivElement;
Args: {
imgURL?: string | null;
isLoading?: boolean;
email: string;
size: `${HermesAvatarSize}`;
size?: `${HermesPersonAvatarSize}`;
};
Blocks: {
default: [];
};
}

export default class PersonAvatarComponent extends Component<PersonAvatarComponentSignature> {
protected size = this.args.size ?? HermesAvatarSize.Small;
protected size = this.args.size ?? HermesBasicAvatarSize.Small;
}

declare module "@glint/environment-ember-loose/registry" {
Expand Down
6 changes: 3 additions & 3 deletions web/app/components/product/avatar.gts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [];
Expand All @@ -27,7 +27,7 @@ export default class ProductAvatarComponent extends Component<ProductAvatarCompo
}

private get size() {
return this.args.size ?? HermesAvatarSize.Small;
return this.args.size ?? HermesBasicAvatarSize.Small;
}

<template>
Expand Down
4 changes: 4 additions & 0 deletions web/app/styles/hermes/avatar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@
&.large {
@apply h-9 w-9;
}

&.xl {
@apply h-16 w-16;
}
}
10 changes: 9 additions & 1 deletion web/app/types/avatar-size.ts
Original file line number Diff line number Diff line change
@@ -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;
76 changes: 76 additions & 0 deletions web/tests/integration/components/person/avatar-test.ts
Original file line number Diff line number Diff line change
@@ -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<PersonAvatarTestContext>(hbs`
<Person::Avatar class="default" @email="" />
<Person::Avatar class="small" @email="" @size="small" />
<Person::Avatar class="medium" @email="" @size="medium" />
<Person::Avatar class="large" @email="" @size="large" />
<Person::Avatar class="xl" @email="" @size="xl" />
`);

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<PersonAvatarTestContext>(hbs`
<Person::Avatar @email="Test User" @isLoading={{this.isLoading}} />
`);

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<PersonAvatarTestContext>(hbs`
<Person::Avatar @email="Barbara" @imgURL={{this.imgURL}} />
`);

assert.dom(IMAGE).hasAttribute("src", "#");
assert.dom(FALLBACK).doesNotExist();

this.set("imgURL", undefined);

assert.dom(IMAGE).doesNotExist();
assert.dom(FALLBACK).hasText("B");
});
});

0 comments on commit 84438a5

Please sign in to comment.