Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,14 @@ tiptap-editor::ng-deep .ProseMirror blockquote p:last-child {
margin-bottom: 0;
}

tiptap-editor::ng-deep .ProseMirror sup {
vertical-align: super;
}

tiptap-editor::ng-deep .ProseMirror sub {
vertical-align: sub;
}

tiptap-editor::ng-deep .ProseMirror pre {
font-size: 0.875rem;
line-height: 1.5;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,14 @@
<p-button
text
size="small"
(onClick)="editor().chain().focus().toggleSuperscript().run()"
(onClick)="toggleSuperscript()"
[class.is-active]="editor().isActive('superscript')">
<span class="material-icons bubble-menu-icon">superscript</span>
</p-button>
<p-button
text
size="small"
(onClick)="editor().chain().focus().toggleSubscript().run()"
(onClick)="toggleSubscript()"
[class.is-active]="editor().isActive('subscript')">
<span class="material-icons bubble-menu-icon">subscript</span>
</p-button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,122 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { TestBed } from '@angular/core/testing';

import { of } from 'rxjs';

import { Editor } from '@tiptap/core';
import { Document } from '@tiptap/extension-document';
import { Paragraph } from '@tiptap/extension-paragraph';
import { Subscript } from '@tiptap/extension-subscript';
import { Superscript } from '@tiptap/extension-superscript';
import { Text } from '@tiptap/extension-text';

import { DotAiService, DotContentTypeService, DotMessageService } from '@dotcms/data-access';

import { DotBubbleMenuComponent } from './dot-bubble-menu.component';

describe('DotBubbleMenuComponent', () => {
/**
* Builds a spy for `editor.chain()` that records the order methods are called on
* the fluent chain and returns the chain itself for each call (except `run`).
*/
function makeOrderedEditorChainSpy(editor: Editor) {
const callOrder: string[] = [];
const runSpy = jest.fn();

const chain: Record<string, jest.Mock> = {
focus: jest.fn(() => {
callOrder.push('focus');

return chain;
}),
unsetSubscript: jest.fn(() => {
callOrder.push('unsetSubscript');

return chain;
}),
unsetSuperscript: jest.fn(() => {
callOrder.push('unsetSuperscript');

return chain;
}),
toggleSuperscript: jest.fn(() => {
callOrder.push('toggleSuperscript');

return chain;
}),
toggleSubscript: jest.fn(() => {
callOrder.push('toggleSubscript');

return chain;
}),
run: runSpy
};

jest.spyOn(editor, 'chain').mockReturnValue(chain as never);

return { chain, callOrder, runSpy };
}

describe('DotBubbleMenuComponent - superscript/subscript mutual exclusion', () => {
let component: DotBubbleMenuComponent;
let fixture: ComponentFixture<DotBubbleMenuComponent>;
let editor: Editor;

beforeEach(async () => {
editor = new Editor({
extensions: [Document, Paragraph, Text, Superscript, Subscript],
content: '<p>Hello world</p>'
});

await TestBed.configureTestingModule({
declarations: [DotBubbleMenuComponent],
teardown: { destroyAfterEach: false }
}).compileComponents();
});
imports: [DotBubbleMenuComponent],
providers: [
{
provide: DotAiService,
useValue: { checkPluginInstallation: () => of(false) }
},
{ provide: DotContentTypeService, useValue: {} },
{
provide: DotMessageService,
useValue: { get: (key: string) => key }
}
],
schemas: [NO_ERRORS_SCHEMA]
})
.overrideComponent(DotBubbleMenuComponent, { set: { template: '<div></div>' } })
.compileComponents();

beforeEach(() => {
fixture = TestBed.createComponent(DotBubbleMenuComponent);
const fixture = TestBed.createComponent(DotBubbleMenuComponent);
component = fixture.componentInstance;
fixture.componentRef.setInput('editor', editor);
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
afterEach(() => {
editor.destroy();
});

describe('toggleSuperscript', () => {
it('should remove subscript before enabling superscript', () => {
const { chain, callOrder, runSpy } = makeOrderedEditorChainSpy(editor);

component['toggleSuperscript']();

expect(callOrder).toEqual(['focus', 'unsetSubscript', 'toggleSuperscript']);
expect(chain.unsetSubscript).toHaveBeenCalledTimes(1);
expect(chain.toggleSuperscript).toHaveBeenCalledTimes(1);
expect(runSpy).toHaveBeenCalledTimes(1);
});
});

describe('toggleSubscript', () => {
it('should remove superscript before enabling subscript', () => {
const { chain, callOrder, runSpy } = makeOrderedEditorChainSpy(editor);

component['toggleSubscript']();

expect(callOrder).toEqual(['focus', 'unsetSuperscript', 'toggleSubscript']);
expect(chain.unsetSuperscript).toHaveBeenCalledTimes(1);
expect(chain.toggleSubscript).toHaveBeenCalledTimes(1);
expect(runSpy).toHaveBeenCalledTimes(1);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,22 @@ export class DotBubbleMenuComponent implements OnInit {
this.imageTextAlign.set(resolvedAlign);
this.imageTextWrap.set(null);
}
/**
* Toggles superscript on the selected text, removing subscript first to
* ensure the two marks are mutually exclusive.
*/
protected toggleSuperscript() {
this.editor().chain().focus().unsetSubscript().toggleSuperscript().run();
}

/**
* Toggles subscript on the selected text, removing superscript first to
* ensure the two marks are mutually exclusive.
*/
protected toggleSubscript() {
this.editor().chain().focus().unsetSuperscript().toggleSubscript().run();
}

protected goToContentlet() {
// Validate selection exists before proceeding

Expand Down
Loading