diff --git a/.changelog/25378.txt b/.changelog/25378.txt new file mode 100644 index 000000000..6de1b693e --- /dev/null +++ b/.changelog/25378.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: Makes jobs list filtering case-insensitive +``` diff --git a/ui/app/controllers/jobs/index.js b/ui/app/controllers/jobs/index.js index b13be7e7f..c643ff260 100644 --- a/ui/app/controllers/jobs/index.js +++ b/ui/app/controllers/jobs/index.js @@ -653,7 +653,7 @@ export default class JobsIndexController extends Controller { this.searchText = newFilter; } else { // If it's a string without a filter operator, assume the user is trying to look up a job name - this.searchText = `Name contains "${newFilter}"`; + this.searchText = `Name matches "(?i)${newFilter}"`; } } diff --git a/ui/mirage/config.js b/ui/mirage/config.js index b304ae237..f2aed8dec 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -231,6 +231,12 @@ export default function () { job[condition.field] && job[condition.field].includes(condition.value) ); + } else if (condition.operator === 'matches') { + // strip the (?i) bit out of the value; used for case-insensitive matching + // but JS doesn't support PCRE-style regex modifiers the way our backend does, + // so strip 'em out here. + const value = condition.value.replace('(?i)', ''); + return new RegExp(value, 'i').test(job[condition.field]); } else if (condition.operator === '==') { return job[condition.field] === condition.value; } else if (condition.operator === '!=') { diff --git a/ui/tests/acceptance/jobs-list-test.js b/ui/tests/acceptance/jobs-list-test.js index 56f488d15..3a9ceee49 100644 --- a/ui/tests/acceptance/jobs-list-test.js +++ b/ui/tests/acceptance/jobs-list-test.js @@ -188,7 +188,7 @@ module('Acceptance | jobs list', function (hooks) { assert.equal( currentURL(), - '/jobs?filter=Name%20contains%20%22foobar%22', + '/jobs?filter=Name%20matches%20%22(%3Fi)foobar%22', 'No page query param' ); }); @@ -1271,7 +1271,7 @@ module('Acceptance | jobs list', function (hooks) { assert.ok( server.pretender.handledRequests.find((req) => decodeURIComponent(req.url).includes( - '?filter=Name contains "something-that-surely-doesnt-exist"' + '?filter=Name matches "(?i)something-that-surely-doesnt-exist"' ) ), 'A request was made with a filter query param that assumed job name' @@ -1386,6 +1386,28 @@ module('Acceptance | jobs list', function (hooks) { localStorage.removeItem('nomadPageSize'); }); + test('Searching by name filters the list case-insensitively', async function (assert) { + localStorage.setItem('nomadPageSize', '10'); + createJobs(server, 10); + server.create('job', { + name: 'hashi-one', + id: 'hashi-one', + modifyIndex: 0, + }); + server.create('job', { + name: 'Hashi-two', + id: 'hashi-two', + modifyIndex: 0, + }); + + await JobsList.visit(); + + await JobsList.search.fillIn('Hashi'); + assert.dom('.job-row').exists({ count: 2 }); + assert.dom('[data-test-job-row="hashi-one"]').exists(); + assert.dom('[data-test-job-row="hashi-two"]').exists(); + }); + test('Searching by type filters the list', async function (assert) { localStorage.setItem('nomadPageSize', '10'); server.createList('job', 10, {