From 8481d21e383b1b89d9a69cecf9734c34c52f93fc Mon Sep 17 00:00:00 2001 From: bblaisATcoveo Date: Tue, 6 May 2025 09:08:12 -0400 Subject: [PATCH 01/13] Notification trigger implementation --- src/connector.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/connector.js b/src/connector.js index 0b52807..01bd7b4 100644 --- a/src/connector.js +++ b/src/connector.js @@ -9,6 +9,7 @@ import { buildDidYouMean, buildContext, buildInteractiveResult, + buildNotifyTrigger, loadAdvancedSearchQueryActions, loadSortCriteriaActions, HighlightUtils, @@ -54,6 +55,7 @@ let querySummaryController; let didYouMeanController; let pagerController; let statusController; +let notifyTriggerController; let urlManager; let unsubscribeManager; let unsubscribeSearchBoxController; @@ -61,12 +63,14 @@ let unsubscribeResultListController; let unsubscribeQuerySummaryController; let unsubscribeDidYouMeanController; let unsubscribePagerController; +let unsubscribeNotifyTriggerController; // UI states let updateSearchBoxFromState = false; let searchBoxState; let resultListState; let querySummaryState; +let notificationState; let didYouMeanState; let pagerState; let lastCharKeyUp; @@ -83,6 +87,7 @@ let formElement = document.querySelector( '#gc-searchbox, form[action="#wb-land" let resultsSection = document.querySelector( '#wb-land' ); let resultListElement = document.querySelector( '#result-list' ); let querySummaryElement = document.querySelector( '#query-summary' ); +let notificationTriggerElement = document.querySelector( '#notification-trigger' ); let pagerElement = document.querySelector( '#pager' ); let suggestionsElement = document.querySelector( '#suggestions' ); let didYouMeanElement = document.querySelector( '#did-you-mean' ); @@ -91,6 +96,7 @@ let didYouMeanElement = document.querySelector( '#did-you-mean' ); let resultTemplateHTML = document.getElementById( 'sr-single' )?.innerHTML; let noResultTemplateHTML = document.getElementById( 'sr-nores' )?.innerHTML; let resultErrorTemplateHTML = document.getElementById( 'sr-error' )?.innerHTML; +let notificationTriggerTemplateHTML = document.getElementById( 'sr-notification-trigger' )?.innerHTML; let querySummaryTemplateHTML = document.getElementById( 'sr-query-summary' )?.innerHTML; let didYouMeanTemplateHTML = document.getElementById( 'sr-did-you-mean' )?.innerHTML; let noQuerySummaryTemplateHTML = document.getElementById( 'sr-noquery-summary' )?.innerHTML; @@ -230,6 +236,23 @@ function initTpl() { } } + if ( !notificationTriggerTemplateHTML ) { + if ( lang === "fr" ) { + notificationTriggerTemplateHTML = + `
+

Notification

+

%[notification]

+
`; + } + else { + notificationTriggerTemplateHTML = + `
+

Notification

+

%[notification]

+
`; + } + } + if ( !querySummaryTemplateHTML ) { if ( lang === "fr" ) { querySummaryTemplateHTML = @@ -323,6 +346,14 @@ function initTpl() { baseElement.prepend( resultsSection ); } + // auto-create notification trigger element + if ( !notificationTriggerElement ) { + notificationTriggerElement = document.createElement( "div" ); + notificationTriggerElement.id = "notification-trigger"; + + resultsSection.append( notificationTriggerElement ); + } + // auto-create query summary element if ( !querySummaryElement ) { querySummaryElement = document.createElement( "div" ); @@ -462,6 +493,7 @@ function initEngine() { didYouMeanController = buildDidYouMean( headlessEngine, { options: { automaticallyCorrectQuery: false } } ); pagerController = buildPager( headlessEngine, { options: { numberOfPages: 9 } } ); statusController = buildSearchStatus( headlessEngine ); + notifyTriggerController = buildNotifyTrigger( headlessEngine ); if ( urlParams.allq || urlParams.exctq || urlParams.anyq || urlParams.noneq || urlParams.fqupdate || urlParams.dmn || urlParams.fqocct || urlParams.elctn_cat || urlParams.filetype || urlParams.site || urlParams.year ) { @@ -667,6 +699,7 @@ function initEngine() { unsubscribeQuerySummaryController = querySummaryController.subscribe( () => updateQuerySummaryState( querySummaryController.state ) ); unsubscribeDidYouMeanController = didYouMeanController.subscribe( () => updateDidYouMeanState( didYouMeanController.state ) ); unsubscribePagerController = pagerController.subscribe( () => updatePagerState( pagerController.state ) ); + unsubscribeNotifyTriggerController = notifyTriggerController.subscribe( () => updateNotifyTriggerState( notifyTriggerController.state ) ); // Clear event tracking, for legacy browsers const onUnload = () => { @@ -677,6 +710,7 @@ function initEngine() { unsubscribeQuerySummaryController?.(); unsubscribeDidYouMeanController?.(); unsubscribePagerController?.(); + unsubscribeNotifyTriggerController?.(); }; // Listen to URL change (hash) @@ -1028,6 +1062,18 @@ function updateResultListState( newState ) { } } +// Update notification displayed +function updateNotifyTriggerState ( newState ) { + notificationState = newState; + + if ( notificationState.notifications?.length ) { + notificationTriggerElement.innerHTML = notificationTriggerTemplateHTML.replace( "%[notification]", notificationState.notifications[0] ); + } + else { + notificationTriggerElement.textContent = ""; + } +} + // Update heading that has number of results displayed function updateQuerySummaryState( newState ) { querySummaryState = newState; From 74b2be4c7c7a7a2ed476c70cb1d6af23a602b38e Mon Sep 17 00:00:00 2001 From: Francis Gorman Date: Tue, 20 May 2025 14:03:49 -0400 Subject: [PATCH 02/13] Misc: Add token page to test search pages in session (#39) Patch - No impact on Distribution files --- README.md | 10 +++++++++- index.html | 1 + test/assets/token.js | 32 ++++++++++++++++++++++++++++++++ test/budget.html | 1 + test/election.html | 1 + test/gazette.html | 1 + test/index.html | 24 ++++++++++++++++++++++++ test/qs-en.html | 1 + test/qs-fr.html | 1 + test/sra-en.html | 1 + test/sra-fr.html | 1 + test/srb-en.html | 1 + test/srb-fr.html | 1 + test/src-en.html | 1 + test/src-fr.html | 1 + test/template.html | 1 + 16 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 test/assets/token.js create mode 100644 test/index.html diff --git a/README.md b/README.md index 54f220d..b1d4dea 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Search UI follows [Semantic Versioning 2.0.0](https://semver.org/) ## Getting started -This rubric is for developers. +This rubric is for developers and testers. ### Build files for release or to test code quality before opening a Pull request @@ -73,6 +73,14 @@ Since you need a token to communicate with the Coveo API, you can do the followi #### Testing through GitHub Pages +##### Main website + +At all time, you can visit the GitHub website to see the GC Search UI with the latest Pull requests merged at play: https://servicecanada.github.io/search-ui/ + +##### In your fork + +This is to test your changes usually before opening a Pull request. + 1. Add the required token on HTML pages would like to test by [following the instructions on Alternative to the API key by getting a token](#alternative-to-the-api-key-by-getting-a-token). Do not use the `token.yml` approach documented for testing locally, since it may generate potential a [security risk](SECURITY.md) in the context of GitHub pages. 2. Push your code to a branch of your choice in your origin remote (fork). It is recommended that you use a dedicated branch for testing, one that you would never open a Pull request from. 3. Make sure your repository has GitHub Pages enabled, on that specific above-mentioned branch. diff --git a/index.html b/index.html index 2b0227c..9f80f7e 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,7 @@

This is a demo site for the GC Search UI.

Test pages

+

To test search pages, please make sure you have a valid token.

Regular pages

Advanced tests

diff --git a/test/no-qs-en.html b/test/no-qs-en.html new file mode 100644 index 0000000..d8adea8 --- /dev/null +++ b/test/no-qs-en.html @@ -0,0 +1,46 @@ +--- +title: Search page without Query Suggestions (QS) +description: Demo page without the QS feature in the Canada.ca Search UI Basic +lang: en +altLangPage: no-qs-fr.html +nositesearch: true +pageclass: page-type-search +pageType: recherche +share: false +deptfeature: false +dateModified: 2025-05-21 +css: "../src/connector.css" +script: +- src: "assets/token.js" +- src: "../src/connector.js" + type: module +--- + + + + + +
+

Perform an advanced search

+ + +

Expected output for the result section

+
+ Output for Results section + [To be completed, see Connector.js for reference until then] +
diff --git a/test/no-qs-fr.html b/test/no-qs-fr.html new file mode 100644 index 0000000..59fcdd1 --- /dev/null +++ b/test/no-qs-fr.html @@ -0,0 +1,46 @@ +--- +title: Résultats de la recherche sans Suggestions de termes +description: Page démo sans la fonctionnalité Suggestions de termes dans l'interface utilisateur de recherche Canada.ca +lang: fr +altLangPage: no-qs-en.html +nositesearch: true +pageclass: page-type-search +pageType: recherche +share: false +deptfeature: false +dateModified: 2025-05-21 +css: "../src/connector.css" +script: +- src: "assets/token.js" +- src: "../src/connector.js" + type: module +--- + + + + + +
+

Effectuer une recherche avancée

+ + +

Section Résultats attendu pour la section de résultats

+
+ Section Résultats générée + [À compléter, voir Connector.js comme référence pour l'instant] +
diff --git a/test/qs-en.html b/test/qs-en.html index c08d7b2..73ca02b 100644 --- a/test/qs-en.html +++ b/test/qs-en.html @@ -1,6 +1,6 @@ --- -title: Search page with Query Suggestions (QS) -description: Demo page for the QS feature in the Canada.ca Search UI Basic +title: Search page with 10 Query Suggestions after at least 2 character entered +description: Demo page without the QS feature in the Canada.ca Search UI Basic, customized to show 10 suggestion after just 2 characters entered lang: en altLangPage: qs-fr.html nositesearch: true @@ -8,7 +8,7 @@ pageType: recherche share: false deptfeature: false -dateModified: 2025-02-19 +dateModified: 2025-05-21 css: "../src/connector.css" script: - src: "assets/token.js" @@ -34,7 +34,8 @@ "organizationId": "employmentandsocialdevelopmentcanadanonproduction14o5d9wry", "accessToken":"{{ site.data.token.API_KEY }}", "originLevel3": "/en/sr/srb.html", - "numberOfSuggestions": 5 + "numberOfSuggestions": 10, + "minimumCharsForSuggestions": 2 }'>

Perform an advanced search

diff --git a/test/qs-fr.html b/test/qs-fr.html index 927407e..f84f500 100644 --- a/test/qs-fr.html +++ b/test/qs-fr.html @@ -1,6 +1,6 @@ --- -title: Résultats de la recherche avec Suggestions de termes -description: Page démo pour la fonctionnalité Suggestions de termes dans l'interface utilisateur de recherche Canada.ca +title: Recherche avec 10 Suggestions de termes avec minimum de 2 caractères entrés +description: Page démo avec la fonctionnalité Suggestions de termes dans l'interface utilisateur de recherche Canada.ca, personnalisée en montrant 10 suggestions lorsque 2 caractères sont entrés ou plus lang: fr altLangPage: qs-en.html nositesearch: true @@ -8,7 +8,7 @@ pageType: recherche share: false deptfeature: false -dateModified: 2025-02-19 +dateModified: 2025-05-21 css: "../src/connector.css" script: - src: "assets/token.js" @@ -34,7 +34,8 @@ "organizationId": "employmentandsocialdevelopmentcanadanonproduction14o5d9wry", "accessToken":"{{ site.data.token.API_KEY }}", "originLevel3": "/fr/sr/srb.html", - "numberOfSuggestions": 5 + "numberOfSuggestions": 10, + "minimumCharsForSuggestions": 2 }'>

Effectuer une recherche avancée

From ee2a154c2dbb1fba2d736c95a615d5d4fd670599 Mon Sep 17 00:00:00 2001 From: bblaisATcoveo Date: Fri, 23 May 2025 14:47:04 -0400 Subject: [PATCH 04/13] enable QS by default (5 suggestions, 3 minimum chars) (#33) * enable QS by default (5 suggestions, 3 minimum chars) * Restrict QS to basic search pages --- src/connector.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/connector.js b/src/connector.js index 0b52807..5cdac5c 100644 --- a/src/connector.js +++ b/src/connector.js @@ -30,8 +30,8 @@ const defaults = { "accessToken":"", "searchBoxQuery": "#sch-inp-ac", "lang": "en", - "numberOfSuggestions": 0, - "minimumCharsForSuggestions": 2, + "numberOfSuggestions": 5, + "minimumCharsForSuggestions": 3, "enableHistoryPush": true, "isContextSearch": false, "isAdvancedSearch": false, @@ -359,7 +359,7 @@ function initTpl() { // auto-create suggestions element searchBoxElement = document.querySelector( params.searchBoxQuery ); - if ( !suggestionsElement && searchBoxElement && params.numberOfSuggestions > 0 ) { + if ( !suggestionsElement && searchBoxElement && params.numberOfSuggestions > 0 && !params.isAdvancedSearch ) { searchBoxElement.role = "combobox"; searchBoxElement.setAttribute( 'aria-autocomplete', 'list' ); From 1793cf7f95690fa4c1e3f4c6beeb12086bc2a80d Mon Sep 17 00:00:00 2001 From: bblaisATcoveo Date: Fri, 23 May 2025 14:58:00 -0400 Subject: [PATCH 05/13] Fix url with html encoded values (#38) * Fix url with html encoded values * Revert DOMPurify on the title and use stripHtml * PrintableUri re-adjustments - use encodeURI instead of DOMPurify - replace & for & in all printable uri - use clickUri in the breadcrumb * use breadcrumb for %[breadcrumb] --- src/connector.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/connector.js b/src/connector.js index 5cdac5c..be07001 100644 --- a/src/connector.js +++ b/src/connector.js @@ -983,8 +983,9 @@ function updateResultListState( newState ) { } let breadcrumb = ""; - let printableUri = stripHtml( result.printableUri ); - let clickUri = stripHtml( result.clickUri ); + let printableUri = encodeURI( result.printableUri ); + printableUri = printableUri.replaceAll( '&' , '&' ); + let clickUri = encodeURI( result.clickUri ); let title = stripHtml( result.title ); if ( result.raw.hostname && result.raw.displaynavlabel ) { const splittedNavLabel = ( Array.isArray( result.raw.displaynavlabel ) ? result.raw.displaynavlabel[0] : result.raw.displaynavlabel).split( '>' ); @@ -992,7 +993,7 @@ function updateResultListState( newState ) { ' 
  • ' + stripHtml( splittedNavLabel[splittedNavLabel.length-1] ) + '
  • '; } else { - breadcrumb = '

    ' + printableUri + '

    '; + breadcrumb = '

    ' + printableUri + '

    '; } sectionNode.innerHTML = resultTemplateHTML @@ -1002,7 +1003,7 @@ function updateResultListState( newState ) { .replace( '%[result.title]', title ) .replace( '%[result.raw.author]', author ) .replace( '%[result.breadcrumb]', breadcrumb ) - .replace( '%[result.printableUri]', printableUri.replaceAll( '&' , '&' ) ) + .replace( '%[result.printableUri]', printableUri ) .replace( '%[short-date-en]', getShortDateFormat( resultDate ) ) .replace( '%[short-date-fr]', getShortDateFormat( resultDate ) ) .replace( '%[long-date-en]', getLongDateFormat( resultDate, 'en' ) ) From 352a615b2c9cd3561a42c4f5b31e821bb325b856 Mon Sep 17 00:00:00 2001 From: bblaisATcoveo Date: Mon, 26 May 2025 08:29:45 -0400 Subject: [PATCH 06/13] Add relative url for ML (#34) * Add relative url for ML * adjust context variable in one json * remove unused variable * auto detect relative url from originLevel3 * Always recalculate relative url from absolute url * allow the possibility of using realative originLevel3 * initialize relative url to empty string Co-authored-by: Francis Gorman --------- Co-authored-by: Francis Gorman --- src/connector.js | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/connector.js b/src/connector.js index be07001..26dd679 100644 --- a/src/connector.js +++ b/src/connector.js @@ -44,6 +44,7 @@ let paramsDetect = {}; let params = {}; let urlParams; let hashParams; +let originLevel3RelativeUrl = ""; // Headless controllers let headlessEngine; @@ -151,6 +152,20 @@ function initSearchUI() { if ( urlParams.originLevel3 ){ params.originLevel3 = urlParams.originLevel3; } + + // Auto detect relative path from originLevel3 + if( !params.originLevel3.startsWith( "/" ) && /http|www/.test( params.originLevel3 ) ) { + try { + const absoluteURL = new URL( params.originLevel3 ); + originLevel3RelativeUrl = absoluteURL.pathname; + } + catch( exception ) { + console.warn( "Exception while auto detecting relative path: " + exception.message ); + } + } + else { + originLevel3RelativeUrl = params.originLevel3; + } if ( !params.endpoints ) { params.endpoints = getOrganizationEndpoints( params.organizationId, 'prod' ); @@ -418,7 +433,12 @@ function initEngine() { // filter user sensitive content requestContent.enableQuerySyntax = params.isAdvancedSearch; - requestContent.mlParameters = { "filters": { "c_context_searchpageurl": params.originLevel3 } }; + requestContent.mlParameters = { + "filters": { + "c_context_searchpageurl": params.originLevel3, + "c_context_searchpagerelativeurl": originLevel3RelativeUrl + } + }; if ( requestContent.analytics ) { requestContent.analytics.originLevel3 = params.originLevel3; @@ -438,8 +458,8 @@ function initEngine() { } ); contextController = buildContext( headlessEngine ); - contextController.set( { "searchPageUrl" : params.originLevel3 } ); - + contextController.set( { "searchPageUrl" : params.originLevel3, "searchPageRelativeUrl" : originLevel3RelativeUrl } ); + // build controllers searchBoxController = buildSearchBox( headlessEngine, { options: { From fafe711503e0bee2bcc703f82d417ded4dc99cd7 Mon Sep 17 00:00:00 2001 From: Francis Gorman Date: Mon, 26 May 2025 10:53:31 -0400 Subject: [PATCH 07/13] Doc: Add more custom templates to showcase possibilities --- test/budget.html | 5 +++++ test/template.html | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/test/budget.html b/test/budget.html index e32fcfc..bbb9fd8 100644 --- a/test/budget.html +++ b/test/budget.html @@ -55,3 +55,8 @@ "isAdvancedSearch": true, "originLevel3": "/en/department-finance/search-budget/advanced-search.html" }'> + + + diff --git a/test/template.html b/test/template.html index eef62f6..fbb36b4 100644 --- a/test/template.html +++ b/test/template.html @@ -54,6 +54,41 @@

    +

    Did you mean ?

    + + + + + + +

    Expected output for the result section

    From b12da716c9afa52503db6f01a113450b655d184f Mon Sep 17 00:00:00 2001 From: bblaisATcoveo Date: Mon, 26 May 2025 13:49:51 -0400 Subject: [PATCH 08/13] Fix an accessibility issue on pager when no results (#35) * Fix an accessibility issue on pager when no results * Remove pager parent element and fix pager state issue * remove extra dash to pager selector --- src/connector.js | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/connector.js b/src/connector.js index 26dd679..1ff7738 100644 --- a/src/connector.js +++ b/src/connector.js @@ -73,6 +73,7 @@ let pagerState; let lastCharKeyUp; let activeSuggestion = 0; let activeSuggestionWaitMouseMove = true; +let pagerManuallyCleared = false; // Firefox patch let isFirefox = navigator.userAgent.indexOf( "Firefox" ) !== -1; @@ -96,7 +97,7 @@ let querySummaryTemplateHTML = document.getElementById( 'sr-query-summary' )?.in let didYouMeanTemplateHTML = document.getElementById( 'sr-did-you-mean' )?.innerHTML; let noQuerySummaryTemplateHTML = document.getElementById( 'sr-noquery-summary' )?.innerHTML; let previousPageTemplateHTML = document.getElementById( 'sr-pager-previous' )?.innerHTML; -let pageTemplateHTML = document.getElementById( 'sr-pager-page-' )?.innerHTML; +let pageTemplateHTML = document.getElementById( 'sr-pager-page' )?.innerHTML; let nextPageTemplateHTML = document.getElementById( 'sr-pager-next' )?.innerHTML; let pagerContainerTemplateHTML = document.getElementById( 'sr-pager-container' )?.innerHTML; @@ -369,7 +370,7 @@ function initTpl() { newPagerElement.innerHTML = pagerContainerTemplateHTML; resultsSection.append( newPagerElement ); - pagerElement = newPagerElement.querySelector( "#pager" ); + pagerElement = newPagerElement; } // auto-create suggestions element @@ -783,6 +784,7 @@ function initEngine() { querySummaryElement.textContent = ""; didYouMeanElement.textContent = ""; pagerElement.textContent = ""; + pagerManuallyCleared = true; } }; } @@ -1060,6 +1062,11 @@ function updateQuerySummaryState( newState ) { if ( resultListState.firstSearchExecuted && !querySummaryState.isLoading && !querySummaryState.hasError ) { querySummaryElement.textContent = ""; if ( querySummaryState.total > 0 ) { + // Manually ask pager to redraw since even is not sent when manually cleared + if ( pagerManuallyCleared ) { + updatePagerState( pagerState ); + } + let numberOfResults = querySummaryState.total.toLocaleString( params.lang ); // Generate the text content const querySummaryHTML = ( ( querySummaryState.query !== "" && !params.isAdvancedSearch ) ? querySummaryTemplateHTML : noQuerySummaryTemplateHTML ) @@ -1078,11 +1085,13 @@ function updateQuerySummaryState( newState ) { querySummaryElement.innerHTML = noResultTemplateHTML; } focusToView(); + pagerManuallyCleared = false; } else if ( querySummaryState.hasError ) { querySummaryElement.textContent = ""; querySummaryElement.innerHTML = resultErrorTemplateHTML; focusToView(); + pagerManuallyCleared = false; } } @@ -1122,7 +1131,16 @@ function updateDidYouMeanState( newState ) { // Update pagination function updatePagerState( newState ) { pagerState = newState; - pagerElement.textContent = ""; + if ( pagerState.maxPage === 0 ) { + pagerElement.textContent = ""; + return; + } + else if ( pagerElement.textContent === "" ) { + pagerElement.innerHTML = pagerContainerTemplateHTML; + } + + let pagerComponentElement = pagerElement.querySelector( "#pager" ); + pagerComponentElement.textContent = ""; if ( pagerState.hasPreviousPage ) { const liNode = document.createElement( "li" ); @@ -1135,7 +1153,7 @@ function updatePagerState( newState ) { pagerController.previousPage(); }; - pagerElement.appendChild( liNode ); + pagerComponentElement.appendChild( liNode ); } pagerState.currentPages.forEach( ( page ) => { @@ -1162,7 +1180,7 @@ function updatePagerState( newState ) { pagerController.selectPage( pageNo ); }; - pagerElement.appendChild( liNode ); + pagerComponentElement.appendChild( liNode ); } ); if ( pagerState.hasNextPage ) { @@ -1176,7 +1194,7 @@ function updatePagerState( newState ) { pagerController.nextPage(); }; - pagerElement.appendChild( liNode ); + pagerComponentElement.appendChild( liNode ); } } From 5ecde6c5d7aff405327fddba44c4d3254a44ef5e Mon Sep 17 00:00:00 2001 From: Francis Gorman Date: Tue, 27 May 2025 08:27:16 -0400 Subject: [PATCH 09/13] Release v1.6.0 (#42) --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 224d009..6fc07d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "search-ui", - "version": "1.5.0", + "version": "1.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 4f5f090..631927a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "search-ui", - "version": "1.5.0", + "version": "1.6.0", "description": "Canada.ca Search UI with Headless", "main": "index.html", "repository": { From 6d071fe14dc85dffa1cef773fc057c75c717614f Mon Sep 17 00:00:00 2001 From: Francis Gorman Date: Wed, 28 May 2025 13:33:03 -0400 Subject: [PATCH 10/13] Doc: Add breadcrumb to test pages for ease of navigation + document customEvent (#43) --- README.md | 109 ++++++++++++++++++++++++++++++++++++++++++++- test/budget.html | 3 ++ test/election.html | 3 ++ test/gazette.html | 3 ++ test/no-qs-en.html | 3 ++ test/no-qs-fr.html | 3 ++ test/qs-en.html | 3 ++ test/qs-fr.html | 3 ++ test/sra-en.html | 3 ++ test/sra-fr.html | 3 ++ test/srb-en.html | 3 ++ test/srb-fr.html | 3 ++ test/src-en.html | 3 ++ test/src-fr.html | 3 ++ test/template.html | 3 ++ todo.md | 2 +- 16 files changed, 151 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8941bf5..4b596cd 100644 --- a/README.md +++ b/README.md @@ -234,4 +234,111 @@ Sometimes your search pages contain more than one input relevant to the search's #### Analytics tracking -Custom event named `searchEvent` can used to hook onto from Analytics tools, such as Adobe Analytics. This allows to listen to search actions, more specifically "doing a search", since the Search UI is acting similar to a Single Page App (SPA). +Custom event named `searchEvent` can used to hook onto from Analytics tools, such as Adobe Analytics. This allows to listen to search actions, more specifically "doing a search", since the Search UI is acting similar to a Single Page App (SPA). The payload varies based on the event type triggered, which is usually dictated by the `actionCause`. In the case where there is no `actionCause` in the payload of a beacon, then the `eventType` will tell you more about it. There are currently 5 types of action cause: + +- interfaceLoad +: Search interface was initially loaded (or refreshed) with a search term +- searchboxSubmit +: any subsequent searches from the search box +- omniboxAnalytics +: User clicks on a query suggestion +- documentOpen +: User clicks on a search result + +And the main event type for when an action cause is not provided is: + +- getMoreResults +: User clicked on a pagination button + +They each carry the following fields in their respective payloads: + +##### interfaceLoad + +- language +- userAgent +- originContext +- originLevel1 +- originLevel2 +- originLevel3 +- splitTestRunName +- splitTestRunVersion +- customData + - context_searchPageUrl + - c_context_searchpagerelativeurl + - coveoHeadlessVersion +- facetState +- anonymous +- clientId +- queryText +- responseTime +- results + - documentUri + - documentUriHash +- numberOfResults +- queryPipeline +- actionCause +- searchQueryUid + +##### searchboxSubmit + +Same as [interfaceLoad](#interfaceLoad). + +##### documentOpen + +Same as [interfaceLoad](#interfaceLoad), plus the following: + +- collectionName +- documentAuthor +- documentPosition +- documentTitle +- documentUri +- documentUriHash +- documentUrl +- rankingModifier +- sourceName +- customData + - context_searchPageUrl + - c_context_searchpagerelativeurl + - coveoHeadlessVersion + - contentIDKey + - contentIDValue + +##### omniboxAnalytics + +Same as [interfaceLoad](#interfaceLoad), plus the following: + +- customData + - context_searchPageUrl + - c_context_searchpagerelativeurl + - coveoHeadlessVersion + - suggestionRanking + - partialQuery + - partialQueries + - suggestions + - querySuggestResponseId + +##### getMoreResults + +- language +- userAgent +- originContext +- originLevel1 +- originLevel2 +- originLevel3 +- splitTestRunName +- splitTestRunVersion +- customData + - context_searchPageUrl + - c_context_searchpagerelativeurl + - coveoHeadlessVersion + - pagerNumber +- facetState +- anonymous +- clientId +- eventType +- eventValue +- lastSearchQueryUid + +#### Removing on-page content from index + +You can add the class `sr-no-index` to any HTML element inside the main content of your page if you wish for that content to be ignored by the search engine. In this case, it won't be indexed, nor returned in the search result excerpts. diff --git a/test/budget.html b/test/budget.html index bbb9fd8..fe6acf0 100644 --- a/test/budget.html +++ b/test/budget.html @@ -8,6 +8,9 @@ share: false deptfeature: false dateModified: 2025-02-24 +breadcrumbs: +- title: "GC Search UI" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/election.html b/test/election.html index ab4567f..38b9fa0 100644 --- a/test/election.html +++ b/test/election.html @@ -8,6 +8,9 @@ share: false deptfeature: false dateModified: 2025-02-24 +breadcrumbs: +- title: "GC Search UI" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/gazette.html b/test/gazette.html index fd4ffd8..cd47964 100644 --- a/test/gazette.html +++ b/test/gazette.html @@ -8,6 +8,9 @@ share: false deptfeature: false dateModified: 2025-02-24 +breadcrumbs: +- title: "GC Search UI" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/no-qs-en.html b/test/no-qs-en.html index d8adea8..49ab41a 100644 --- a/test/no-qs-en.html +++ b/test/no-qs-en.html @@ -9,6 +9,9 @@ share: false deptfeature: false dateModified: 2025-05-21 +breadcrumbs: +- title: "GC Search UI" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/no-qs-fr.html b/test/no-qs-fr.html index 59fcdd1..3152e4a 100644 --- a/test/no-qs-fr.html +++ b/test/no-qs-fr.html @@ -9,6 +9,9 @@ share: false deptfeature: false dateModified: 2025-05-21 +breadcrumbs: +- title: "IU Recherche GC" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/qs-en.html b/test/qs-en.html index 73ca02b..3a79361 100644 --- a/test/qs-en.html +++ b/test/qs-en.html @@ -9,6 +9,9 @@ share: false deptfeature: false dateModified: 2025-05-21 +breadcrumbs: +- title: "GC Search UI" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/qs-fr.html b/test/qs-fr.html index f84f500..d1e4940 100644 --- a/test/qs-fr.html +++ b/test/qs-fr.html @@ -9,6 +9,9 @@ share: false deptfeature: false dateModified: 2025-05-21 +breadcrumbs: +- title: "IU Recherche GC" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/sra-en.html b/test/sra-en.html index 8f192a0..8d7e761 100644 --- a/test/sra-en.html +++ b/test/sra-en.html @@ -9,6 +9,9 @@ share: false deptfeature: false dateModified: 2024-06-05 +breadcrumbs: +- title: "GC Search UI" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/sra-fr.html b/test/sra-fr.html index 57f9e15..928e6c9 100644 --- a/test/sra-fr.html +++ b/test/sra-fr.html @@ -9,6 +9,9 @@ share: false deptfeature: false dateModified: 2024-06-05 +breadcrumbs: +- title: "IU Recherche GC" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/srb-en.html b/test/srb-en.html index 3be6c40..a5fb890 100644 --- a/test/srb-en.html +++ b/test/srb-en.html @@ -9,6 +9,9 @@ share: false deptfeature: false dateModified: 2024-06-05 +breadcrumbs: +- title: "GC Search UI" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/srb-fr.html b/test/srb-fr.html index 10ed6cf..27c906f 100644 --- a/test/srb-fr.html +++ b/test/srb-fr.html @@ -9,6 +9,9 @@ share: false deptfeature: false dateModified: 2024-06-05 +breadcrumbs: +- title: "IU Recherche GC" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/src-en.html b/test/src-en.html index 6d662f2..620025e 100644 --- a/test/src-en.html +++ b/test/src-en.html @@ -9,6 +9,9 @@ share: false deptfeature: false dateModified: 2024-06-05 +breadcrumbs: +- title: "GC Search UI" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/src-fr.html b/test/src-fr.html index f03461b..b899454 100644 --- a/test/src-fr.html +++ b/test/src-fr.html @@ -9,6 +9,9 @@ share: false deptfeature: false dateModified: 2024-06-05 +breadcrumbs: +- title: "IU Recherche GC" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/test/template.html b/test/template.html index fbb36b4..6b9966f 100644 --- a/test/template.html +++ b/test/template.html @@ -8,6 +8,9 @@ share: false deptfeature: false dateModified: 2025-02-19 +breadcrumbs: +- title: "GC Search UI" + link: "../index.html" css: "../src/connector.css" script: - src: "assets/token.js" diff --git a/todo.md b/todo.md index 6af20b0..d54b996 100644 --- a/todo.md +++ b/todo.md @@ -10,13 +10,13 @@ This list contains outstanding suggestions / non-critical issues identified in p - [x] Investigate #wb-land focus on Advanced search - [x] Document customEvent - [x] Finish proper development of Query Suggestion (QS), outside of GCWeb +- [x] Improve the form code to not rely on an action that points to an anchor for a dynamically added element, which doesn't exist on the page prior to JS - [ ] Move QS generic integration to GCWeb - [ ] Remove the need for having a CSS file to be handled by GCWeb instead! - [ ] Add missing pieces such as "error message", "no result" and "did you mean" into our reference implementation as an example - [ ] Add Expected output on test pages (HTML) and use Jekyll highlights - [ ] Align search pages with new GCWeb template and/or define new GCWeb templates - [ ] Ensure no section or heading or any element with semantic is added alone/empty on the page -- [ ] Improve the form code to not rely on an action that points to an anchor for a dynamically added element, which doesn't exist on the page prior to JS - [ ] Create search template specific styles (.page-type-search), to get rid of overusage of .h3 class for example - [ ] Leverage wb core features instead of reinving the wheel, such as for language of page and dates. For dates, native JS functions could be leveraged such as: toLocaleDateString - [ ] Improve caching of variable that are used multiple times in the script, such as: window.location, then window.location.pathname From b42ec348228072cff9395d26e5f0f147f3d2e774 Mon Sep 17 00:00:00 2001 From: Francis Gorman Date: Wed, 28 May 2025 16:40:45 -0400 Subject: [PATCH 11/13] Update README.md (#44) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4b596cd..03bb87c 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,7 @@ Sometimes your search pages contain more than one input relevant to the search's #### Analytics tracking -Custom event named `searchEvent` can used to hook onto from Analytics tools, such as Adobe Analytics. This allows to listen to search actions, more specifically "doing a search", since the Search UI is acting similar to a Single Page App (SPA). The payload varies based on the event type triggered, which is usually dictated by the `actionCause`. In the case where there is no `actionCause` in the payload of a beacon, then the `eventType` will tell you more about it. There are currently 5 types of action cause: +Custom event named `searchEvent` can used to hook onto from Analytics tools, such as Adobe Analytics. This allows to listen to search actions, more specifically "doing a search", since the Search UI is acting similar to a Single Page App (SPA). The payload varies based on the event type triggered, which is usually dictated by the `actionCause`. In the case where there is no `actionCause` in the payload of a beacon, then the `eventType` will tell you more about it. List of action causes: - interfaceLoad : Search interface was initially loaded (or refreshed) with a search term @@ -245,7 +245,7 @@ Custom event named `searchEvent` can used to hook onto from Analytics tools, suc - documentOpen : User clicks on a search result -And the main event type for when an action cause is not provided is: +And the main event type for when an action cause is not provided: - getMoreResults : User clicked on a pagination button From 17881d1cca0282ef0cefef64fc0ac7f854bb583a Mon Sep 17 00:00:00 2001 From: Francis Gorman Date: Tue, 17 Jun 2025 15:31:39 -0400 Subject: [PATCH 12/13] Doc: Minor edits to the Readme documentation (#45) * Update README.md * Update README.md * Update README.md - suite * Update index.html to match readme * Update README.md - after garneauma review --- README.md | 49 +++++++++++++++++++++++++------------------------ test/index.html | 2 +- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 03bb87c..82b86de 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,33 @@ # Project: Search UI -Purpose of this repository is to provide MWS pages with the proper JS and CSS assets to achieve a working search page with the vendor's (Coveo) technology called Headless. +Purpose of this repository is to provide MWS pages with the proper JS and CSS assets to achieve working search pages with the vendor's (Coveo) technology called Headless, sitting on top of GCWeb. ## References - Coveo Headless: https://docs.coveo.com/en/headless/latest/ +- GCWeb: https://wet-boew.github.io/GCWeb/index-en.html ## Key details ### Sponsor / Contact -This project is led by Principal Publisher at ESDC. The key contact in case of questions related to the project is Francis Gorman, who can be reached at francis.gorman@hrsdc-rhdcc.gc.ca. If no reply is received from this person, fallback contact is ESDC.SD.DEV-DEV.DS.EDSC@servicecanada.gc.ca. +This project is led by Principal Publisher at Service Canada (ESDC). The key contact in case of questions related to the project is Francis Gorman, who can be reached at francis.gorman@hrsdc-rhdcc.gc.ca. If no reply is received from this person, fallback contact is ESDC.SD.DEV-DEV.DS.EDSC@servicecanada.gc.ca. ### Timeline and frequency The goal is to continue to refine and improve this code base on a regular basis. Every 6 months, if no activity is recorded on this repository, the key contact shall be reached out to in order to ensure it isn't stale. -**Removal date** will coincide with end of contract with vendor. +**Removal date** would align with end of contract with current vendor. ### Improvement plan To manage development activities related to this project, a standard internal issue tracking system used at Principal Publisher will be used. Also, regular touchpoints with the search vendor, as well as formal service requests entered through their portal, could also spark some development activities from a vendor perspective. -In the medium to long term, some activities may take place related to: -- stabilization of the query suggestion combobox; -- porting of some parts of the codebase to GCWeb; -- addition of machine learning features. +Example of code contributions may be related to: + +- development/configurations of AI-powered features and other innovations +- bug fixes, accessibility and security improvements +- project maintenance chores For more details, please [consult full checklist of to do items](todo.md). @@ -54,38 +56,36 @@ This rubric is for developers and testers. #### Test locally 1. Install Docker -2. Add an API key to your site settings as described below in [Setting an API key](#setting-an-api-key). Otherwise, please see [Alternative to the API key by getting a token](#alternative-to-the-api-key-by-getting-a-token) below. +2. Add an API key to your site settings as described below in [Setting an API key](#setting-an-api-key). 3. run `docker compose up --build` ##### Setting an API key -1. Create a `_data` folder at the root. Then, add a file named `token.yml` inside the `_data` folder. This file needs to simply have a key-value pair of `API_KEY: "[API KEY HERE]"` on line 1. The key value can be found at https://github.com/ServiceCanada/devops-documentation/blob/master/search/local-testing.md to replace the `[API KEY HERE]`. If you do not have access to the previous link, please see the next section on how to use a token as described below. - -##### Alternative to the API key by getting a token - -Since you need a token to communicate with the Coveo API, you can do the following to go to get a token valid for 24 hrs: - -1. Go to a search page on the Canada.ca Preview server such as: **/en/sr/srb.html**. -2. Open the inspector (developer tool) and look for the `div` tag that has the attribute called `data-gc-search`. -3. Inside this attribute, you'll find a Javascript object that has a field called `accessToken`. Grab the value of that token. -4. a) If you are **testing locally**, create a `_data` folder at the root. Then, add a file named `token.yml` inside the `_data` folder. This file needs to simply have a key-value pair of `API_KEY: "[API KEY HERE]"` on line 1. Add the token value as the key in `[API KEY HERE]`. b) If you are **testing through GitHub pages**, replace instances of `{{ site.data.token.API_KEY }}` with the token, on HTML pages would like to test. -5. If the token doesn't seem valid or if you have passed the 24 hours time-to-live (TTL), go back to step one and take another one from the Canada.ca Preview server. +Create a `_data` folder at the root. Then, add a file named `token.yml` inside the `_data` folder. This file needs to simply have a key-value pair of `API_KEY: "[API KEY HERE]"` on line 1. The key value can be found at https://github.com/ServiceCanada/devops-documentation/blob/master/search/local-testing.md to replace the `[API KEY HERE]`. If you do not have access to the previous link and/or are unable to get an API key, you can use a token instead; in which case, see the [Getting a token](#getting-a-token) section below. #### Testing through GitHub Pages ##### Main website -At all time, you can visit the GitHub website to see the GC Search UI with the latest Pull requests merged at play: https://servicecanada.github.io/search-ui/ +At all time, you can visit the GitHub website to view and test the GC Search UI with the latest Pull requests merged at play: https://servicecanada.github.io/search-ui/. + +Since search pages need a token to communicate with the Coveo API and functioning properly, you will need to get your token by following the instructions in the [Getting a token](#getting-a-token) section below. You can then take this token and save it through the form linked from the website's index page under the "Test pages" section. Your token will be saved for the duration of your session on the website. If you close the tab or stay inactive for a while, you will need to go back and generate a new one. ##### In your fork -This is to test your changes usually before opening a Pull request. +This is usually to test your changes before opening a pull request, or to conduct usability testing and accessibility assessments. -1. Add the required token on HTML pages would like to test by [following the instructions on Alternative to the API key by getting a token](#alternative-to-the-api-key-by-getting-a-token). Do not use the `token.yml` approach documented for testing locally, since it may generate potential a [security risk](SECURITY.md) in the context of GitHub pages. -2. Push your code to a branch of your choice in your origin remote (fork). It is recommended that you use a dedicated branch for testing, one that you would never open a Pull request from. +1. Do not use the `token.yml` approach documented for testing locally, since it may generate potential a [security risk](SECURITY.md) in the context of GitHub pages. Instead, get a token described in the [Getting a token](#getting-a-token) section below and replace instances of `{{ site.data.token.API_KEY }}` with the token, on HTML pages would like to test. +2. Push your code to a branch of your choice in your origin remote (fork). It is recommendeded that you use a dedicated branch for testing, one that you would never open a Pull request from. 3. Make sure your repository has GitHub Pages enabled, on that specific above-mentioned branch. 4. Your site is live on GitHub pages! +**Important note:** A token is only available for 24 hrs, after which a new one must be generated. Alternatively, instead of putting a token on all pages, you can let the user get their own token through the same process as described in the [Main website](#main-website) section above. + +##### Getting a token + +While on the GC network, go to the [Search token](https://canada-preview.adobecqms.net/en/service-canada/francis/get-sr-token.html) page and copy the value of the entire token loaded inside the page. + ### Deployment 1. The content of the "dist" folder is what's needed for a release / deployment. See [Build files](#build-files) section above to generate this folder. @@ -316,6 +316,7 @@ Same as [interfaceLoad](#interfaceLoad), plus the following: - partialQueries - suggestions - querySuggestResponseId + - queryText ##### getMoreResults @@ -341,4 +342,4 @@ Same as [interfaceLoad](#interfaceLoad), plus the following: #### Removing on-page content from index -You can add the class `sr-no-index` to any HTML element inside the main content of your page if you wish for that content to be ignored by the search engine. In this case, it won't be indexed, nor returned in the search result excerpts. +You can add the class `sr-no-index` to any HTML element inside the main content of your page if you wish for that content to be ignored by the search engine. In which case, said content won't be indexed nor returned in the search result excerpts. diff --git a/test/index.html b/test/index.html index 49d05ab..532dd04 100644 --- a/test/index.html +++ b/test/index.html @@ -10,7 +10,7 @@ ---
    From 469c72b122f80d542ce91abb6680ba8d482142d5 Mon Sep 17 00:00:00 2001 From: bblaisATcoveo Date: Tue, 6 May 2025 09:08:12 -0400 Subject: [PATCH 13/13] Notification trigger implementation --- src/connector.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/connector.js b/src/connector.js index 1ff7738..a37156b 100644 --- a/src/connector.js +++ b/src/connector.js @@ -9,6 +9,7 @@ import { buildDidYouMean, buildContext, buildInteractiveResult, + buildNotifyTrigger, loadAdvancedSearchQueryActions, loadSortCriteriaActions, HighlightUtils, @@ -55,6 +56,7 @@ let querySummaryController; let didYouMeanController; let pagerController; let statusController; +let notifyTriggerController; let urlManager; let unsubscribeManager; let unsubscribeSearchBoxController; @@ -62,12 +64,14 @@ let unsubscribeResultListController; let unsubscribeQuerySummaryController; let unsubscribeDidYouMeanController; let unsubscribePagerController; +let unsubscribeNotifyTriggerController; // UI states let updateSearchBoxFromState = false; let searchBoxState; let resultListState; let querySummaryState; +let notificationState; let didYouMeanState; let pagerState; let lastCharKeyUp; @@ -85,6 +89,7 @@ let formElement = document.querySelector( '#gc-searchbox, form[action="#wb-land" let resultsSection = document.querySelector( '#wb-land' ); let resultListElement = document.querySelector( '#result-list' ); let querySummaryElement = document.querySelector( '#query-summary' ); +let notificationTriggerElement = document.querySelector( '#notification-trigger' ); let pagerElement = document.querySelector( '#pager' ); let suggestionsElement = document.querySelector( '#suggestions' ); let didYouMeanElement = document.querySelector( '#did-you-mean' ); @@ -93,6 +98,7 @@ let didYouMeanElement = document.querySelector( '#did-you-mean' ); let resultTemplateHTML = document.getElementById( 'sr-single' )?.innerHTML; let noResultTemplateHTML = document.getElementById( 'sr-nores' )?.innerHTML; let resultErrorTemplateHTML = document.getElementById( 'sr-error' )?.innerHTML; +let notificationTriggerTemplateHTML = document.getElementById( 'sr-notification-trigger' )?.innerHTML; let querySummaryTemplateHTML = document.getElementById( 'sr-query-summary' )?.innerHTML; let didYouMeanTemplateHTML = document.getElementById( 'sr-did-you-mean' )?.innerHTML; let noQuerySummaryTemplateHTML = document.getElementById( 'sr-noquery-summary' )?.innerHTML; @@ -246,6 +252,23 @@ function initTpl() { } } + if ( !notificationTriggerTemplateHTML ) { + if ( lang === "fr" ) { + notificationTriggerTemplateHTML = + `
    +

    Notification

    +

    %[notification]

    +
    `; + } + else { + notificationTriggerTemplateHTML = + `
    +

    Notification

    +

    %[notification]

    +
    `; + } + } + if ( !querySummaryTemplateHTML ) { if ( lang === "fr" ) { querySummaryTemplateHTML = @@ -339,6 +362,14 @@ function initTpl() { baseElement.prepend( resultsSection ); } + // auto-create notification trigger element + if ( !notificationTriggerElement ) { + notificationTriggerElement = document.createElement( "div" ); + notificationTriggerElement.id = "notification-trigger"; + + resultsSection.append( notificationTriggerElement ); + } + // auto-create query summary element if ( !querySummaryElement ) { querySummaryElement = document.createElement( "div" ); @@ -483,6 +514,7 @@ function initEngine() { didYouMeanController = buildDidYouMean( headlessEngine, { options: { automaticallyCorrectQuery: false } } ); pagerController = buildPager( headlessEngine, { options: { numberOfPages: 9 } } ); statusController = buildSearchStatus( headlessEngine ); + notifyTriggerController = buildNotifyTrigger( headlessEngine ); if ( urlParams.allq || urlParams.exctq || urlParams.anyq || urlParams.noneq || urlParams.fqupdate || urlParams.dmn || urlParams.fqocct || urlParams.elctn_cat || urlParams.filetype || urlParams.site || urlParams.year ) { @@ -688,6 +720,7 @@ function initEngine() { unsubscribeQuerySummaryController = querySummaryController.subscribe( () => updateQuerySummaryState( querySummaryController.state ) ); unsubscribeDidYouMeanController = didYouMeanController.subscribe( () => updateDidYouMeanState( didYouMeanController.state ) ); unsubscribePagerController = pagerController.subscribe( () => updatePagerState( pagerController.state ) ); + unsubscribeNotifyTriggerController = notifyTriggerController.subscribe( () => updateNotifyTriggerState( notifyTriggerController.state ) ); // Clear event tracking, for legacy browsers const onUnload = () => { @@ -698,6 +731,7 @@ function initEngine() { unsubscribeQuerySummaryController?.(); unsubscribeDidYouMeanController?.(); unsubscribePagerController?.(); + unsubscribeNotifyTriggerController?.(); }; // Listen to URL change (hash) @@ -1051,6 +1085,18 @@ function updateResultListState( newState ) { } } +// Update notification displayed +function updateNotifyTriggerState ( newState ) { + notificationState = newState; + + if ( notificationState.notifications?.length ) { + notificationTriggerElement.innerHTML = notificationTriggerTemplateHTML.replace( "%[notification]", notificationState.notifications[0] ); + } + else { + notificationTriggerElement.textContent = ""; + } +} + // Update heading that has number of results displayed function updateQuerySummaryState( newState ) { querySummaryState = newState;