Skip to content

Commit

Permalink
Rewrite LiveSearch as an ES Module
Browse files Browse the repository at this point in the history
We want to migrate all of our legacy Javascript
files to ESM, so this is the latest in the series
of conversions.

Rewrite follows the same conventions we previously
used for FocusBanner, Collapsible checkboxes,
ColourPreview, FileUpload, Autofocus,
PreviewPane and CopyToClipboard.

Added bonus: removes the use of jQuery as well
  • Loading branch information
kr8n3r committed Feb 6, 2025
1 parent e473e54 commit b2bd12f
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 88 deletions.
7 changes: 7 additions & 0 deletions app/assets/javascripts/esm/all-esm.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import Homepage from './homepage.mjs';
import PreviewPane from './preview-pane.mjs';
import CopyToClipboard from './copy-to-clipboard.mjs';

import LiveSearch from './live-search.mjs';

// Modules from 3rd party vendors
import morphdom from 'morphdom';

Expand All @@ -20,6 +22,11 @@ createAll(ErrorSummary);
createAll(SkipLink);
createAll(Tabs);

const $livesearch = document.querySelector('[data-notify-module="live-search"]');
if ($livesearch) {
new LiveSearch($livesearch);
}

const $collapsibleCheckboxes = document.querySelector('[data-notify-module="collapsible-checkboxes"]');
if ($collapsibleCheckboxes) {
new CollapsibleCheckboxes($collapsibleCheckboxes);
Expand Down
87 changes: 87 additions & 0 deletions app/assets/javascripts/esm/live-search.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { isSupported } from 'govuk-frontend';

class LiveSearch {
constructor($module) {
if (!isSupported()) {
return this;
}

this.$module = $module;

this.$searchBox = this.$module.querySelector('input');
this.$searchLabel = this.$module.querySelector('label');
this.$liveRegion = this.$module.querySelector('.live-search__status');
this.$targets = document.querySelectorAll(this.$module.dataset.targets);
this.state = 'loaded';


this.$searchBox.addEventListener("input", () => {
this.filter(this.$searchBox, this.$searchLabel, this.$liveRegion, this.$targets);
});

this.filter(this.$searchBox, this.$searchLabel, this.$liveRegion, this.$targets);

}

filter ($searchBox, $searchLabel, $liveRegion, $targets) {

let query = this.normalize(this.$searchBox.value);
let results = 0;

$targets.forEach((node) => {

let content = node.querySelector('.live-search-relevant') ? node.querySelector('.live-search-relevant').textContent : node.textContent;
let isMatch = this.normalize(content).includes(this.normalize(query));
// if there is a child node with checked state
if (node.querySelectorAll(':checked').length > 0) {
node.style.display = 'block';
results++;
return;
}

if (query == '') {
node.style.display = '';
results++;
return;
}

node.style.display = isMatch ? 'block': 'none';

if (isMatch) {
results++;
}

});

if (this.state === 'loaded') {
if (query !== '') {
$searchBox.setAttribute('aria-label', $searchLabel.textContent.trim() + ', ' + this.resultsSummary(results));
}
this.state = 'active';
} else {
$searchBox.removeAttribute('aria-label');
$liveRegion.textContent = this.resultsSummary(results);
}

// make sticky JS recalculate its cache of the element's position
// because live search can change the height document
if ('stickAtBottomWhenScrolling' in window.GOVUK) {
window.GOVUK.stickAtBottomWhenScrolling.recalculate();
}

}

normalize (string) {
return string.toLowerCase().replace(/ /g,'');
}

resultsSummary (num) {
if (num === 0) {
return "no results";
} else {
return num + (num === 1 ? " result" : " results");
}
};
}

export default LiveSearch;
88 changes: 0 additions & 88 deletions app/assets/javascripts/liveSearch.js

This file was deleted.

0 comments on commit b2bd12f

Please sign in to comment.