Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/components/editor/components/blocks/code/Code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, EditorElementProps<CodeNode>
({ node, children, ...attributes }, ref) => {
const { language, handleChangeLanguage } = useCodeBlock(node);
const [showToolbar, setShowToolbar] = useState(false);
const isMermaid = language === 'mermaid';

const editor = useSlateStatic();
const readOnly = useReadOnly() || editor.isElementReadOnly(node as unknown as Element);
Expand Down Expand Up @@ -52,10 +53,18 @@ export const CodeBlock = forwardRef<HTMLDivElement, EditorElementProps<CodeNode>
<div {...attributes} ref={ref} className={`${attributes.className ?? ''} flex w-full`}>
<pre
spellCheck={false}
className={`appflowy-scroller flex w-full flex-col overflow-auto rounded-[8px] border border-border-primary bg-fill-list-active p-5 pt-12`}
className={`appflowy-scroller relative flex w-full flex-col overflow-auto rounded-[8px] border border-border-primary bg-fill-list-active p-5 pt-12`}
>
<code>{children}</code>
{language === 'mermaid' && (
<code
className={
isMermaid
? 'pointer-events-none absolute h-px w-px overflow-hidden opacity-0'
: undefined
}
>
{children}
</code>
{isMermaid && (
<Suspense>
<MermaidChat node={node} />
</Suspense>
Expand Down
98 changes: 98 additions & 0 deletions src/components/editor/parsers/__tests__/block-converters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ describe('block-converters', () => {

const block = parseParagraph(node);

if (Array.isArray(block)) throw new Error('Expected one paragraph block');
expect(block.type).toBe(BlockType.Paragraph);
expect(block.text).toBe('Simple paragraph text');
expect(block.formats).toEqual([]);
Expand Down Expand Up @@ -109,6 +110,7 @@ describe('block-converters', () => {

const block = parseParagraph(node);

if (Array.isArray(block)) throw new Error('Expected one paragraph block');
expect(block.text).toBe('Text with bold and italic');
expect(block.formats).toHaveLength(2);
});
Expand All @@ -131,13 +133,109 @@ describe('block-converters', () => {

const block = parseParagraph(node);

if (Array.isArray(block)) throw new Error('Expected one paragraph block');
expect(block.text).toBe('Visit our site');
expect(block.formats).toHaveLength(1);
expect(block.formats[0]).toMatchObject({
type: 'link',
data: { href: 'https://example.com' },
});
});

it('should split br-separated clipboard lines into readable paragraphs', () => {
const node: HastElement = {
type: 'element',
tagName: 'div',
properties: {},
children: [
{ type: 'text', value: 'Overview' },
{ type: 'element', tagName: 'br', properties: {}, children: [] },
{ type: 'text', value: 'Desktop checklist' },
{ type: 'element', tagName: 'br', properties: {}, children: [] },
{ type: 'text', value: ' Mobile checklist' },
],
};

const blocks = parseParagraph(node);

expect(blocks).toEqual([
expect.objectContaining({ type: BlockType.Paragraph, text: 'Overview' }),
expect.objectContaining({ type: BlockType.Paragraph, text: 'Desktop checklist' }),
expect.objectContaining({ type: BlockType.Paragraph, text: ' Mobile checklist' }),
]);
});

it('should split nested div clipboard lines into readable paragraphs', () => {
const node: HastElement = {
type: 'element',
tagName: 'div',
properties: {},
children: [
{
type: 'element',
tagName: 'div',
properties: {},
children: [{ type: 'text', value: 'Summary' }],
},
{
type: 'element',
tagName: 'div',
properties: {},
children: [{ type: 'text', value: 'Owner: Alex' }],
},
{
type: 'element',
tagName: 'div',
properties: {},
children: [{ type: 'text', value: 'Status: ready' }],
},
],
};

const blocks = parseParagraph(node);

expect(blocks).toEqual([
expect.objectContaining({ type: BlockType.Paragraph, text: 'Summary' }),
expect.objectContaining({ type: BlockType.Paragraph, text: 'Owner: Alex' }),
expect.objectContaining({ type: BlockType.Paragraph, text: 'Status: ready' }),
]);
});

it('should preserve inline formats when splitting clipboard lines', () => {
const node: HastElement = {
type: 'element',
tagName: 'div',
properties: {},
children: [
{
type: 'element',
tagName: 'strong',
properties: {},
children: [{ type: 'text', value: 'Important' }],
},
{ type: 'element', tagName: 'br', properties: {}, children: [] },
{
type: 'element',
tagName: 'em',
properties: {},
children: [{ type: 'text', value: 'Follow up' }],
},
],
};

const blocks = parseParagraph(node);

expect(blocks).toEqual([
expect.objectContaining({
text: 'Important',
formats: [{ start: 0, end: 9, type: 'bold', data: undefined }],
}),
expect.objectContaining({
text: 'Follow up',
formats: [{ start: 0, end: 9, type: 'italic', data: undefined }],
}),
]);
});
});

describe('parseCodeBlock', () => {
Expand Down
Loading
Loading