Skip to content

Commit 7f25b05

Browse files
committed
⚗️(frontend) add button Restore this version near title
When a user is on a page version, we will display a button "Restore this version" near the title of the page. It gives an obvious way to restore the version of the doc.
1 parent 296b5db commit 7f25b05

File tree

5 files changed

+161
-72
lines changed

5 files changed

+161
-72
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to
1313

1414
- ✨Add image attachments with access control
1515
- ✨(frontend) Upload image to a document #211
16+
- ✨(frontend) Versions #217
1617

1718
## Changed
1819

src/frontend/apps/e2e/__tests__/app-impress/doc-version.spec.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,62 @@ test.describe('Doc Version', () => {
133133
await expect(page.getByText('Hello')).toBeVisible();
134134
await expect(page.getByText('World')).toBeHidden();
135135
});
136+
137+
test('it restores the doc version from button title', async ({
138+
page,
139+
browserName,
140+
}) => {
141+
const [randomDoc] = await createDoc(page, 'doc-version', browserName, 1);
142+
143+
await expect(page.locator('h2').getByText(randomDoc)).toBeVisible();
144+
145+
await page.locator('.bn-block-outer').last().click();
146+
await page.locator('.bn-block-outer').last().fill('Hello');
147+
148+
await goToGridDoc(page, {
149+
title: randomDoc,
150+
});
151+
152+
await expect(page.getByText('Hello')).toBeVisible();
153+
await page.locator('.bn-block-outer').last().click();
154+
await page.keyboard.press('Enter');
155+
await page.locator('.bn-block-outer').last().fill('World');
156+
157+
await goToGridDoc(page, {
158+
title: randomDoc,
159+
});
160+
161+
await expect(page.getByText('World')).toBeVisible();
162+
163+
await page.getByLabel('Open the document options').click();
164+
await page
165+
.getByRole('button', {
166+
name: 'Version history',
167+
})
168+
.click();
169+
170+
const panel = page.getByLabel('Document version panel');
171+
await panel.locator('li').nth(1).click();
172+
await expect(page.getByText('World')).toBeHidden();
173+
174+
await page
175+
.getByRole('button', {
176+
name: 'Restore this version',
177+
})
178+
.click();
179+
180+
await expect(page.getByText('Restore this version?')).toBeVisible();
181+
182+
await page
183+
.getByRole('button', {
184+
name: 'Restore',
185+
})
186+
.click();
187+
188+
await expect(panel.locator('li')).toHaveCount(3);
189+
190+
await panel.getByText('Current version').click();
191+
await expect(page.getByText('Hello')).toBeVisible();
192+
await expect(page.getByText('World')).toBeHidden();
193+
});
136194
});

src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const DocEditor = ({ doc }: DocEditorProps) => {
3535

3636
return (
3737
<>
38-
<DocHeader doc={doc} />
38+
<DocHeader doc={doc} versionId={versionId as Versions['version_id']} />
3939
{!doc.abilities.partial_update && (
4040
<Box $margin={{ all: 'small', top: 'none' }}>
4141
<Alert type={VariantType.WARNING}>
Lines changed: 100 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React, { Fragment } from 'react';
1+
import { Button } from '@openfun/cunningham-react';
2+
import React, { Fragment, useState } from 'react';
23
import { useTranslation } from 'react-i18next';
34

45
import { Box, Card, StyledLink, Text } from '@/components';
@@ -9,96 +10,124 @@ import {
910
currentDocRole,
1011
useTransRole,
1112
} from '@/features/docs/doc-management';
13+
import { ModalVersion, Versions } from '@/features/docs/doc-versioning';
1214
import { useDate } from '@/hook';
1315

1416
import { DocToolBox } from './DocToolBox';
1517

1618
interface DocHeaderProps {
1719
doc: Doc;
20+
versionId?: Versions['version_id'];
1821
}
1922

20-
export const DocHeader = ({ doc }: DocHeaderProps) => {
23+
export const DocHeader = ({ doc, versionId }: DocHeaderProps) => {
2124
const { colorsTokens } = useCunninghamTheme();
2225
const { t } = useTranslation();
2326
const { formatDate } = useDate();
2427
const transRole = useTransRole();
28+
const [isModalVersionOpen, setIsModalVersionOpen] = useState(false);
2529

2630
return (
27-
<Card
28-
$margin="small"
29-
aria-label={t('It is the card information about the document.')}
30-
>
31-
<Box $padding="small" $direction="row" $align="center">
32-
<StyledLink href="/">
33-
<Text
34-
$isMaterialIcon
35-
$theme="primary"
36-
$size="2rem"
37-
$css={`&:hover {background-color: ${colorsTokens()['primary-100']}; };`}
38-
$hasTransition
39-
$radius="5px"
40-
$padding="tiny"
41-
>
42-
home
43-
</Text>
44-
</StyledLink>
45-
<Box
46-
$width="1px"
47-
$height="70%"
48-
$background={colorsTokens()['greyscale-100']}
49-
$margin={{ horizontal: 'small' }}
50-
/>
51-
<Text as="h2" $align="center" $margin={{ all: 'none', left: 'tiny' }}>
52-
{doc.title}
53-
</Text>
54-
<DocToolBox doc={doc} />
55-
</Box>
56-
<Box
57-
$direction="row"
58-
$align="center"
59-
$css="border-top:1px solid #eee"
60-
$padding={{ horizontal: 'big', vertical: 'tiny' }}
61-
$gap="0.5rem 2rem"
62-
$justify="space-between"
63-
$wrap="wrap"
31+
<>
32+
<Card
33+
$margin="small"
34+
aria-label={t('It is the card information about the document.')}
6435
>
65-
<Box $direction="row" $align="center" $gap="0.5rem 2rem" $wrap="wrap">
66-
{doc.is_public && (
36+
<Box $padding="small" $direction="row" $align="center">
37+
<StyledLink href="/">
38+
<Text
39+
$isMaterialIcon
40+
$theme="primary"
41+
$size="2rem"
42+
$css={`&:hover {background-color: ${colorsTokens()['primary-100']}; };`}
43+
$hasTransition
44+
$radius="5px"
45+
$padding="tiny"
46+
>
47+
home
48+
</Text>
49+
</StyledLink>
50+
<Box
51+
$width="1px"
52+
$height="70%"
53+
$background={colorsTokens()['greyscale-100']}
54+
$margin={{ horizontal: 'small' }}
55+
/>
56+
<Box $gap="1rem" $direction="row">
6757
<Text
68-
$weight="bold"
69-
$background={colorsTokens()['primary-600']}
70-
$color="white"
71-
$padding="xtiny"
72-
$radius="3px"
73-
$size="s"
58+
as="h2"
59+
$align="center"
60+
$margin={{ all: 'none', left: 'tiny' }}
7461
>
75-
{t('Public')}
62+
{doc.title}
7663
</Text>
77-
)}
64+
{versionId && (
65+
<Button
66+
onClick={() => {
67+
setIsModalVersionOpen(true);
68+
}}
69+
size="small"
70+
>
71+
{t('Restore this version')}
72+
</Button>
73+
)}
74+
</Box>
75+
<DocToolBox doc={doc} />
76+
</Box>
77+
<Box
78+
$direction="row"
79+
$align="center"
80+
$css="border-top:1px solid #eee"
81+
$padding={{ horizontal: 'big', vertical: 'tiny' }}
82+
$gap="0.5rem 2rem"
83+
$justify="space-between"
84+
$wrap="wrap"
85+
>
86+
<Box $direction="row" $align="center" $gap="0.5rem 2rem" $wrap="wrap">
87+
{doc.is_public && (
88+
<Text
89+
$weight="bold"
90+
$background={colorsTokens()['primary-600']}
91+
$color="white"
92+
$padding="xtiny"
93+
$radius="3px"
94+
$size="s"
95+
>
96+
{t('Public')}
97+
</Text>
98+
)}
99+
<Text $size="s" $display="inline">
100+
{t('Created at')} <strong>{formatDate(doc.created_at)}</strong>
101+
</Text>
102+
<Text $size="s" $display="inline" $elipsis $maxWidth="60vw">
103+
{t('Owners:')}{' '}
104+
<strong>
105+
{doc.accesses
106+
.filter(
107+
(access) => access.role === Role.OWNER && access.user.email,
108+
)
109+
.map((access, index, accesses) => (
110+
<Fragment key={`access-${index}`}>
111+
{access.user.email}{' '}
112+
{index < accesses.length - 1 ? ' / ' : ''}
113+
</Fragment>
114+
))}
115+
</strong>
116+
</Text>
117+
</Box>
78118
<Text $size="s" $display="inline">
79-
{t('Created at')} <strong>{formatDate(doc.created_at)}</strong>
80-
</Text>
81-
<Text $size="s" $display="inline" $elipsis $maxWidth="60vw">
82-
{t('Owners:')}{' '}
83-
<strong>
84-
{doc.accesses
85-
.filter(
86-
(access) => access.role === Role.OWNER && access.user.email,
87-
)
88-
.map((access, index, accesses) => (
89-
<Fragment key={`access-${index}`}>
90-
{access.user.email}{' '}
91-
{index < accesses.length - 1 ? ' / ' : ''}
92-
</Fragment>
93-
))}
94-
</strong>
119+
{t('Your role:')}{' '}
120+
<strong>{transRole(currentDocRole(doc.abilities))}</strong>
95121
</Text>
96122
</Box>
97-
<Text $size="s" $display="inline">
98-
{t('Your role:')}{' '}
99-
<strong>{transRole(currentDocRole(doc.abilities))}</strong>
100-
</Text>
101-
</Box>
102-
</Card>
123+
</Card>
124+
{isModalVersionOpen && versionId && (
125+
<ModalVersion
126+
onClose={() => setIsModalVersionOpen(false)}
127+
docId={doc.id}
128+
versionId={versionId}
129+
/>
130+
)}
131+
</>
103132
);
104133
};
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
export * from './ModalVersion';
12
export * from './Panel';

0 commit comments

Comments
 (0)