diff --git a/packages/elements/src/account/elements/logto-user-email.test.ts b/packages/elements/src/account/elements/logto-user-email.test.ts new file mode 100644 index 000000000..034c2c885 --- /dev/null +++ b/packages/elements/src/account/elements/logto-user-email.test.ts @@ -0,0 +1,65 @@ +import { assert, fixture, html, waitUntil } from '@open-wc/testing'; + +import { createMockAccountApi } from '../__mocks__/account-api.js'; +import { type LogtoAccountProvider } from '../providers/logto-account-provider.js'; + +import { LogtoUserEmail } from './logto-user-email.js'; + +suite('logto-user-email', () => { + test('is defined', () => { + const element = document.createElement(LogtoUserEmail.tagName); + assert.instanceOf(element, LogtoUserEmail); + }); + + test('should render error message when account context is not available', async () => { + const element = await fixture(html``); + await element.updateComplete; + + assert.equal(element.shadowRoot?.textContent, 'Unable to retrieve account context.'); + }); + + test('should render email if the user has permission to view email information', async () => { + const mockAccountApi = createMockAccountApi({ + fetchUserProfile: async () => ({ + primaryEmail: 'user@example.com', + }), + }); + + const provider = await fixture( + html` + + ` + ); + + await provider.updateComplete; + + const logtoUserEmail = provider.querySelector(LogtoUserEmail.tagName); + + await waitUntil( + () => + logtoUserEmail?.shadowRoot?.querySelector('div[slot="content"]')?.textContent === + 'user@example.com', + 'Unable to get email from account context' + ); + }); + + test('should render nothing if the user lacks permission to view email information', async () => { + const mockAccountApi = createMockAccountApi({ + fetchUserProfile: async () => ({ + primaryEmail: undefined, + }), + }); + + const provider = await fixture( + html` + + ` + ); + + await provider.updateComplete; + const logtoUserEmail = provider.querySelector(LogtoUserEmail.tagName); + + await logtoUserEmail?.updateComplete; + assert.equal(logtoUserEmail?.shadowRoot?.children.length, 0); + }); +}); diff --git a/packages/elements/src/account/elements/logto-user-email.ts b/packages/elements/src/account/elements/logto-user-email.ts new file mode 100644 index 000000000..c798e2158 --- /dev/null +++ b/packages/elements/src/account/elements/logto-user-email.ts @@ -0,0 +1,38 @@ +import { html, type TemplateResult } from 'lit'; +import { customElement } from 'lit/decorators.js'; +import { when } from 'lit/directives/when.js'; + +import emailIcon from '../icons/email.svg'; + +import { LogtoProfileItemElement } from './LogtoProfileItemElement.js'; + +const tagName = 'logto-user-email'; + +@customElement(tagName) +export class LogtoUserEmail extends LogtoProfileItemElement { + static tagName = tagName; + + protected isAccessible(): boolean { + return this.accountContext?.userProfile.primaryEmail !== undefined; + } + + protected getItemLabelInfo() { + return { + icon: emailIcon, + label: 'Email address', + }; + } + + protected renderContent(): TemplateResult { + const { primaryEmail } = this.accountContext?.userProfile ?? {}; + + return when(primaryEmail, (email) => html`
${email}
`); + } +} + +declare global { + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions + interface HTMLElementTagNameMap { + [tagName]: LogtoUserEmail; + } +} diff --git a/packages/elements/src/account/icons/email.svg b/packages/elements/src/account/icons/email.svg new file mode 100644 index 000000000..60b2738fb --- /dev/null +++ b/packages/elements/src/account/icons/email.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/elements/src/account/index.ts b/packages/elements/src/account/index.ts index 7c4f2be75..99e4a3952 100644 --- a/packages/elements/src/account/index.ts +++ b/packages/elements/src/account/index.ts @@ -6,3 +6,4 @@ export * from './components/logto-profile-item.js'; export * from './providers/logto-account-provider.js'; export * from './elements/logto-username.js'; +export * from './elements/logto-user-email.js'; diff --git a/packages/elements/src/account/react.ts b/packages/elements/src/account/react.ts index d9a7cc755..b42db8bf8 100644 --- a/packages/elements/src/account/react.ts +++ b/packages/elements/src/account/react.ts @@ -1,7 +1,7 @@ import { createComponent } from '@lit/react'; import { LogtoUsername } from './elements/logto-username.js'; -import { LogtoAccountProvider } from './index.js'; +import { LogtoAccountProvider, LogtoUserEmail } from './index.js'; export * from './api/index.js'; @@ -17,5 +17,10 @@ export const createReactComponents = (react: Parameters[ elementClass: LogtoUsername, react, }), + LogtoUserEmail: createComponent({ + tagName: LogtoUserEmail.tagName, + elementClass: LogtoUserEmail, + react, + }), }; };