Skip to content

Commit 9ec0f88

Browse files
committed
fix(web-components): remove empty text nodes from avatar
1 parent ec07aac commit 9ec0f88

File tree

6 files changed

+52
-4
lines changed

6 files changed

+52
-4
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "fix: remove empty text nodes from avatar",
4+
"packageName": "@fluentui/web-components",
5+
"email": "863023+radium-v@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}

packages/web-components/docs/web-components.api.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,9 +466,13 @@ export class BaseAnchor extends FASTElement {
466466
export class BaseAvatar extends FASTElement {
467467
constructor();
468468
// @internal
469+
defaultSlot: HTMLSlotElement;
470+
// @internal
469471
elementInternals: ElementInternals;
470472
initials?: string | undefined;
471473
name?: string | undefined;
474+
// @internal
475+
slotchangeHandler(e: Event): void;
472476
}
473477

474478
// @public

packages/web-components/src/avatar/avatar.base.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import { attr, FASTElement } from '@microsoft/fast-element';
55
* @public
66
*/
77
export class BaseAvatar extends FASTElement {
8+
/**
9+
* Reference to the default slot element.
10+
* @internal
11+
*/
12+
public defaultSlot!: HTMLSlotElement;
13+
814
/**
915
* The internal {@link https://developer.mozilla.org/docs/Web/API/ElementInternals | `ElementInternals`} instance for the component.
1016
*
@@ -37,4 +43,20 @@ export class BaseAvatar extends FASTElement {
3743

3844
this.elementInternals.role = 'img';
3945
}
46+
47+
/**
48+
* Removes any empty text nodes from the default slot when the slotted content changes.
49+
*
50+
* @param e - The event object
51+
* @internal
52+
*/
53+
public slotchangeHandler(e: Event): void {
54+
if (!this.innerText.trim()) {
55+
this.defaultSlot.assignedNodes().forEach(node => {
56+
if (node.nodeType === Node.TEXT_NODE) {
57+
(node as Element).remove();
58+
}
59+
});
60+
}
61+
}
4062
}

packages/web-components/src/avatar/avatar.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ test.describe('Avatar', () => {
1212
await expect(element).toHaveJSProperty('elementInternals.role', 'img');
1313
});
1414

15+
test('should render an icon when no name or initials are provided', async ({ fastPage }) => {
16+
const { element } = fastPage;
17+
const icon = element.locator('svg');
18+
19+
await expect(icon).toBeVisible();
20+
});
21+
1522
test('When no name value is set, should render with custom initials based on the provided initials value', async ({
1623
fastPage,
1724
}) => {
@@ -20,6 +27,12 @@ test.describe('Avatar', () => {
2027
await fastPage.setTemplate({ attributes: { initials: 'JD' } });
2128

2229
await expect(element).toContainText('JD');
30+
31+
await test.step('should not render the default icon', async () => {
32+
const icon = element.locator('svg');
33+
34+
await expect(icon).toBeHidden();
35+
});
2336
});
2437

2538
test('When name value is set, should generate initials based on the provided name value', async ({ fastPage }) => {

packages/web-components/src/avatar/avatar.template.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type ElementViewTemplate, html } from '@microsoft/fast-element';
1+
import { type ElementViewTemplate, html, ref } from '@microsoft/fast-element';
22
import type { Avatar } from './avatar.js';
33

44
const defaultIconTemplate = html`<svg
@@ -21,7 +21,9 @@ const defaultIconTemplate = html`<svg
2121
*/
2222
export function avatarTemplate<T extends Avatar>(): ElementViewTemplate<T> {
2323
return html<T>`
24-
<slot>${x => (x.name || x.initials ? x.generateInitials() : defaultIconTemplate)}</slot>
24+
<slot @slotchange="${(x, c) => x.slotchangeHandler(c.event)}" ${ref('defaultSlot')}>
25+
${x => (x.name || x.initials ? x.generateInitials() : defaultIconTemplate)}
26+
</slot>
2527
<slot name="badge"></slot>
2628
`;
2729
}

packages/web-components/src/avatar/avatar.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ export class Avatar extends BaseAvatar {
123123
const size = this.size ?? 32;
124124

125125
return (
126-
this.initials ??
127-
getInitials(this.name, window.getComputedStyle(this as unknown as HTMLElement).direction === 'rtl', {
126+
this.initials ||
127+
getInitials(this.name, window.getComputedStyle(this).direction === 'rtl', {
128128
firstInitialOnly: size <= 16,
129129
})
130130
);

0 commit comments

Comments
 (0)