Skip to content

Commit

Permalink
PI-1449 Improve searching on input + added examples page
Browse files Browse the repository at this point in the history
  • Loading branch information
marcus-bcl committed Sep 20, 2023
1 parent 58bb895 commit bd03d8d
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 65 deletions.
7 changes: 7 additions & 0 deletions assets/scss/local.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
.govuk-main-wrapper {
min-height: 600px;
}

.example-output {
background-color: govuk-colour('light-grey');
border: 1px solid $govuk-border-colour;
margin-bottom: govuk-spacing(4);
padding: govuk-spacing(4);
}
11 changes: 5 additions & 6 deletions assets/scss/new-tech.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ $govuk-page-width: $moj-page-width;
@import 'govuk/all';
@import 'moj/all';

#search-form {
.probation-search__form {
background-color: $govuk-brand-colour;
padding-top: govuk-spacing(5);
.govuk-form-group {
margin-bottom: 0;
}
}

#search-input-container {
.probation-search__input-container {
@include govuk-width-container;
@include govuk-clearfix;
margin-bottom: govuk-spacing(4);
Expand All @@ -24,18 +24,17 @@ $govuk-page-width: $moj-page-width;
}
}

#search-result-summary, #search-pagination, #search-results {
.probation-search__results-container {
@include govuk-width-container;
}

#search-results {
@include govuk-width-container;
.probation-search__has-results {
& ~ .govuk-width-container > .govuk-main-wrapper {
display: none; // hide welcome content when results are rendered
}
}

#search-suggestions {
.probation-search__suggestions {
color: govuk-colour("white");
float: left;
margin-top: govuk-spacing(2)
Expand Down
8 changes: 6 additions & 2 deletions packages/probation-search-frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ A Nunjucks component to search for probation cases.

Easily build probation case search functionality into your HMPPS service, while delivering a consistent search experience to probation practitioners.

Try it out in the dev environment: https://probation-search-dev.hmpps.service.justice.gov.uk
Try it out in the dev environment: https://probation-search-dev.hmpps.service.justice.gov.uk/examples

## Get started

Expand All @@ -21,8 +21,11 @@ Register the macro by adding the `'node_modules/@ministryofjustice/probation-sea
```nunjuck
{% from "probationSearch/macro.njk" import probationSearch %}
{{ probationSearch({ id: "search", name: "search", results: probationSearchResults }) }}
{{ probationSearch({ id: "search", results: probationSearchResults }) }}
```
Example:
* [nunjucksSetup.ts](https://github.com/ministryofjustice/hmpps-sentence-plan-ui/blob/4cf961428e4c4e69565367bf7af17bac8c8da674/server/utils/nunjucksSetup.ts#L34)
* [search.njk](https://github.com/ministryofjustice/hmpps-sentence-plan-ui/blob/4cf961428e4c4e69565367bf7af17bac8c8da674/server/views/pages/search.njk)

### 3. Configure the Express routes

Expand All @@ -39,6 +42,7 @@ probationSearchRoutes({
oauthClient: service.hmppsAuthClient, // a reference to your HMPPS Auth client
})
```
Example: [routes/index.ts](https://github.com/ministryofjustice/hmpps-sentence-plan-ui/blob/4cf961428e4c4e69565367bf7af17bac8c8da674/server/routes/index.ts#L16)

That's it! Start your service and visit http://localhost:3000/search to try it out.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% from "govuk/components/pagination/macro.njk" import govukPagination %}
{% from "govuk/components/table/macro.njk" import govukTable %}

{% if params.results.response.totalElements > 0 %}
<p>Showing {{ params.results.page.from }} to {{ params.results.page.to }} of {{ params.results.page.totalResults }} results.</p>
{% endif %}

{% if params.results.results is string %}
{{ params.results.results | safe }}
{% elif params.results.response.totalElements > 0 %}
{{ govukTable({ firstCellIsHeader: true, head: params.results.results.head, rows: params.results.results.rows }) }}
{% elif params.results.query != null %}
<p>There are no results for your search. Try refining your query above.</p>
{% endif %}

{% if params.results.page.items | length > 1 %}
{{ govukPagination({ previous: { href: params.results.page.prev }, next: { href: params.results.page.next }, items: params.results.page.items }) }}
{% endif %}
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
{% from "govuk/components/input/macro.njk" import govukInput %}
{% from "govuk/components/button/macro.njk" import govukButton %}
{% from "govuk/components/pagination/macro.njk" import govukPagination %}
{% from "govuk/components/table/macro.njk" import govukTable %}

<form id="{{ params.id }}-form" method="POST">
<form id="{{ params.id }}-form" class="probation-search__form" method="POST">
<input type="hidden" name="_csrf" value="{{ params.results.csrfToken }}">

<div id="{{ params.id }}-input-container">
<div class="probation-search__input-container">
{{ govukInput({
attributes: params.attributes,
autocomplete: params.autocomplete,
Expand All @@ -21,21 +19,21 @@
classes: "govuk-label--l",
isPageHeading: true
},
name: params.name,
name: "probation-search-input",
type: params.type if params.type else "search",
value: params.results.query
}) }}

{% if params.results.suggestions | length %}
<p id="{{ params.id }}-suggestions">
<p id="{{ params.id }}-suggestions" class="probation-search__suggestions">
{% if params.results.suggestions | length %}
Did you mean
{%- set comma = joiner() %}
{%- for suggestion in params.results.suggestions %}{{ comma() }}
<a href="?q={{ suggestion }}" class="govuk-link govuk-link--no-visited-state"
title="Search again using {{ suggestion }}">{{ suggestion }}</a>
{%- endfor %}?
</p>
{% endif %}
{% endif %}
</p>

{% if params.postHint %}
{{ params.postHint.html | safe if params.postHint.html else '<p>' + params.postHint.text + '</p>' }}
Expand All @@ -44,10 +42,24 @@
{% if params.searchOnInput %}
<label for="{{ params.id }}" class="govuk-visually-hidden">Results will be updated as you type</label>
<script nonce="{{ params.results.cspNonce }}">
let timeoutId = null;
document.getElementById("{{ params.id }}").addEventListener("input", function() {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(function() { document.getElementById("{{ params.id }}-form").submit() }, 250);
let timeoutId = null
document.getElementById("{{ params.id }}").addEventListener('input', function() {
clearTimeout(timeoutId)
const url = new URL(location.href)
url.searchParams.forEach((value, name) => url.searchParams.delete(name))
url.searchParams.set('q', this.value)
timeoutId = setTimeout(() => fetch(url).then(async response => {
if (response.status === 200) {
const doc = new DOMParser().parseFromString(await response.text(), 'text/html')
document.getElementById("{{ params.id }}-suggestions").innerHTML = doc.getElementById("{{ params.id }}-suggestions").innerHTML
document.getElementById("{{ params.id }}-results-container").innerHTML = doc.getElementById("{{ params.id }}-results-container").innerHTML
document.getElementById("{{ params.id }}-results-container").classList = doc.getElementById("{{ params.id }}-results-container").classList
history.pushState({}, '', url)
} else {
document.getElementById("{{ params.id }}-results-container").innerHTML = '<p>Something went wrong. The error has been logged. Please try again.</p>'
document.getElementById("{{ params.id }}-suggestions").innerHTML = ''
}
}), 250)
})
</script>
{% else %}
Expand All @@ -56,24 +68,7 @@
</div>
</form>

{% if params.results.response.totalElements > 0 %}
<div id="{{ params.id }}-result-summary">
<p>Showing {{ params.results.page.from }} to {{ params.results.page.to }} of {{ params.results.page.totalResults }} results.</p>
</div>
{% endif %}

{% if params.results.results is string %}
{{ params.results.results | safe }}
{% elif params.results.response.totalElements > 0 %}
<div id="{{ params.id }}-results">
{{ govukTable({ firstCellIsHeader: true, head: params.results.results.head, rows: params.results.results.rows }) }}
</div>
{% elif params.results.query != null %}
<p>There are no results for your search. Try refining your query above.</p>
{% endif %}

{% if params.results.page.items | length > 1 %}
<div id="{{ params.id }}-pagination">
{{ govukPagination({ previous: { href: params.results.page.prev }, next: { href: params.results.page.next }, items: params.results.page.items }) }}
</div>
{% endif %}
<div id="{{ params.id }}-results-container"
class="probation-search__results-container {{ 'probation-search__has-results' if params.results.results else '' }}">
{% include "./results.njk" %}
</div>
6 changes: 3 additions & 3 deletions packages/probation-search-frontend/routes/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,16 @@ export default function probationSearchRoutes({
const client = new ProbationSearchClient(oauthClient, environment === 'local' ? localData : environment)

router.post(path, (req, res) => {
const { search } = req.body
if (!allowEmptyQuery && (search == null || search.length === 0)) {
const query = req.body['probation-search-input']
if (!allowEmptyQuery && (query == null || query.length === 0)) {
res.render(template, {
probationSearchResults: {
errorMessage: { text: 'Please enter a search term' },
...defaultResult(res),
},
})
} else {
res.redirect(`${path}?q=${req.body.search}`)
res.redirect(`${path}?q=${query}`)
}
})

Expand Down
11 changes: 10 additions & 1 deletion server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { type RequestHandler, Router } from 'express'
import probationSearchRoutes from '@ministryofjustice/probation-search-frontend/routes/search'
import nunjucks from 'nunjucks'
import {
ProbationSearchResponse,
ProbationSearchRequest,
ProbationSearchResponse,
} from '@ministryofjustice/probation-search-frontend/data/probationSearchClient'
import asyncMiddleware from '../middleware/asyncMiddleware'
import config from '../config'
Expand All @@ -29,6 +29,15 @@ export default function routes(service: Services): Router {
oauthClient: service.hmppsAuthClient,
})

// Examples page
probationSearchRoutes({
router,
path: '/examples',
template: 'pages/examples',
environment: config.environment,
oauthClient: service.hmppsAuthClient,
})

// Delius search screen (fka new tech)
probationSearchRoutes({
router,
Expand Down
26 changes: 26 additions & 0 deletions server/views/pages/examples.njk
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% extends "../partials/layout.njk" %}
{% from "probationSearch/macro.njk" import probationSearch %}

{% set pageTitle = applicationName + " - Home" %}
{% set mainClasses = "app-container govuk-body" %}

{% block content %}
<h1 class="govuk-heading-l">Examples</h1>
<p>A collection of examples showing how the <a href="https://www.npmjs.com/package/@ministryofjustice/probation-search-frontend">probation-search-frontend</a> UI component can be configured.</p>

<div class="govuk-!-width-two-thirds">
<ul class="govuk-list govuk-list--bullet">
<li>
<h2 class="govuk-heading-m">Basic example</h2>
<pre>{% raw %}{{ probationSearch({ id: "basic", results: probationSearchResults }) }}{% endraw %}</pre>
<div class="example-output">{{ probationSearch({ id: "basic", results: probationSearchResults }) }}</div>
</li>

<li>
<h2 class="govuk-heading-m">Search as you type</h2>
<pre>{% raw %}{{ probationSearch({ id: "on-input", searchOnInput: true, results: probationSearchResults }) }}{% endraw %}</pre>
<div class="example-output">{{ probationSearch({ id: "on-input", searchOnInput: true, results: probationSearchResults }) }}</div>
</li>
</ul>
</div>
{% endblock %}
2 changes: 1 addition & 1 deletion server/views/pages/index.njk
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
{% block content %}

<div class="govuk-!-width-two-thirds">
{{ probationSearch({ id: "search", name: "search", results: probationSearchResults }) }}
{{ probationSearch({ id: "search", results: probationSearchResults }) }}
</div>

{% endblock %}
36 changes: 18 additions & 18 deletions server/views/pages/newTech/index.njk
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

{{ probationSearch({
id: "search",
name: "search",
classes: "app-search-input govuk-body-l govuk-!-padding-3 govuk-!-margin-bottom-2",
label: {
text: "Search for people on probation",
Expand Down Expand Up @@ -61,23 +60,24 @@
<script src="/assets/govukFrontendInit.js"></script>
<script src="/assets/moj/all.js"></script>
<script nonce="{{ cspNonce }}">
// This code will be replaced as part of adding live result / ajax functionality. It's just here to enable testing of the filters.
document.getElementsByName('match-all-terms').forEach(function(el) {
el.addEventListener('input', function() {
const url = new URL(window.location.href)
url.searchParams.set('matchAllTerms', this.value)
window.location.href = url.toString()
})
})
document.getElementsByName('providers-filter').forEach(function(el) {
el.addEventListener('input', function() {
const url = new URL(window.location.href)
url.searchParams.delete('providers[]')
document.querySelectorAll('input[name="providers-filter"]:checked').forEach(function(el) {
url.searchParams.append('providers[]', el.value)
})
window.location.href = url.toString()
})
function debounce(func, wait) {
let timeoutId
return function(...args) {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => func.apply(this, args), wait)
}
}
function updateFilters() {
const url = new URL(window.location.href)
url.searchParams.set('matchAllTerms', document.querySelector('input[name="match-all-terms"]:checked').value)
url.searchParams.delete('providers[]')
document.querySelectorAll('input[name="providers-filter"]:checked').forEach(el => url.searchParams.append('providers[]', el.value))
window.location.href = url.toString()
}
document.getElementById('search-results-container').addEventListener('input', e => {
if (e.target.name === 'match-all-terms' || e.target.name === 'providers-filter') debounce(updateFilters, 250)()
})
</script>
{% endblock %}

0 comments on commit bd03d8d

Please sign in to comment.