Skip to content

Commit

Permalink
Move code for accessible autocomplete component into app
Browse files Browse the repository at this point in the history
This component was removed in version 18 of govuk_publishing_components alphagov/govuk_publishing_components#1038

We need to move the code into the app so that we can update govuk_publishing_components and other dependencies.

https://trello.com/c/egaF1kjo/3110-replace-accessible-autocomplete-for-content-data-admin
  • Loading branch information
MuriloDalRi committed Mar 29, 2023
1 parent a8baf6a commit 087720b
Show file tree
Hide file tree
Showing 12 changed files with 451 additions and 4 deletions.
79 changes: 79 additions & 0 deletions app/assets/javascripts/components/accessible-autocomplete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* eslint-env jquery */
/* global accessibleAutocomplete */
// = require accessible-autocomplete/dist/accessible-autocomplete.min.js

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

(function (Modules) {
'use strict'

Modules.AccessibleAutocomplete = function () {
var $selectElem

this.start = function ($element) {
$selectElem = $element.find('select')

var configOptions = {
selectElement: document.getElementById($selectElem.attr('id')),
showAllValues: true,
confirmOnBlur: true,
preserveNullOptions: true, // https://github.com/alphagov/accessible-autocomplete#null-options
defaultValue: ''
}

configOptions.onConfirm = this.onConfirm

new accessibleAutocomplete.enhanceSelectElement(configOptions) // eslint-disable-line no-new, new-cap
// attach the onConfirm function to data attr, to call it in finder-frontend when clearing facet tags
$selectElem.data('onconfirm', this.onConfirm)
}

this.onConfirm = function (label, value, removeDropDown) {
function escapeHTML (str) {
return new window.Option(str).innerHTML
}

if ($selectElem.data('track-category') !== undefined && $selectElem.data('track-action') !== undefined) {
track($selectElem.data('track-category'), $selectElem.data('track-action'), label, $selectElem.data('track-options'))
}
// This is to compensate for the fact that the accessible-autocomplete library will not
// update the hidden select if the onConfirm function is supplied
// https://github.com/alphagov/accessible-autocomplete/issues/322
if (typeof label !== 'undefined') {
if (typeof value === 'undefined') {
value = $selectElem.children('option').filter(function () { return $(this).html() === escapeHTML(label) }).val()
}

if (typeof value !== 'undefined') {
var $option = $selectElem.find('option[value=\'' + value + '\']')
// if removeDropDown we are clearing the selection from outside the component
var selectState = typeof removeDropDown === 'undefined'
$option.prop('selected', selectState)
$selectElem.change()
}

// used to clear the autocomplete when clicking on a facet tag in finder-frontend
// very brittle but menu visibility is determined by autocomplete after this function is called
// setting autocomplete val to '' causes menu to appear, we don't want that, this solves it
// ideally will rewrite autocomplete to have better hooks in future
if (removeDropDown) {
$selectElem.closest('.app-c-accessible-autocomplete').addClass('app-c-accessible-autocomplete--hide-menu')
setTimeout(function () {
$('.autocomplete__menu').remove() // this element is recreated every time the user starts typing
$selectElem.closest('.app-c-accessible-autocomplete').removeClass('app-c-accessible-autocomplete--hide-menu')
}, 100)
}
}
}

function track (category, action, label, options) {
if (window.GOVUK.analytics && window.GOVUK.analytics.trackEvent) {
options = options || {}
options.label = label

window.GOVUK.analytics.trackEvent(category, action, options)
}
}
}
})(window.GOVUK.Modules)
1 change: 1 addition & 0 deletions app/assets/stylesheets/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ $govuk-global-styles: true;
@include _govuk-font-face-nta;

// local components
@import "components/accessible-autocomplete";
@import "components/chart";
@import "components/ga_data_notice";
@import "components/glance-metric";
Expand Down
39 changes: 39 additions & 0 deletions app/assets/stylesheets/components/_accessible-autocomplete.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
.app-c-accessible-autocomplete {
.autocomplete__input {
z-index: 1;
}

.autocomplete__dropdown-arrow-down {
z-index: 0;
}

.autocomplete__option {
@include govuk-font(19);
}

// needed as frontend doesn't currently support select multiples
// hopefully will in future and this can be removed
.govuk-select[multiple] {
height: auto;
}

.js-enabled & .app-c-autocomplete__multiselect-instructions {
display: none;
}

.autocomplete__list .autocomplete__option {
padding: 5px 6px;

&:before {
position: relative;
top: 3px;
padding-top: 2px;
}
}
}

.app-c-accessible-autocomplete--hide-menu {
.autocomplete__menu {
display: none;
}
}
33 changes: 33 additions & 0 deletions app/views/components/_accessible_autocomplete.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<%
id ||= "autocomplete-#{SecureRandom.hex(4)}"
label ||= nil
data_attributes ||= nil
options ||= []
selected_option ||= nil
multiple ||= false

classes = %w(app-c-accessible-autocomplete govuk-form-group)
%>
<% if label && options.any? %>
<%= tag.div class: classes, data: { module: "accessible-autocomplete" } do %>
<%=
render "govuk_publishing_components/components/label", {
html_for: id
}.merge(label.symbolize_keys)
%>
<% if multiple %>
<span class="govuk-hint app-c-autocomplete__multiselect-instructions">
<%= t('components.autocomplete.multiselect') %>
</span>
<% end %>
<%=
select_tag(
id,
options_for_select(options, selected_option),
multiple: multiple,
class: "govuk-select",
data: data_attributes
)
%>
<% end %>
<% end %>
48 changes: 48 additions & 0 deletions app/views/components/docs/accessible_autocomplete.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Accessible autocomplete
description: An autocomplete component, built to be accessible.
body: |
This component uses the [Accessible Autocomplete](https://github.com/alphagov/accessible-autocomplete) code to create an accessible autocomplete element. The autocomplete is created with the `showAllValues`
option set to `true` and the `confirmOnBlur` option set to `false` (see [Autocomplete examples](https://alphagov.github.io/accessible-autocomplete/examples) here). It also depends upon the
[label component](https://github.com/component-guide/label).
If Javascript is disabled, the component appears as a select box, so the user can still select an option.
accessibility_criteria: |
[Accessibility acceptance criteria](https://github.com/alphagov/accessible-autocomplete/blob/master/accessibility-criteria.md)
examples:
default:
data:
label:
text: 'Countries'
options: [['', ''], ['France', 'fr'], ['Germany', 'de'], ['Sweden', 'se'], ['Switzerland', 'ch'], ['United Kingdom', 'gb'], ['United States', 'us'], ['The Separate Customs Territory of Taiwan, Penghu, Kinmen, and Matsu (Chinese Taipei)', 'tw']]
with_unique_identifier:
data:
id: 'unique-autocomplete'
label:
text: 'Countries'
options: [['', ''], ['France', 'fr'], ['Germany', 'de'], ['Sweden', 'se'], ['Switzerland', 'ch'], ['United Kingdom', 'gb'], ['United States', 'us']]
with_selected_option_chosen:
data:
id: 'selected-option-chosen-autocomplete'
label:
text: 'Countries'
options: [['', ''], ['France', 'fr'], ['Germany', 'de'], ['Sweden', 'se'], ['Switzerland', 'ch'], ['United Kingdom', 'gb'], ['United States', 'us']]
selected_option: ['United Kingdom', 'gb']
with_tracking_enabled:
description: |
This example shows tracking enabled on an autocomplete. Tracking will be enabled automatically when `track_category` and `track_action` are specified in `data_attributes`.
data:
id: 'tracking-enabled-autocomplete'
label:
text: 'Countries'
options: [['', ''], ['France', 'fr'], ['Germany', 'de'], ['Sweden', 'se'], ['Switzerland', 'ch'], ['United Kingdom', 'gb'], ['United States', 'us']]
data_attributes:
track_category: 'chosen_category'
track_action: 'chosen_action'
track_option:
custom_dimension: 'your_custom_dimension'
with_multiple_selections:
description: This parameter allows the user to choose more than one option from the autocomplete. The component generates a select multiple in place of a regular select, and the autocomplete Javascript detects this and provides the multiple selection functionality.
data:
multiple: true
label:
text: 'Countries'
options: [['France', 'fr'], ['Germany', 'de'], ['Sweden', 'se'], ['Switzerland', 'ch'], ['United Kingdom', 'gb'], ['United States', 'us'], ['The Separate Customs Territory of Taiwan, Penghu, Kinmen, and Matsu (Chinese Taipei)', 'tw']]
4 changes: 2 additions & 2 deletions app/views/content/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
value: params[:search_term],
} %>

<%= render "govuk_publishing_components/components/accessible_autocomplete",
<%= render "components/accessible_autocomplete",
{
id: 'document_type',
label: {
Expand All @@ -51,7 +51,7 @@
selected_option: @filter.selected_document_type(params)
}
%>
<%= render "govuk_publishing_components/components/accessible_autocomplete",
<%= render "components/accessible_autocomplete",
{
id: 'organisation_id',
label: {
Expand Down
2 changes: 1 addition & 1 deletion app/views/development/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<%= content_for :title, "Dev page" %>


<%= render "govuk_publishing_components/components/accessible_autocomplete", {
<%= render "components/accessible_autocomplete", {
label: {
text: "Countries"
},
Expand Down
1 change: 1 addition & 0 deletions config/initializers/assets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
# Rails.application.config.assets.precompile += %w( admin.js admin.css )
Rails.application.config.assets.precompile += %w[accessible-autocomplete/dist/accessible-autocomplete.min.css]
4 changes: 3 additions & 1 deletion config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@
# available at https://guides.rubyonrails.org/i18n.html.

en:
hello: "Hello world"
components:
autocomplete:
multiselect: "To select multiple items in a list, hold down Ctrl (PC) or Cmd (Mac) key."
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
"stylelint": {
"extends": "stylelint-config-gds/scss"
},
"Dependencies": {
"accessible-autocomplete": "git://github.com/alphagov/accessible-autocomplete.git#add-multiselect-support"
},
"devDependencies": {
"standardx": "^7.0.0",
"stylelint": "^14.16.1",
Expand Down
66 changes: 66 additions & 0 deletions spec/components/accessible_autocomplete_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require "rails_helper"

RSpec.describe "AccessibleAutocomplete", type: :view do
it "renders select element" do
render_component(
id: "basic-autocomplete",
label: { text: "Countries" },
options: [["United Kingdom", "gb"], ["United States", "us"]],
)

assert_select ".govuk-label", text: "Countries", for: "basic-autocomplete"
assert_select "select#basic-autocomplete"
assert_select "select[multiple]", false
assert_select "select#basic-autocomplete option[value=gb]"
assert_select "select#basic-autocomplete option[value=gb]", text: "United Kingdom"
assert_select "select#basic-autocomplete option[value=us]"
assert_select "select#basic-autocomplete option[value=us]", text: "United States"
end

it "renders select element with selected value" do
render_component(
id: "basic-autocomplete",
label: { text: "Countries" },
options: [["United Kingdom", "gb"], ["United States", "us"]],
selected_option: ["United States", "us"],
)

assert_select ".govuk-label", text: "Countries", for: "basic-autocomplete"
assert_select "select#basic-autocomplete"
assert_select "select#basic-autocomplete option[value=gb]"
assert_select "select#basic-autocomplete option[value=us][selected]"
end

it "does not render when no data is specified" do
assert_empty render_component({})
end

it "does not render when no label is specified" do
assert_empty render_component(
id: "basic-autocomplete",
options: [["United Kingdom", "gb"], ["United States", "us"]],
)
end

it "does not render when no options are specified" do
assert_empty render_component(
id: "basic-autocomplete",
label: { text: "Countries" },
)
end

it "renders the multiple selection version correctly" do
render_component(
multiple: true,
label: { text: "Countries" },
options: [["United Kingdom", "gb"], ["United States", "us"]],
)

assert_select "select[multiple]"
assert_select ".app-c-autocomplete__multiselect-instructions"
end

def render_component(locals)
render partial: "components/accessible_autocomplete", locals:
end
end
Loading

0 comments on commit 087720b

Please sign in to comment.