Skip to content

Instantly share code, notes, and snippets.

@andysellick
Created July 17, 2017 15:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andysellick/90d68e33e8cf5056ad9ce5d86b0b3269 to your computer and use it in GitHub Desktop.
Save andysellick/90d68e33e8cf5056ad9ce5d86b0b3269 to your computer and use it in GitHub Desktop.
<%
appearance_class ||= "on-light-background"
layout_class ||= ""
search_term ||= ""
input_id ||= SecureRandom.hex(4)
label_text ||= "Search GOV.UK"
label_text = "#{label_text}".html_safe
%>
<div class="govuk-search <%= appearance_class %> <%= layout_class %>"
data-module="toggle-class"
data-toggle-class="focus"
data-toggle-onload="onloadCheckInputValue"
data-toggle-onexit="checkValueAndRemoveClassFromElement"
>
<label for="search-main-<%= input_id %>" class="search-label">
<%= label_text %>
</label>
<div class="search-wrapper">
<input type="search" value="<%= search_term %>" id="search-main-<%= input_id %>" name="q" title="Search" class="search-element search-input js-toggle-action js-toggle-target">
<div class="search-element search-submit-wrapper">
<button type="submit" class="search-submit">Search</button>
</div>
</div>
</div>
/* eslint-env jasmine */
/* eslint-disable no-multi-str */
describe('A toggle class module applied to a search box', function () {
'use strict';
var toggle;
//base element for all tests, requires some additions for the JS to work correctly
//created as a string and then converted into HTML (rather than as an element) as tests modify it
var element = '\
<div id="toggle-class-test" class="govuk-search" data-module="toggle-class">\
<label for="search-main-12345" class="search-label">\
Search GOV.UK\
</label>\
<div class="search-wrapper">\
<input type="search" value="" id="search-main-12345" name="q" title="Search" class="search-element search-input">\
<div class="search-element search-submit-wrapper">\
<button type="submit" class="search-submit">Search</button>\
</div>\
</div>\
</div>';
var elementid = '#toggle-class-test';
afterEach(function(){
$('body').find(elementid).remove();
});
describe('when the needed js-toggle-action element is not present', function () {
beforeEach(function () {
$('body').append($('<div/>').html(element));
toggle = new GOVUK.Modules.ToggleClass();
toggle.start($(elementid));
});
it('does nothing', function () {
var searchInput = $('body').find('.search-input');
expect(searchInput.is('.active')).toBe(false);
searchInput.trigger('focus');
expect(searchInput.is('.active')).toBe(false);
searchInput.trigger('blur');
expect(searchInput.is('.active')).toBe(false);
});
});
describe('when the search box is interacted with', function () {
beforeEach(function () {
$('body').append($('<div/>').html(element));
$('body').find('#search-main-12345').addClass('js-toggle-action'); //class needed to module to operate
toggle = new GOVUK.Modules.ToggleClass();
toggle.start($(elementid));
});
it('applies the focus style on focus and removes it on blur', function () {
var searchInput = $('body').find('.search-input');
expect($(elementid).is('.active')).toBe(false);
searchInput.trigger('focus');
expect($(elementid).is('.active')).toBe(true);
searchInput.trigger('blur');
expect($(elementid).is('.active')).toBe(false);
});
});
describe('when different options are passed to the module', function () {
beforeEach(function () {
$('body').append($('<div/>').html(element));
$('body').find('#search-main-12345').addClass('js-toggle-action'); //class needed to module to operate
$('body').find('#search-main-12345').addClass('js-toggle-target'); //element to toggle the class on
$('body').find('#toggle-class-test').attr('data-toggle-class','custom-focus-class'); //set a custom focus class
$('body').find('#toggle-class-test').attr('data-toggle-on-action','keydown'); //set a custom event for adding the class
$('body').find('#toggle-class-test').attr('data-toggle-off-action','keyup'); //set a custom event for removing the class
toggle = new GOVUK.Modules.ToggleClass();
toggle.start($(elementid));
});
it('works with a custom focus class and custom events', function() {
var searchInput = $('body').find('.js-toggle-action');
var targetElement = $('body').find('.js-toggle-target');
expect(targetElement.is('.custom-focus-class')).toBe(false);
searchInput.trigger('keydown');
expect(targetElement.is('.custom-focus-class')).toBe(true);
searchInput.trigger('keyup');
expect(targetElement.is('.custom-focus-class')).toBe(false);
});
});
describe('when the search box already has a value', function () {
beforeEach(function () {
$('body').append($('<div/>').html(element));
$('body').find('#search-main-12345').addClass('js-toggle-action'); //class needed to module to operate
$('body').find('#search-main-12345').addClass('js-toggle-target'); //element to toggle the class on
$('body').find('#search-main-12345').attr('value','I have already searched'); //set the value of the search box
$('body').find('#toggle-class-test').attr('data-toggle-onload','onloadCheckInputValue');
$('body').find('#toggle-class-test').attr('data-toggle-onexit','checkValueAndRemoveClassFromElement');
toggle = new GOVUK.Modules.ToggleClass();
toggle.start($(elementid));
});
it('applies the focus style on load', function() {
var targetElement = $('body').find('.js-toggle-target');
expect(targetElement.is('.active')).toBe(true);
});
it('does not remove the focus style on blur unless the value is removed', function() {
var searchInput = $('body').find('.js-toggle-action');
var targetElement = $('body').find('.js-toggle-target');
searchInput.trigger('focus');
expect(targetElement.is('.active')).toBe(true);
searchInput.trigger('blur');
expect(targetElement.is('.active')).toBe(true);
searchInput.trigger('focus');
searchInput.attr('value','');
searchInput.trigger('blur');
expect(targetElement.is('.active')).toBe(false);
});
});
});
/*
This module will apply and remove a class to an element when specified actions are performed.
Default behaviour:
- module checks for .js-toggle-action before proceeding, this element will trigger the class toggle
- if .js-toggle-action receives a focus, active class is added to container
- if .js-toggle-action receives a blur, active class is removed from container
Options:
- an element of class '.js-toggle-target' will receive the toggle class, defaults to container
- data-toggle-on-action specifies the event that will add the class, e.g. 'keydown', 'mouseenter'
- data-toggle-off-action specifies the event that will remove the class, e.g. 'keyup', 'mouseleave'
- data-toggle-class specifies the class name to toggle, defaults to 'active'
- data-toggle-onload specifies an optional function to run when the page loads
- data-toggle-onexit specifies an optional function to use when the toggle off is triggered
*/
(function (Modules) {
'use strict'
Modules.ToggleClass = function () {
this.start = function ($el) {
var self = this;
var $toggleElement = $el.find('.js-toggle-action');
if($toggleElement.length) {
var $toggleClassOnElement = $el.find('.js-toggle-target');
$toggleClassOnElement = $toggleClassOnElement.length > 0 ? $toggleClassOnElement : $el;
var userActionIn = $el.attr('data-toggle-on-action') || 'focus';
var userActionOut = $el.attr('data-toggle-off-action') || 'blur';
var focusClass = $el.attr('data-toggle-class') || 'active';
//check if we should run a function when the page loads
var onloadFunction = $el.attr('data-toggle-onload') || 0;
if (onloadFunction && typeof this[onloadFunction] === "function") {
this[onloadFunction]($toggleClassOnElement,$toggleElement,focusClass);
}
// check if we should use a function other than the default for removing the class
var onExitFunction = $el.attr('data-toggle-onexit') || 0;
if (onExitFunction && typeof this[onExitFunction] === "function") {
this.onExitFunction = this[onExitFunction];
}
$toggleElement.on(userActionIn, function() {
$toggleClassOnElement.addClass(focusClass);
});
$toggleElement.on(userActionOut, function(e) {
self.onExitFunction(e, $toggleClassOnElement, focusClass);
});
}
}
// default function for removing class
this.onExitFunction = function (e, $toggleClassOnElement, focusClass) {
$toggleClassOnElement.removeClass(focusClass);
}
// used by search boxes, only removes class if search input has no value
this.checkValueAndRemoveClassFromElement = function (e, $toggleClassOnElement, focusClass) {
if ($(e.target).val() === '') {
$toggleClassOnElement.removeClass(focusClass);
}
}
// used by search boxes to hide label if page loads with search already in search box, e.g. on search results page
this.onloadCheckInputValue = function (toggleClassOnElement, $toggleElement, focusClass) {
if (toggleClassOnElement.val() !== '') {
$toggleElement.addClass(focusClass);
}
}
}
})(window.GOVUK.Modules)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment