Skip to content

Commit

Permalink
feat(elements): add logto-account-center element (#6737)
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoyijun authored Oct 25, 2024
1 parent f48d3a1 commit 5003136
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 1 deletion.
2 changes: 1 addition & 1 deletion packages/elements/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down
Original file line number Diff line number Diff line change
@@ -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: '[email protected]',
primaryPhone: '1234567890',
hasPassword: true,
identities: {
google: {
userId: 'google-123',
details: { name: 'John Doe', email: '[email protected]' },
},
facebook: {
userId: 'facebook-123',
details: { name: 'Jane Doe', email: '[email protected]' },
},
},
}),
});

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'));
});
});
54 changes: 54 additions & 0 deletions packages/elements/src/account/elements/logto-account-center.ts
Original file line number Diff line number Diff line change
@@ -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;
}
}
1 change: 1 addition & 0 deletions packages/elements/src/account/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
6 changes: 6 additions & 0 deletions packages/elements/src/account/react.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createComponent } from '@lit/react';

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

0 comments on commit 5003136

Please sign in to comment.