0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00

feat(ui): app notification (#999)

* feat(ui): app notification

app notification

* feat(ui): remove session storage

remove session storage
This commit is contained in:
simeng-li 2022-05-31 14:24:32 +08:00 committed by GitHub
parent 3c37739107
commit f4e380f0b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 91 additions and 51 deletions

View file

@ -72,7 +72,6 @@ const translation = {
social_create_account: 'No account? You can create a new account and bind.',
social_bind_account: 'Already have an account? Sign in to bind it with your social identity.',
social_bind_with_existing: 'We find a related account, you can bind it directly.',
demo_message: 'Use the Admin username and password to sign in this demo.',
},
error: {
username_password_mismatch: 'Username and password do not match.',

View file

@ -72,7 +72,6 @@ const translation = {
social_create_account: 'No account? You can create a new account and bind.',
social_bind_account: 'Already have an account? Sign in to bind it with your social identity.',
social_bind_with_existing: 'We find a related account, you can bind it directly.',
demo_message: '请使用 admin 用户名和密码登录',
},
error: {
username_password_mismatch: '用户名和密码不匹配。',

View file

@ -1,10 +1,5 @@
@use '@/scss/underscore' as _;
.overlay {
background: transparent;
position: absolute;
}
.notification {
padding: _.unit(3) _.unit(4);
font: var(--font-body);
@ -15,16 +10,13 @@
max-width: 520px;
margin: 0 auto _.unit(2);
box-shadow: var(--shadow-1);
@include _.flex_row;
&:focus-visible {
outline: none;
}
}
.container {
@include _.flex_row;
}
.icon {
color: var(--color-outline);
width: 20px;
@ -42,23 +34,8 @@
cursor: pointer;
}
:global(body.mobile) {
.overlay {
top: _.unit(6);
left: _.unit(5);
right: _.unit(5);
}
}
:global(body.desktop) {
.overlay {
top: _.unit(-6);
left: 0;
width: 100%;
transform: translateY(-100%);
}
.link {
&:hover {
color: var(--color-primary);

View file

@ -1,37 +1,28 @@
import classNames from 'classnames';
import React from 'react';
import { useTranslation } from 'react-i18next';
import ReactModal, { Props as ModalProps } from 'react-modal';
import InfoIcon from '@/assets/icons/info-icon.svg';
import * as styles from './index.module.scss';
type Props = ModalProps & {
type Props = {
className?: string;
message: string;
onClose: () => void;
};
const Notification = ({ className, message, onClose, overlayClassName, ...rest }: Props) => {
const Notification = ({ className, message, onClose }: Props) => {
const { t } = useTranslation(undefined, { keyPrefix: 'main_flow' });
return (
<ReactModal
className={classNames(styles.notification, className)}
overlayClassName={classNames(styles.overlay, overlayClassName)}
ariaHideApp={false}
parentSelector={() => document.querySelector('main') ?? document.body}
onRequestClose={onClose}
{...rest}
>
<div className={styles.container}>
<InfoIcon className={styles.icon} />
<div className={styles.message}>{message}</div>
<a className={styles.link} onClick={onClose}>
{t('action.got_it')}
</a>
</div>
</ReactModal>
<div className={classNames(styles.notification, className)}>
<InfoIcon className={styles.icon} />
<div className={styles.message}>{message}</div>
<a className={styles.link} onClick={onClose}>
{t('action.got_it')}
</a>
</div>
);
};

View file

@ -0,0 +1,29 @@
@use '@/scss/underscore' as _;
.appNotification {
position: absolute;
top: _.unit(6);
left: 50%;
transform: translateX(-50%);
}
:global(body.mobile) {
.appNotification {
top: _.unit(6);
left: _.unit(5);
right: _.unit(5);
transform: none;
}
}
:global(body.desktop) {
.appNotification {
top: _.unit(-6);
transform: translate(-50%, -100%);
width: 100%;
max-width: 520px;
}
}

View file

@ -0,0 +1,24 @@
import { render, fireEvent } from '@testing-library/react';
import React from 'react';
import { appNotificationStorageKey } from '@/utils/session-storage';
import AppNotification from '.';
describe('AppNotification', () => {
it('render properly', () => {
const message = 'This is a notification message';
sessionStorage.setItem(appNotificationStorageKey, message);
const { queryByText, getByText } = render(<AppNotification />);
expect(queryByText(message)).not.toBeNull();
const closeLink = getByText('action.got_it');
expect(closeLink).not.toBeNull();
fireEvent.click(closeLink);
expect(queryByText(message)).toBeNull();
});
});

View file

@ -1,21 +1,31 @@
import { Nullable } from '@silverhand/essentials';
import React, { useState, useEffect, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import Notification from '@/components/Notification';
import { getAppNotificationInfo, clearAppNotificationInfo } from '@/utils/session-storage';
import * as styles from './index.module.scss';
const AppNotification = () => {
const { t } = useTranslation(undefined, { keyPrefix: 'main_flow' });
const [isOpen, setIsOpen] = useState(false);
const [notification, setNotification] = useState<Nullable<string>>(null);
const onClose = useCallback(() => {
setIsOpen(false);
setNotification(null);
clearAppNotificationInfo();
}, []);
useEffect(() => {
setIsOpen(true);
const notification = getAppNotificationInfo();
setNotification(notification);
}, []);
return <Notification isOpen={isOpen} message={t('description.demo_message')} onClose={onClose} />;
if (!notification) {
return null;
}
return (
<Notification className={styles.appNotification} message={notification} onClose={onClose} />
);
};
export default AppNotification;

View file

@ -3,6 +3,7 @@ import classNames from 'classnames';
import React, { useContext } from 'react';
import BrandingHeader from '@/components/BrandingHeader';
import AppNotification from '@/containers/AppNotification';
import { PageContext } from '@/hooks/use-page-context';
import * as styles from './index.module.scss';
@ -35,6 +36,7 @@ const SignIn = () => {
socialConnectors={experienceSettings.socialConnectors}
/>
<CreateAccountLink primarySignInMethod={experienceSettings.primarySignInMethod} />
<AppNotification />
</div>
);
};

View file

@ -0,0 +1,9 @@
export const appNotificationStorageKey = 'logto:client:notification';
export const getAppNotificationInfo = () => {
return sessionStorage.getItem(appNotificationStorageKey);
};
export const clearAppNotificationInfo = () => {
sessionStorage.removeItem(appNotificationStorageKey);
};