From 9d843331aa7ee6fa66d330e3dd2196b0e7e3bafe Mon Sep 17 00:00:00 2001 From: Thomas Neumann Date: Fri, 15 May 2026 12:42:01 -0500 Subject: [PATCH 1/2] Make sure all component elements get an id --- js/components/dcf-autoplay-video-toggle.js | 7 ++++++ js/components/dcf-card-as-link.js | 11 +++++++++ js/components/dcf-collapsible-fieldset.js | 3 +++ js/components/dcf-datepicker.js | 12 ++++++++-- js/components/dcf-dialog.js | 2 +- js/components/dcf-file-size-validator.js | 7 +++--- js/components/dcf-gallery.js | 3 +++ js/components/dcf-pagination.js | 8 +++++++ js/components/dcf-popup.js | 26 ++++++++++------------ js/components/dcf-search-select.js | 8 ++++++- js/components/dcf-slideshow.js | 2 +- js/components/dcf-tab.js | 2 +- 12 files changed, 68 insertions(+), 23 deletions(-) diff --git a/js/components/dcf-autoplay-video-toggle.js b/js/components/dcf-autoplay-video-toggle.js index 0e33c8f7..15114cad 100644 --- a/js/components/dcf-autoplay-video-toggle.js +++ b/js/components/dcf-autoplay-video-toggle.js @@ -1,5 +1,9 @@ +import { uuidv4 } from '../dcf-utility.js'; + export default class DCFAutoplayVideoToggle { + uuid = uuidv4(); + autoplayVideoContainer = null; video = null; @@ -56,6 +60,9 @@ export default class DCFAutoplayVideoToggle { } this.autoplayVideoContainer = autoPlayVideoContainer; + if (this.autoplayVideoContainer.getAttribute('id') === '' || this.autoplayVideoContainer.getAttribute('id') === null) { + this.autoplayVideoContainer.setAttribute('id', this.uuid.concat('-autoplay-video')); + } if (this.videoContainerClassList) { this.autoplayVideoContainer.classList.add(...this.videoContainerClassList); diff --git a/js/components/dcf-card-as-link.js b/js/components/dcf-card-as-link.js index 79483e2f..e866289c 100644 --- a/js/components/dcf-card-as-link.js +++ b/js/components/dcf-card-as-link.js @@ -1,6 +1,10 @@ +import { uuidv4 } from '../dcf-utility.js'; + // Based on https://inclusive-components.design/cards/ // Using mousedown and mouseup to allow selecting text without trigger click export default class DCFCardAsLink { + uuid = uuidv4(); + card = null; link = null; @@ -16,7 +20,14 @@ export default class DCFCardAsLink { // Set up the Card as Link component constructor(card) { this.card = card; + if (this.card.getAttribute('id') === '' || this.card.getAttribute('id') === null) { + this.card.setAttribute('id', this.uuid.concat('-card-as-link')); + } + this.link = card.querySelector('.dcf-card-link'); + if (this.link.getAttribute('id') === '' || this.link.getAttribute('id') === null) { + this.link.setAttribute('id', this.uuid.concat('-card-as-link-link')); + } // Add event listeners only if a link is present in the card if (this.link) { diff --git a/js/components/dcf-collapsible-fieldset.js b/js/components/dcf-collapsible-fieldset.js index b3a1a134..1d8cad89 100644 --- a/js/components/dcf-collapsible-fieldset.js +++ b/js/components/dcf-collapsible-fieldset.js @@ -114,6 +114,9 @@ export default class DCFCollapsibleFieldsets { } this.fieldsetElement = fieldset; + if (this.fieldsetElement.getAttribute('id') === '' || this.fieldsetElement.getAttribute('id') === null) { + this.fieldsetElement.setAttribute('id', this.uuid.concat('-collapsible-fieldset')); + } // We want to put everything inside the fieldset into a div // That div is what will toggle and not the fieldset diff --git a/js/components/dcf-datepicker.js b/js/components/dcf-datepicker.js index bb9eeeef..c04e129f 100644 --- a/js/components/dcf-datepicker.js +++ b/js/components/dcf-datepicker.js @@ -1,6 +1,8 @@ import { magicNumbers, uuidv4 } from '../dcf-utility.js'; export default class DCFDatepicker { + uuid = uuidv4(); + constructor(datepicker) { // Define common magic numbers used this.int0 = magicNumbers('int0'); @@ -12,7 +14,10 @@ export default class DCFDatepicker { this.intMinus1 = magicNumbers('intMinus1'); this.datepicker = datepicker; - this.uuid = uuidv4(); + if (this.datepicker.getAttribute('id') === '' || this.datepicker.getAttribute('id') === null) { + this.datepicker.setAttribute('id', this.uuid.concat('-datepicker')); + } + this.buttonLabelChoose = 'Choose Date'; this.buttonLabelChange = 'Change Date'; this.dayLabels = [ @@ -49,6 +54,9 @@ export default class DCFDatepicker { this.isMouseDownOnBackground = false; this.textboxNode = this.datepicker.querySelector('input[type="text"]'); + if (this.textboxNode.getAttribute('id') === '' || this.textboxNode.getAttribute('id') === null) { + this.textboxNode.setAttribute('id', this.uuid.concat('-datepicker-text-box')); + } this.appendPickerContainer(); this.appendDialog(); @@ -90,7 +98,7 @@ export default class DCFDatepicker { const dialogGridID = this.uuid.concat('-dialog-grid'); this.dialogNode = document.createElement('div'); - this.dialogNode.setAttribute('id', this.uuid.concat('-datepicker')); + this.dialogNode.setAttribute('id', this.uuid.concat('-datepicker-dialog')); this.dialogNode.setAttribute('role', 'dialog'); this.dialogNode.setAttribute('aria-modal', 'true'); this.dialogNode.setAttribute('aria-labelledby', dialogLabelID); diff --git a/js/components/dcf-dialog.js b/js/components/dcf-dialog.js index 7e7c880c..739d3039 100644 --- a/js/components/dcf-dialog.js +++ b/js/components/dcf-dialog.js @@ -107,7 +107,7 @@ export default class DCFDialog { } this.heading = this.dialogHeaderElement.querySelector('h1, h2, h3, h4, h5, h6'); - if (this.heading.getAttribute('id') === '') { + if (this.heading.getAttribute('id') === '' || this.heading.getAttribute('id') === null) { this.heading.setAttribute('id', this.uuid.concat('-heading')); } this.dialogElement.setAttribute('aria-labelledby', this.heading.getAttribute('id')); diff --git a/js/components/dcf-file-size-validator.js b/js/components/dcf-file-size-validator.js index a87bf95c..a658e37c 100644 --- a/js/components/dcf-file-size-validator.js +++ b/js/components/dcf-file-size-validator.js @@ -20,11 +20,12 @@ export default class DCFFileSizeValidator { // Get the input element this.fileInputElement = fileInput; - - if (this.fileInputElement.getAttribute('id') !== null && this.fileInputElement.getAttribute('id') !== '') { - this.formattedSizeOutputs = document.querySelectorAll(`.dcf-file-size-validator-size[data-input="${this.fileInputElement.getAttribute('id')}"]`); + if (this.fileInputElement.getAttribute('id') === null) { + throw new Error('File Input element is missing ID'); } + this.formattedSizeOutputs = document.querySelectorAll(`.dcf-file-size-validator-size[data-input="${this.fileInputElement.getAttribute('id')}"]`); + // Get the max size limit this.sizeLimit = this.#parseSize(this.fileInputElement.dataset.maxSize); this.validationFailedClass = this.fileInputElement.dataset.validationFailedClass; diff --git a/js/components/dcf-gallery.js b/js/components/dcf-gallery.js index 8c352b06..05e81e18 100644 --- a/js/components/dcf-gallery.js +++ b/js/components/dcf-gallery.js @@ -18,6 +18,9 @@ export default class DCFGallery { this.dialog = sharedDialog; this.image = galleryImage; + if (this.image.getAttribute('id') === '' || this.image.getAttribute('id') === null) { + this.image.setAttribute('id', this.uuid.concat('-gallery-image')); + } this.image.addEventListener('click', () => { this.dialog.open(this.image); }); diff --git a/js/components/dcf-pagination.js b/js/components/dcf-pagination.js index 12e898e0..65be0f4e 100644 --- a/js/components/dcf-pagination.js +++ b/js/components/dcf-pagination.js @@ -1,4 +1,9 @@ +import { uuidv4 } from '../dcf-utility.js'; + export default class DCFPagination { + + uuid = uuidv4(); + paginationNav = null; list = null; @@ -9,6 +14,9 @@ export default class DCFPagination { constructor(paginationNav) { this.paginationNav = paginationNav; + if (this.paginationNav.getAttribute('id') === '' || this.paginationNav.getAttribute('id') === null) { + this.paginationNav.setAttribute('id', this.uuid.concat('-pagination')); + } this.paginationNav.setAttribute('role', 'navigation'); this.paginationNav.setAttribute('aria-label', 'Pagination Navigation'); diff --git a/js/components/dcf-popup.js b/js/components/dcf-popup.js index 831369c2..4613e07d 100644 --- a/js/components/dcf-popup.js +++ b/js/components/dcf-popup.js @@ -73,20 +73,6 @@ export default class DCFPopup { throw new Error('Popup Content Is Missing'); } - // We need do do some funky stuff to get the correct close button and not the nested one - this.closeButtons = this.popupElement.querySelectorAll( - ':scope > .dcf-popup-content > .dcf-btn-close-popup' + - ', :scope > .dcf-popup-content > .dcf-btn-popup-close' + - `, :scope > .dcf-popup-content .dcf-btn-close-popup[data-for="${this.popupElement.getAttribute('id')}"]` + - `, :scope > .dcf-popup-content .dcf-btn-popup-close[data-for="${this.popupElement.getAttribute('id')}"]`, - ); - - this.closeButtons.forEach((closeButton) => { - if (closeButton !== null && closeButton.tagName !== 'BUTTON') { - throw new Error('Close Button is Not a Button Tag'); - } - }); - // Sets the IDs for the btn and content if they aren't already set if ( this.popupElement.getAttribute('id') === '' || @@ -129,8 +115,20 @@ export default class DCFPopup { this.toggleButtonObj = new DCFButtonToggles(this.popupButton); + + // We need do do some funky stuff to get the correct close button and not the nested one + this.closeButtons = this.popupElement.querySelectorAll( + ':scope > .dcf-popup-content > .dcf-btn-close-popup' + + ', :scope > .dcf-popup-content > .dcf-btn-popup-close' + + `, :scope > .dcf-popup-content .dcf-btn-close-popup[data-for="${this.popupElement.getAttribute('id')}"]` + + `, :scope > .dcf-popup-content .dcf-btn-popup-close[data-for="${this.popupElement.getAttribute('id')}"]`, + ); + // if there is a close button and its clicked close the popup this.closeButtons.forEach((closeButton) => { + if (closeButton.tagName !== 'BUTTON') { + throw new Error('Close Button is Not a Button Tag'); + } closeButton.addEventListener('click', () => { this.popupButton.dispatchEvent(this.commandClose); }); diff --git a/js/components/dcf-search-select.js b/js/components/dcf-search-select.js index d9711d68..8cf533e0 100644 --- a/js/components/dcf-search-select.js +++ b/js/components/dcf-search-select.js @@ -261,13 +261,19 @@ aria-hidden="true" } this.selectElement = selectElement; + if ( + this.selectElement.getAttribute('id') === '' || + this.selectElement.getAttribute('id') === null + ) { + this.selectElement.setAttribute('id', this.uuid.concat('-search-and-select-select')); + } if (this.selectElement.getAttribute('multiple') !== null) { this.multiple = true; } // These are the IDs that will be used for the whole component - this.selectID = this.selectElement.getAttribute('id') || this.uuid.concat('-search-and-select-select'); + this.selectID = this.selectElement.getAttribute('id'); this.searchAndSelectID = this.uuid.concat('-search-and-select'); this.inputID = this.uuid.concat('-search-and-select-input'); this.availableItemsListID = this.uuid.concat('-search-and-select-available-items-list'); diff --git a/js/components/dcf-slideshow.js b/js/components/dcf-slideshow.js index 321eb836..8a7d80a9 100644 --- a/js/components/dcf-slideshow.js +++ b/js/components/dcf-slideshow.js @@ -189,7 +189,7 @@ width="24" height="24" viewBox="0 0 24 24" focusable="false" aria-hidden="true"> this.slideshowContainer.classList.add(...this.slideContainerClassList); // If the tabGroup has no ID then it will set it - if (this.slideshowContainer.getAttribute('id') === null) { + if (this.slideshowContainer.getAttribute('id') === '' || this.slideshowContainer.getAttribute('id') === null) { this.slideshowContainer.setAttribute('id', this.uuid.concat('-slideshow')); } diff --git a/js/components/dcf-tab.js b/js/components/dcf-tab.js index 2aad44f8..ff3f5732 100644 --- a/js/components/dcf-tab.js +++ b/js/components/dcf-tab.js @@ -33,7 +33,7 @@ export default class DCFTabs { this.tabsPanelList = Array.from(this.tabsGroup.querySelectorAll('.dcf-tabs > div:not(:empty), .dcf-tabs > section:not(:empty)')); // If the tabGroup has no ID then it will set it - if (this.tabsGroup.getAttribute('id') === null) { + if (this.tabsGroup.getAttribute('id') === '' || this.tabsGroup.getAttribute('id') === null) { this.tabsGroup.setAttribute('id', checkSetElementId(this.tabsGroup, this.uuid.concat('-tab-group'))); } From 65a89ea3c6df4500680bd91ed7c77e14212feea6 Mon Sep 17 00:00:00 2001 From: Thomas Neumann Date: Fri, 22 May 2026 08:48:21 -0500 Subject: [PATCH 2/2] Add more ids to more components --- js/components/dcf-dialog.js | 5 ++++- js/components/dcf-slideshow.js | 2 +- js/components/dcf-tab.js | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/js/components/dcf-dialog.js b/js/components/dcf-dialog.js index 739d3039..aff4541d 100644 --- a/js/components/dcf-dialog.js +++ b/js/components/dcf-dialog.js @@ -131,7 +131,10 @@ export default class DCFDialog { this.toggleButtons = Array.from(document.querySelectorAll(`.dcf-btn-toggle-dialog[data-controls='${this.dialogElement.getAttribute('id')}']`)); - this.toggleButtons.forEach((singleToggleButton) => { + this.toggleButtons.forEach((singleToggleButton, index) => { + if (singleToggleButton.getAttribute('id') === '' || singleToggleButton.getAttribute('id') === null) { + singleToggleButton.setAttribute('id', this.uuid.concat(`-dialog-toggle-button-${index}`)); + } if (this.dialogElement.classList.contains('dcf-dialog-non-modal')) { singleToggleButton.setAttribute('aria-expanded', false); } else { diff --git a/js/components/dcf-slideshow.js b/js/components/dcf-slideshow.js index 8a7d80a9..76601b65 100644 --- a/js/components/dcf-slideshow.js +++ b/js/components/dcf-slideshow.js @@ -198,7 +198,7 @@ width="24" height="24" viewBox="0 0 24 24" focusable="false" aria-hidden="true"> this.slideDeck.setAttribute('tabindex', '0'); this.slideDeck.classList.add('dcf-slide-deck'); this.slideDeck.setAttribute('aria-live', 'polite'); - if (this.slideDeck.getAttribute('id') === null) { + if (this.slideDeck.getAttribute('id') === '' || this.slideDeck.getAttribute('id') === null) { this.slideDeck.setAttribute('id', this.uuid.concat('-slide-deck')); } this.slideDeck.classList.add(...this.slideDeckClassList); diff --git a/js/components/dcf-tab.js b/js/components/dcf-tab.js index ff3f5732..4f886a7b 100644 --- a/js/components/dcf-tab.js +++ b/js/components/dcf-tab.js @@ -359,7 +359,7 @@ export default class DCFTabs { this.switchToFirstTab(); }, true); this.tabsGroup.addEventListener(DCFTabs.events('commandEnd'), () => { - this.switchToEndTab(); + this.switchToLastTab(); }, true); }