From 913ad0c8dc7fbef485a0d1782ea3aeaa1e58b42a Mon Sep 17 00:00:00 2001 From: Tatiana Fomina Date: Thu, 1 Dec 2022 21:58:58 +0400 Subject: [PATCH] feat: Add toggle group support (#2195) * Add toggle group support * Update version and changelog * Fix * Simplify * Update test/cypress/tests/utils/popover.spec.ts Co-authored-by: Peter Savchenko Co-authored-by: Peter Savchenko --- docs/CHANGELOG.md | 3 + package.json | 2 +- src/components/utils/popover.ts | 46 +++++++++++++-- test/cypress/tests/utils/popover.spec.ts | 75 ++++++++++++++++++++++++ types/configs/popover.d.ts | 6 +- 5 files changed, 125 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 981cd59e..5ed2e932 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog +### 2.26.1 +- `Improvement` — *Menu Config* — Now it becomes possible to create toggle groups. + ### 2.26.0 - `New` — *UI* — Block Tunes became vertical just like the Toolbox 🤩 diff --git a/package.json b/package.json index 2fa60ef3..b6d4812e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@editorjs/editorjs", - "version": "2.26.0", + "version": "2.26.1", "description": "Editor.js — Native JS, based on API and Open Source", "main": "dist/editor.js", "types": "./types/index.d.ts", diff --git a/src/components/utils/popover.ts b/src/components/utils/popover.ts index 96c5e2be..ad051f9c 100644 --- a/src/components/utils/popover.ts +++ b/src/components/utils/popover.ts @@ -491,16 +491,54 @@ export default class Popover extends EventsDispatcher { } clickedItem.onActivate(clickedItem, event); - if (clickedItem.toggle) { - clickedItem.isActive = !clickedItem.isActive; - itemEl.classList.toggle(Popover.CSS.itemActive); - } + this.toggleIfNeeded(itemIndex, allItems); if (clickedItem.closeOnActivate) { this.hide(); } } + /** + * - Toggles item active state, if the item has property 'toggle' set to true. + * + * - Performs radiobutton-like behavior if the item has property 'toggle' set to string key. + * (All the other items with the same key get unactive, and the item gets active) + * + * @param index - clicked item index + * @param itemEls - array of html elements representing popover items + */ + private toggleIfNeeded(index: number, itemEls: Element[]): void { + const clickedItem = this.items[index]; + + if (clickedItem.toggle === true) { + clickedItem.isActive = !clickedItem.isActive; + itemEls[index].classList.toggle(Popover.CSS.itemActive); + + return; + } + + if (typeof clickedItem.toggle === 'string') { + const itemsInToggleGroup = this.items.filter(item => item.toggle === clickedItem.toggle); + + /** If there's only one item in toggle group, toggle it */ + if (itemsInToggleGroup.length === 1) { + clickedItem.isActive = !clickedItem.isActive; + itemEls[index].classList.toggle(Popover.CSS.itemActive); + + return; + } + + /** Set clicked item as active and the rest items with same toggle key value as inactive */ + itemsInToggleGroup.forEach((item: PopoverItem) => { + const i = this.items.indexOf(item); + const newState = item === clickedItem; + + item.isActive = newState; + itemEls[i].classList.toggle(Popover.CSS.itemActive, newState); + }); + } + } + /** * Enables confirmation state for specified item. * Replaces item element in popover so that is becomes highlighted in a special way diff --git a/test/cypress/tests/utils/popover.spec.ts b/test/cypress/tests/utils/popover.spec.ts index d0191da4..fd5a2bed 100644 --- a/test/cypress/tests/utils/popover.spec.ts +++ b/test/cypress/tests/utils/popover.spec.ts @@ -185,4 +185,79 @@ describe('Popover', () => { .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: PopoverItem[] = [ + { + icon: 'Icon 1', + label: 'Label 1', + toggle: 'group-name', + name: 'testItem1', + isActive: true, + onActivate: (): void => {}, + }, + { + icon: 'Icon 2', + label: 'Label 2', + toggle: 'group-name', + name: 'testItem2', + onActivate: (): void => {}, + }, + ]; + + const popover = new Popover({ + items, + filterLabel: '', + nothingFoundLabel: '', + scopeElement: null, + }); + + 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: PopoverItem[] = [ + { + icon: 'Icon', + label: 'Label', + toggle: 'key', + name: 'testItem', + onActivate: (): void => {}, + }, + ]; + const popover = new Popover({ + items, + filterLabel: '', + nothingFoundLabel: '', + scopeElement: null, + }); + + 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'); + }); + }); }); diff --git a/types/configs/popover.d.ts b/types/configs/popover.d.ts index b30ce452..a34fe44c 100644 --- a/types/configs/popover.d.ts +++ b/types/configs/popover.d.ts @@ -39,9 +39,11 @@ interface PopoverItemBase { name?: string; /** - * True if item should be highlighted once activated + * Defines whether item should toggle on click. + * Can be represented as boolean value or a string key. + * In case of string, works like radio buttons group and highlights as inactive any other item that has same toggle key value. */ - toggle?: boolean; + toggle?: boolean | string; } /**