From 3d3162de41198e268dea238d8bf4e068e432a2e4 Mon Sep 17 00:00:00 2001 From: esvyridov Date: Thu, 19 Aug 2021 21:44:44 +0200 Subject: [PATCH] Update UI for user comments sidebar --- .../app/components/profile/profile.module.css | 10 +++- .../app/components/profile/profile.spec.tsx | 49 +++++++++++++------ frontend/app/components/profile/profile.tsx | 39 ++++++++++++--- frontend/app/locales/be.json | 1 + frontend/app/locales/bg.json | 1 + frontend/app/locales/bp.json | 1 + frontend/app/locales/de.json | 1 + frontend/app/locales/en.json | 1 + frontend/app/locales/es.json | 1 + frontend/app/locales/fi.json | 1 + frontend/app/locales/fr.json | 1 + frontend/app/locales/ja.json | 1 + frontend/app/locales/ko.json | 1 + frontend/app/locales/pl.json | 1 + frontend/app/locales/ru.json | 1 + frontend/app/locales/tr.json | 1 + frontend/app/locales/ua.json | 1 + frontend/app/locales/vi.json | 1 + frontend/app/locales/zh.json | 1 + 19 files changed, 91 insertions(+), 23 deletions(-) diff --git a/frontend/app/components/profile/profile.module.css b/frontend/app/components/profile/profile.module.css index 0fe1c0aa..45796a7d 100644 --- a/frontend/app/components/profile/profile.module.css +++ b/frontend/app/components/profile/profile.module.css @@ -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); } diff --git a/frontend/app/components/profile/profile.spec.tsx b/frontend/app/components/profile/profile.spec.tsx index 3f38c20b..f6fea318 100644 --- a/frontend/app/components/profile/profile.spec.tsx +++ b/frontend/app/components/profile/profile.spec.tsx @@ -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('', () => { it('should render preloader', () => { jest.spyOn(pq, 'parseQuery').mockImplementation(() => ({ ...userParamsStub })); - const { container } = render(); + const { queryByLabelText, queryByRole } = render(); - 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(); + + 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(); + const { findByText, queryByLabelText, queryByRole } = render(); - 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(); + const { findByText, queryByLabelText, queryByRole } = render(); - 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(); + const { getByText, getByTitle, findByText } = render(); 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(); + const { findByText, queryByTitle, queryByText } = render(); - 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 () => { diff --git a/frontend/app/components/profile/profile.tsx b/frontend/app/components/profile/profile.tsx index 65e38133..53396c78 100644 --- a/frontend/app/components/profile/profile.tsx +++ b/frontend/app/components/profile/profile.tsx @@ -32,10 +32,27 @@ export function Profile() { const intl = useIntl(); const rootRef = useRef(null); const user = useMemo(() => parseQuery(), []); + const [isCommentsLoading, setIsCommentsLoading] = useState(false); const [error, setError] = useState(false); const [comments, setComments] = useState(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() {
{error && ( -

- -

+ <> +

+ +

+ + )} - {comments === null ? : commentsJSX} + {isCommentsLoading && } + {comments !== null && commentsJSX}
{isCurrent ? (