0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-06 20:40:08 -05:00

feat(elements): add logto-user-password element ()

This commit is contained in:
Xiao Yijun 2024-10-25 14:45:52 +08:00 committed by GitHub
parent 20a714c94d
commit 11d9ed4315
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 140 additions and 1 deletions

View file

@ -0,0 +1,68 @@
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 { LogtoUserPassword } from './logto-user-password.js';
suite('logto-user-password', () => {
test('is defined', () => {
const element = document.createElement(LogtoUserPassword.tagName);
assert.instanceOf(element, LogtoUserPassword);
});
test('should render error message when account context is not available', async () => {
const element = await fixture<LogtoUserPassword>(
html`<logto-user-password></logto-user-password>`
);
await element.updateComplete;
assert.equal(element.shadowRoot?.textContent, 'Unable to retrieve account context.');
});
test('should render configured status when user has password', async () => {
const mockAccountApi = createMockAccountApi({
fetchUserProfile: async () => ({
hasPassword: true,
}),
});
const provider = await fixture<LogtoAccountProvider>(
html`<logto-account-provider .accountApi=${mockAccountApi}>
<logto-user-password></logto-user-password>
</logto-account-provider>`
);
await provider.updateComplete;
const logtoUserPassword = provider.querySelector<LogtoUserPassword>(LogtoUserPassword.tagName);
await waitUntil(
() =>
logtoUserPassword?.shadowRoot
?.querySelector('.status')
?.textContent?.includes('Configured'),
'Unable to get password status from account context'
);
});
test('should not render status when user has no password', async () => {
const mockAccountApi = createMockAccountApi({
fetchUserProfile: async () => ({
hasPassword: false,
}),
});
const provider = await fixture<LogtoAccountProvider>(
html`<logto-account-provider .accountApi=${mockAccountApi}>
<logto-user-password></logto-user-password>
</logto-account-provider>`
);
await provider.updateComplete;
const logtoUserPassword = provider.querySelector<LogtoUserPassword>(LogtoUserPassword.tagName);
await logtoUserPassword?.updateComplete;
assert.isNull(logtoUserPassword?.shadowRoot?.querySelector('.status'));
});
});

View file

@ -0,0 +1,62 @@
import { css, html, type TemplateResult } from 'lit';
import { customElement } from 'lit/decorators.js';
import { when } from 'lit/directives/when.js';
import passwordIcon from '../icons/password.svg';
import { LogtoProfileItemElement } from './LogtoProfileItemElement.js';
const tagName = 'logto-user-password';
@customElement(tagName)
export class LogtoUserPassword extends LogtoProfileItemElement {
static tagName = tagName;
static styles = css`
.status {
display: flex;
align-items: center;
gap: var(--logto-spacing-sm);
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--logto-color-container-on-success);
}
`;
protected isAccessible(): boolean {
// The password is always accessible
return true;
}
protected getItemLabelInfo() {
return {
icon: passwordIcon,
label: 'Password',
};
}
protected renderContent(): TemplateResult {
const { hasPassword } = this.accountContext?.userProfile ?? {};
return when(
hasPassword,
() =>
html`<div slot="content">
<div class="status">
<span class="status-dot"></span>
Configured
</div>
</div>`
);
}
}
declare global {
// eslint-disable-next-line @typescript-eslint/consistent-type-definitions
interface HTMLElementTagNameMap {
[tagName]: LogtoUserPassword;
}
}

View file

@ -0,0 +1,3 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.5001 3.66667L18.0834 3.08333C18.4167 2.75 18.4167 2.25 18.0834 1.91667C17.7501 1.58333 17.2501 1.58333 16.9167 1.91667L8.16675 10.6667C7.50008 10.25 6.66675 10 5.83342 10C3.50008 10 1.66675 11.8333 1.66675 14.1667C1.66675 16.5 3.50008 18.3333 5.83342 18.3333C8.16675 18.3333 10.0001 16.5 10.0001 14.1667C10.0001 13.3333 9.75008 12.5 9.33342 11.8333L14.0001 7.16667L15.7501 8.91667C16.0834 9.25 16.5834 9.25 16.9167 8.91667C17.2501 8.58333 17.2501 8.08333 16.9167 7.75L15.1667 6L16.3334 4.83333L16.9167 5.41667C17.2501 5.75 17.7501 5.75 18.0834 5.41667C18.4167 5.08333 18.4167 4.58333 18.0834 4.25L17.5001 3.66667ZM5.83342 16.6667C4.41675 16.6667 3.33341 15.5833 3.33341 14.1667C3.33341 12.75 4.41675 11.6667 5.83342 11.6667C7.25008 11.6667 8.33342 12.75 8.33342 14.1667C8.33342 15.5833 7.25008 16.6667 5.83342 16.6667Z" fill="currentColor"/>
</svg>

After

(image error) Size: 958 B

View file

@ -7,3 +7,4 @@ export * from './providers/logto-account-provider.js';
export * from './elements/logto-username.js';
export * from './elements/logto-user-email.js';
export * from './elements/logto-user-password.js';

View file

@ -1,7 +1,7 @@
import { createComponent } from '@lit/react';
import { LogtoUsername } from './elements/logto-username.js';
import { LogtoAccountProvider, LogtoUserEmail } from './index.js';
import { LogtoAccountProvider, LogtoUserEmail, LogtoUserPassword } from './index.js';
export * from './api/index.js';
@ -22,5 +22,10 @@ export const createReactComponents = (react: Parameters<typeof createComponent>[
elementClass: LogtoUserEmail,
react,
}),
LogtoUserPassword: createComponent({
tagName: LogtoUserPassword.tagName,
elementClass: LogtoUserPassword,
react,
}),
};
};