mirror of
https://github.com/codex-team/editor.js
synced 2024-05-19 06:47:16 +02:00
e1c70b4fb8
* Support delimiter * Rename types, move types to popover-item folder * Fix ts errors * Add tests * Review fixes * Review fixes 2 * Fix delimiter while search * Fix flipper issue * Fix block tunes types * Fix types * Fixes * Make search input emit event * Fix types * Rename delimiter to separator * Update chengelog
772 lines
19 KiB
TypeScript
772 lines
19 KiB
TypeScript
import { PopoverDesktop as Popover } from '../../../../src/components/utils/popover';
|
|
import { PopoverItemParams } from '../../../../types';
|
|
import { TunesMenuConfig } from '../../../../types/tools';
|
|
|
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
|
|
describe('Popover', () => {
|
|
it('should support confirmation chains', () => {
|
|
const actionIcon = 'Icon 1';
|
|
const actionTitle = 'Action';
|
|
const confirmActionIcon = 'Icon 2';
|
|
const confirmActionTitle = 'Confirm action';
|
|
|
|
/**
|
|
* Confirmation is moved to separate variable to be able to test it's callback execution.
|
|
* (Inside popover null value is set to confirmation property, so, object becomes unavailable otherwise)
|
|
*/
|
|
const confirmation: PopoverItemParams = {
|
|
type: 'default',
|
|
icon: confirmActionIcon,
|
|
title: confirmActionTitle,
|
|
onActivate: cy.stub(),
|
|
};
|
|
|
|
const items: PopoverItemParams[] = [
|
|
{
|
|
type: 'default',
|
|
icon: actionIcon,
|
|
title: actionTitle,
|
|
name: 'testItem',
|
|
confirmation,
|
|
},
|
|
];
|
|
|
|
const popover = new Popover({
|
|
items,
|
|
});
|
|
|
|
cy.document().then(doc => {
|
|
doc.body.append(popover.getElement());
|
|
|
|
cy.get('[data-item-name=testItem]')
|
|
.get('.ce-popover-item__icon')
|
|
.should('have.text', actionIcon);
|
|
|
|
cy.get('[data-item-name=testItem]')
|
|
.get('.ce-popover-item__title')
|
|
.should('have.text', actionTitle);
|
|
|
|
// First click on item
|
|
cy.get('[data-item-name=testItem]').click();
|
|
|
|
// Check icon has changed
|
|
cy.get('[data-item-name=testItem]')
|
|
.get('.ce-popover-item__icon')
|
|
.should('have.text', confirmActionIcon);
|
|
|
|
// Check label has changed
|
|
cy.get('[data-item-name=testItem]')
|
|
.get('.ce-popover-item__title')
|
|
.should('have.text', confirmActionTitle);
|
|
|
|
// Second click
|
|
cy.get('[data-item-name=testItem]')
|
|
.click()
|
|
.then(() => {
|
|
// Check onActivate callback has been called
|
|
expect(confirmation.onActivate).to.have.been.calledOnce;
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should render the items with true isActive property value as active', () => {
|
|
const items = [
|
|
{
|
|
type: 'default',
|
|
icon: 'Icon',
|
|
title: 'Title',
|
|
isActive: true,
|
|
name: 'testItem',
|
|
onActivate: (): void => {},
|
|
},
|
|
];
|
|
|
|
const popover = new Popover({
|
|
items,
|
|
});
|
|
|
|
cy.document().then(doc => {
|
|
doc.body.append(popover.getElement());
|
|
|
|
/* Check item has active class */
|
|
cy.get('[data-item-name=testItem]')
|
|
.should('have.class', 'ce-popover-item--active');
|
|
});
|
|
});
|
|
|
|
it('should not execute item\'s onActivate callback if the item is disabled', () => {
|
|
const items: PopoverItemParams[] = [
|
|
{
|
|
type: 'default',
|
|
icon: 'Icon',
|
|
title: 'Title',
|
|
isDisabled: true,
|
|
name: 'testItem',
|
|
onActivate: cy.stub(),
|
|
},
|
|
];
|
|
|
|
const popover = new Popover({
|
|
items,
|
|
});
|
|
|
|
cy.document().then(doc => {
|
|
doc.body.append(popover.getElement());
|
|
|
|
/* Check item has disabled class */
|
|
cy.get('[data-item-name=testItem]')
|
|
.should('have.class', 'ce-popover-item--disabled')
|
|
.click()
|
|
.then(() => {
|
|
if (items[0].type !== 'default') {
|
|
return;
|
|
}
|
|
// Check onActivate callback has never been called
|
|
expect(items[0].onActivate).to.have.not.been.called;
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should close once item with closeOnActivate property set to true is activated', () => {
|
|
const items = [
|
|
{
|
|
type: 'default',
|
|
icon: 'Icon',
|
|
title: 'Title',
|
|
closeOnActivate: true,
|
|
name: 'testItem',
|
|
onActivate: (): void => {},
|
|
},
|
|
];
|
|
const popover = new Popover({
|
|
items,
|
|
});
|
|
|
|
cy.spy(popover, 'hide');
|
|
|
|
cy.document().then(doc => {
|
|
doc.body.append(popover.getElement());
|
|
|
|
cy.get('[data-item-name=testItem]')
|
|
.click()
|
|
.then(() => {
|
|
expect(popover.hide).to.have.been.called;
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should highlight as active the item with toggle property set to true once activated', () => {
|
|
const items = [
|
|
{
|
|
type: 'default',
|
|
icon: 'Icon',
|
|
title: 'Title',
|
|
toggle: true,
|
|
name: 'testItem',
|
|
onActivate: (): void => {},
|
|
},
|
|
];
|
|
const popover = new Popover({
|
|
items,
|
|
});
|
|
|
|
cy.document().then(doc => {
|
|
doc.body.append(popover.getElement());
|
|
|
|
/* Check item has active class */
|
|
cy.get('[data-item-name=testItem]')
|
|
.click()
|
|
.should('have.class', 'ce-popover-item--active');
|
|
});
|
|
});
|
|
|
|
it('should perform radiobutton-like behavior among the items that have toggle property value set to the same string value', () => {
|
|
const items = [
|
|
{
|
|
type: 'default',
|
|
icon: 'Icon 1',
|
|
title: 'Title 1',
|
|
toggle: 'group-name',
|
|
name: 'testItem1',
|
|
isActive: true,
|
|
onActivate: (): void => {},
|
|
},
|
|
{
|
|
type: 'default',
|
|
icon: 'Icon 2',
|
|
title: 'Title 2',
|
|
toggle: 'group-name',
|
|
name: 'testItem2',
|
|
onActivate: (): void => {},
|
|
},
|
|
];
|
|
|
|
const popover = new Popover({
|
|
items,
|
|
});
|
|
|
|
cy.document().then(doc => {
|
|
doc.body.append(popover.getElement());
|
|
|
|
/** Check first item is active */
|
|
cy.get('[data-item-name=testItem1]')
|
|
.should('have.class', 'ce-popover-item--active');
|
|
|
|
/** Check second item is not active */
|
|
cy.get('[data-item-name=testItem2]')
|
|
.should('not.have.class', 'ce-popover-item--active');
|
|
|
|
/* Click second item and check it became active */
|
|
cy.get('[data-item-name=testItem2]')
|
|
.click()
|
|
.should('have.class', 'ce-popover-item--active');
|
|
|
|
/** Check first item became not active */
|
|
cy.get('[data-item-name=testItem1]')
|
|
.should('not.have.class', 'ce-popover-item--active');
|
|
});
|
|
});
|
|
|
|
it('should toggle item if it is the only item in toggle group', () => {
|
|
const items = [
|
|
{
|
|
type: 'default',
|
|
icon: 'Icon',
|
|
title: 'Title',
|
|
toggle: 'key',
|
|
name: 'testItem',
|
|
onActivate: (): void => {},
|
|
},
|
|
];
|
|
const popover = new Popover({
|
|
items,
|
|
});
|
|
|
|
cy.document().then(doc => {
|
|
doc.body.append(popover.getElement());
|
|
|
|
/* Check item has active class */
|
|
cy.get('[data-item-name=testItem]')
|
|
.click()
|
|
.should('have.class', 'ce-popover-item--active');
|
|
});
|
|
});
|
|
|
|
it('should render custom html content', () => {
|
|
const customHtml = document.createElement('div');
|
|
|
|
customHtml.setAttribute('data-cy-name', 'customContent');
|
|
customHtml.innerText = 'custom html content';
|
|
const popover = new Popover({
|
|
customContent: customHtml,
|
|
items: [],
|
|
});
|
|
|
|
cy.document().then(doc => {
|
|
doc.body.append(popover.getElement());
|
|
|
|
/* Check custom content exists in the popover */
|
|
cy.get('[data-cy-name=customContent]');
|
|
});
|
|
});
|
|
|
|
it('should display nested popover (desktop)', () => {
|
|
/** Tool class to test how it is displayed inside block tunes popover */
|
|
class TestTune {
|
|
public static isTune = true;
|
|
|
|
/** Tool data displayed in block tunes popover */
|
|
public render(): TunesMenuConfig {
|
|
return {
|
|
type: 'default',
|
|
icon: 'Icon',
|
|
title: 'Title',
|
|
toggle: 'key',
|
|
name: 'test-item',
|
|
children: {
|
|
items: [
|
|
{
|
|
type: 'default',
|
|
icon: 'Icon',
|
|
title: 'Title',
|
|
name: 'nested-test-item',
|
|
},
|
|
],
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
/** Create editor instance */
|
|
cy.createEditor({
|
|
tools: {
|
|
testTool: TestTune,
|
|
},
|
|
tunes: [ 'testTool' ],
|
|
data: {
|
|
blocks: [
|
|
{
|
|
type: 'paragraph',
|
|
data: {
|
|
text: 'Hello',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
/** Open block tunes menu */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.cdx-block')
|
|
.click();
|
|
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-toolbar__settings-btn')
|
|
.click();
|
|
|
|
/** Check item with children has arrow icon */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('[data-item-name="test-item"]')
|
|
.get('.ce-popover-item__icon--chevron-right')
|
|
.should('be.visible');
|
|
|
|
/** Click the item */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('[data-item-name="test-item"]')
|
|
.click();
|
|
|
|
/** Check nested popover opened */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover--nested .ce-popover__container')
|
|
.should('be.visible');
|
|
|
|
/** Check child item displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover--nested .ce-popover__container')
|
|
.get('[data-item-name="nested-test-item"]')
|
|
.should('be.visible');
|
|
});
|
|
|
|
|
|
it('should display children items, back button and item header and correctly switch between parent and child states (mobile)', () => {
|
|
/** Tool class to test how it is displayed inside block tunes popover */
|
|
class TestTune {
|
|
public static isTune = true;
|
|
|
|
/** Tool data displayed in block tunes popover */
|
|
public render(): TunesMenuConfig {
|
|
return {
|
|
type: 'default',
|
|
icon: 'Icon',
|
|
title: 'Tune',
|
|
toggle: 'key',
|
|
name: 'test-item',
|
|
children: {
|
|
items: [
|
|
{
|
|
type: 'default',
|
|
icon: 'Icon',
|
|
title: 'Title',
|
|
name: 'nested-test-item',
|
|
},
|
|
],
|
|
},
|
|
};
|
|
}
|
|
}
|
|
|
|
cy.viewport('iphone-6+');
|
|
|
|
|
|
/** Create editor instance */
|
|
cy.createEditor({
|
|
tools: {
|
|
testTool: TestTune,
|
|
},
|
|
tunes: [ 'testTool' ],
|
|
data: {
|
|
blocks: [
|
|
{
|
|
type: 'paragraph',
|
|
data: {
|
|
text: 'Hello',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
/** Open block tunes menu */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.cdx-block')
|
|
.click();
|
|
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-toolbar__settings-btn')
|
|
.click();
|
|
|
|
/** Check item with children has arrow icon */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('[data-item-name="test-item"]')
|
|
.get('.ce-popover-item__icon--chevron-right')
|
|
.should('be.visible');
|
|
|
|
/** Click the item */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('[data-item-name="test-item"]')
|
|
.click();
|
|
|
|
/** Check child item displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="nested-test-item"]')
|
|
.should('be.visible');
|
|
|
|
/** Check header displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover-header')
|
|
.should('have.text', 'Tune');
|
|
|
|
/** Check back button displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('.ce-popover-header__back-button')
|
|
.should('be.visible');
|
|
|
|
/** Click back button */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('.ce-popover-header__back-button')
|
|
.click();
|
|
|
|
/** Check child item is not displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="nested-test-item"]')
|
|
.should('not.exist');
|
|
|
|
/** Check back button is not displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('.ce-popover-header__back-button')
|
|
.should('not.exist');
|
|
|
|
/** Check header is not displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover-header')
|
|
.should('not.exist');
|
|
});
|
|
|
|
|
|
it('should display default (non-separator) items without specifying type: default', () => {
|
|
/** Tool class to test how it is displayed inside block tunes popover */
|
|
class TestTune {
|
|
public static isTune = true;
|
|
|
|
/** Tool data displayed in block tunes popover */
|
|
public render(): TunesMenuConfig {
|
|
return {
|
|
// @ts-expect-error type is not specified on purpose to test the back compatibility
|
|
onActivate: (): void => {},
|
|
icon: 'Icon',
|
|
title: 'Tune',
|
|
toggle: 'key',
|
|
name: 'test-item',
|
|
};
|
|
}
|
|
}
|
|
|
|
|
|
/** Create editor instance */
|
|
cy.createEditor({
|
|
tools: {
|
|
testTool: TestTune,
|
|
},
|
|
tunes: [ 'testTool' ],
|
|
data: {
|
|
blocks: [
|
|
{
|
|
type: 'paragraph',
|
|
data: {
|
|
text: 'Hello',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
/** Open block tunes menu */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.cdx-block')
|
|
.click();
|
|
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-toolbar__settings-btn')
|
|
.click();
|
|
|
|
/** Check item displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="test-item"]')
|
|
.should('be.visible');
|
|
});
|
|
|
|
it('should display separator', () => {
|
|
/** Tool class to test how it is displayed inside block tunes popover */
|
|
class TestTune {
|
|
public static isTune = true;
|
|
|
|
/** Tool data displayed in block tunes popover */
|
|
public render(): TunesMenuConfig {
|
|
return [
|
|
{
|
|
type: 'default',
|
|
onActivate: (): void => {},
|
|
icon: 'Icon',
|
|
title: 'Tune',
|
|
toggle: 'key',
|
|
name: 'test-item',
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
];
|
|
}
|
|
}
|
|
|
|
|
|
/** Create editor instance */
|
|
cy.createEditor({
|
|
tools: {
|
|
testTool: TestTune,
|
|
},
|
|
tunes: [ 'testTool' ],
|
|
data: {
|
|
blocks: [
|
|
{
|
|
type: 'paragraph',
|
|
data: {
|
|
text: 'Hello',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
/** Open block tunes menu */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.cdx-block')
|
|
.click();
|
|
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-toolbar__settings-btn')
|
|
.click();
|
|
|
|
/** Check item displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="test-item"]')
|
|
.should('be.visible');
|
|
|
|
/** Check separator displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('.ce-popover-item-separator')
|
|
.should('be.visible');
|
|
});
|
|
|
|
it('should perform keyboard navigation between items ignoring separators', () => {
|
|
/** Tool class to test how it is displayed inside block tunes popover */
|
|
class TestTune {
|
|
public static isTune = true;
|
|
|
|
/** Tool data displayed in block tunes popover */
|
|
public render(): TunesMenuConfig {
|
|
return [
|
|
{
|
|
type: 'default',
|
|
onActivate: (): void => {},
|
|
icon: 'Icon',
|
|
title: 'Tune 1',
|
|
name: 'test-item-1',
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
type: 'default',
|
|
onActivate: (): void => {},
|
|
icon: 'Icon',
|
|
title: 'Tune 2',
|
|
name: 'test-item-2',
|
|
},
|
|
];
|
|
}
|
|
}
|
|
|
|
/** Create editor instance */
|
|
cy.createEditor({
|
|
tools: {
|
|
testTool: TestTune,
|
|
},
|
|
tunes: [ 'testTool' ],
|
|
data: {
|
|
blocks: [
|
|
{
|
|
type: 'paragraph',
|
|
data: {
|
|
text: 'Hello',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
/** Open block tunes menu */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.cdx-block')
|
|
.click();
|
|
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-toolbar__settings-btn')
|
|
.click();
|
|
|
|
/** Press Tab */
|
|
cy.tab();
|
|
|
|
/** Check first item is focused */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="test-item-1"].ce-popover-item--focused')
|
|
.should('exist');
|
|
|
|
/** Check second item is not focused */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="test-item-2"].ce-popover-item--focused')
|
|
.should('not.exist');
|
|
|
|
/** Press Tab */
|
|
cy.tab();
|
|
|
|
/** Check first item is not focused */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="test-item-1"].ce-popover-item--focused')
|
|
.should('not.exist');
|
|
|
|
/** Check second item is focused */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="test-item-2"].ce-popover-item--focused')
|
|
.should('exist');
|
|
});
|
|
|
|
it('should perform keyboard navigation between items ignoring separators when search query is applied', () => {
|
|
/** Tool class to test how it is displayed inside block tunes popover */
|
|
class TestTune {
|
|
public static isTune = true;
|
|
|
|
/** Tool data displayed in block tunes popover */
|
|
public render(): TunesMenuConfig {
|
|
return [
|
|
{
|
|
type: 'default',
|
|
onActivate: (): void => {},
|
|
icon: 'Icon',
|
|
title: 'Tune 1',
|
|
name: 'test-item-1',
|
|
},
|
|
{
|
|
type: 'separator',
|
|
},
|
|
{
|
|
type: 'default',
|
|
onActivate: (): void => {},
|
|
icon: 'Icon',
|
|
title: 'Tune 2',
|
|
name: 'test-item-2',
|
|
},
|
|
];
|
|
}
|
|
}
|
|
|
|
/** Create editor instance */
|
|
cy.createEditor({
|
|
tools: {
|
|
testTool: TestTune,
|
|
},
|
|
tunes: [ 'testTool' ],
|
|
data: {
|
|
blocks: [
|
|
{
|
|
type: 'paragraph',
|
|
data: {
|
|
text: 'Hello',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
/** Open block tunes menu */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.cdx-block')
|
|
.click();
|
|
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-toolbar__settings-btn')
|
|
.click();
|
|
|
|
/** Check separator displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('.ce-popover-item-separator')
|
|
.should('be.visible');
|
|
|
|
/** Enter search query */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('[data-cy=block-tunes] .cdx-search-field__input')
|
|
.type('Tune');
|
|
|
|
/** Check separator not displayed */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('.ce-popover-item-separator')
|
|
.should('not.be.visible');
|
|
|
|
/** Press Tab */
|
|
// eslint-disable-next-line cypress/require-data-selectors -- cy.tab() not working here
|
|
cy.get('body').tab();
|
|
|
|
/** Check first item is focused */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="test-item-1"].ce-popover-item--focused')
|
|
.should('exist');
|
|
|
|
/** Check second item is not focused */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="test-item-2"].ce-popover-item--focused')
|
|
.should('not.exist');
|
|
|
|
/** Press Tab */
|
|
// eslint-disable-next-line cypress/require-data-selectors -- cy.tab() not working here
|
|
cy.get('body').tab();
|
|
|
|
/** Check first item is not focused */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="test-item-1"].ce-popover-item--focused')
|
|
.should('not.exist');
|
|
|
|
/** Check second item is focused */
|
|
cy.get('[data-cy=editorjs]')
|
|
.get('.ce-popover__container')
|
|
.get('[data-item-name="test-item-2"].ce-popover-item--focused')
|
|
.should('exist');
|
|
});
|
|
});
|