editor.js/test/cypress/tests/utils/popover.cy.ts
Tatiana Fomina e1c70b4fb8
feat(popover): separator (#2690)
* 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
2024-04-22 22:38:20 +03:00

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');
});
});