From 9c72c9bd62db18856fc457624d9b518d438be899 Mon Sep 17 00:00:00 2001 From: Jonathan Bobel Date: Wed, 21 Aug 2024 11:33:18 -0400 Subject: [PATCH] 1871 - Adding search functionality to the Activity landing page in the Dashboard --- app/assets/javascripts/searchActivity.js | 53 +++++ .../uswds/_uswds-theme-custom-styles.scss | 14 ++ .../views/activity/all-activity.html | 17 +- gulpfile.js | 3 +- tests/javascripts/searchActivity.test.js | 189 ++++++++++++++++++ 5 files changed, 273 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/searchActivity.js create mode 100644 tests/javascripts/searchActivity.test.js diff --git a/app/assets/javascripts/searchActivity.js b/app/assets/javascripts/searchActivity.js new file mode 100644 index 000000000..cedffdbfd --- /dev/null +++ b/app/assets/javascripts/searchActivity.js @@ -0,0 +1,53 @@ +document.addEventListener('DOMContentLoaded', function() { + const searchInput = document.getElementById('activity-search'); + const tableRows = document.querySelectorAll('.job-table tbody .table-row'); + const tableBody = document.querySelector('.job-table tbody'); + const liveRegion = document.querySelector('.usa-table__announcement-region'); + + function filterTable() { + const filter = searchInput.value.toLowerCase(); + let visibleRowCount = 0; + + tableRows.forEach(row => { + const rowText = row.textContent.toLowerCase(); + + if (rowText.includes(filter)) { + row.style.display = 'table-row'; // Show the row + visibleRowCount++; + } else { + row.style.display = 'none'; // Hide the row + } + }); + + // Update ARIA live region + liveRegion.textContent = `${visibleRowCount} result${visibleRowCount !== 1 ? 's' : ''} found`; + + // Check if there are no visible rows + if (visibleRowCount === 0) { + let noResultsRow = document.querySelector('.no-results'); + if (!noResultsRow) { + const newRow = document.createElement('tr'); + newRow.classList.add('no-results'); + newRow.innerHTML = ` + No results found + `; + tableBody.appendChild(newRow); + } + } else { + const noResultsRow = document.querySelector('.no-results'); + if (noResultsRow) { + noResultsRow.remove(); + } + } + } + + // Listen for input events to catch when the "X" button is used + searchInput.addEventListener('input', function() { + filterTable(); + }); + + // Also handle the keyup event for other input changes + searchInput.addEventListener('keyup', function() { + filterTable(); + }); +}); diff --git a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss index 61cd543ef..ad1201c4f 100644 --- a/app/assets/sass/uswds/_uswds-theme-custom-styles.scss +++ b/app/assets/sass/uswds/_uswds-theme-custom-styles.scss @@ -444,6 +444,20 @@ td.table-empty-message { } } +.usa-search .usa-button { + img { + margin-left: 0; + } +} + +.table-filter { + flex-direction: column; + input { + margin-top: units(1); + border-right: 1px solid color('gray-cool-60'); + } +} + .usage-table { ul { list-style: none; diff --git a/app/templates/views/activity/all-activity.html b/app/templates/views/activity/all-activity.html index bd0843aa4..b7a992989 100644 --- a/app/templates/views/activity/all-activity.html +++ b/app/templates/views/activity/all-activity.html @@ -64,9 +64,20 @@

All activity

All activity

Sent jobs

+
+ +
- +
All activity table
@@ -125,7 +136,9 @@ {% endif %}
-
+
+ +

Note: Report data is only available for 7 days after your message has been sent

{{show_pagination}} diff --git a/gulpfile.js b/gulpfile.js index d4d6ac81f..e59d2a028 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -79,6 +79,7 @@ const javascripts = () => { paths.src + 'javascripts/main.js', paths.src + 'javascripts/totalMessagesChart.js', paths.src + 'javascripts/activityChart.js', + paths.src + 'javascripts/searchActivity.js', ]) .pipe(plugins.prettyerror()) .pipe(plugins.babel({ @@ -97,7 +98,7 @@ const copyGtmHead = () => { .pipe(dest(paths.dist + 'js/')); }; -// Task to copy images +// Task to copy imag const copyImages = () => { return src(paths.src + 'images/**/*', { encoding: false }) .pipe(dest(paths.dist + 'images/')); diff --git a/tests/javascripts/searchActivity.test.js b/tests/javascripts/searchActivity.test.js new file mode 100644 index 000000000..55189372e --- /dev/null +++ b/tests/javascripts/searchActivity.test.js @@ -0,0 +1,189 @@ +// Ensure your filterTable function is defined and accessible in your test environment +beforeEach(() => { + document.body.innerHTML = ` +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Job ID#TemplateTime sentSenderReportDeliveredFailed
Job 1Template 12024-08-20 10:00Sender 1File Download Icon102
Job 2Template 22024-08-20 11:00Sender 2File Download Icon50
Job 3Template 32024-08-20 12:00Sender 3File Download Icon01
+
+ +
+
+ `; + + // Attach the filterTable function to the window object for testing + window.filterTable = function() { + const searchInput = document.getElementById('all-activity-search'); + const tableRows = document.querySelectorAll('.job-table tbody .table-row'); + const tableBody = document.querySelector('.job-table tbody'); + const liveRegion = document.querySelector('.usa-table__announcement-region'); + + const filter = searchInput.value.toLowerCase(); + let visibleRowCount = 0; + + tableRows.forEach(row => { + const rowText = row.textContent.toLowerCase(); + + if (rowText.includes(filter)) { + row.style.display = 'table-row'; // Show the row + visibleRowCount++; + } else { + row.style.display = 'none'; // Hide the row + } + }); + + // Update ARIA live region + liveRegion.textContent = `${visibleRowCount} result${visibleRowCount !== 1 ? 's' : ''} found`; + + // Check if there are no visible rows + if (visibleRowCount === 0) { + let noResultsRow = document.querySelector('.no-results'); + if (!noResultsRow) { + const newRow = document.createElement('tr'); + newRow.classList.add('no-results'); + newRow.innerHTML = ` + No results found + `; + tableBody.appendChild(newRow); + } + } else { + const noResultsRow = document.querySelector('.no-results'); + if (noResultsRow) { + noResultsRow.remove(); + } + } + }; +}); + +test('filterTable function filters rows based on input', () => { + const searchInput = document.getElementById('all-activity-search'); + searchInput.value = 'Job 2'; + + // Call the filterTable function directly + window.filterTable(); + + const tableRows = document.querySelectorAll('.job-table tbody .table-row'); + expect(getComputedStyle(tableRows[0]).display).toBe('none'); + expect(getComputedStyle(tableRows[1]).display).toBe('table-row'); + expect(getComputedStyle(tableRows[2]).display).toBe('none'); + + const liveRegion = document.querySelector('.usa-table__announcement-region'); + expect(liveRegion.textContent).toBe('1 result found'); +}); + +test('filterTable function shows "No results found" when no rows match', () => { + const searchInput = document.getElementById('all-activity-search'); + searchInput.value = 'Non-existent Job'; + + // Call the filterTable function directly + window.filterTable(); + + const tableRows = document.querySelectorAll('.job-table tbody .table-row'); + tableRows.forEach(row => { + expect(getComputedStyle(row).display).toBe('none'); + }); + + const noResultsRow = document.querySelector('.no-results'); + expect(noResultsRow).not.toBeNull(); + expect(noResultsRow.textContent.trim()).toBe('No results found'); + + const liveRegion = document.querySelector('.usa-table__announcement-region'); + expect(liveRegion.textContent).toBe('0 results found'); +}); + +test('filterTable function filters rows only after 2 characters are entered', () => { + const searchInput = document.getElementById('all-activity-search'); + const tableRows = document.querySelectorAll('.job-table tbody .table-row'); + + // Set input value to 1 character and run filterTable + searchInput.value = 'J'; + window.filterTable(); + + // Expect all rows to still be visible + tableRows.forEach(row => { + expect(getComputedStyle(row).display).toBe('table-row'); + }); + + // Set input value to 2 characters and run filterTable + searchInput.value = 'Jo'; + window.filterTable(); + + // Expect filtering to take place + expect(getComputedStyle(tableRows[0]).display).toBe('table-row'); + expect(getComputedStyle(tableRows[1]).display).toBe('table-row'); + expect(getComputedStyle(tableRows[2]).display).toBe('table-row'); + + // Set input value to filter down to one row + searchInput.value = 'Job 2'; + window.filterTable(); + + // Expect only the second row to be visible + expect(getComputedStyle(tableRows[0]).display).toBe('none'); + expect(getComputedStyle(tableRows[1]).display).toBe('table-row'); + expect(getComputedStyle(tableRows[2]).display).toBe('none'); +}); + +test('filterTable function shows "No results found" when no rows match after 2 characters', () => { + const searchInput = document.getElementById('all-activity-search'); + searchInput.value = 'Non-existent Job'; + + // Call the filterTable function directly + window.filterTable(); + + const tableRows = document.querySelectorAll('.job-table tbody .table-row'); + tableRows.forEach(row => { + expect(getComputedStyle(row).display).toBe('none'); + }); + + const noResultsRow = document.querySelector('.no-results'); + expect(noResultsRow).not.toBeNull(); + expect(noResultsRow.textContent.trim()).toBe('No results found'); + + const liveRegion = document.querySelector('.usa-table__announcement-region'); + expect(liveRegion.textContent).toBe('0 results found'); +});