diff --git a/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx b/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx
index d77cdf3db9..0b0674d823 100644
--- a/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx
+++ b/services/web/frontend/js/features/source-editor/components/figure-modal/figure-modal.tsx
@@ -10,6 +10,7 @@ import { FigureModalFooter } from './figure-modal-footer'
import { lazy, memo, Suspense, useCallback, useEffect } from 'react'
import { useCodeMirrorViewContext } from '../codemirror-editor'
import { ChangeSpec } from '@codemirror/state'
+import { snippet } from '@codemirror/autocomplete'
import {
FigureData,
PastedImageData,
@@ -206,29 +207,26 @@ const FigureModalContent = () => {
effects: editFigureDataEffect.of(null),
})
} else {
- view.dispatch(
- view.state.changeByRange(range => {
- const { pos, suffix } = ensureEmptyLine(view.state, range)
- const widthArgument =
- width !== undefined ? `[width=${width}\\linewidth]` : ''
- const changes: ChangeSpec = view.state.changes({
- insert: prepareLines(
- [
- '\\begin{figure}',
- '\t\\centering',
- `\t\\includegraphics${widthArgument}{${path}}`,
- `\t${captionCommand}`,
- `\t${labelCommand}`,
- `\\end{figure}${suffix}`,
- ],
- view.state,
- pos
- ),
- from: pos,
- })
+ const { pos, suffix } = ensureEmptyLine(
+ view.state,
+ view.state.selection.main
+ )
- return { range: range.map(changes), changes }
- })
+ const widthArgument =
+ width !== undefined ? `[width=${width}\\linewidth]` : ''
+ const caption = includeCaption ? `\n\t\\caption{\${Enter Caption}}` : ''
+ const label = includeLabel ? `\n\t\\label{\${fig:enter-label}}` : ''
+
+ snippet(
+ `\\begin{figure}
+\t\\centering
+\t\\includegraphics${widthArgument}{${path}}${caption}${label}
+\\end{figure}${suffix}\${}`
+ )(
+ { state: view.state, dispatch: view.dispatch },
+ { label: 'figure' },
+ pos,
+ pos
)
}
hide()
diff --git a/services/web/test/frontend/features/source-editor/components/codemirror-editor-figure-modal.spec.tsx b/services/web/test/frontend/features/source-editor/components/codemirror-editor-figure-modal.spec.tsx
index 8d0696aab3..122acc22d0 100644
--- a/services/web/test/frontend/features/source-editor/components/codemirror-editor-figure-modal.spec.tsx
+++ b/services/web/test/frontend/features/source-editor/components/codemirror-editor-figure-modal.spec.tsx
@@ -113,7 +113,7 @@ describe('', function () {
cy.get('.cm-content').should(
'have.text',
- '\\begin{figure} \\centering Enter Caption 🏷fig:enter-label\\end{figure}'
+ '\\begin{figure} \\centering \\caption{Enter Caption} 🏷fig:enter-label\\end{figure}'
)
})
@@ -159,7 +159,7 @@ describe('', function () {
cy.findByText('Insert figure').click()
cy.get('.cm-content').should(
'have.text',
- '\\begin{figure} \\centering Enter Caption 🏷fig:enter-label\\end{figure}'
+ '\\begin{figure} \\centering \\caption{Enter Caption} 🏷fig:enter-label\\end{figure}'
)
})
})
@@ -233,7 +233,7 @@ describe('', function () {
cy.get('.cm-content').should(
'have.text',
- '\\begin{figure} \\centering Enter Caption 🏷fig:enter-label\\end{figure}'
+ '\\begin{figure} \\centering \\caption{Enter Caption} 🏷fig:enter-label\\end{figure}'
)
})
@@ -261,7 +261,7 @@ describe('', function () {
cy.get('.cm-content').should(
'have.text',
- '\\begin{figure} \\centering Enter Caption 🏷fig:enter-label\\end{figure}'
+ '\\begin{figure} \\centering \\caption{Enter Caption} 🏷fig:enter-label\\end{figure}'
)
})
})
@@ -403,7 +403,89 @@ describe('', function () {
cy.get('.cm-content').should(
'have.text',
- '\\begin{figure} \\centering Enter Caption 🏷fig:enter-label\\end{figure}'
+ '\\begin{figure} \\centering \\caption{Enter Caption} 🏷fig:enter-label\\end{figure}'
+ )
+ })
+
+ it('Selects the caption when the figure is inserted with a caption', function () {
+ cy.get('@image-url-input').type('https://my-fake-website.com/frog.jpg')
+ cy.findByText('Insert figure').click()
+
+ cy.get('@linked-file-request').should('have.been.calledWithMatch', {
+ body: {
+ provider: 'url',
+ data: {
+ url: 'https://my-fake-website.com/frog.jpg',
+ },
+ },
+ })
+
+ cy.get('.cm-selectionLayer .cm-selectionBackground').should(
+ 'have.length',
+ 1
+ )
+
+ // If caption is selected then typing will replace the whole caption
+ cy.focused().type('My caption')
+ cy.get('.cm-content').should(
+ 'have.text',
+ '\\begin{figure} \\centering \\caption{My caption} 🏷fig:enter-label\\end{figure}'
+ )
+ })
+
+ it('Selects the label when the figure is inserted without a caption', function () {
+ cy.get('@image-url-input').type('https://my-fake-website.com/frog.jpg')
+ cy.get('@include-caption-checkbox').uncheck()
+ cy.findByText('Insert figure').click()
+
+ cy.get('@linked-file-request').should('have.been.calledWithMatch', {
+ body: {
+ provider: 'url',
+ data: {
+ url: 'https://my-fake-website.com/frog.jpg',
+ },
+ },
+ })
+
+ cy.get('.cm-selectionLayer .cm-selectionBackground').should(
+ 'have.length',
+ 1
+ )
+
+ // If label is selected then typing will replace the whole label
+ cy.focused().type('fig:my-label')
+ cy.get('.cm-content').should(
+ 'have.text',
+ '\\begin{figure} \\centering \\label{fig:my-label}\\end{figure}'
+ )
+ })
+
+ it('Places the cursor after the figure if it is inserted without a caption or a label', function () {
+ cy.get('@image-url-input').type('https://my-fake-website.com/frog.jpg')
+ cy.get('@include-caption-checkbox').uncheck()
+ cy.get('@include-label-checkbox').uncheck()
+
+ cy.findByText('Insert figure').click()
+
+ cy.get('@linked-file-request').should('have.been.calledWithMatch', {
+ body: {
+ provider: 'url',
+ data: {
+ url: 'https://my-fake-website.com/frog.jpg',
+ },
+ },
+ })
+
+ cy.get('.cm-content').should(
+ 'have.text',
+ '\\begin{figure} \\centering\\end{figure}'
+ )
+
+ cy.focused().type('Some more text')
+
+ cy.get('.cm-content').should(
+ 'have.text',
+ '\\begin{figure} \\centering\\end{figure}Some more text'
)
})
})