♻️(frontend) update some elements

- add panel information when document is
authenticated
- add a copy link button in the toolbox
on the document
- fix when long title document
- modals fit design
- mobile responsive changes
This commit is contained in:
Anthony LC
2025-02-24 10:24:06 +01:00
committed by Anthony LC
parent 50d098c777
commit 54a75bc338
17 changed files with 130 additions and 98 deletions

View File

@@ -7,17 +7,9 @@ type SmallDoc = {
title: string;
};
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test.describe('Documents Grid mobile', () => {
test.use({ viewport: { width: 500, height: 1200 } });
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('it checks the grid when mobile', async ({ page }) => {
await page.route('**/documents/**', async (route) => {
const request = route.request();
@@ -94,6 +86,10 @@ test.describe('Documents Grid mobile', () => {
});
test.describe('Document grid item options', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('it pins a document', async ({ page, browserName }) => {
const [docTitle] = await createDoc(page, `Favorite doc`, browserName);
@@ -212,6 +208,8 @@ test.describe('Document grid item options', () => {
test.describe('Documents filters', () => {
test('it checks the prebuild left panel filters', async ({ page }) => {
await page.goto('/');
// All Docs
const response = await page.waitForResponse(
(response) =>
@@ -282,11 +280,9 @@ test.describe('Documents filters', () => {
});
test.describe('Documents Grid', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/');
});
test('checks all the elements are visible', async ({ page }) => {
await page.goto('/');
let docs: SmallDoc[] = [];
const response = await page.waitForResponse(
(response) =>
@@ -314,11 +310,12 @@ test.describe('Documents Grid', () => {
test('checks the infinite scroll', async ({ page }) => {
let docs: SmallDoc[] = [];
const responsePromisePage1 = page.waitForResponse(
(response) =>
const responsePromisePage1 = page.waitForResponse((response) => {
return (
response.url().endsWith(`/documents/?page=1`) &&
response.status() === 200,
);
response.status() === 200
);
});
const responsePromisePage2 = page.waitForResponse(
(response) =>
@@ -326,6 +323,8 @@ test.describe('Documents Grid', () => {
response.status() === 200,
);
await page.goto('/');
const responsePage1 = await responsePromisePage1;
expect(responsePage1.ok()).toBeTruthy();
let result = await responsePage1.json();

View File

@@ -416,6 +416,14 @@ test.describe('Doc Visibility: Authenticated', () => {
page.getByText('The document visibility has been updated.'),
).toBeVisible();
await expect(
page
.getByLabel('It is the card information about the document.')
.getByText('Document accessible to any connected person', {
exact: true,
}),
).toBeVisible();
await page.getByRole('button', { name: 'close' }).click();
const urlDoc = page.url();

View File

@@ -76,13 +76,13 @@ export const cssEditor = (readonly: boolean) => css`
.bn-block-outer:not(:first-child) {
&:has(h1) {
padding-top: 32px;
margin-top: 32px;
}
&:has(h2) {
padding-top: 24px;
margin-top: 24px;
}
&:has(h3) {
padding-top: 16px;
margin-top: 16px;
}
}
@@ -92,9 +92,16 @@ export const cssEditor = (readonly: boolean) => css`
border-radius: 4px;
}
@media screen and (width <= 768px) {
& .bn-editor {
padding-right: 36px;
}
}
@media screen and (width <= 560px) {
& .bn-editor {
${readonly && `padding-left: 10px;`}
padding-right: 10px;
}
.bn-side-menu[data-block-type='heading'][data-level='1'] {
height: 46px;

View File

@@ -27,6 +27,7 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
const { t } = useTranslation();
const docIsPublic = doc.link_reach === LinkReach.PUBLIC;
const docIsAuth = doc.link_reach === LinkReach.AUTHENTICATED;
const { transRole } = useTrans();
@@ -38,7 +39,7 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
$gap={spacings['base']}
aria-label={t('It is the card information about the document.')}
>
{docIsPublic && (
{(docIsPublic || docIsAuth) && (
<Box
aria-label={t('Public document')}
$color={colors['primary-800']}
@@ -57,10 +58,12 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
$theme="primary"
$variation="800"
data-testid="public-icon"
iconName="public"
iconName={docIsPublic ? 'public' : 'vpn_lock'}
/>
<Text $theme="primary" $variation="800">
{t('Public document')}
{docIsPublic
? t('Public document')
: t('Document accessible to any connected person')}
</Text>
</Box>
)}
@@ -76,8 +79,9 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
$css="flex:1;"
$gap="0.5rem 1rem"
$align="center"
$maxWidth="100%"
>
<Box $gap={spacings['3xs']}>
<Box $gap={spacings['3xs']} $overflow="auto">
<DocTitle doc={doc} />
<Box $direction="row">

View File

@@ -101,39 +101,35 @@ const DocTitleInput = ({ doc }: DocTitleProps) => {
}, [doc]);
return (
<>
<Tooltip content={t('Rename')} placement="top">
<Box
as="span"
role="textbox"
contentEditable
defaultValue={titleDisplay || undefined}
onKeyDownCapture={handleKeyDown}
suppressContentEditableWarning={true}
aria-label="doc title input"
onBlurCapture={(event) =>
handleTitleSubmit(event.target.textContent || '')
<Tooltip content={t('Rename')} placement="top">
<Box
as="span"
role="textbox"
contentEditable
defaultValue={titleDisplay || undefined}
onKeyDownCapture={handleKeyDown}
suppressContentEditableWarning={true}
aria-label="doc title input"
onBlurCapture={(event) =>
handleTitleSubmit(event.target.textContent || '')
}
$color={colorsTokens()['greyscale-1000']}
$css={css`
&[contenteditable='true']:empty:not(:focus):before {
content: '${untitledDocument}';
color: grey;
pointer-events: none;
font-style: italic;
}
$color={colorsTokens()['greyscale-1000']}
$margin={{ left: '-2px', right: '10px' }}
$css={css`
&[contenteditable='true']:empty:not(:focus):before {
content: '${untitledDocument}';
color: grey;
pointer-events: none;
font-style: italic;
}
font-size: ${isDesktop
? css`var(--c--theme--font--sizes--h2)`
: css`var(--c--theme--font--sizes--sm)`};
font-weight: 700;
outline: none;
`}
>
{titleDisplay}
</Box>
</Tooltip>
</>
font-size: ${isDesktop
? css`var(--c--theme--font--sizes--h2)`
: css`var(--c--theme--font--sizes--sm)`};
font-weight: 700;
outline: none;
`}
>
{titleDisplay}
</Box>
</Tooltip>
);
};

View File

@@ -18,7 +18,11 @@ import {
} from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { useEditorStore } from '@/features/docs/doc-editor/';
import { Doc, ModalRemoveDoc } from '@/features/docs/doc-management';
import {
Doc,
ModalRemoveDoc,
useCopyDocLink,
} from '@/features/docs/doc-management';
import { DocShareModal } from '@/features/docs/doc-share';
import {
KEY_LIST_DOC_VERSIONS,
@@ -34,7 +38,7 @@ interface DocToolBoxProps {
export const DocToolBox = ({ doc }: DocToolBoxProps) => {
const { t } = useTranslation();
const hasAccesses = doc.nb_accesses > 1;
const hasAccesses = doc.nb_accesses > 1 && doc.abilities.accesses_view;
const queryClient = useQueryClient();
const { spacingsTokens, colorsTokens } = useCunninghamTheme();
@@ -50,6 +54,7 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
const { isSmallMobile, isDesktop } = useResponsiveStore();
const { editor } = useEditorStore();
const { toast } = useToastProvider();
const copyDocLink = useCopyDocLink(doc.id);
const options: DropdownMenuOption[] = [
...(isSmallMobile
@@ -66,6 +71,11 @@ export const DocToolBox = ({ doc }: DocToolBoxProps) => {
setIsModalExportOpen(true);
},
},
{
label: t('Copy link'),
icon: 'add_link',
callback: copyDocLink,
},
]
: []),

View File

@@ -71,9 +71,15 @@ export const ModalRemoveDoc = ({ onClose, doc }: ModalRemoveDocProps) => {
</Button>
</>
}
size={ModalSize.MEDIUM}
size={ModalSize.SMALL}
title={
<Text $size="h6" as="h6" $margin={{ all: '0' }} $align="flex-start">
<Text
$size="h6"
as="h6"
$margin={{ all: '0' }}
$align="flex-start"
$variation="1000"
>
{t('Delete a doc')}
</Text>
}

View File

@@ -33,9 +33,7 @@ export const DocShareModalFooter = ({ doc, onClose }: Props) => {
>
<Button
fullWidth={false}
onClick={() => {
copyDocLink();
}}
onClick={copyDocLink}
color="tertiary"
icon={<span className="material-icons">add_link</span>}
>

View File

@@ -100,17 +100,21 @@ export const ModalConfirmationVersion = ({
</Button>
</>
}
size={ModalSize.MEDIUM}
size={ModalSize.SMALL}
title={
<Text $size="h6" $align="flex-start">
<Text $size="h6" $align="flex-start" $variation="1000">
{t('Warning')}
</Text>
}
>
<Box aria-label={t('Modal confirmation to restore the version')}>
<Box>
<Text>{t('Your current document will revert to this version.')}</Text>
<Text>{t('If a member is editing, his works can be lost.')}</Text>
<Text $variation="600">
{t('Your current document will revert to this version.')}
</Text>
<Text $variation="600">
{t('If a member is editing, his works can be lost.')}
</Text>
</Box>
</Box>
</Modal>

View File

@@ -61,12 +61,8 @@ export const DocsGrid = ({
$position="relative"
$width="100%"
$maxWidth="960px"
$maxHeight="calc(100vh - 52px - 1rem)"
$maxHeight="calc(100vh - 52px - 2rem)"
$align="center"
$css={css`
overflow-x: hidden;
overflow-y: auto;
`}
>
<DocsGridLoader isLoading={isRefetching || loading} />
<Card
@@ -75,8 +71,7 @@ export const DocsGrid = ({
$height="100%"
$width="100%"
$css={css`
overflow-x: hidden;
overflow-y: auto;
${!isDesktop ? 'border: none;' : ''}
`}
$padding={{
top: 'base',
@@ -101,7 +96,7 @@ export const DocsGrid = ({
</Box>
)}
{hasDocs && (
<Box $gap="6px">
<Box $gap="6px" $overflow="auto">
<Box
$direction="row"
$padding={{ horizontal: 'xs' }}
@@ -122,27 +117,29 @@ export const DocsGrid = ({
)}
</Box>
{/* Body */}
{data?.pages.map((currentPage) => {
return currentPage.results.map((doc) => (
<DocsGridItem doc={doc} key={doc.id} />
));
})}
</Box>
)}
{hasNextPage && !loading && (
<InView
data-testid="infinite-scroll-trigger"
as="div"
onChange={loadMore}
>
{!isFetching && hasNextPage && (
<Button onClick={() => void fetchNextPage()} color="primary-text">
{t('More docs')}
</Button>
{hasNextPage && !loading && (
<InView
data-testid="infinite-scroll-trigger"
as="div"
onChange={loadMore}
>
{!isFetching && hasNextPage && (
<Button
onClick={() => void fetchNextPage()}
color="primary-text"
>
{t('More docs')}
</Button>
)}
</InView>
)}
</InView>
</Box>
)}
</Card>
</Box>

View File

@@ -54,6 +54,7 @@ export const DocsGridItem = ({ doc }: DocsGridItemProps) => {
$css={css`
flex: ${flexLeft};
align-items: center;
min-width: 0;
`}
href={`/docs/${doc.id}`}
>
@@ -64,6 +65,7 @@ export const DocsGridItem = ({ doc }: DocsGridItemProps) => {
$gap={spacings.xs}
$flex={flexLeft}
$padding={{ right: isDesktop ? 'md' : '3xs' }}
$maxWidth="100%"
>
<SimpleDocItem isPinned={doc.is_favorite} doc={doc} />
{showAccesses && (

View File

@@ -38,7 +38,7 @@ export const SimpleDocItem = ({
const { untitledDocument } = useTrans();
return (
<Box $direction="row" $gap={spacings.sm}>
<Box $direction="row" $gap={spacings.sm} $overflow="auto">
<Box
$direction="row"
$align="center"
@@ -53,7 +53,7 @@ export const SimpleDocItem = ({
<SimpleFileIcon aria-label={t('Simple document icon')} />
)}
</Box>
<Box $justify="center">
<Box $justify="center" $overflow="auto">
<Text
aria-describedby="doc-title"
aria-label={doc.title}

View File

@@ -55,7 +55,7 @@ export default function HomeBanner() {
$textAlign="center"
$margin="none"
$css={css`
line-height: 56px;
line-height: ${!isMobile ? '56px' : '45px'};
`}
>
{t('Collaborative writing, Simplified.')}

View File

@@ -116,8 +116,8 @@ export const HomeSection = ({
`}
$variation="1000"
$weight="bold"
$size={!isSmallDevice ? 'xs-alt' : 'h4'}
$textAlign={isSmallMobile ? 'center' : 'left'}
$size={!isSmallDevice ? 'xs-alt' : isSmallMobile ? 'h6' : 'h4'}
$textAlign="left"
$margin="none"
>
{title}

View File

@@ -39,7 +39,7 @@ export const LeftPanelFavoriteItem = ({ doc }: LeftPanelFavoriteItemProps) => {
`}
key={doc.id}
>
<StyledLink href={`/docs/${doc.id}`}>
<StyledLink href={`/docs/${doc.id}`} $css="overflow: auto;">
<SimpleDocItem showAccesses doc={doc} />
</StyledLink>
<div className="pinned-actions">

View File

@@ -19,8 +19,8 @@ export function MainLayout({
}: PropsWithChildren<MainLayoutProps>) {
const { isDesktop } = useResponsiveStore();
const { colorsTokens } = useCunninghamTheme();
const colors = colorsTokens();
const currentBackgroundColor = !isDesktop ? 'white' : backgroundColor;
return (
<div>
@@ -39,10 +39,10 @@ export function MainLayout({
$width="100%"
$height={`calc(100dvh - ${HEADER_HEIGHT}px)`}
$padding={{
all: isDesktop ? 'base' : '2xs',
all: isDesktop ? 'base' : '0',
}}
$background={
backgroundColor === 'white'
currentBackgroundColor === 'white'
? colors['greyscale-000']
: colors['greyscale-050']
}