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
+
{{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 = `
+
+
+ `;
+
+ // 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');
+});