💄(frontend) update document summary UI

- Enhanced the document summary UI for better visibility
and interaction.
- Refactored the DocHeader and DocEditor components to
 improve layout and responsiveness.
- Updated tests for the DocTableContent to reflect changes
in heading interactions and visibility checks.
This commit is contained in:
Nathan Panchout
2024-12-03 16:22:59 +01:00
committed by Anthony LC
parent 7696872416
commit 23b11e4096
11 changed files with 171 additions and 375 deletions

View File

@@ -25,6 +25,7 @@ and this project adheres to
- 💄(frontend) update DocsGridOptions component #432 - 💄(frontend) update DocsGridOptions component #432
- 💄(frontend) update DocHeader ui #446 - 💄(frontend) update DocHeader ui #446
- 💄(frontend) update doc versioning ui #463 - 💄(frontend) update doc versioning ui #463
- 💄(frontend) update doc summary ui #473
## [1.10.0] - 2024-12-17 ## [1.10.0] - 2024-12-17

View File

@@ -1,6 +1,6 @@
import { expect, test } from '@playwright/test'; import { expect, test } from '@playwright/test';
import { createDoc, goToGridDoc, verifyDocName } from './common'; import { createDoc, verifyDocName } from './common';
test.beforeEach(async ({ page }) => { test.beforeEach(async ({ page }) => {
await page.goto('/'); await page.goto('/');
@@ -19,20 +19,13 @@ test.describe('Doc Table Content', () => {
await verifyDocName(page, randomDoc); await verifyDocName(page, randomDoc);
await page.getByLabel('Open the document options').click();
await page
.getByRole('button', {
name: 'Table of contents',
})
.click();
const panel = page.getByLabel('Document panel');
const editor = page.locator('.ProseMirror'); const editor = page.locator('.ProseMirror');
await editor.locator('.bn-block-outer').last().fill('/'); await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 1').click(); await page.getByText('Heading 1').click();
await page.keyboard.type('Hello World'); await page.keyboard.type('Level 1');
await editor.getByText('Hello').dblclick(); await editor.getByText('Level 1').dblclick();
await page.getByRole('button', { name: 'Strike' }).click(); await page.getByRole('button', { name: 'Strike' }).click();
await page.locator('.bn-block-outer').first().click(); await page.locator('.bn-block-outer').first().click();
@@ -40,101 +33,44 @@ test.describe('Doc Table Content', () => {
await page.locator('.bn-block-outer').last().click(); await page.locator('.bn-block-outer').last().click();
// Create space to fill the viewport // Create space to fill the viewport
for (let i = 0; i < 10; i++) { for (let i = 0; i < 2; i++) {
await page.keyboard.press('Enter'); await page.keyboard.press('Enter');
} }
await editor.locator('.bn-block-outer').last().fill('/'); await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 2').click(); await page.getByText('Heading 2').click();
await page.keyboard.type('Super World', { delay: 100 }); await page.keyboard.type('Level 2');
await page.locator('.bn-block-outer').last().click(); await page.locator('.bn-block-outer').last().click();
// Create space to fill the viewport // Create space to fill the viewport
for (let i = 0; i < 10; i++) { for (let i = 0; i < 2; i++) {
await page.keyboard.press('Enter'); await page.keyboard.press('Enter');
} }
await editor.locator('.bn-block-outer').last().fill('/'); await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 3').click(); await page.getByText('Heading 3').click();
await page.keyboard.type('Another World'); await page.keyboard.type('Level 3');
const hello = panel.getByText('Hello World'); expect(true).toBe(true);
const superW = panel.getByText('Super World');
const another = panel.getByText('Another World');
await expect(hello).toBeVisible(); const summaryContainer = page.locator('#summaryContainer');
await expect(hello).toHaveCSS('font-size', /17/); await summaryContainer.hover();
await expect(hello).toHaveAttribute('aria-selected', 'true');
await expect(superW).toBeVisible(); const level1 = summaryContainer.getByText('Level 1');
await expect(superW).toHaveCSS('font-size', /14/); const level2 = summaryContainer.getByText('Level 2');
await expect(superW).toHaveAttribute('aria-selected', 'false'); const level3 = summaryContainer.getByText('Level 3');
await expect(another).toBeVisible(); await expect(level1).toBeVisible();
await expect(another).toHaveCSS('font-size', /12/); await expect(level1).toHaveCSS('padding', /4px 0px/);
await expect(another).toHaveAttribute('aria-selected', 'false'); await expect(level1).toHaveAttribute('aria-selected', 'true');
await hello.click(); await expect(level2).toBeVisible();
await expect(level2).toHaveCSS('padding-left', /14.4px/);
await expect(level2).toHaveAttribute('aria-selected', 'false');
await expect(editor.getByText('Hello World')).toBeInViewport(); await expect(level3).toBeVisible();
await expect(hello).toHaveAttribute('aria-selected', 'true'); await expect(level3).toHaveCSS('padding-left', /24px/);
await expect(superW).toHaveAttribute('aria-selected', 'false'); await expect(level3).toHaveAttribute('aria-selected', 'false');
await another.click();
await expect(editor.getByText('Hello World')).not.toBeInViewport();
await expect(hello).toHaveAttribute('aria-selected', 'false');
await expect(superW).toHaveAttribute('aria-selected', 'true');
await panel.getByText('Back to top').click();
await expect(editor.getByText('Hello World')).toBeInViewport();
await expect(hello).toHaveAttribute('aria-selected', 'true');
await expect(superW).toHaveAttribute('aria-selected', 'false');
await panel.getByText('Go to bottom').click();
await expect(editor.getByText('Hello World')).not.toBeInViewport();
await expect(superW).toHaveAttribute('aria-selected', 'true');
});
test('it checks that table contents panel is opened automaticaly if more that 2 headings', async ({
page,
browserName,
}) => {
const [randomDoc] = await createDoc(
page,
'doc-table-content',
browserName,
1,
);
await verifyDocName(page, randomDoc);
await expect(page.getByLabel('Open the panel')).toBeHidden();
const editor = page.locator('.ProseMirror');
await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 1').click();
await page.keyboard.type('Hello World', { delay: 100 });
await page.keyboard.press('Enter');
await editor.locator('.bn-block-outer').last().fill('/');
await page.getByText('Heading 2').click();
await page.keyboard.type('Super World', { delay: 100 });
await goToGridDoc(page, {
title: randomDoc,
});
await expect(page.getByLabel('Close the panel')).toBeVisible();
const panel = page.getByLabel('Document panel');
await expect(panel.getByText('Hello World')).toBeVisible();
await expect(panel.getByText('Super World')).toBeVisible();
await page.getByLabel('Close the panel').click();
await expect(panel).toHaveAttribute('aria-hidden', 'true');
}); });
}); });

View File

@@ -9,10 +9,12 @@ export enum SeparatorVariant {
type Props = { type Props = {
variant?: SeparatorVariant; variant?: SeparatorVariant;
$withPadding?: boolean;
}; };
export const HorizontalSeparator = ({ export const HorizontalSeparator = ({
variant = SeparatorVariant.LIGHT, variant = SeparatorVariant.LIGHT,
$withPadding = true,
}: Props) => { }: Props) => {
const { colorsTokens } = useCunninghamTheme(); const { colorsTokens } = useCunninghamTheme();
@@ -20,7 +22,7 @@ export const HorizontalSeparator = ({
<Box <Box
$height="1px" $height="1px"
$width="100%" $width="100%"
$margin={{ vertical: 'base' }} $margin={{ vertical: $withPadding ? 'base' : 'none' }}
$background={ $background={
variant === SeparatorVariant.DARK variant === SeparatorVariant.DARK
? '#e5e5e533' ? '#e5e5e533'

View File

@@ -4,7 +4,7 @@ import { BlockNoteView } from '@blocknote/mantine';
import '@blocknote/mantine/style.css'; import '@blocknote/mantine/style.css';
import { useCreateBlockNote } from '@blocknote/react'; import { useCreateBlockNote } from '@blocknote/react';
import { HocuspocusProvider } from '@hocuspocus/provider'; import { HocuspocusProvider } from '@hocuspocus/provider';
import React, { useEffect } from 'react'; import { useEffect } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import * as Y from 'yjs'; import * as Y from 'yjs';
@@ -24,10 +24,7 @@ const cssEditor = (readonly: boolean) => `
&, & > .bn-container, & .ProseMirror { &, & > .bn-container, & .ProseMirror {
height:100% height:100%
}; };
& .bn-editor {
padding-right: 30px;
${readonly && `padding-left: 30px;`}
};
& .bn-inline-content code { & .bn-inline-content code {
background-color: gainsboro; background-color: gainsboro;
padding: 2px; padding: 2px;

View File

@@ -2,9 +2,10 @@ import { Alert, Loader, VariantType } from '@openfun/cunningham-react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import * as Y from 'yjs'; import * as Y from 'yjs';
import { Box, Card, Text, TextErrors } from '@/components'; import { Box, Text, TextErrors } from '@/components';
import { useCunninghamTheme } from '@/cunningham'; import { useCunninghamTheme } from '@/cunningham';
import { DocHeader, DocVersionHeader } from '@/features/docs/doc-header/'; import { DocHeader, DocVersionHeader } from '@/features/docs/doc-header/';
import { import {
@@ -12,11 +13,11 @@ import {
base64ToBlocknoteXmlFragment, base64ToBlocknoteXmlFragment,
useProviderStore, useProviderStore,
} from '@/features/docs/doc-management'; } from '@/features/docs/doc-management';
import { TableContent } from '@/features/docs/doc-table-content/';
import { Versions, useDocVersion } from '@/features/docs/doc-versioning/'; import { Versions, useDocVersion } from '@/features/docs/doc-versioning/';
import { useResponsiveStore } from '@/stores'; import { useResponsiveStore } from '@/stores';
import { BlockNoteEditor, BlockNoteEditorVersion } from './BlockNoteEditor'; import { BlockNoteEditor, BlockNoteEditorVersion } from './BlockNoteEditor';
import { IconOpenPanelEditor, PanelEditor } from './PanelEditor';
interface DocEditorProps { interface DocEditorProps {
doc: Doc; doc: Doc;
@@ -25,9 +26,9 @@ interface DocEditorProps {
export const DocEditor = ({ doc, versionId }: DocEditorProps) => { export const DocEditor = ({ doc, versionId }: DocEditorProps) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isMobile } = useResponsiveStore(); const { isDesktop } = useResponsiveStore();
const isVersion = versionId && typeof versionId === 'string'; const isVersion = !!versionId && typeof versionId === 'string';
const { colorsTokens } = useCunninghamTheme(); const { colorsTokens } = useCunninghamTheme();
@@ -39,41 +40,49 @@ export const DocEditor = ({ doc, versionId }: DocEditorProps) => {
return ( return (
<> <>
{isVersion ? ( {isDesktop && !isVersion && (
<DocVersionHeader title={doc.title} /> <Box
) : ( $position="absolute"
<DocHeader doc={doc} /> $css={css`
)} top: 72px;
right: 20px;
{!doc.abilities.partial_update && ( `}
<Box $width="100%" $margin={{ all: 'small', top: 'none' }}> >
<Alert type={VariantType.WARNING}> <TableContent />
{t(`Read only, you cannot edit this document.`)}
</Alert>
</Box> </Box>
)} )}
<Box $maxWidth="868px" $width="100%">
<Box $padding={{ horizontal: '54px' }}>
{isVersion ? (
<DocVersionHeader title={doc.title} />
) : (
<DocHeader doc={doc} />
)}
</Box>
<Box {!doc.abilities.partial_update && (
$background={colorsTokens()['primary-bg']} <Box $width="100%" $margin={{ all: 'small', top: 'none' }}>
$direction="row" <Alert type={VariantType.WARNING}>
$width="100%" {t(`Read only, you cannot edit this document.`)}
$css="overflow-x: clip; flex: 1;" </Alert>
$position="relative" </Box>
> )}
<Card
$padding={isMobile ? 'small' : 'big'} <Box
$css="flex:1;" $background={colorsTokens()['primary-bg']}
$overflow="auto" $direction="row"
$width="100%"
$css="overflow-x: clip; flex: 1;"
$position="relative" $position="relative"
> >
{isVersion ? ( <Box $css="flex:1;" $overflow="auto" $position="relative">
<DocVersionEditor docId={doc.id} versionId={versionId} /> {isVersion ? (
) : ( <DocVersionEditor docId={doc.id} versionId={versionId} />
<BlockNoteEditor doc={doc} provider={provider} /> ) : (
)} <BlockNoteEditor doc={doc} provider={provider} />
{!isMobile && !isVersion && <IconOpenPanelEditor />} )}
</Card> </Box>
{!isVersion && <PanelEditor />} </Box>
</Box> </Box>
</> </>
); );

View File

@@ -1,170 +0,0 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Box, BoxButton, Card, IconBG, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham';
import { TableContent } from '@/features/docs/doc-table-content';
import { useResponsiveStore } from '@/stores';
import { useHeadingStore, usePanelEditorStore } from '../stores';
export const PanelEditor = () => {
const { t } = useTranslation();
const { colorsTokens } = useCunninghamTheme();
const { isMobile } = useResponsiveStore();
const { isPanelTableContentOpen, setIsPanelTableContentOpen, isPanelOpen } =
usePanelEditorStore();
return (
<Card
$width="100%"
$maxWidth="20rem"
$position={isMobile ? 'absolute' : 'sticky'}
$height="100%"
$hasTransition="slow"
$css={`
top: 0vh;
right: 0;
transform: translateX(0%);
flex: 1;
margin-left: 1rem;
${
!isPanelOpen &&
`
transform: translateX(200%);
opacity: 0;
flex: 0;
margin-left: 0rem;
max-width: 0rem;
`
}
`}
aria-label={t('Document panel')}
aria-hidden={!isPanelOpen}
>
<Box
$overflow="inherit"
$position="sticky"
$hasTransition="slow"
$css={`
top: 0;
opacity: ${isPanelOpen ? '1' : '0'};
`}
$maxHeight="99vh"
>
{isMobile && <IconOpenPanelEditor />}
<Box
$direction="row"
$justify="space-between"
$align="center"
$position="relative"
$background={colorsTokens()['primary-400']}
$margin={{ bottom: 'tiny' }}
$radius="4px 4px 0 0"
>
<Box
$background="white"
$position="absolute"
$height="100%"
$width="100%"
$hasTransition="slow"
$css={`
border-top: 2px solid ${colorsTokens()['primary-600']};
border-radius: 0 4px 0 0;
${
isPanelTableContentOpen
? `
transform: translateX(0);
border-radius: 4px 0 0 0;
`
: `transform: translateX(100%);`
}
`}
/>
<BoxButton
$minWidth="100%"
onClick={() => setIsPanelTableContentOpen(true)}
$zIndex={1}
>
<Text
$width="100%"
$weight="bold"
$size="m"
$theme="primary"
$variation="600"
$padding={{ vertical: 'small', horizontal: 'small' }}
>
{t('Table of content')}
</Text>
</BoxButton>
</Box>
<TableContent />
</Box>
</Card>
);
};
export const IconOpenPanelEditor = () => {
const { headings } = useHeadingStore();
const { t } = useTranslation();
const { setIsPanelOpen, isPanelOpen, setIsPanelTableContentOpen } =
usePanelEditorStore();
const [hasBeenOpen, setHasBeenOpen] = useState(isPanelOpen);
const { isMobile } = useResponsiveStore();
const setClosePanel = () => {
setHasBeenOpen(true);
setIsPanelOpen(!isPanelOpen);
};
// Open the panel if there are more than 1 heading
useEffect(() => {
if (headings?.length && headings.length > 1 && !hasBeenOpen && !isMobile) {
setIsPanelTableContentOpen(true);
setIsPanelOpen(true);
setHasBeenOpen(true);
}
}, [
headings,
setIsPanelTableContentOpen,
setIsPanelOpen,
hasBeenOpen,
isMobile,
]);
// If open from the doc header we set the state as well
useEffect(() => {
if (isPanelOpen && !hasBeenOpen) {
setHasBeenOpen(true);
}
}, [hasBeenOpen, isPanelOpen]);
// Close the panel unmount
useEffect(() => {
return () => {
setIsPanelOpen(false);
};
}, [setIsPanelOpen]);
return (
<IconBG
iconName="menu_open"
aria-label={isPanelOpen ? t('Close the panel') : t('Open the panel')}
$background="transparent"
$size="h2"
$zIndex={10}
$hasTransition="slow"
$css={`
cursor: pointer;
right: 0rem;
top: 0.1rem;
transform: rotate(${isPanelOpen ? '180deg' : '0deg'});
user-select: none;
${hasBeenOpen ? 'display:flex;' : 'display: none;'}
`}
$position="absolute"
onClick={setClosePanel}
$radius="2px"
/>
);
};

View File

@@ -35,7 +35,7 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
<> <>
<Box <Box
$width="100%" $width="100%"
$padding={{ vertical: 'base' }} $padding={{ top: 'base' }}
$gap={spacings['base']} $gap={spacings['base']}
aria-label={t('It is the card information about the document.')} aria-label={t('It is the card information about the document.')}
> >
@@ -92,7 +92,7 @@ export const DocHeader = ({ doc }: DocHeaderProps) => {
<DocToolBox doc={doc} /> <DocToolBox doc={doc} />
</Box> </Box>
</Box> </Box>
<HorizontalSeparator /> <HorizontalSeparator $withPadding={false} />
</Box> </Box>
</> </>
); );

View File

@@ -5,10 +5,10 @@ import { BoxButton, Text } from '@/components';
import { useCunninghamTheme } from '@/cunningham'; import { useCunninghamTheme } from '@/cunningham';
import { useResponsiveStore } from '@/stores'; import { useResponsiveStore } from '@/stores';
const sizeMap: { [key: number]: string } = { const leftPaddingMap: { [key: number]: string } = {
1: '1.1rem', 3: '1.5rem',
2: '0.9rem', 2: '0.9rem',
3: '0.8rem', 1: '0.3',
}; };
export type HeadingsHighlight = { export type HeadingsHighlight = {
@@ -34,9 +34,12 @@ export const Heading = ({
const [isHover, setIsHover] = useState(isHighlight); const [isHover, setIsHover] = useState(isHighlight);
const { colorsTokens } = useCunninghamTheme(); const { colorsTokens } = useCunninghamTheme();
const { isMobile } = useResponsiveStore(); const { isMobile } = useResponsiveStore();
const isActive = isHighlight || isHover;
return ( return (
<BoxButton <BoxButton
id={`heading-${headingId}`}
$width="100%"
key={headingId} key={headingId}
onMouseOver={() => setIsHover(true)} onMouseOver={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)} onMouseLeave={() => setIsHover(false)}
@@ -47,23 +50,24 @@ export const Heading = ({
} }
editor.setTextCursorPosition(headingId, 'end'); editor.setTextCursorPosition(headingId, 'end');
document.querySelector(`[data-id="${headingId}"]`)?.scrollIntoView({ document.querySelector(`[data-id="${headingId}"]`)?.scrollIntoView({
behavior: 'smooth', behavior: 'smooth',
inline: 'start',
block: 'start', block: 'start',
}); });
}} }}
$radius="4px"
$background={isActive ? `${colorsTokens()['greyscale-100']}` : 'none'}
$css="text-align: left;" $css="text-align: left;"
> >
<Text <Text
$theme="primary" $width="100%"
$padding={{ vertical: 'xtiny', left: 'tiny' }} $padding={{ vertical: 'xtiny', left: leftPaddingMap[level] }}
$size={sizeMap[level]} $variation={isActive ? '1000' : '700'}
$weight={isActive ? 'bold' : 'normal'}
$css="overflow-wrap: break-word;"
$hasTransition $hasTransition
$css={
isHover || isHighlight
? `box-shadow: -2px 0px 0px ${colorsTokens()[isHighlight ? 'primary-500' : 'primary-400']};`
: ''
}
aria-selected={isHighlight} aria-selected={isHighlight}
> >
{text} {text}

View File

@@ -1,21 +1,22 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { css } from 'styled-components';
import { Box, BoxButton, Text } from '@/components'; import { Box, Icon, Text } from '@/components';
import { useEditorStore, useHeadingStore } from '@/features/docs/doc-editor'; import { useEditorStore, useHeadingStore } from '@/features/docs/doc-editor';
import { MAIN_LAYOUT_ID } from '@/layouts/conf'; import { MAIN_LAYOUT_ID } from '@/layouts/conf';
import { useResponsiveStore } from '@/stores';
import { Heading } from './Heading'; import { Heading } from './Heading';
export const TableContent = () => { export const TableContent = () => {
const { headings } = useHeadingStore(); const { headings } = useHeadingStore();
const { editor } = useEditorStore(); const { editor } = useEditorStore();
const { isMobile } = useResponsiveStore();
const { t } = useTranslation();
const [headingIdHighlight, setHeadingIdHighlight] = useState<string>(); const [headingIdHighlight, setHeadingIdHighlight] = useState<string>();
// To highlight the first heading in the viewport const { t } = useTranslation();
const [isHover, setIsHover] = useState(false);
useEffect(() => { useEffect(() => {
const handleScroll = () => { const handleScroll = () => {
if (!headings) { if (!headings) {
@@ -62,69 +63,84 @@ export const TableContent = () => {
} }
return ( return (
<Box $padding={{ all: 'small', right: 'none' }} $maxHeight="95%"> <Box
<Box $overflow="auto" $padding={{ left: '2px' }}> onMouseEnter={() => {
{headings?.map( setIsHover(true);
(heading) => setTimeout(() => {
heading.contentText && ( const element = document.getElementById(
<Heading `heading-${headingIdHighlight}`,
editor={editor} );
headingId={heading.id}
level={heading.props.level}
text={heading.contentText}
key={heading.id}
isHighlight={headingIdHighlight === heading.id}
/>
),
)}
</Box>
<Box
$height="1px"
$width="auto"
$background="#e5e5e5"
$margin={{ vertical: 'small' }}
$css="flex: none;"
/>
<BoxButton
onClick={() => {
// With mobile the focus open the keyboard and the scroll is not working
if (!isMobile) {
editor.focus();
}
document.querySelector(`.bn-editor`)?.scrollIntoView({ element?.scrollIntoView({
behavior: 'smooth', behavior: 'smooth',
block: 'start', inline: 'center',
block: 'center',
}); });
}} }, 250); // 300ms is the transition time of the box
$align="start" }}
> onMouseLeave={() => {
<Text $theme="primary" $padding={{ vertical: 'xtiny' }}> setIsHover(false);
{t('Back to top')} }}
</Text> id="summaryContainer"
</BoxButton> $effect="show"
<BoxButton $width="40px"
onClick={() => { $height="40px"
// With mobile the focus open the keyboard and the scroll is not working $zIndex={1000}
if (!isMobile) { $align="center"
editor.focus(); $padding="xs"
} $justify="center"
$css={css`
border: 1px solid #ccc;
overflow: hidden;
border-radius: var(--c--theme--spacings--3xs);
background: var(--c--theme--colors--greyscale-000);
document &:hover {
.querySelector( overflow-y: auto;
`.bn-editor > .bn-block-group > .bn-block-outer:last-child`, display: flex;
) flex-direction: column;
?.scrollIntoView({ justify-content: flex-start;
behavior: 'smooth', align-items: flex-start;
block: 'start', gap: var(--c--theme--spacings--2xs);
}); width: 200px;
}} height: auto;
$align="start" max-height: calc(100vh - 60px - 15vh);
> }
<Text $theme="primary" $padding={{ vertical: 'xtiny' }}> `}
{t('Go to bottom')} >
</Text> {!isHover && (
</BoxButton> <Box $justify="center" $align="center">
<Icon iconName="list" $theme="primary" $variation="800" />
</Box>
)}
{isHover && (
<Box $width="100%">
<Box
$margin={{ bottom: '20px' }}
$direction="row"
$justify="space-between"
$align="center"
>
<Text $weight="bold" $variation="800" $theme="primary">
{t('Summary')}
</Text>
<Icon iconName="list" $theme="primary" $variation="800" />
</Box>
{headings?.map(
(heading) =>
heading.contentText && (
<Heading
editor={editor}
headingId={heading.id}
level={heading.props.level}
text={heading.contentText}
key={heading.id}
isHighlight={headingIdHighlight === heading.id}
/>
),
)}
</Box>
)}
</Box> </Box>
); );
}; };

View File

@@ -1 +1,2 @@
export * from './TableContent'; export * from './TableContent';
export * from './Heading';

View File

@@ -64,7 +64,7 @@ export const ModalSelectVersion = ({
flex: 1; flex: 1;
`} `}
> >
<Box $width="100%" $padding="base"> <Box $width="100%" $padding="base" $align="center">
{selectedVersionId && ( {selectedVersionId && (
<DocEditor doc={doc} versionId={selectedVersionId} /> <DocEditor doc={doc} versionId={selectedVersionId} />
)} )}