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:
parent
f48d3a1f95
commit
50031369a1
5 changed files with 157 additions and 1 deletions
|
@ -10,7 +10,7 @@
|
|||
</head>
|
||||
<body style="background-color: #ecebf5;">
|
||||
<logto-account-provider>
|
||||
<logto-username></logto-username>
|
||||
<logto-account-center></logto-account-center>
|
||||
</logto-account-provider>
|
||||
</body>
|
||||
|
||||
|
|
|
@ -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'));
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -11,3 +11,4 @@ export * from './elements/logto-user-email.js';
|
|||
export * from './elements/logto-user-password.js';
|
||||
export * from './elements/logto-user-phone.js';
|
||||
export * from './elements/logto-social-identity.js';
|
||||
export * from './elements/logto-account-center.js';
|
||||
|
|
|
@ -2,6 +2,7 @@ import { createComponent } from '@lit/react';
|
|||
|
||||
import { LogtoUsername } from './elements/logto-username.js';
|
||||
import {
|
||||
LogtoAccountCenter,
|
||||
LogtoAccountProvider,
|
||||
LogtoSocialIdentity,
|
||||
LogtoUserEmail,
|
||||
|
@ -43,5 +44,10 @@ export const createReactComponents = (react: Parameters<typeof createComponent>[
|
|||
elementClass: LogtoSocialIdentity,
|
||||
react,
|
||||
}),
|
||||
LogtoAccountCenter: createComponent({
|
||||
tagName: LogtoAccountCenter.tagName,
|
||||
elementClass: LogtoAccountCenter,
|
||||
react,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue