From 7e322e4f7b85a60ec637b38ed22387ae484642ba Mon Sep 17 00:00:00 2001 From: Suzanne Aitchison Date: Thu, 3 Jun 2021 09:13:17 +0100 Subject: [PATCH] Optimise dropdowns for accessibility - Post comments and share (#13868) * WIP - basic init of comment dropdowns with open and close on click * WIP - initialize the share dropdown * initialize all post dropdowns within packs, init copy to clipboard announcer * refactor and add JSDocs to helper * undo changes to base jsx * update accessible name of post actions button in cypress test * make sure dropdowns pack loaded on comment index page * undo prettier changes in base jsx * undo prettier changes in base jsx * initialize comment dropdowns in podcasts * add test for the post actions * add article comment tests * add cypress tests for comment dropdowns --- app/assets/javascripts/initializePage.js | 3 +- .../initializers/initializeCommentDropdown.js | 175 --------------- .../initializeCommentsPage.js.erb | 1 - .../utilities/buildCommentHTML.js.erb | 4 +- app/javascript/packs/articlePage.jsx | 126 ++++++++--- app/javascript/packs/commentDropdowns.js | 71 ++++++ app/javascript/utilities/dropdownUtils.js | 206 ++++++++++++++++++ app/views/articles/_actions.html.erb | 8 +- app/views/articles/show.html.erb | 4 +- app/views/comments/_comment_header.html.erb | 4 +- app/views/comments/index.html.erb | 2 + app/views/podcast_episodes/show.html.erb | 1 + .../articleFlows/commentOnArticle.spec.js | 76 +++++++ .../articleFlows/postSidebarActions.spec.js | 56 ++++- 14 files changed, 509 insertions(+), 228 deletions(-) delete mode 100644 app/assets/javascripts/initializers/initializeCommentDropdown.js create mode 100644 app/javascript/packs/commentDropdowns.js create mode 100644 app/javascript/utilities/dropdownUtils.js diff --git a/app/assets/javascripts/initializePage.js b/app/assets/javascripts/initializePage.js index 17d8e4a2c..e934b64db 100644 --- a/app/assets/javascripts/initializePage.js +++ b/app/assets/javascripts/initializePage.js @@ -3,7 +3,7 @@ initializeAllChatButtons, initializeAllTagEditButtons, initializeUserFollowButts, initializeBaseTracking, initializeCommentsPage, initializeArticleDate, initializeArticleReactions, initNotifications, - initializeCommentDate, initializeCommentDropdown, initializeSettings, + initializeCommentDate, initializeSettings, initializeCommentPreview, initializeRuntimeBanner, initializeTimeFixer, initializeDashboardSort, initializePWAFunctionality, initializeEllipsisMenu, initializeArchivedPostFilter, initializeCreditsPage, @@ -24,7 +24,6 @@ function callInitializers() { initializeArticleReactions(); initNotifications(); initializeCommentDate(); - initializeCommentDropdown(); initializeSettings(); initializeCommentPreview(); initializeTimeFixer(); diff --git a/app/assets/javascripts/initializers/initializeCommentDropdown.js b/app/assets/javascripts/initializers/initializeCommentDropdown.js deleted file mode 100644 index ed4018bdd..000000000 --- a/app/assets/javascripts/initializers/initializeCommentDropdown.js +++ /dev/null @@ -1,175 +0,0 @@ -/* global Runtime */ - -function initializeCommentDropdown() { - const announcer = document.getElementById('article-copy-link-announcer'); - - function removeClass(className) { - return (element) => element.classList.remove(className); - } - - function getAllByClassName(className) { - return Array.from(document.getElementsByClassName(className)); - } - - function showAnnouncer() { - const { activeElement } = document; - const input = - activeElement.localName === 'clipboard-copy' - ? activeElement.getElementsByTagName('input')[0] - : document.getElementById('article-copy-link-input'); - input.focus(); - input.setSelectionRange(0, input.value.length); - announcer.hidden = false; - } - - function hideAnnouncer() { - if (announcer) { - announcer.hidden = true; - } - } - - function copyPermalink(event) { - event.preventDefault(); - const permalink = event.target.href; - - Runtime.copyToClipboard(permalink).then(() => { - // eslint-disable-next-line no-undef - addSnackbarItem({ message: 'Copied to clipboard' }); - }); - } - - function copyArticleLink() { - const inputValue = document.getElementById('article-copy-link-input').value; - Runtime.copyToClipboard(inputValue).then(() => { - showAnnouncer(); - }); - } - - function shouldCloseDropdown(event) { - var copyIcon = document.getElementById('article-copy-icon'); - var isCopyIconChild = copyIcon && copyIcon.contains(event.target); - return !( - event.target.matches('.dropdown-icon') || - event.target.parentElement.classList.contains('dropdown-icon') || - event.target.matches('.dropbtn') || - event.target.matches('clipboard-copy') || - isCopyIconChild || - event.target.parentElement.classList.contains('dropdown-link-row') - ); - } - - function removeClickListener() { - // disabling this rule because `removeEventListener` needs - // a reference to the specific handler. The function is hoisted. - // eslint-disable-next-line no-use-before-define - document.removeEventListener('click', outsideClickListener); - } - - function removeCopyListener() { - const clipboardCopyElement = document.getElementsByTagName( - 'clipboard-copy', - )[0]; - if (clipboardCopyElement) { - clipboardCopyElement.removeEventListener('click', copyArticleLink); - } - } - - function removeAllShowing() { - getAllByClassName('crayons-dropdown').forEach(removeClass('block')); - } - - function outsideClickListener(event) { - if (shouldCloseDropdown(event)) { - removeAllShowing(); - hideAnnouncer(); - removeClickListener(); - } - } - - function initializeDropDownClick(dropdownOrDropdownContainer) { - return (event) => { - const { target } = event; - const button = (function getButton(potentialButton) { - while ( - !potentialButton.classList.contains('dropbtn') || - !potentialButton - ) { - if (potentialButton === dropdownOrDropdownContainer) { - break; - } - - potentialButton = potentialButton.parentElement; - } - - return potentialButton; - })(target); - - const dropdownContent = button.parentElement.querySelector( - '.crayons-dropdown', - ); - - if (!dropdownContent) { - return; - } - - // Android native apps have enhanced sharing capabilities for Articles - const articleShowMoreClicked = button.id === 'article-show-more-button'; - if (articleShowMoreClicked && Runtime.isNativeAndroid('shareText')) { - AndroidBridge.shareText(location.href); - return; - } - - finalizeAbuseReportLink( - dropdownContent.getElementsByClassName('report-abuse-link-wrapper')[0], - ); - - if (dropdownContent.classList.contains('block')) { - dropdownContent.classList.remove('block'); - removeClickListener(); - removeCopyListener(); - hideAnnouncer(); - } else { - removeAllShowing(); - dropdownContent.classList.add('block'); - const clipboardCopyElement = document.getElementsByTagName( - 'clipboard-copy', - )[0]; - - document.addEventListener('click', outsideClickListener); - if (clipboardCopyElement) { - clipboardCopyElement.addEventListener('click', copyArticleLink); - } - } - }; - } - - function finalizeAbuseReportLink(reportAbuseLink) { - // Add actual link location (SEO doesn't like these "useless" links, so adding in here instead of in HTML) - if (!reportAbuseLink) { - return; - } - - reportAbuseLink.innerHTML = `Report abuse`; - } - - function copyPermalinkListener(copyPermalinkButton) { - copyPermalinkButton.addEventListener('click', copyPermalink); - } - - const commentsContainer = document.getElementById('comment-trees-container'); - const articleShowMoreButton = document.getElementById( - 'article-show-more-button', - ); - - // We only want to add an event listener for the click once - for (const element of [commentsContainer, articleShowMoreButton]) { - if (element && !element.dataset.initialized) { - element.dataset.initialized = true; - element.addEventListener('click', initializeDropDownClick(element)); - } - } - - setTimeout(function addListeners() { - getAllByClassName('permalink-copybtn').forEach(copyPermalinkListener); - }, 100); -} diff --git a/app/assets/javascripts/initializers/initializeCommentsPage.js.erb b/app/assets/javascripts/initializers/initializeCommentsPage.js.erb index abed749b6..66836e952 100644 --- a/app/assets/javascripts/initializers/initializeCommentsPage.js.erb +++ b/app/assets/javascripts/initializers/initializeCommentsPage.js.erb @@ -286,7 +286,6 @@ function handleCommentSubmit(event) { updateCommentsCount(); initializeCommentsPage(); initializeCommentDate(); - initializeCommentDropdown(); activateRunkitTags(); }) } else { diff --git a/app/assets/javascripts/utilities/buildCommentHTML.js.erb b/app/assets/javascripts/utilities/buildCommentHTML.js.erb index 5a241da28..59fbc01c7 100644 --- a/app/assets/javascripts/utilities/buildCommentHTML.js.erb +++ b/app/assets/javascripts/utilities/buildCommentHTML.js.erb @@ -60,10 +60,10 @@ function buildCommentHTML(comment) {
- -