From 7a906666ddcb00e1e8fe8aa74cf0a527e7de9efb Mon Sep 17 00:00:00 2001 From: mricoul Date: Thu, 3 Oct 2024 11:08:55 +0200 Subject: [PATCH] feat (Dropdown): add new methods addItem, removeItem and removeAllItems --- examples/accessible-dropdown/index.html | 43 +++++++++++++++++++- src/classes/Dropdown.js | 52 +++++++++++++++++++++++++ src/classes/Dropdown.test.js | 23 +++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/examples/accessible-dropdown/index.html b/examples/accessible-dropdown/index.html index f7a6b80..54aa8bc 100644 --- a/examples/accessible-dropdown/index.html +++ b/examples/accessible-dropdown/index.html @@ -86,6 +86,24 @@

Dropdown only for < 1024px devices

  • Paint
  • + +

    Action buttons

    + + + + + +

    Code

    @@ -126,10 +144,31 @@

    Code

    }, '#dropdown-5': { mediaQuery: window.matchMedia('(max-width: 1024px)') - } + }, + '#dropdown-6': {}, } - + Dropdown.initFromPreset() + + const addBtn = document.getElementById('add') + const removeBtn = document.getElementById('remove') + const removeAllBtn = document.getElementById('remove-all') + const dropdownInstance = Dropdown.getInstance('#dropdown-6') + + addBtn.addEventListener('click', function() { + const listItem = document.createElement('li') + listItem.innerText = 'Dummy' + dropdownInstance.addItem(listItem) + }) + + removeBtn.addEventListener('click', function() { + dropdownInstance.removeItem(document.getElementById('dropdown-6').querySelectorAll('li')[0]) + }) + + removeAllBtn.addEventListener('click', function() { + dropdownInstance.removeAllItems() + }) + \ No newline at end of file diff --git a/src/classes/Dropdown.js b/src/classes/Dropdown.js index 1d813c5..cb59291 100644 --- a/src/classes/Dropdown.js +++ b/src/classes/Dropdown.js @@ -166,6 +166,58 @@ class Dropdown extends AbstractDomElement { return this } + /** + * Adds a new item to the list. + * + * @param {HTMLElement} listItem - The list item to be added. + * @returns {void} + */ + addItem(listItem) { + const el = this._element + listItem.role = 'option' + listItem.id = `${this.id}-item-${this.listItems.length + 1}` + listItem.addEventListener('click', this._handleListItemClick) + + if (this.listItems.length === 0) { + listItem.setAttribute('aria-selected', 'true') + } + + this.list.appendChild(listItem) + this.listItems = el.querySelectorAll('li') + this.updateFocusedListItem(el.querySelector('li[aria-selected="true"]')) + + if (this.button.hasAttribute('hidden')) { + this.button.removeAttribute('hidden') + } + } + + /** + * Removes a specific item from the list. + * + * @param {HTMLElement} listItem - The list item to be removed. + * @returns {void} + */ + removeItem(listItem) { + const el = this._element + listItem.remove() + this.listItems = el.querySelectorAll('li') + this.listItems[0].setAttribute('aria-selected', 'true') + this.updateFocusedListItem(el.querySelector('li[aria-selected="true"]')) + } + + /** + * Removes all items from the list. + * + * @returns {void} + */ + removeAllItems() { + this.listItems.forEach((listItem) => { + listItem.remove() + }) + this.listItems = this._element.querySelectorAll('li') + this.button.setAttribute('hidden', 'hidden') + } + /** * Check if media query matches * diff --git a/src/classes/Dropdown.test.js b/src/classes/Dropdown.test.js index e26da26..6ca6bf3 100644 --- a/src/classes/Dropdown.test.js +++ b/src/classes/Dropdown.test.js @@ -77,4 +77,27 @@ test.describe('Dropdown', () => { expect(display).toBe('none') }) + + test('Click on "Add item" button, expect there is a Dummy list item.', async ({ page }) => { + await page.click('#add') + + const lastItemText = await page.locator('#dropdown-6 li').last().textContent() + expect(lastItemText).toBe('Dummy') + }) + + test('Click on "Remove first item" button, expect the new first item is "Movies".', async ({ page }) => { + await page.click('#remove') + + const firstItemText = await page.locator('#dropdown-6 li').first().textContent() + expect(firstItemText).toBe('Movies') + }) + + test('Click on "Remove all items" button, expect there is not list items anymore.', async ({ page }) => { + await page.click('#remove-all') + + const isListItemsEmpty = await page + .locator('#dropdown-6 ul') + .evaluate((element) => element.textContent.trim() === '') + expect(isListItemsEmpty).toBe(true) + }) })