Update UI for user comments sidebar
This commit is contained in:
@@ -67,6 +67,11 @@
|
||||
padding: 0 16px 16px;
|
||||
}
|
||||
|
||||
.error {
|
||||
font-size: 14px;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.content::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
}
|
||||
@@ -101,6 +106,7 @@
|
||||
.info {
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
padding-right: 8px;
|
||||
overflow: hidden;
|
||||
line-height: 1;
|
||||
}
|
||||
@@ -109,6 +115,7 @@
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
@@ -117,6 +124,7 @@
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
color: var(--color13);
|
||||
}
|
||||
@@ -160,7 +168,7 @@
|
||||
}
|
||||
|
||||
.preloader {
|
||||
margin: 0 auto 18px;
|
||||
margin: 0 auto;
|
||||
color: var(--color13);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { h } from 'preact';
|
||||
import '@testing-library/jest-dom';
|
||||
import { waitFor } from '@testing-library/preact';
|
||||
|
||||
import { render } from 'tests/utils';
|
||||
import * as api from 'common/api';
|
||||
@@ -43,48 +42,68 @@ const commentsStub = [commentStub, commentStub, commentStub];
|
||||
describe('<Profile />', () => {
|
||||
it('should render preloader', () => {
|
||||
jest.spyOn(pq, 'parseQuery').mockImplementation(() => ({ ...userParamsStub }));
|
||||
const { container } = render(<Profile />);
|
||||
const { queryByLabelText, queryByRole } = render(<Profile />);
|
||||
|
||||
expect(container.querySelector('[aria-label="Loading..."]')).toBeInTheDocument();
|
||||
expect(queryByLabelText('Loading...')).toBeInTheDocument();
|
||||
expect(queryByRole('button', { name: /retry/i })).not.toBeInTheDocument();
|
||||
expect(queryByRole('heading', { name: /recent comments/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render error', async () => {
|
||||
jest.spyOn(pq, 'parseQuery').mockImplementation(() => ({ ...userParamsStub }));
|
||||
jest.spyOn(api, 'getUserComments').mockImplementation(async () => {
|
||||
throw new Error('error');
|
||||
});
|
||||
const { queryByLabelText, queryByRole, findByRole } = render(<Profile />);
|
||||
|
||||
expect(await findByRole('button', { name: /retry/i })).toBeInTheDocument();
|
||||
expect(queryByLabelText('Loading...')).not.toBeInTheDocument();
|
||||
expect(queryByRole('heading', { name: /recent comments/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render without comments', async () => {
|
||||
jest.spyOn(pq, 'parseQuery').mockImplementation(() => ({ ...userParamsStub }));
|
||||
const getUserComments = jest.spyOn(api, 'getUserComments').mockImplementation(async () => ({ comments: [] }));
|
||||
|
||||
const { getByText } = render(<Profile />);
|
||||
const { findByText, queryByLabelText, queryByRole } = render(<Profile />);
|
||||
|
||||
await waitFor(() => expect(getUserComments).toHaveBeenCalledWith('1'));
|
||||
await waitFor(() => expect(getByText("Don't have comments yet")).toBeInTheDocument());
|
||||
expect(getUserComments).toHaveBeenCalledWith('1');
|
||||
expect(await findByText("Don't have comments yet")).toBeInTheDocument();
|
||||
expect(queryByLabelText('Loading...')).not.toBeInTheDocument();
|
||||
expect(queryByRole('button', { name: /retry/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render user with comments', async () => {
|
||||
jest.spyOn(pq, 'parseQuery').mockImplementation(() => userParamsStub);
|
||||
jest.spyOn(api, 'getUserComments').mockImplementation(async () => ({ comments: commentsStub }));
|
||||
|
||||
const { getByText } = render(<Profile />);
|
||||
const { findByText, queryByLabelText, queryByRole } = render(<Profile />);
|
||||
|
||||
await waitFor(() => expect(getByText('Recent comments')).toBeInTheDocument());
|
||||
expect(await findByText('Recent comments')).toBeInTheDocument();
|
||||
expect(queryByLabelText('Loading...')).not.toBeInTheDocument();
|
||||
expect(queryByRole('button', { name: /retry/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shoud render current user without comments', async () => {
|
||||
it('should render current user without comments', async () => {
|
||||
jest.spyOn(pq, 'parseQuery').mockImplementation(() => ({ ...userParamsStub, current: '1' }));
|
||||
jest.spyOn(api, 'getUserComments').mockImplementation(async () => ({ comments: [] }));
|
||||
|
||||
const { getByText, getByTitle } = render(<Profile />);
|
||||
const { getByText, getByTitle, findByText } = render(<Profile />);
|
||||
|
||||
expect(getByTitle('Sign Out')).toBeInTheDocument();
|
||||
expect(getByText('Request my data removal')).toBeInTheDocument();
|
||||
expect(await findByText("Don't have comments yet")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shoud render current user with comments', async () => {
|
||||
it('should render current user with comments', async () => {
|
||||
jest.spyOn(pq, 'parseQuery').mockImplementation(() => ({ ...userParamsStub, current: '1' }));
|
||||
jest.spyOn(api, 'getUserComments').mockImplementation(async () => ({ comments: commentsStub }));
|
||||
|
||||
const { getByText, getByTitle } = render(<Profile />);
|
||||
const { findByText, queryByTitle, queryByText } = render(<Profile />);
|
||||
|
||||
expect(getByTitle('Sign Out')).toBeInTheDocument();
|
||||
expect(getByText('Request my data removal')).toBeInTheDocument();
|
||||
await waitFor(() => expect(getByText('My recent comments')).toBeInTheDocument());
|
||||
expect(queryByTitle('Sign Out')).toBeInTheDocument();
|
||||
expect(queryByText('Request my data removal')).toBeInTheDocument();
|
||||
expect(await findByText('My recent comments')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render user without footer', async () => {
|
||||
|
||||
@@ -32,10 +32,27 @@ export function Profile() {
|
||||
const intl = useIntl();
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const user = useMemo(() => parseQuery(), []);
|
||||
const [isCommentsLoading, setIsCommentsLoading] = useState(false);
|
||||
const [error, setError] = useState(false);
|
||||
const [comments, setComments] = useState<CommentType[] | null>(null);
|
||||
const [isSigningOut, setSigningOut] = useState(false);
|
||||
|
||||
async function fetchUserComments(userId: string) {
|
||||
setIsCommentsLoading(true);
|
||||
setError(false);
|
||||
setComments(null);
|
||||
|
||||
try {
|
||||
const { comments } = await getUserComments(userId);
|
||||
|
||||
setComments(comments);
|
||||
} catch (err) {
|
||||
setError(true);
|
||||
} finally {
|
||||
setIsCommentsLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleClickClose() {
|
||||
const rootElement = rootRef.current;
|
||||
|
||||
@@ -58,10 +75,12 @@ export function Profile() {
|
||||
await signout();
|
||||
}
|
||||
|
||||
function handleClickRetryCommentsRequest() {
|
||||
fetchUserComments(user.id);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getUserComments(user.id)
|
||||
.then(({ comments }) => setComments(comments))
|
||||
.catch(() => setError(true));
|
||||
fetchUserComments(user.id);
|
||||
}, [user.id]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -154,11 +173,17 @@ export function Profile() {
|
||||
</header>
|
||||
<section className={clsx('profile-content', styles.content)}>
|
||||
{error && (
|
||||
<p className={clsx('profile-error', styles.error)}>
|
||||
<FormattedMessage id="errors.0" defaultMessage="Something went wrong. Please try again a bit later." />
|
||||
</p>
|
||||
<>
|
||||
<p className={clsx('profile-error', styles.error)}>
|
||||
<FormattedMessage id="errors.0" defaultMessage="Something went wrong. Please try again a bit later." />
|
||||
</p>
|
||||
<Button kind="link" size="sm" onClick={handleClickRetryCommentsRequest}>
|
||||
<FormattedMessage id="retry" defaultMessage="Retry" />
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
{comments === null ? <Preloader /> : commentsJSX}
|
||||
{isCommentsLoading && <Preloader className={styles.preloader} />}
|
||||
{comments !== null && commentsJSX}
|
||||
</section>
|
||||
{isCurrent ? (
|
||||
<footer className={clsx('profile-footer', styles.footer)}>
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Нешта пайшло не так.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "Замацаваныя каментары",
|
||||
"root.powered-by": "Powered by <a>Remark42</a>",
|
||||
"root.show-more": "Паказаць яшчэ",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Има някакъв проблем.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "Фиксирани коментари",
|
||||
"root.powered-by": "Използва <a>Remark42</a>",
|
||||
"root.show-more": "Покажи още",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Algo deu errado.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "Comentários afixados",
|
||||
"root.powered-by": "Com a tecnologia <a>Remark42</a>",
|
||||
"root.show-more": "Mostrar mais",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Leider ist etwas schiefgegangen.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "Angeheftete Kommentare",
|
||||
"root.powered-by": "Unterstützt von <a>Remark42</a>",
|
||||
"root.show-more": "Mehr anzeigen",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Something went wrong.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "Pinned comments",
|
||||
"root.powered-by": "Powered by <a>Remark42</a>",
|
||||
"root.show-more": "Show more",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Algo salió mal.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "Comentarios anclados",
|
||||
"root.powered-by": "Con la tecnología de <a>Remark42</a>",
|
||||
"root.show-more": "Mostrar más",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Jotain meni pieleen.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "Kiinnitetyt kommentit",
|
||||
"root.powered-by": "Powered by <a>Remark42</a>",
|
||||
"root.show-more": "Näytä lisää",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Un problème est survenu.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "Commentaires épinglés",
|
||||
"root.powered-by": "Powered by <a>Remark42</a>",
|
||||
"root.show-more": "Afficher plus",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "不明なエラーが発生しました.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "ピン留めしたコメント",
|
||||
"root.powered-by": "提供元: <a>Remark42</a>",
|
||||
"root.show-more": "その他を表示",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "문제가 발생했습니다.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "고정된 댓글",
|
||||
"root.powered-by": "<a>Remark42</a> 제공",
|
||||
"root.show-more": "더보기",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Coś poszło nie tak.",
|
||||
"profile.close": "Zamknij profil",
|
||||
"profile.request-to-delete-data": "Poproś o usunięcie moich danych",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "Przypięte komentarze",
|
||||
"root.powered-by": "Obsługiwane przez <a>Remark42</a>",
|
||||
"root.show-more": "Pokaż więcej",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Что-то пошло не так.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Повторить",
|
||||
"root.pinned-comments": "Закрепленные комментарии",
|
||||
"root.powered-by": "Работает на базе <a>Remark42</a>",
|
||||
"root.show-more": "Показать еще",
|
||||
|
||||
@@ -106,6 +106,7 @@
|
||||
"errors.not-authorized": "Yetki yetersiz.",
|
||||
"errors.to-many-request": "Maksimum talep limitine ulaştınız.",
|
||||
"errors.unexpected-error": "Bir hata oluştu.",
|
||||
"retry": "Retry",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"root.pinned-comments": "Sabitlenmiş yorumlar",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Щось пішло не так.",
|
||||
"profile.close": "Закрити профіль",
|
||||
"profile.request-to-delete-data": "Зробити запит на знищення моїх даних",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "Закріпленні коментарі",
|
||||
"root.powered-by": "Powered by <a>Remark42</a>",
|
||||
"root.show-more": "Показати ще",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "Có gì đó sai sai.",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "Bình buận đã ghim",
|
||||
"root.powered-by": "Powered by <a>Remark42</a>",
|
||||
"root.show-more": "Xem thêm",
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"errors.unexpected-error": "出了些问题。",
|
||||
"profile.close": "Close profile",
|
||||
"profile.request-to-delete-data": "Request my data removal",
|
||||
"retry": "Retry",
|
||||
"root.pinned-comments": "固定的评论",
|
||||
"root.powered-by": "由 <a>Remark42</a> 提供支持",
|
||||
"root.show-more": "显示更多",
|
||||
|
||||
Reference in New Issue
Block a user