Skip to content
Closed
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
5 changes: 5 additions & 0 deletions .changeset/selfish-peas-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

Remove `markdown-toolbar-element` dependency, inlining the logic into the `MarkdownEditor` component
4 changes: 1 addition & 3 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ module.exports = {
'<rootDir>/src/utils/test-helpers.tsx'
],
testMatch: ['<rootDir>/(src|codemods)/**/*.test.[jt]s?(x)', '!**/*.types.test.[jt]s?(x)'],
transformIgnorePatterns: [
'node_modules/(?!@fd.fxlcf.dpdns.orgbobox-nav|@koddsson/textarea-caret|@github/markdown-toolbar-element)'
],
transformIgnorePatterns: ['node_modules/(?!@fd.fxlcf.dpdns.orgbobox-nav|@koddsson/textarea-caret)'],
watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname']
}
15 changes: 2 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@
},
"dependencies": {
"@fd.fxlcf.dpdns.orgbobox-nav": "^2.1.5",
"@github/markdown-toolbar-element": "^2.1.0",
"@github/paste-markdown": "^1.3.1",
"@primer/behaviors": "^1.1.1",
"@primer/octicons-react": "^17.3.0",
Expand Down
2 changes: 1 addition & 1 deletion src/drafts/MarkdownEditor/MarkdownEditor.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ describe('MarkdownEditor', () => {
describe.each<
[description: string, result: string, shortcut?: [key: string, shift: boolean], caretPosition?: number]
>([
['Add header text', 'text### '],
['Add heading', 'text### '],
['Bold', 'text****', ['b', false], 6],
['Italic', 'text__', ['i', false], 5],
['Insert a quote', 'text\n\n> ', ['.', true]],
Expand Down
26 changes: 12 additions & 14 deletions src/drafts/MarkdownEditor/MarkdownEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import MarkdownViewer from '../MarkdownViewer'
import {SxProp} from '../../sx'
import createSlots from '../../utils/create-slots'
import VisuallyHidden from '../../_VisuallyHidden'
import {FormattingTools} from './_FormattingTools'
import {MarkdownEditorContext} from './_MarkdownEditorContext'
import {CoreToolbar, DefaultToolbarButtons} from './Toolbar'
import {Footer} from './_Footer'
Expand All @@ -24,6 +23,7 @@ import {Emoji} from './suggestions/_useEmojiSuggestions'
import {Mentionable} from './suggestions/_useMentionSuggestions'
import {Reference} from './suggestions/_useReferenceSuggestions'
import {isModifierKey} from './utils'
import {useFormattingTools} from './_useFormattingTools'

export type MarkdownEditorProps = SxProp & {
/** Current value of the editor as a multiline markdown string. */
Expand Down Expand Up @@ -221,7 +221,7 @@ const MarkdownEditor = forwardRef<MarkdownEditorHandle, MarkdownEditorProps>(
const listEditor = useListEditing({emitChange})
const indenter = useIndenting({emitChange})

const formattingToolsRef = useRef<FormattingTools>(null)
const formattingTools = useFormattingTools(inputRef, emitChange)

// use state instead of ref since we need to recalculate when the element mounts
const containerRef = useRef<HTMLDivElement>(null)
Expand All @@ -246,7 +246,6 @@ const MarkdownEditor = forwardRef<MarkdownEditorHandle, MarkdownEditorProps>(

const inputCompositionProps = useIgnoreKeyboardActionsWhileComposing(
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
const format = formattingToolsRef.current
if (disabled) return

if (e.ctrlKey && e.key === '.') {
Expand All @@ -256,14 +255,14 @@ const MarkdownEditor = forwardRef<MarkdownEditorHandle, MarkdownEditorProps>(
e.stopPropagation()
} else if (isModifierKey(e)) {
if (e.key === 'Enter') onPrimaryAction?.()
else if (e.key === 'b') format?.bold()
else if (e.key === 'i') format?.italic()
else if (e.shiftKey && e.key === '.') format?.quote()
else if (e.key === 'e') format?.code()
else if (e.key === 'k') format?.link()
else if (e.key === '8') format?.unorderedList()
else if (e.shiftKey && e.key === '7') format?.orderedList()
else if (e.shiftKey && e.key === 'l') format?.taskList()
else if (e.key === 'b') formattingTools.bold()
else if (e.key === 'i') formattingTools.italic()
else if (e.shiftKey && e.key === '.') formattingTools.quote()
else if (e.key === 'e') formattingTools.code()
else if (e.key === 'k') formattingTools.link()
else if (e.key === '8') formattingTools.unorderedList()
else if (e.shiftKey && e.key === '7') formattingTools.orderedList()
else if (e.shiftKey && e.key === 'l') formattingTools.taskList()
else if (e.shiftKey && e.key === 'p') setView?.('preview')
else return

Expand Down Expand Up @@ -306,8 +305,8 @@ const MarkdownEditor = forwardRef<MarkdownEditorHandle, MarkdownEditorProps>(

// If we don't memoize the context object, every child will rerender on every render even if memoized
const context = useMemo(
() => ({disabled, formattingToolsRef, condensed, required}),
[disabled, formattingToolsRef, condensed, required]
() => ({disabled, formattingTools, condensed, required}),
[disabled, formattingTools, condensed, required]
)

// We are using MarkdownEditorContext instead of the built-in Slots context because Slots' context is not typesafe
Expand All @@ -322,7 +321,6 @@ const MarkdownEditor = forwardRef<MarkdownEditorHandle, MarkdownEditorProps>(
aria-describedby={describedBy ? `${descriptionId} ${describedBy}` : descriptionId}
style={{appearance: 'none', border: 'none'}}
>
<FormattingTools ref={formattingToolsRef} forInputId={id} />
<div style={{display: 'none'}}>{children}</div>

{slots.Label}
Expand Down
32 changes: 12 additions & 20 deletions src/drafts/MarkdownEditor/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,73 +40,65 @@ export const ToolbarButton = forwardRef<HTMLButtonElement, IconButtonProps>((pro
ToolbarButton.displayName = 'MarkdownEditor.ToolbarButton'

export const DefaultToolbarButtons = memo(() => {
const {condensed, formattingToolsRef} = useContext(MarkdownEditorContext)
const {condensed, formattingTools} = useContext(MarkdownEditorContext)

const cmdOrCtrl = isMacOS() ? 'Cmd' : 'Ctrl'

// Important: do not replace `() => ref.current?.format()` with `ref.current?.format` - it will refer to an outdated ref.current!
return (
<>
<Box>
<ToolbarButton onClick={() => formattingTools?.heading(3)} icon={HeadingIcon} aria-label="Add heading" />
<ToolbarButton onClick={() => formattingTools?.bold()} icon={BoldIcon} aria-label={`Bold (${cmdOrCtrl} + B)`} />
<ToolbarButton
onClick={() => formattingToolsRef.current?.header()}
icon={HeadingIcon}
aria-label="Add header text"
/>
<ToolbarButton
onClick={() => formattingToolsRef.current?.bold()}
icon={BoldIcon}
aria-label={`Bold (${cmdOrCtrl} + B)`}
/>
<ToolbarButton
onClick={() => formattingToolsRef.current?.italic()}
onClick={() => formattingTools?.italic()}
icon={ItalicIcon}
aria-label={`Italic (${cmdOrCtrl} + I)`}
/>
</Box>
<Box>
<ToolbarButton
onClick={() => formattingToolsRef.current?.quote()}
onClick={() => formattingTools?.quote()}
icon={QuoteIcon}
aria-label={`Insert a quote (${cmdOrCtrl} + Shift + .)`}
/>
<ToolbarButton
onClick={() => formattingToolsRef.current?.code()}
onClick={() => formattingTools?.code()}
icon={CodeIcon}
aria-label={`Insert code (${cmdOrCtrl} + E)`}
/>
<ToolbarButton
onClick={() => formattingToolsRef.current?.link()}
onClick={() => formattingTools?.link()}
icon={LinkIcon}
aria-label={`Add a link (${cmdOrCtrl} + K)`}
/>
</Box>
<Box>
<ToolbarButton
onClick={() => formattingToolsRef.current?.unorderedList()}
onClick={() => formattingTools?.unorderedList()}
icon={ListUnorderedIcon}
aria-label={`Add a bulleted list (${cmdOrCtrl} + 8)`}
/>
<ToolbarButton
onClick={() => formattingToolsRef.current?.orderedList()}
onClick={() => formattingTools?.orderedList()}
icon={ListOrderedIcon}
aria-label={`Add a numbered list (${cmdOrCtrl} + Shift + 7)`}
/>
<ToolbarButton
onClick={() => formattingToolsRef.current?.taskList()}
onClick={() => formattingTools?.taskList()}
icon={TasklistIcon}
aria-label={`Add a task list (${cmdOrCtrl} + Shift + L)`}
/>
</Box>
{!condensed && (
<Box>
<ToolbarButton
onClick={() => formattingToolsRef.current?.mention()}
onClick={() => formattingTools?.mention()}
icon={MentionIcon}
aria-label="Mention a user or team (@)"
/>
<ToolbarButton
onClick={() => formattingToolsRef.current?.reference()}
onClick={() => formattingTools?.reference()}
icon={CrossReferenceIcon}
aria-label="Reference an issue, pull request, or discussion (#)"
/>
Expand Down
74 changes: 0 additions & 74 deletions src/drafts/MarkdownEditor/_FormattingTools.tsx

This file was deleted.

8 changes: 4 additions & 4 deletions src/drafts/MarkdownEditor/_MarkdownEditorContext.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import {createContext, RefObject} from 'react'
import {FormattingTools} from './_FormattingTools'
import {createContext} from 'react'
import {FormattingTools} from './_useFormattingTools'

// For performance, the properties in context MUST NOT be values that change often - every time
// any of the properties change, all components including memoized ones will be re-rendered
type MarkdownEditorContextProps = {
disabled: boolean
condensed: boolean
required: boolean
formattingToolsRef: RefObject<FormattingTools>
formattingTools: FormattingTools | null
}

export const MarkdownEditorContext = createContext<MarkdownEditorContextProps>({
disabled: false,
condensed: false,
required: false,
formattingToolsRef: {current: null}
formattingTools: null
})
Loading