Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trying to solve https://github.com/mwood23/slate-test-utils/issues/8 #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,7 @@ redo: () => Promise<void>
selectAll: () => Promise<void>
isApple: () => boolean
rerender: () => void
getEditorElement: () => HTMLElement
```

#### Queries
Expand Down
17 changes: 10 additions & 7 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,26 @@
"postinstall": "patch-package"
},
"dependencies": {
"emotion": "^10.0.9",
"image-extensions": "^1.1.0",
"is-url": "^1.2.4",
"slate": "^0.70.0",
"slate-react": "^0.70.0",
"slate-history": "^0.66.0",
"slate-hyperscript": "^0.67.0",
"emotion": "^10.0.9"
"slate-react": "^0.70.0"
},
"devDependencies": {
"patch-package": "^6.4.7",
"@testing-library/react": "^12.1.2",
"@testing-library/jest-dom": "^5.15.0",
"@testing-library/react": "^12.1.2",
"@types/is-url": "^1.2.30",
"@types/jest": "^27.0.0",
"@types/react": "^16.8.0",
"@types/react-dom": "^16.8.0",
"@vitejs/plugin-react": "^1.0.0",
"typescript": "^4.3.2",
"vite": "^2.6.4",
"jest": "^27.3.1",
"ts-jest": "^27.0.7"
"patch-package": "^6.4.7",
"ts-jest": "^27.0.7",
"typescript": "^4.3.2",
"vite": "^2.6.4"
}
}
37 changes: 35 additions & 2 deletions example/src/Components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import React, { PropsWithChildren } from 'react'
import ReactDOM from 'react-dom'
import { cx, css } from 'emotion'
import { FC } from 'react'
import { useSelected, useFocused } from 'slate-react'
import { ImageElement } from './custom-types'

interface BaseProps {
className: string
Expand Down Expand Up @@ -40,8 +42,8 @@ export const Button = React.forwardRef(
? 'white'
: '#aaa'
: active
? 'black'
: '#ccc'};
? 'black'
: '#ccc'};
`,
)}
/>
Expand Down Expand Up @@ -128,3 +130,34 @@ export const Toolbar = React.forwardRef(
/>
),
)

export const Image = React.forwardRef(
(
{ element, attributes, children }: { element: ImageElement, attributes: any, children: React.ReactNode },
ref: any
) => {
const selected = useSelected();
const focused = useFocused();

return (
<div
ref={ref} {...attributes}
>
<div contentEditable={false}
style={{ position: 'relative', height: '20rem' }}
>
<img
src={element.url}
alt={element.caption}
className={selected && focused ? 'selected focused' : ''}
/>
</div>
{
/* see https://github.com/ianstormtaylor/slate/issues/3930#issuecomment-723288696 */
children
}
</div>

)
}
);
117 changes: 71 additions & 46 deletions example/src/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import {
} from 'slate'
import { withHistory } from 'slate-history'

import { Button, Icon, Toolbar } from './Components'
import { Button, Icon, Toolbar, Image } from './Components'
import { FC } from 'react'
import { withImages } from './plugins'
import { EditableProps } from 'slate-react/dist/components/editable'

const HOTKEYS = {
'mod+b': 'bold',
Expand All @@ -43,52 +45,73 @@ export const RichTextExample: FC<{
variant = 'wordProcessor',
initialValue = emptyEditor,
}) => {
const [value, setValue] = useState<Descendant[]>(initialValue)
const renderElement = useCallback((props) => <Element {...props} />, [])
const renderLeaf = useCallback((props) => <Leaf {...props} />, [])
const editor = useMemo(
() => withHistory(withReact(mockEditor ?? createEditor())),
[],
)

return (
<Slate editor={editor} value={value} onChange={(value) => setValue(value)}>
<Toolbar>
<MarkButton format="bold" icon="format_bold" />
<MarkButton format="italic" icon="format_italic" />
<MarkButton format="underline" icon="format_underlined" />
<MarkButton format="code" icon="code" />
{variant === 'wordProcessor' && (
<>
<BlockButton format="heading-one" icon="looks_one" />
<BlockButton format="heading-two" icon="looks_two" />
<BlockButton format="block-quote" icon="format_quote" />
<BlockButton format="numbered-list" icon="format_list_numbered" />
<BlockButton format="bulleted-list" icon="format_list_bulleted" />
</>
)}
</Toolbar>
<Editable
data-variant={variant}
data-testid="slate-content-editable"
renderElement={renderElement}
renderLeaf={renderLeaf}
placeholder="Enter some rich text…"
autoFocus
onKeyDown={(event) => {
for (const hotkey in HOTKEYS) {
if (isHotkey(hotkey, event as any)) {
event.preventDefault()
// @ts-ignore
const mark = HOTKEYS[hotkey]
toggleMark(editor, mark)
}
const [value, setValue] = useState<Descendant[]>(initialValue)
const renderElement = useCallback((props) => <Element {...props} />, [])
const renderLeaf = useCallback((props) => <Leaf {...props} />, [])
const editor = useMemo(
() => withImages(withHistory(withReact(mockEditor ?? createEditor()))),
[],
)


const editableProps: EditableProps = {
renderElement,
renderLeaf,
placeholder: "Enter some rich text…",
autoFocus: true,
onKeyDown: (event) => {
for (const hotkey in HOTKEYS) {
if (isHotkey(hotkey, event as any)) {
event.preventDefault()
// @ts-ignore
const mark = HOTKEYS[hotkey]
toggleMark(editor, mark)
}
}}
/>
</Slate>
)
}
}
}
}

/**
* Disable scrollSelectionIntoView when testing.
* We do this to fix `TypeError: Cannot read property 'bind' of undefined`
* that stems from https://github.com/ianstormtaylor/slate/blob/43ca2b56c8bd8bcc30dd38808dd191f804d53ae4/packages/slate-react/src/components/editable.tsx#L1369
* and https://github.com/ianstormtaylor/slate/blob/43ca2b56c8bd8bcc30dd38808dd191f804d53ae4/packages/slate-react/src/components/editable.tsx#L234
*
* This error was encountered when testing dropEvent for images
*/
// TODO: Maybe there is a better fix than this, please check the test file to figure if there is any
if (mockEditor) {
editableProps.scrollSelectionIntoView = () => { }
}

return (
<Slate editor={editor} value={value} onChange={(value) => setValue(value)}>
<Toolbar>
<MarkButton format="bold" icon="format_bold" />
<MarkButton format="italic" icon="format_italic" />
<MarkButton format="underline" icon="format_underlined" />
<MarkButton format="code" icon="code" />
{variant === 'wordProcessor' && (
<>
<BlockButton format="heading-one" icon="looks_one" />
<BlockButton format="heading-two" icon="looks_two" />
<BlockButton format="block-quote" icon="format_quote" />
<BlockButton format="numbered-list" icon="format_list_numbered" />
<BlockButton format="bulleted-list" icon="format_list_bulleted" />
</>
)}
</Toolbar>
<Editable
data-variant={variant}
data-testid="slate-content-editable"
{...editableProps}
onDrop={({dataTransfer})=>{
console.log(dataTransfer.files[0].name)
}}
/>
</Slate>
)
}

const toggleBlock = (editor: Editor, format: any) => {
const isActive = isBlockActive(editor, format)
Expand Down Expand Up @@ -157,6 +180,8 @@ const Element: FC<any> = ({ attributes, children, element }) => {
return <li {...attributes}>{children}</li>
case 'numbered-list':
return <ol {...attributes}>{children}</ol>
case 'image':
return <Image element={element} {...attributes}>{children}</Image>
default:
return <p {...attributes}>{children}</p>
}
Expand Down
1 change: 1 addition & 0 deletions example/src/custom-types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export type HeadingTwoElement = { type: 'heading-two'; children: Descendant[] }
export type ImageElement = {
type: 'image'
url: string
caption: string
children: EmptyText[]
}

Expand Down
1 change: 1 addition & 0 deletions example/src/hyperscript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ declare namespace JSX {
italic?: boolean
children?: any
}
himage: any
hbulletedlist: any
hlistitem: any
cursor: any
Expand Down
65 changes: 65 additions & 0 deletions example/src/plugins.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import imageExtensions from 'image-extensions';
import isUrl from "is-url";
import { Transforms } from "slate";
import { CustomEditor, ImageElement } from "./custom-types";

type SlatePlugin = (editor: CustomEditor) => CustomEditor;

export const withImages: SlatePlugin = editor => {
const { insertData, isVoid } = editor

const insertImage = (editor: CustomEditor, url: string, caption: string) => {

const text = { text: '' }
const image: ImageElement = { type: 'image', url, caption, children: [text] }

Transforms.insertNodes(editor, image);

}

const isImageUrl = (url: string) => {
if (!url) return false
if (!isUrl(url)) return false
const ext = new URL(url).pathname.split('.').pop()
return imageExtensions.includes(ext as string)
};

editor.isVoid = element => {
return element.type === 'image' ? true : isVoid(element)
}

editor.insertData = data => {
const text = data.getData('text/plain')
const { files } = data;

if (files && files.length > 0) {
for (const file of files) {
const reader = new FileReader()
const [mime, extention] = file.type.split('/')

if (mime === 'image') {
reader.addEventListener('load', () => {
const url = reader.result;

let caption = file.name;
caption = caption.substring(0, caption.indexOf(`.${extention}`));

if (url) {
insertImage(editor, url.toString(), caption)
} else {
throw 'image url should not be null'
}
})

reader.readAsDataURL(file)
}
}
} else if (isImageUrl(text)) {
insertImage(editor, text, '')
} else {
insertData(data)
}
}

return editor
}
1 change: 1 addition & 0 deletions example/src/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const jsx = createHyperscript({
hp: { type: 'paragraph' },
hbulletedlist: { type: 'bulleted-list' },
hlistitem: { type: 'list-item' },
himage: { type: 'image' },
inline: { inline: true },
block: {},
wrapper: {},
Expand Down
Loading