(frontend) make the team pannel closable

Team panel was quite wide, and took too much space on small screens.
Johann decided to make it closable. Thus, user that needs to navigate quickly
between their team can open it, use it and then close it.

This animation is a first draft, and should be improved later on. Also, some
accessibility issues might ariase, I have ignored them in this first draft.
This commit is contained in:
Lebaud Antoine
2024-03-26 00:17:02 +01:00
committed by aleb_the_flash
parent ebaa1360e7
commit e8aba29a68
7 changed files with 106 additions and 15 deletions

View File

@@ -23,7 +23,7 @@ export const Menu = () => {
$height="100%"
$justify="space-between"
>
<Box className="pt-b" $direction="column" $gap="0.8rem">
<Box className="pt-l" $direction="column" $gap="0.8rem">
<MenuItem Icon={IconSearch} label={t('Search')} href="/" />
<MenuItem Icon={IconFavorite} label={t('Favorite')} href="/favorite" />
<MenuItem Icon={IconRecent} label={t('Recent')} href="/recent" />

View File

@@ -1,7 +1,9 @@
import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import fetchMock from 'fetch-mock';
import { Panel } from '@/features/teams';
import { AppWrapper } from '@/tests/utils';
import { TeamList } from '../components/Panel/TeamList';
@@ -127,4 +129,46 @@ describe('PanelTeams', () => {
),
).toBeInTheDocument();
});
it('renders with team panel open', async () => {
fetchMock.mock(`/api/teams/?page=1&ordering=-created_at`, {
count: 1,
results: [],
});
render(<Panel />, { wrapper: AppWrapper });
expect(
screen.getByRole('button', { name: 'Close the teams panel' }),
).toBeVisible();
expect(await screen.findByText('Recents')).toBeVisible();
});
it('closes and opens the team panel', async () => {
fetchMock.mock(`/api/teams/?page=1&ordering=-created_at`, {
count: 1,
results: [],
});
render(<Panel />, { wrapper: AppWrapper });
expect(await screen.findByText('Recents')).toBeVisible();
await userEvent.click(
screen.getByRole('button', {
name: 'Close the teams panel',
}),
);
expect(await screen.findByText('Recents')).not.toBeVisible();
await userEvent.click(
screen.getByRole('button', {
name: 'Open the teams panel',
}),
);
expect(await screen.findByText('Recents')).toBeVisible();
});
});

View File

@@ -0,0 +1,4 @@
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="11.5" transform="rotate(-180 12 12)" fill="white" stroke="currentColor"/>
<path d="M14.1683 16.232C14.4803 15.92 14.4803 15.416 14.1683 15.104L11.0643 12L14.1683 8.896C14.4803 8.584 14.4803 8.08 14.1683 7.768C13.8563 7.456 13.3523 7.456 13.0403 7.768L9.36834 11.44C9.05634 11.752 9.05634 12.256 9.36834 12.568L13.0403 16.24C13.3443 16.544 13.8563 16.544 14.1683 16.232Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 500 B

View File

@@ -1,8 +1,9 @@
import React from 'react';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, Text } from '@/components';
import { Box, BoxButton, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import IconOpenClose from '@/features/teams/assets/icon-open-close.svg';
import { PanelActions } from './PanelActions';
import { TeamList } from './TeamList';
@@ -11,32 +12,69 @@ export const Panel = () => {
const { t } = useTranslation();
const { colorsTokens } = useCunninghamTheme();
const [isOpen, setIsOpen] = useState(true);
const closedOverridingStyles = !isOpen && {
$width: '0',
$maxWidth: '0',
$minWidth: '0',
};
const transition = 'all 0.5s ease-in-out';
return (
<Box
$width="100%"
$maxWidth="20rem"
$minWidth="14rem"
$css={`
position: relative;
border-right: 1px solid ${colorsTokens()['primary-300']};
transition: ${transition};
`}
$height="inherit"
aria-label="Teams panel"
{...closedOverridingStyles}
>
<Box
className="p-s"
$direction="row"
$align="center"
$justify="space-between"
<BoxButton
aria-label={
isOpen ? t('Close the teams panel') : t('Open the teams panel')
}
$color={colorsTokens()['primary-600']}
$css={`
border-bottom: 1px solid ${colorsTokens()['primary-300']};
position: absolute;
right: -1.2rem;
top: 1.03rem;
transform: rotate(${isOpen ? '0' : '180'}deg);
transition: ${transition};
`}
onClick={() => setIsOpen(!isOpen)}
>
<IconOpenClose width={24} height={24} />
</BoxButton>
<Box
$css={`
overflow: hidden;
opacity: ${isOpen ? '1' : '0'};
transition: ${transition};
`}
>
<Text $weight="bold" $size="1.25rem">
{t('Recents')}
</Text>
<PanelActions />
<Box
className="pr-l pl-s pt-s pb-s"
$direction="row"
$align="center"
$justify="space-between"
$css={`
border-bottom: 1px solid ${colorsTokens()['primary-300']};
`}
>
<Text $weight="bold" $size="1.25rem">
{t('Recents')}
</Text>
<PanelActions />
</Box>
<TeamList />
</Box>
<TeamList />
</Box>
);
};

View File

@@ -88,6 +88,9 @@ export const TeamItem = ({ team }: TeamProps) => {
<Text
$weight="bold"
$color={!hasMembers ? colorsTokens()['greyscale-600'] : undefined}
$css={`
min-width: 14rem;
`}
>
{team.name}
</Text>

View File

@@ -73,7 +73,7 @@ export const TeamList = () => {
}, [data?.pages]);
return (
<Box $css="overflow: auto;" ref={containerRef}>
<Box $css="overflow-y: auto; overflow-x: hidden;" ref={containerRef}>
<InfiniteScroll
hasMore={hasNextPage}
isLoading={isFetchingNextPage}

View File

@@ -24,6 +24,7 @@
"Cells icon": "Icône Cellules",
"Choose a role": "Choisissez un rôle",
"Close the modal": "Fermer la modale",
"Close the teams panel": "Fermer le menu des groupes",
"Confirm deletion": "Confirmer la suppression",
"Contacts": "Contacts",
"Content modal to delete the team": "Contenu modal pour supprimer le groupe",
@@ -62,6 +63,7 @@
"Names": "Noms",
"New name...": "Nouveau nom...",
"Open the member options modal": "Ouvrir les options de membre dans la fenêtre modale",
"Open the teams panel": "Ouvrir le menu des groupes",
"Open the team options": "Ouvrir les options de groupe",
"Ouch !": "Ooops !",
"Owner": "Propriétaire",