Font, size, mark colour and block attributes all answer the same question — the one value the selection agrees on, blank when it mixes. One `uniform` walk now serves all four, framework-free, so any chrome can read it inside its own reactive wrapper. The classic toolbar keeps its own copies for now: it is the baseline the ribbon is compared against, and it should adopt these only once that comparison ends.
98 lines
3.8 KiB
TypeScript
98 lines
3.8 KiB
TypeScript
// What a toolbar shows for the selection. Drives a real body editor, so the
|
|
// readers see the same state the ribbon does.
|
|
import { describe, it, expect } from 'vitest';
|
|
import { Editor } from '@tiptap/core';
|
|
import { extensions } from '../../src/lib/editor/extensions';
|
|
import {
|
|
DEFAULT_EDITOR_FONT, uniformFont, uniformFontSize, uniformMarkColor, uniformBlockAttr,
|
|
} from '../../src/lib/utils/selectionFormat';
|
|
|
|
type N = any;
|
|
|
|
function makeEditor(content: N) {
|
|
const el = document.createElement('div');
|
|
document.body.appendChild(el);
|
|
return new Editor({ element: el, extensions, content });
|
|
}
|
|
|
|
const run = (text: string, attrs?: N): N => ({
|
|
type: 'text', text, ...(attrs ? { marks: [{ type: 'textStyle', attrs }] } : {}),
|
|
});
|
|
|
|
const doc = (...paras: N[]): N => ({ type: 'doc', content: paras });
|
|
const para = (attrs: N, ...content: N[]): N => ({ type: 'paragraph', attrs, content });
|
|
|
|
describe('uniformFont', () => {
|
|
it('falls back to the editor default where no run sets one', () => {
|
|
const editor = makeEditor(doc(para({}, run('plain'))));
|
|
editor.commands.selectAll();
|
|
expect(uniformFont(editor.state)).toBe(DEFAULT_EDITOR_FONT);
|
|
editor.destroy();
|
|
});
|
|
|
|
it('reports one font across a selection and blanks a mixed one', () => {
|
|
const editor = makeEditor(doc(para({}, run('aa', { fontFamily: 'Arial' }), run('bb', { fontFamily: 'Arial' }))));
|
|
editor.commands.selectAll();
|
|
expect(uniformFont(editor.state)).toBe('Arial');
|
|
|
|
const mixed = makeEditor(doc(para({}, run('aa', { fontFamily: 'Arial' }), run('bb', { fontFamily: 'Georgia' }))));
|
|
mixed.commands.selectAll();
|
|
expect(uniformFont(mixed.state)).toBe('');
|
|
editor.destroy();
|
|
mixed.destroy();
|
|
});
|
|
});
|
|
|
|
describe('uniformFontSize', () => {
|
|
it('prefers a run size over the block size', () => {
|
|
const editor = makeEditor(doc(para({ fontSize: '20pt' }, run('big', { fontSize: '9pt' }))));
|
|
editor.commands.selectAll();
|
|
expect(uniformFontSize(editor.state)).toBe('9pt');
|
|
editor.destroy();
|
|
});
|
|
|
|
it('reads the block size for a run that carries none', () => {
|
|
const editor = makeEditor(doc(para({ fontSize: '20pt' }, run('plain'))));
|
|
editor.commands.selectAll();
|
|
expect(uniformFontSize(editor.state)).toBe('20pt');
|
|
editor.destroy();
|
|
});
|
|
});
|
|
|
|
describe('uniformMarkColor', () => {
|
|
it('is null without a colour, the colour with one, blank when mixed', () => {
|
|
const none = makeEditor(doc(para({}, run('plain'))));
|
|
none.commands.selectAll();
|
|
expect(uniformMarkColor(none.state, 'textStyle')).toBeNull();
|
|
|
|
const one = makeEditor(doc(para({}, run('red', { color: '#ff0000' }))));
|
|
one.commands.selectAll();
|
|
expect(uniformMarkColor(one.state, 'textStyle')).toBe('#ff0000');
|
|
|
|
const two = makeEditor(doc(para({}, run('r', { color: '#ff0000' }), run('b', { color: '#0000ff' }))));
|
|
two.commands.selectAll();
|
|
expect(uniformMarkColor(two.state, 'textStyle')).toBe('');
|
|
none.destroy(); one.destroy(); two.destroy();
|
|
});
|
|
});
|
|
|
|
describe('uniformBlockAttr', () => {
|
|
it('agrees across blocks and blanks when they differ', () => {
|
|
const same = makeEditor(doc(para({ lineHeight: '1.5' }, run('a')), para({ lineHeight: '1.5' }, run('b'))));
|
|
same.commands.selectAll();
|
|
expect(uniformBlockAttr(same.state, 'lineHeight', '1')).toBe('1.5');
|
|
|
|
const differ = makeEditor(doc(para({ lineHeight: '1.5' }, run('a')), para({ lineHeight: '2' }, run('b'))));
|
|
differ.commands.selectAll();
|
|
expect(uniformBlockAttr(differ.state, 'lineHeight', '1')).toBe('');
|
|
same.destroy(); differ.destroy();
|
|
});
|
|
|
|
it('reports the fallback for a block that never sets the attribute', () => {
|
|
const editor = makeEditor(doc(para({}, run('a'))));
|
|
editor.commands.selectAll();
|
|
expect(uniformBlockAttr(editor.state, 'lineHeight', '1')).toBe('1');
|
|
editor.destroy();
|
|
});
|
|
});
|