Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/staging' into feat/stepByStep/ro…
Browse files Browse the repository at this point in the history
…utingAndController
  • Loading branch information
GeorgeGoodall committed Aug 6, 2024
2 parents 84ccb5f + 04122a1 commit d559418
Show file tree
Hide file tree
Showing 10 changed files with 413 additions and 21 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/featureDeploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ jobs:
strategy:
matrix:
environment: ${{ fromJSON(needs.detect-environments.outputs.environments) }}
if: ${{ matrix.environment != 'production' }}
if: ${{ inputs.environment != 'production' }}
uses: ./.github/workflows/deploy.yml
with:
environment: '${{ matrix.environment }}'
environment: '${{ inputs.environment }}'
secrets: inherit


4 changes: 2 additions & 2 deletions src/assets/js/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
as it will be loaded into the base nunjucks template.
*/

import hideElementsWithJsHidden from './js-hidden.js'
import initiateJsHiddenChecks from './js-hidden.js'

window.addEventListener('load', () => {
hideElementsWithJsHidden()
initiateJsHiddenChecks()
})
41 changes: 38 additions & 3 deletions src/assets/js/js-hidden.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,43 @@
const hideElementsWithJsHidden = () => {
/* globals MutationObserver, document */

/**
* Initiates checks for elements with the class 'js-hidden' and updates their display and visibility styles accordingly.
*
* When an element gains the 'js-hidden' class, its display and visibility styles are set to 'none' and 'hidden', respectively.
* When an element loses the 'js-hidden' class, its display and visibility styles are reset to their default values.
*
* This function also hides any elements that already have the 'js-hidden' class when it is called.
*/
const initiateJsHiddenChecks = () => {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
const target = mutation.target
const classList = target.classList
if (classList.contains('js-hidden')) {
// Class js-hidden was added
target.style.display = 'none'
target.style.visibility = 'hidden'
} else {
// Class js-hidden was removed
target.style.display = ''
target.style.visibility = ''
}
}
})
})

observer.observe(document.body, {
attributes: true,
attributeFilter: ['class'],
subtree: true
})

document.querySelectorAll('.js-hidden').forEach((el, i) => {
console.log(el)
console.log('Hiding element', el)
el.style.display = 'none'
el.style.visibility = 'none'
})
}

export default hideElementsWithJsHidden
export default initiateJsHiddenChecks
127 changes: 127 additions & 0 deletions src/assets/js/list-filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/**
* This file is taken from https://github.com/alphagov/collections/blob/main/app/assets/javascripts/modules/list-filter.js
*/

/* eslint-disable no-var */
//= require govuk_publishing_components/vendor/polyfills/closest

const keyPauseTime = 20

window.GOVUK = window.GOVUK || {}
window.GOVUK.Modules = window.GOVUK.Modules || {};

(function (Modules) {
function ListFilter ($module) {
this.$module = $module
this.filterTimeout = null
this.form = this.$module.querySelector('[data-filter="form"]')
this.searchResults = this.$module.querySelector('#search_results')
}

ListFilter.prototype.init = function () {
this.$module.filterList = this.filterList.bind(this)
// Form should only appear if the JS is working
this.form.classList.add('filter-list__form--active')
this.results = document.createElement('div')
this.results.classList.add('filter-list__results', 'govuk-heading-m', 'js-search-results')
this.results.setAttribute('aria-live', 'polite')
this.results.innerHTML = this.countInitialItems() + ' results found'
this.searchResults.insertBefore(this.results, this.searchResults.firstChild)

// We don't want the form to submit/refresh the page on enter key
this.form.onsubmit = function () { return false }

this.form.addEventListener('keyup', function (e) {
var searchTerm = e.target.value
clearTimeout(this.filterTimeout)
this.filterTimeout = setTimeout(function () {
this.$module.filterList(searchTerm)
}.bind(this), keyPauseTime)
}.bind(this))
}

ListFilter.prototype.filterList = function (searchTerm) {
var itemsToFilter = this.$module.querySelectorAll('[data-filter="item"]')
var blocksToFilter = this.$module.querySelectorAll('[data-filter="block"]')
for (var i = 0; i <= itemsToFilter.length - 1; i++) {
var currentItem = itemsToFilter[i]
if (!this.matchSearchTerm(currentItem, searchTerm)) {
currentItem.classList.add('js-hidden')
}
}
this.updateItemCount(blocksToFilter)
}

ListFilter.prototype.matchSearchTerm = function (item, term) {
var normaliseWhitespace = function (string) {
return string
.trim() // Removes spaces at beginning and end of string.
.replace(/\r?\n|\r/g, ' ') // Replaces line breaks with one space.
.replace(/\s+/g, ' ') // Squashes multiple spaces to one space.
}

var searchTerms = item.getAttribute('data-filter-terms') || ''
var normalisedTerms = normaliseWhitespace(searchTerms)

item.classList.remove('js-hidden')

var searchTermRegexp = new RegExp(term.trim().replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i')
return searchTermRegexp.exec(normalisedTerms) !== null
}

ListFilter.prototype.countInitialItems = function () {
return this.$module.querySelectorAll('[data-filter="item"]').length
}

ListFilter.prototype.updateItemCount = function (blocksToFilter) {
var totalMatchingItems = 0

for (var i = 0; i < blocksToFilter.length; i++) {
var block = blocksToFilter[i].closest('[data-filter="block"]')
block.classList.remove('js-hidden')

var matchingItems = block.querySelectorAll('[data-filter="item"]')
var matchingItemCount = 0

var innerBlocks = block.querySelectorAll('[data-filter="inner-block"]')
for (var r = 0; r < innerBlocks.length; r++) {
innerBlocks[r].classList.add('js-hidden')
}

for (var j = 0; j < matchingItems.length; j++) {
if (!matchingItems[j].classList.contains('js-hidden')) {
matchingItemCount++

if (matchingItems[j].closest('[data-filter="inner-block"]') !== null) { matchingItems[j].closest('[data-filter="inner-block"]').classList.remove('js-hidden') }
}
}

var itemCount = block.querySelectorAll('[data-item-count="true"]')
var accessibleItemCount = block.querySelectorAll('.js-accessible-item-count')

if (matchingItemCount === 0) {
block.classList.toggle('js-hidden')
}

if (matchingItemCount > 0) {
for (var l = 0; l < itemCount.length; l++) {
itemCount[l].textContent = matchingItemCount
}

for (var k = 0; k < accessibleItemCount.length; k++) {
accessibleItemCount[k].textContent = matchingItemCount
}
}

totalMatchingItems += matchingItemCount
}

var text = ' results found'
if (totalMatchingItems === 1) {
text = ' result found'
}
this.results.innerHTML = totalMatchingItems + text
}

Modules.ListFilter = ListFilter
})(window.GOVUK.Modules)
2 changes: 1 addition & 1 deletion src/assets/scss/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,4 @@ $govuk-global-styles: true;
code,
code * {
font-family: monospace;
}
}
30 changes: 29 additions & 1 deletion src/controllers/OrganisationsController.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datasette from '../services/datasette.js'
import performanceDbApi from '../services/performanceDbApi.js' // Assume you have an API service module
import logger from '../utils/logger.js'
import { dataSubjects } from '../utils/utils.js'
Expand Down Expand Up @@ -70,13 +71,40 @@ const organisationsController = {
}
},

/**
* Handles the GET /organisations request
*
* @param {Request} req
* @param {Response} res
* @param {NextFunction} next
*/
async getOrganisations (req, res, next) {
res.render('organisations/find.html')
try{
const sql = 'select name, organisation from organisation'
const result = await datasette.runQuery(sql)

const sortedResults = result.formattedData.sort((a, b) => {
return a.name.localeCompare(b.name)
})

const alphabetisedOrgs = sortedResults.reduce((acc, current) => {
const firstLetter = current.name.charAt(0).toUpperCase()
acc[firstLetter] = acc[firstLetter] || []
acc[firstLetter].push(current)
return acc
}, {})

res.render('organisations/find.html', { alphabetisedOrgs })
} catch (err) {
logger.warn(err)
next(err)
}
},

async getGetStarted (req, res, next) {
res.render('organisations/get-started.html')
}

}

export default organisationsController
56 changes: 50 additions & 6 deletions src/views/organisations/find.html
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{% extends "layouts/main.html" %}

{% from "govuk/components/breadcrumbs/macro.njk" import govukBreadcrumbs %}
{% from "govuk/components/tag/macro.njk" import govukTag %}
{% set pageName = "Find your organisation" %}

{% block beforeContent %}

{{ super() }}

{% endblock %}
Expand All @@ -12,15 +12,59 @@

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<h1 class="govuk-heading-xl govuk-!-margin-bottom-5">{{ pageName }}</h1>
</div>
</div>

<h1 class="govuk-heading-xl">
{{ pageName }}
</h1>
<div id="list-filter">

<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds">
<form class="govuk-!-margin-bottom-6" data-filter="form">
<label class="govuk-label" for="filter-organisations-list">Search for an organisation</label>
<input class="govuk-input" type="text" id="filter-organisations-list">
</form>
</div>
</div>

<div class="govuk-grid-row">
<div class="govuk-grid-column-full" id="search_results">
{% for letter, orgs in alphabetisedOrgs %}
<div class="govuk-grid-row js-filter-item" data-filter="block">
<div class="govuk-grid-column-one-third">
<h2 class="blockHeading govuk-heading-xl">{{ letter }}</h2>
</div>
<div class="govuk-grid-column-two-thirds" data-filter="inner-block">
<ul class="govuk-list govuk-!-margin-bottom-0">
{% for org in orgs %}
<li class="govuk-!-margin-bottom-1 js-filter-item" data-filter="item" data-filter-terms="{{org.name}}">
<a href="/organisations/{{ org.organisation }}/overview" class="govuk-link">{{ org.name }}</a>
</li>
{% endfor %}
</ul>
</div>
<div class="govuk-grid-column-full">
<hr class="govuk-section-break govuk-section-break--l govuk-section-break--visible">
</div>
</div>
{% endfor %}



</div>
</div>

</div>

<h1>Find page placeholder</h1>


{% endblock %}

{% block scripts %}
{{ super() }}
<script src="/public/js/list-filter.bundle.js"></script>
<script>
const listFilterSearchBox = document.getElementById('list-filter')
new GOVUK.Modules.ListFilter(listFilterSearchBox).init()
</script>
{% endblock %}
Loading

0 comments on commit d559418

Please sign in to comment.