Files
overleaf-cep/services/web/test/frontend/features/source-editor/extensions/editable.test.ts
Malik Glossop 472e05f32b Merge pull request #29971 from overleaf/mg-edit-search-bug
Make editor focusable in view-only mode

GitOrigin-RevId: ed9b079fa379d84f7f410669fa2d865f82e21cb1
2025-12-02 09:04:59 +00:00

131 lines
3.7 KiB
TypeScript

import { expect } from 'chai'
import { EditorState } from '@codemirror/state'
import { EditorView } from '@codemirror/view'
import {
editable,
setEditable,
} from '../../../../../frontend/js/features/source-editor/extensions/editable'
const doc = `\\documentclass{article}
\\begin{document}
Hello world
\\end{document}`
describe('editable extension', function () {
let view: EditorView
let container: HTMLElement
beforeEach(function () {
container = document.createElement('div')
document.body.appendChild(container)
})
afterEach(function () {
view?.destroy()
container?.remove()
})
function createView(extensions = [editable()]) {
view = new EditorView({
parent: container,
state: EditorState.create({
doc,
extensions,
}),
})
return view
}
describe('initial read-only state', function () {
beforeEach(function () {
createView()
})
it('should set EditorState.readOnly to true', function () {
expect(view.state.readOnly).to.be.true
})
it('should set EditorView.editable to false', function () {
expect(view.state.facet(EditorView.editable)).to.be.false
})
it('should set contenteditable="false" on the content element', function () {
expect(view.contentDOM.getAttribute('contenteditable')).to.equal('false')
})
it('should set tabindex="0" to allow focus in read-only mode', function () {
expect(view.contentDOM.getAttribute('tabindex')).to.equal('0')
})
it('should allow the editor to receive focus via tabindex', function () {
view.contentDOM.focus()
expect(document.activeElement).to.equal(view.contentDOM)
})
})
describe('setEditable(true) - switching to editable mode', function () {
beforeEach(function () {
createView()
view.dispatch(setEditable(true))
})
it('should set EditorState.readOnly to false', function () {
expect(view.state.readOnly).to.be.false
})
it('should set EditorView.editable to true', function () {
expect(view.state.facet(EditorView.editable)).to.be.true
})
it('should set contenteditable="true" on the content element', function () {
expect(view.contentDOM.getAttribute('contenteditable')).to.equal('true')
})
it('should not have tabindex attribute (not needed when contenteditable)', function () {
expect(view.contentDOM.getAttribute('tabindex')).to.be.null
})
it('should allow document modifications', function () {
view.dispatch({
changes: { from: 0, insert: 'New text ' },
})
expect(view.state.doc.toString().startsWith('New text ')).to.be.true
})
it('should allow the editor to receive focus', function () {
view.contentDOM.focus()
expect(document.activeElement).to.equal(view.contentDOM)
})
})
describe('setEditable(false) - switching to read-only mode', function () {
beforeEach(function () {
createView()
view.dispatch(setEditable(true))
view.dispatch(setEditable(false))
})
it('should set EditorState.readOnly to true', function () {
expect(view.state.readOnly).to.be.true
})
it('should set EditorView.editable to false', function () {
expect(view.state.facet(EditorView.editable)).to.be.false
})
it('should set contenteditable="false" on the content element', function () {
expect(view.contentDOM.getAttribute('contenteditable')).to.equal('false')
})
it('should restore tabindex="0" for focusability', function () {
expect(view.contentDOM.getAttribute('tabindex')).to.equal('0')
})
it('should still allow the editor to receive focus after switching modes', function () {
view.contentDOM.focus()
expect(document.activeElement).to.equal(view.contentDOM)
})
})
})