Merge branch 'next' into feature/luis-remove-enter-svg-path

This commit is contained in:
Luis Sanguino 2023-12-17 18:54:57 -04:00 committed by GitHub
commit 5b3cafb127
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 397 additions and 153 deletions

View file

@ -11,6 +11,8 @@ jobs:
check-for-no-version-changing:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
permissions:
actions: write
steps:
# Checkout to target branch
- uses: actions/checkout@v2
@ -32,16 +34,22 @@ jobs:
uses: codex-team/action-nodejs-package-info@v1
# Stop workflow and do not bump version if it was changed already
- name: Stop workflow and do not bump version if it was changed already
uses: actions/github-script@v3
- name: Stop workflow if version was changed already
if: steps.packageOld.outputs.version != steps.packageNew.outputs.version
with:
script: |
core.setFailed('Version was changed! ${{ steps.packageOld.outputs.version }} -> ${{ steps.packageNew.outputs.version }}')
run: |
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/cancel
bump-version:
needs: check-for-no-version-changing
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
# Checkout to target branch
- uses: actions/checkout@v2

View file

@ -11,6 +11,8 @@ jobs:
check-version-changing:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- uses: actions/setup-node@v3
with:
@ -36,16 +38,21 @@ jobs:
# Stop workflow if version was not changed
- name: Stop workflow if version was not changed
uses: actions/github-script@v3
if: steps.packageOld.outputs.version == steps.packageNew.outputs.version
with:
script: |
core.setFailed('No version changes. ${{ steps.packageOld.outputs.version }}')
run: |
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/cancel
# Create a new draft release
release-draft:
needs: check-version-changing
runs-on: ubuntu-latest
permissions:
contents: write
steps:
# Checkout to target branch
- uses: actions/checkout@v2
@ -118,4 +125,4 @@ jobs:
webhook: ${{ secrets.CODEX_BOT_WEBHOOK_FRONTEND }}
message: '🦥 [Draft release v${{ steps.package.outputs.version }}](${{ steps.create_release.outputs.html_url }}) for package [${{ steps.package.outputs.name }}](${{ steps.package.outputs.npmjs-link }}) has been created. Add changelog and publish it!'
parse_mode: 'markdown'
disable_web_page_preview: true
disable_web_page_preview: true

View file

@ -18,4 +18,4 @@ jobs:
with:
config: video=false
browser: ${{ matrix.browser }}
build: yarn build
build: yarn build:test

View file

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
steps:
# Checkout to target branch
- uses: actions/checkout@v2
- uses: actions/checkout@v4
with:
# Pull submodules
submodules: 'recursive'
@ -46,9 +46,11 @@ jobs:
notify:
needs: publish
runs-on: ubuntu-latest
env:
GITHUB_LINK: ${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.ref_name }}
steps:
# Checkout to target branch
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Get package info
id: package
@ -58,6 +60,6 @@ jobs:
uses: codex-team/action-codexbot-notify@v1
with:
webhook: ${{ secrets.CODEX_BOT_NOTIFY_EDITORJS_PUBLIC_CHAT }}
message: '📦 [${{ steps.package.outputs.name }}](${{ steps.package.outputs.npmjs-link }}) ${{ steps.package.outputs.version }} was published'
message: '📦 [${{ steps.package.outputs.name }} ${{ steps.package.outputs.version }}](${{ env.GITHUB_LINK }}) was published'
parse_mode: 'markdown'
disable_web_page_preview: true
disable_web_page_preview: true

View file

@ -7,8 +7,11 @@
- `Fix` — Layout did not shrink when a large document cleared in Chrome
- `Fix` — Multiple Tooltip elements creation fixed
- `Fix` — When the focusing Block is out of the viewport, the page will be scrolled.
- `Fix` - Compiler error "This import is never used as a value and must use 'import type'..." fixed
- `Fix``blocks.render()` won't lead the `onChange` call in Safari
- `Fix` — Editor wrapper element growing on the Inline Toolbar close
- `Fix` — Fix errors thrown by clicks on a document when the editor is being initialized
- `Fix` — Inline Toolbar sometimes opened in an incorrect position. Now it will be aligned by the left side of the selected text. And won't overflow the right side of the text column.
### 2.28.2

View file

@ -69,7 +69,7 @@ Check [Editor.js's community](https://github.com/editor-js/) to see Tools exampl
## Create Editor instance
Create an instance of Editor.js and pass [Configuration Object](../src/types-internal/editor-config.ts).
At least the `holderId` option is required.
At least the `holder` option is required.
```html
<div id="editorjs"></div>
@ -92,7 +92,7 @@ var editor = new EditorJS({
/**
* Create a holder for the Editor and pass its ID
*/
holderId : 'editorjs',
holder : 'editorjs',
/**
* Available Tools list.

View file

@ -70,7 +70,7 @@ to the `tools` property of Editor Config.
```javascript
var editor = new EditorJS({
holderId : 'editorjs',
holder : 'editorjs',
tools: {
text: {
class: Text,

View file

@ -1,6 +1,6 @@
{
"name": "@editorjs/editorjs",
"version": "2.29.0-rc.5",
"version": "2.29.0-rc.7",
"description": "Editor.js — Native JS, based on API and Open Source",
"main": "dist/editorjs.umd.js",
"module": "dist/editorjs.mjs",
@ -14,7 +14,8 @@
],
"scripts": {
"dev": "vite",
"build": "vite build",
"build": "vite build --mode production",
"build:test": "vite build --mode test",
"lint": "eslint src/ --ext .ts && yarn lint:tests",
"lint:errors": "eslint src/ --ext .ts --quiet",
"lint:fix": "eslint src/ --ext .ts --fix",
@ -26,8 +27,8 @@
"_tools:build": "git submodule foreach yarn build",
"_tools:make": "yarn _tools:yarn && yarn _tools:build",
"tools:update": "yarn _tools:checkout && yarn _tools:pull && yarn _tools:make",
"test:e2e": "yarn build && cypress run",
"test:e2e:open": "yarn build && cypress open",
"test:e2e": "yarn build:test && cypress run",
"test:e2e:open": "yarn build:test && cypress open",
"devserver:start": "yarn build && node ./devserver.js"
},
"author": "CodeX",

View file

@ -744,7 +744,11 @@ export default class BlockManager extends Module {
* @param {Node} childNode - node to get Block by
* @returns {Block}
*/
public getBlockByChildNode(childNode: Node): Block {
public getBlockByChildNode(childNode: Node): Block | undefined {
if (!childNode || childNode instanceof Node === false) {
return undefined;
}
/**
* If node is Text TextNode
*/

View file

@ -187,6 +187,13 @@ export default class CrossBlockSelection extends Module {
private onMouseOver = (event: MouseEvent): void => {
const { BlockManager, BlockSelection } = this.Editor;
/**
* Probably, editor is not initialized yet
*/
if (event.relatedTarget === null && event.target === null) {
return;
}
const relatedBlock = BlockManager.getBlockByChildNode(event.relatedTarget as Node) || this.lastSelectedBlock;
const targetBlock = BlockManager.getBlockByChildNode(event.target as Node);

View file

@ -13,7 +13,10 @@ import Popover, { PopoverEvent } from '../../utils/popover';
* HTML Elements that used for BlockSettings
*/
interface BlockSettingsNodes {
wrapper: HTMLElement;
/**
* Block Settings wrapper. Undefined when before "make" method called
*/
wrapper: HTMLElement | undefined;
}
/**

View file

@ -32,12 +32,12 @@ import { BlockHovered } from '../../events/BlockHovered';
* HTML Elements used for Toolbar UI
*/
interface ToolbarNodes {
wrapper: HTMLElement;
content: HTMLElement;
actions: HTMLElement;
wrapper: HTMLElement | undefined;
content: HTMLElement | undefined;
actions: HTMLElement | undefined;
plusButton: HTMLElement;
settingsToggler: HTMLElement;
plusButton: HTMLElement | undefined;
settingsToggler: HTMLElement | undefined;
}
/**
*
@ -316,7 +316,7 @@ export default class Toolbar extends Module<ToolbarNodes> {
return;
}
this.nodes.wrapper.classList.remove(this.CSS.toolbarOpened);
this.nodes.wrapper?.classList.remove(this.CSS.toolbarOpened);
/** Close components */
this.blockActions.hide();

View file

@ -17,16 +17,16 @@ import { IconChevronDown } from '@codexteam/icons';
* Inline Toolbar elements
*/
interface InlineToolbarNodes {
wrapper: HTMLElement;
togglerAndButtonsWrapper: HTMLElement;
buttons: HTMLElement;
conversionToggler: HTMLElement;
conversionTogglerContent: HTMLElement;
wrapper: HTMLElement | undefined;
togglerAndButtonsWrapper: HTMLElement | undefined;
buttons: HTMLElement | undefined;
conversionToggler: HTMLElement | undefined;
conversionTogglerContent: HTMLElement | undefined;
/**
* Zone below the buttons where Tools can create additional actions by 'renderActions()' method
* For example, input for the 'link' tool or textarea for the 'comment' tool
*/
actions: HTMLElement;
actions: HTMLElement | undefined;
}
/**
@ -138,15 +138,16 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
* Avoid to use it just for closing IT, better call .close() clearly.
* @param [needToShowConversionToolbar] - pass false to not to show Conversion Toolbar
*/
public tryToShow(needToClose = false, needToShowConversionToolbar = true): void {
if (!this.allowedToShow()) {
if (needToClose) {
this.close();
}
public async tryToShow(needToClose = false, needToShowConversionToolbar = true): Promise<void> {
if (needToClose) {
this.close();
}
if (!this.allowedToShow()) {
return;
}
await this.addToolsFiltered(needToShowConversionToolbar);
this.move();
this.open(needToShowConversionToolbar);
this.Editor.Toolbar.close();
@ -187,57 +188,16 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
this.Editor.ConversionToolbar.close();
}
/**
* Shows Inline Toolbar
*
* @param [needToShowConversionToolbar] - pass false to not to show Conversion Toolbar
*/
public open(needToShowConversionToolbar = true): void {
if (this.opened) {
return;
}
/**
* Filter inline-tools and show only allowed by Block's Tool
*/
this.addToolsFiltered();
/**
* Show Inline Toolbar
*/
this.nodes.wrapper.classList.add(this.CSS.inlineToolbarShowed);
this.buttonsList = this.nodes.buttons.querySelectorAll(`.${this.CSS.inlineToolButton}`);
this.opened = true;
if (needToShowConversionToolbar && this.Editor.ConversionToolbar.hasTools()) {
/**
* Change Conversion Dropdown content for current tool
*/
this.setConversionTogglerContent();
} else {
/**
* hide Conversion Dropdown with there are no tools
*/
this.nodes.conversionToggler.hidden = true;
}
/**
* Get currently visible buttons to pass it to the Flipper
*/
let visibleTools = Array.from(this.buttonsList);
visibleTools.unshift(this.nodes.conversionToggler);
visibleTools = visibleTools.filter((tool) => !(tool as HTMLElement).hidden);
this.flipper.activate(visibleTools as HTMLElement[]);
}
/**
* Check if node is contained by Inline Toolbar
*
* @param {Node} node node to check
*/
public containsNode(node: Node): boolean {
if (this.nodes.wrapper === undefined) {
return false;
}
return this.nodes.wrapper.contains(node);
}
@ -264,6 +224,11 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
this.CSS.inlineToolbar,
...(this.isRtl ? [ this.Editor.UI.CSS.editorRtlFix ] : []),
]);
if (import.meta.env.MODE === 'test') {
this.nodes.wrapper.setAttribute('data-cy', 'inline-toolbar');
}
/**
* Creates a different wrapper for toggler and buttons.
*/
@ -323,6 +288,33 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
this.enableFlipper();
}
/**
* Shows Inline Toolbar
*/
private open(): void {
if (this.opened) {
return;
}
/**
* Show Inline Toolbar
*/
this.nodes.wrapper.classList.add(this.CSS.inlineToolbarShowed);
this.buttonsList = this.nodes.buttons.querySelectorAll(`.${this.CSS.inlineToolButton}`);
this.opened = true;
/**
* Get currently visible buttons to pass it to the Flipper
*/
let visibleTools = Array.from(this.buttonsList);
visibleTools.unshift(this.nodes.conversionToggler);
visibleTools = visibleTools.filter((tool) => !(tool as HTMLElement).hidden);
this.flipper.activate(visibleTools as HTMLElement[]);
}
/**
* Move Toolbar to the selected text
*/
@ -330,7 +322,7 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
const selectionRect = SelectionUtils.rect as DOMRect;
const wrapperOffset = this.Editor.UI.nodes.wrapper.getBoundingClientRect();
const newCoords = {
x: selectionRect.x - wrapperOffset.left,
x: selectionRect.x - wrapperOffset.x,
y: selectionRect.y +
selectionRect.height -
// + window.scrollY
@ -338,34 +330,15 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
this.toolbarVerticalMargin,
};
const realRightCoord = newCoords.x + this.width + wrapperOffset.x;
/**
* If we know selections width, place InlineToolbar to center
* Prevent InlineToolbar from overflowing the content zone on the right side
*/
if (selectionRect.width) {
newCoords.x += Math.floor(selectionRect.width / 2);
if (realRightCoord > this.Editor.UI.contentRect.right) {
newCoords.x = this.Editor.UI.contentRect.right - this.width - wrapperOffset.x;
}
/**
* Inline Toolbar has -50% translateX, so we need to check real coords to prevent overflowing
*/
const realLeftCoord = newCoords.x - this.width / 2;
const realRightCoord = newCoords.x + this.width / 2;
/**
* By default, Inline Toolbar has top-corner at the center
* We are adding a modifiers for to move corner to the left or right
*/
this.nodes.wrapper.classList.toggle(
this.CSS.inlineToolbarLeftOriented,
realLeftCoord < this.Editor.UI.contentRect.left
);
this.nodes.wrapper.classList.toggle(
this.CSS.inlineToolbarRightOriented,
realRightCoord > this.Editor.UI.contentRect.right
);
this.nodes.wrapper.style.left = Math.floor(newCoords.x) + 'px';
this.nodes.wrapper.style.top = Math.floor(newCoords.y) + 'px';
}
@ -525,8 +498,10 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
/**
* Append only allowed Tools
*
* @param {boolean} needToShowConversionToolbar - pass false to not to show Conversion Toolbar (e.g. for Footnotes-like tools)
*/
private addToolsFiltered(): void {
private async addToolsFiltered(needToShowConversionToolbar = true): Promise<void> {
const currentSelection = SelectionUtils.get();
const currentBlock = this.Editor.BlockManager.getBlock(currentSelection.anchorNode as HTMLElement);
@ -541,6 +516,18 @@ export default class InlineToolbar extends Module<InlineToolbarNodes> {
this.addTool(tool);
});
if (needToShowConversionToolbar && this.Editor.ConversionToolbar.hasTools()) {
/**
* Change Conversion Dropdown content for current tool
*/
await this.setConversionTogglerContent();
} else {
/**
* hide Conversion Dropdown with there are no tools
*/
this.nodes.conversionToggler.hidden = true;
}
/**
* Recalculate width because some buttons can be hidden
*/

View file

@ -150,12 +150,20 @@ export default class UI extends Module<UINodes> {
*/
if (!readOnlyEnabled) {
/**
* Unbind all events
* Postpone events binding to the next tick to make sure all ui elements are ready
*/
this.enableModuleBindings();
window.requestIdleCallback(() => {
/**
* Bind events for the UI elements
*/
this.enableModuleBindings();
}, {
timeout: 2000,
});
} else {
/**
* Bind events for the UI elements
* Unbind all events
*
*/
this.disableModuleBindings();
}
@ -342,9 +350,12 @@ export default class UI extends Module<UINodes> {
/**
* Handle selection change to manipulate Inline Toolbar appearance
*/
this.readOnlyMutableListeners.on(document, 'selectionchange', () => {
const selectionChangeDebounceTimeout = 180;
const selectionChangeDebounced = _.debounce(() => {
this.selectionChanged();
}, true);
}, selectionChangeDebounceTimeout);
this.readOnlyMutableListeners.on(document, 'selectionchange', selectionChangeDebounced, true);
this.readOnlyMutableListeners.on(window, 'resize', () => {
this.resizeDebouncer();
@ -633,8 +644,8 @@ export default class UI extends Module<UINodes> {
* But allow clicking inside Block Settings.
* Also, do not process clicks on the Block Settings Toggler, because it has own click listener
*/
const isClickedInsideBlockSettings = this.Editor.BlockSettings.nodes.wrapper.contains(target);
const isClickedInsideBlockSettingsToggler = this.Editor.Toolbar.nodes.settingsToggler.contains(target);
const isClickedInsideBlockSettings = this.Editor.BlockSettings.nodes.wrapper?.contains(target);
const isClickedInsideBlockSettingsToggler = this.Editor.Toolbar.nodes.settingsToggler?.contains(target);
const doNotProcess = isClickedInsideBlockSettings || isClickedInsideBlockSettingsToggler;
if (this.Editor.BlockSettings.opened && !doNotProcess) {
@ -852,9 +863,6 @@ export default class UI extends Module<UINodes> {
const isNeedToShowConversionToolbar = clickedOutsideBlockContent !== true;
/**
* @todo add debounce
*/
this.Editor.InlineToolbar.tryToShow(true, isNeedToShowConversionToolbar);
}
}

11
src/env.d.ts vendored Normal file
View file

@ -0,0 +1,11 @@
interface ImportMetaEnv {
/**
* Build environment.
* For example, used to detect building for tests and add "data-cy" attributes for DOM querying.
*/
readonly MODE: "test" | "development" | "production";
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}

View file

@ -2,11 +2,10 @@
--y-offset: 8px;
@apply --overlay-pane;
transform: translateX(-50%) translateY(8px) scale(0.94);
opacity: 0;
visibility: hidden;
transition: transform 150ms ease, opacity 250ms ease;
will-change: transform, opacity;
transition: opacity 250ms ease;
will-change: opacity, left, top;
top: 0;
left: 0;
z-index: 3;
@ -14,24 +13,6 @@
&--showed {
opacity: 1;
visibility: visible;
transform: translateX(-50%)
}
&--left-oriented {
transform: translateX(-23px) translateY(8px) scale(0.94);
}
&--left-oriented&--showed {
transform: translateX(-23px);
}
&--right-oriented {
transform: translateX(-100%) translateY(8px) scale(0.94);
margin-left: 23px;
}
&--right-oriented&--showed {
transform: translateX(-100%);
}
[hidden] {

View file

@ -155,3 +155,82 @@ Cypress.Commands.add('selectText', {
return cy.wrap(subject);
});
/**
* Select element's text by offset
* Note. Previous subject should have 'textNode' as firstChild
*
* Usage
* cy.get('[data-cy=editorjs]')
* .find('.ce-paragraph')
* .selectTextByOffset([0, 5])
*
* @param offset - offset to select
*/
Cypress.Commands.add('selectTextByOffset', {
prevSubject: true,
}, (subject, offset: [number, number]) => {
const el = subject[0];
const document = el.ownerDocument;
const range = document.createRange();
const textNode = el.firstChild;
const selectionPositionStart = offset[0];
const selectionPositionEnd = offset[1];
range.setStart(textNode, selectionPositionStart);
range.setEnd(textNode, selectionPositionEnd);
document.getSelection().removeAllRanges();
document.getSelection().addRange(range);
return cy.wrap(subject);
});
/**
* Returns line wrap positions for passed element
*
* Usage
* cy.get('[data-cy=editorjs]')
* .find('.ce-paragraph')
* .getLineWrapPositions()
*
* @returns number[] - array of line wrap positions
*/
Cypress.Commands.add('getLineWrapPositions', {
prevSubject: true,
}, (subject) => {
const element = subject[0];
const document = element.ownerDocument;
const text = element.textContent;
const lineWraps = [];
let currentLineY = 0;
/**
* Iterate all chars in text, create range for each char and get its position
*/
for (let i = 0; i < text.length; i++) {
const range = document.createRange();
range.setStart(element.firstChild, i);
range.setEnd(element.firstChild, i);
const rect = range.getBoundingClientRect();
if (i === 0) {
currentLineY = rect.top;
continue;
}
/**
* If current char Y position is higher than previously saved line Y, that means a line wrap
*/
if (rect.top > currentLineY) {
lineWraps.push(i);
currentLineY = rect.top;
}
}
return cy.wrap(lineWraps);
});

View file

@ -60,6 +60,31 @@ declare global {
* @param text - text to select
*/
selectText(text: string): Chainable<Subject>;
/**
* Select element's text by offset
* Note. Previous subject should have 'textNode' as firstChild
*
* Usage
* cy.get('[data-cy=editorjs]')
* .find('.ce-paragraph')
* .selectTextByOffset([0, 5])
*
* @param offset - offset to select
*/
selectTextByOffset(offset: [number, number]): Chainable<Subject>;
/**
* Returns line wrap positions for passed element
*
* Usage
* cy.get('[data-cy=editorjs]')
* .find('.ce-paragraph')
* .getLineWrapPositions()
*
* @returns number[] - array of line wrap positions
*/
getLineWrapPositions(): Chainable<number[]>;
}
interface ApplicationWindow {

View file

@ -17,6 +17,7 @@ describe('Inline Tool Link', () => {
.find('div.ce-block')
.click()
.type('{selectall}')
.wait(200)
.type('{ctrl}K');
cy.get('[data-cy=editorjs]')
@ -30,4 +31,44 @@ describe('Inline Tool Link', () => {
.find('a')
.should('have.attr', 'href', 'https://codex.so');
});
it('should remove fake background on selection change', () => {
cy.createEditor({
data: {
blocks: [
{
type: 'paragraph',
data: {
text: 'First block text',
},
},
{
type: 'paragraph',
data: {
text: 'Second block text',
},
},
],
},
});
cy.get('[data-cy=editorjs]')
.find('div.ce-block')
.first()
.click()
.type('{selectall}')
.wait(200)
.type('{ctrl}K');
cy.get('[data-cy=editorjs]')
.find('div.ce-block')
.last()
.click()
.type('{selectall}')
.wait(200);
cy.get('[data-cy=editorjs]')
.find('.ce-paragraph span[style]')
.should('not.exist');
});
});

View file

@ -0,0 +1,76 @@
describe('Inline Toolbar', () => {
it('should appear aligned with left coord of selection rect', () => {
cy.createEditor({
data: {
blocks: [
{
type: 'paragraph',
data: {
text: 'First block text',
},
},
],
},
});
cy.get('[data-cy=editorjs]')
.find('.ce-paragraph')
.selectText('block');
cy.get('[data-cy="inline-toolbar"]')
.should('be.visible')
.then(($toolbar) => {
const editorWindow = $toolbar.get(0).ownerDocument.defaultView;
const selection = editorWindow.getSelection();
const range = selection.getRangeAt(0);
const rect = range.getBoundingClientRect();
expect($toolbar.offset().left).to.closeTo(rect.left, 1);
});
});
it('should appear aligned with right side of text column when toolbar\'s width is not fit at right', () => {
cy.createEditor({
data: {
blocks: [
{
type: 'paragraph',
data: {
text: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor.',
},
},
],
},
});
cy.get('[data-cy=editorjs]')
.find('.ce-paragraph')
.as('blockWrapper')
.getLineWrapPositions()
.then((lineWrapIndexes) => {
const firstLineWrapIndex = lineWrapIndexes[0];
/**
* Select last 5 chars of the first line
*/
cy.get('[data-cy=editorjs]')
.find('.ce-paragraph')
.selectTextByOffset([firstLineWrapIndex - 5, firstLineWrapIndex - 1]);
});
cy.get('[data-cy="inline-toolbar"]')
.should('be.visible')
.then(($toolbar) => {
cy.get('@blockWrapper')
.then(($blockWrapper) => {
const blockWrapperRect = $blockWrapper.get(0).getBoundingClientRect();
/**
* Toolbar should be aligned with right side of text column
*/
expect($toolbar.offset().left + $toolbar.width()).to.closeTo(blockWrapperRect.right, 3);
});
});
});
});

View file

@ -64,7 +64,8 @@ describe('Flipper', () => {
cy.get('[data-cy=editorjs]')
.get('.cdx-some-plugin')
.focus()
.type(sampleText);
.type(sampleText)
.wait(100);
// Try to delete the block via keyboard
cy.get('[data-cy=editorjs]')

View file

@ -1,4 +1,4 @@
import { BlockAPI } from '../../api';
import type { BlockAPI } from '../../api';
/**
* Details of CustomEvent fired on block mutation

View file

@ -1,4 +1,4 @@
import { BlockMutationEventDetail } from './Base';
import type { BlockMutationEventDetail } from './Base';
/**
* Type name of CustomEvent related to block added event

View file

@ -1,4 +1,4 @@
import { BlockMutationEventDetail } from './Base';
import type { BlockMutationEventDetail } from './Base';
/**
* Type name of CustomEvent related to block changed event

View file

@ -1,4 +1,4 @@
import { BlockMutationEventDetail } from './Base';
import type { BlockMutationEventDetail } from './Base';
/**
* Type name of CustomEvent related to block moved event

View file

@ -1,4 +1,4 @@
import { BlockMutationEventDetail } from './Base';
import type { BlockMutationEventDetail } from './Base';
/**
* Type name of CustomEvent related to block removed event

View file

@ -1,7 +1,7 @@
import { BlockAddedEvent, BlockAddedMutationType } from './BlockAdded';
import { BlockChangedEvent, BlockChangedMutationType } from './BlockChanged';
import { BlockMovedEvent, BlockMovedMutationType } from './BlockMoved';
import { BlockRemovedEvent, BlockRemovedMutationType } from './BlockRemoved';
import { type BlockAddedEvent, BlockAddedMutationType } from './BlockAdded';
import { type BlockChangedEvent, BlockChangedMutationType } from './BlockChanged';
import { type BlockMovedEvent, BlockMovedMutationType } from './BlockMoved';
import { type BlockRemovedEvent, BlockRemovedMutationType } from './BlockRemoved';
/**
* Map for Custom Events related to block mutation types