0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

feat(elements): add logto-account-center element (#6737)

This commit is contained in:
Xiao Yijun 2024-10-25 17:39:15 +08:00 committed by GitHub
parent f48d3a1f95
commit 50031369a1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 157 additions and 1 deletions

View file

@ -10,7 +10,7 @@
</head> </head>
<body style="background-color: #ecebf5;"> <body style="background-color: #ecebf5;">
<logto-account-provider> <logto-account-provider>
<logto-username></logto-username> <logto-account-center></logto-account-center>
</logto-account-provider> </logto-account-provider>
</body> </body>

View file

@ -0,0 +1,95 @@
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 { LogtoAccountCenter } from './logto-account-center.js';
suite('logto-account-center', () => {
test('is defined', () => {
const element = document.createElement(LogtoAccountCenter.tagName);
assert.instanceOf(element, LogtoAccountCenter);
});
test('should render error message when account context is not available', async () => {
const element = await fixture<LogtoAccountCenter>(
html`<logto-account-center></logto-account-center>`
);
await element.updateComplete;
assert.equal(element.shadowRoot?.textContent?.trim(), 'Unable to retrieve account context.');
});
test('should render components correctly based on user info', async () => {
const mockAccountApi = createMockAccountApi({
fetchUserProfile: async () => ({
username: 'testuser',
primaryEmail: 'test@example.com',
primaryPhone: '1234567890',
hasPassword: true,
identities: {
google: {
userId: 'google-123',
details: { name: 'John Doe', email: 'john@example.com' },
},
facebook: {
userId: 'facebook-123',
details: { name: 'Jane Doe', email: 'jane@example.com' },
},
},
}),
});
const provider = await fixture<LogtoAccountProvider>(
html`<logto-account-provider .accountApi=${mockAccountApi}>
<logto-account-center></logto-account-center>
</logto-account-provider>`
);
await provider.updateComplete;
const accountCenter = provider.querySelector<LogtoAccountCenter>(LogtoAccountCenter.tagName);
await accountCenter?.updateComplete;
await waitUntil(() => {
const shadowRoot = accountCenter?.shadowRoot;
return (
shadowRoot?.querySelector('logto-username') &&
shadowRoot.querySelector('logto-user-email') &&
shadowRoot.querySelector('logto-user-phone') &&
shadowRoot.querySelector('logto-user-password') &&
shadowRoot.querySelectorAll('logto-social-identity').length === 2
);
}, 'Unable to render all expected components');
});
test('should only render components for existing user info', async () => {
const mockAccountApi = createMockAccountApi({
fetchUserProfile: async () => ({
username: 'testuser',
primaryEmail: undefined,
primaryPhone: undefined,
hasPassword: undefined,
identities: {},
}),
});
const provider = await fixture<LogtoAccountProvider>(
html`<logto-account-provider .accountApi=${mockAccountApi}>
<logto-account-center></logto-account-center>
</logto-account-provider>`
);
await provider.updateComplete;
const accountCenter = provider.querySelector<LogtoAccountCenter>(LogtoAccountCenter.tagName);
await accountCenter?.updateComplete;
const shadowRoot = accountCenter?.shadowRoot;
assert.exists(shadowRoot?.querySelector('logto-username'));
assert.notExists(shadowRoot.querySelector('logto-user-email'));
assert.notExists(shadowRoot.querySelector('logto-user-phone'));
assert.notExists(shadowRoot.querySelector('logto-user-password'));
assert.notExists(shadowRoot.querySelector('logto-social-identity'));
});
});

View file

@ -0,0 +1,54 @@
import { consume } from '@lit/context';
import { css, html, LitElement } from 'lit';
import { customElement } from 'lit/decorators.js';
import {
logtoAccountContext,
type LogtoAccountContextType,
} from '../providers/logto-account-provider.js';
const tagName = 'logto-account-center';
@customElement(tagName)
export class LogtoAccountCenter extends LitElement {
static tagName = tagName;
static styles = css`
:host {
display: flex;
flex-direction: column;
gap: var(--logto-account-center-item-spacing, var(--logto-spacing-md));
}
`;
@consume({ context: logtoAccountContext, subscribe: true })
private readonly accountContext?: LogtoAccountContextType;
render() {
if (!this.accountContext) {
return html`<span>Unable to retrieve account context.</span>`;
}
const {
userProfile: { username, primaryEmail, primaryPhone, hasPassword, identities },
} = this.accountContext;
return html`
${username !== undefined && html`<logto-username></logto-username>`}
${primaryEmail !== undefined && html`<logto-user-email></logto-user-email>`}
${primaryPhone !== undefined && html`<logto-user-phone></logto-user-phone>`}
${hasPassword !== undefined && html`<logto-user-password></logto-user-password>`}
${identities !== undefined &&
Object.entries(identities).map(
([target]) => html`<logto-social-identity target=${target}></logto-social-identity>`
)}
`;
}
}
declare global {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface HTMLElementTagNameMap {
[tagName]: LogtoAccountCenter;
}
}

View file

@ -11,3 +11,4 @@ export * from './elements/logto-user-email.js';
export * from './elements/logto-user-password.js'; export * from './elements/logto-user-password.js';
export * from './elements/logto-user-phone.js'; export * from './elements/logto-user-phone.js';
export * from './elements/logto-social-identity.js'; export * from './elements/logto-social-identity.js';
export * from './elements/logto-account-center.js';

View file

@ -2,6 +2,7 @@ import { createComponent } from '@lit/react';
import { LogtoUsername } from './elements/logto-username.js'; import { LogtoUsername } from './elements/logto-username.js';
import { import {
LogtoAccountCenter,
LogtoAccountProvider, LogtoAccountProvider,
LogtoSocialIdentity, LogtoSocialIdentity,
LogtoUserEmail, LogtoUserEmail,
@ -43,5 +44,10 @@ export const createReactComponents = (react: Parameters<typeof createComponent>[
elementClass: LogtoSocialIdentity, elementClass: LogtoSocialIdentity,
react, react,
}), }),
LogtoAccountCenter: createComponent({
tagName: LogtoAccountCenter.tagName,
elementClass: LogtoAccountCenter,
react,
}),
}; };
}; };