Skip to content

Commit

Permalink
Merge pull request #2425 from bitzesty/focus-management-appear-disappear
Browse files Browse the repository at this point in the history
Focus management
  • Loading branch information
Lubosky authored Jul 27, 2023
2 parents a84c263 + 0d41fab commit d412689
Show file tree
Hide file tree
Showing 56 changed files with 294 additions and 139 deletions.
3 changes: 3 additions & 0 deletions Procfile.webpack
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
web: bundle exec rake cf:run_migrations db:migrate && bin/rails server -p $PORT
worker: bundle exec sidekiq -L ./log/worker.log -C ./config/sidekiq.yml
webpack: bin/webpack-dev-server
25 changes: 25 additions & 0 deletions app/assets/javascripts/admin/focusable.js.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
FOCUSABLE_ELEMENTS = 'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), [href], [tabindex]:not([tabindex="-1"]):not([disabled]), details:not([disabled]), summary:not(:disabled)'
FOCUSABLE_FORM_ELEMENTS = 'input:not([disabled]), select:not([disabled]), textarea:not([disabled])'

window.FOCUSABLE_ELEMENTS = FOCUSABLE_ELEMENTS
window.FOCUSABLE_FORM_ELEMENTS = FOCUSABLE_FORM_ELEMENTS

visible = (el) ->
!el.hidden and (!el.type or el.type != 'hidden') and (el.offsetWidth > 0 or el.offsetHeight > 0)

focusable = (el) ->
el.tabIndex >= 0 and !el.disabled and visible(el)

window.focusElement = (el, elements = FOCUSABLE_ELEMENTS) ->
autofocusElement = Array.from(el.querySelectorAll(elements)).filter(focusable)[0]
if autofocusElement
autofocusElement.focus()
return

window.autofocusElement = (el) ->
autofocusElement = Array.from(el.querySelectorAll('[autofocus]')).filter(focusable)[0]
if !autofocusElement
autofocusElement = el
el.setAttribute('tabindex', '-1')
autofocusElement.focus()
return
7 changes: 5 additions & 2 deletions app/assets/javascripts/admin/form_answers.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,12 @@ ready = ->
if $('.form_answer_attachment').length == 0
sidebarSection.find(".document-list .p-empty").removeClass("visuallyhidden")

$(document).on "click", ".form-edit-link", (e) ->
$(document).on 'click', '.form-edit-link', (e) ->
e.preventDefault()
$(this).closest(".form-group").addClass("form-edit")
element = this.closest('.form-group')
if (element)
element.classList.add('form-edit')

$(".submit-assessment").on "ajax:error", (e, data, status, xhr) ->
errors = data.responseJSON
$(this).addClass("field-with-errors")
Expand Down
8 changes: 4 additions & 4 deletions app/assets/javascripts/admin/settings.js.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,20 @@ jQuery ->
settingsWrapper.on "click", ".btn-cancel", (e) ->
e.preventDefault()

form_well = ($ e.currentTarget).closest('.well')
well = ($ e.currentTarget).closest('.well')

if form_well.hasClass("deadline-form")
if well.hasClass("deadline-form")
wrapper = ($ e.currentTarget).closest('.deadline')
($ ".form-value", wrapper).removeClass("hidden")
($ ".deadline-form", wrapper).addClass("hidden")
($ ".edit-deadline", wrapper).removeClass("hidden")

else if form_well.hasClass("notification-edit-form")
else if well.hasClass("notification-edit-form")
wrapper = ($ e.currentTarget).closest('li')
($ ".form-value", wrapper).removeClass("hidden")
($ ".notification-edit-form", wrapper).addClass("hidden")
($ ".actions", wrapper).removeClass("hidden")

else if form_well.hasClass("notification-new-form")
else if well.hasClass("notification-new-form")
wrapper = ($ e.currentTarget).closest('.panel-section')
($ ".notification-form", wrapper).addClass("hidden")
6 changes: 6 additions & 0 deletions app/javascript/admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Application } from '@hotwired/stimulus';
import { definitionsFromContext } from '@hotwired/stimulus-webpack-helpers';

const application = Application.start();
const context = require.context('controllers', true, /_controller\.js$/);
application.load(definitionsFromContext(context));
35 changes: 35 additions & 0 deletions app/javascript/application_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Controller } from '@hotwired/stimulus';

export default class extends Controller {
get classList() {
return this.element.classList;
}

get csrfToken() {
return this.metaValue('csrf-token');
}

dispatch(eventName, { target = this.element, detail = {}, bubbles = true, cancelable = true } = {}) {
const type = `${this.identifier}:${eventName}`;
const event = new CustomEvent(type, { detail, bubbles, cancelable });
target.dispatchEvent(event);
return event;
}

observeMutations(callback, target = this.element, options = { childList: true, subtree: true }) {
const observer = new MutationObserver((mutations) => {
observer.disconnect();
Promise.resolve().then(start); // eslint-disable-line no-use-before-define
callback.call(this, mutations);
});
function start() {
if (target.isConnected) observer.observe(target, options);
}
start();
}

metaValue(name) {
const element = document.head.querySelector(`meta[name="${name}"]`);
return element && element.getAttribute('content');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default class extends ApplicationController {
static targets = ['element'];

connect() {
this.elementTargets[0].focus();
}
}
50 changes: 50 additions & 0 deletions app/javascript/controllers/element_focus_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
export default class extends ApplicationController {
static targets = ['reveal', 'dismiss'];
static values = {
selector: String
}

connect() {
if (this.hasRevealTarget) {
this.revealTarget.addEventListener('click', () => setTimeout(() => this.focusFirstElement(), 1))
}

if (this.hasDismissTarget) {
this.dismissTarget.addEventListener('click', () => setTimeout(() => this.focusElement(), 1))
}
}

focusElement(_event) {
if (this.hasRevealTarget && this.focusable(this.revealTarget)) {
this.revealTarget.focus()
}
}

focusFirstElement(_event) {
const element = this.focusableElements[0]

if (element) {
element.focus()
}
}

// Private

visible(el) {
return !el.hidden && (!el.type || el.type != 'hidden') && (el.offsetWidth > 0 || el.offsetHeight > 0)
}

focusable(el) {
return el.tabIndex >= 0 && !el.disabled && this.visible(el)
}

get focusableElements() {
return Array.from(this.element.querySelectorAll(this.selectors)).filter((el) => this.focusable(el)) || []
}

get selectors() {
return this.hasSelectorValue ?
this.selectorValue :
'input:not([disabled]), select:not([disabled]), textarea:not([disabled])'
}
}
5 changes: 5 additions & 0 deletions app/javascript/controllers/element_removal_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default class extends ApplicationController {
remove() {
this.element.remove();
}
}
1 change: 1 addition & 0 deletions app/javascript/packs/application-admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require('admin')
2 changes: 1 addition & 1 deletion app/views/admin/comments/_form.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
label
' Add a comment
= f.hidden_field :section, id: "#{f.object.section}_section_hidden_field"
= f.text_area :body, id: "#{f.object.section}_comment_body", class: 'form-control', rows: 4, "data-behavior" => "autosave", "data-autosave-key" => "#{@form_answer.id}-#{f.object.section}-new-comment"
= f.text_area :body, id: "#{f.object.section}_comment_body", autofocus: true, class: 'form-control', rows: 4, "data-behavior" => "autosave", "data-autosave-key" => "#{@form_answer.id}-#{f.object.section}-new-comment"
.comment-actions
= f.check_box :flagged, id: "#{f.object.section}_flagged_hidden_checkbox"
= link_to "#flag-comment", class: "link-flag-comment js-link-flag-comment"
Expand Down
6 changes: 3 additions & 3 deletions app/views/admin/feedbacks/_feedback_fields.html.slim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.form-group
.form-group[data-controller="element-focus"]
.form-container
strong = feedback_field_value[:label]
.form-group class=feedback_field
Expand All @@ -25,10 +25,10 @@
.clear

- if policy(feedback).update?
= link_to "#", class: "form-edit-link pull-right" do
= link_to "#", class: "form-edit-link pull-right", data: { element_focus_target: "reveal" } do
span.glyphicon.glyphicon-pencil
' Edit
.form-actions.text-right
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link if-no-js-hide"
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link if-no-js-hide", data: { element_focus_target: "dismiss" }
= link_to "Save", "#", class: "btn btn-primary form-save-link pull-right"
.clear
6 changes: 3 additions & 3 deletions app/views/admin/feedbacks/_overall_feedback_field.html.slim
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.form-group
.form-group[data-controller="element-focus"]
.form-container
strong Overall Summary
.form-value.no-js-update
Expand All @@ -11,10 +11,10 @@
.clear

- if policy(feedback).update?
= link_to "#", class: "form-edit-link pull-right" do
= link_to "#", class: "form-edit-link pull-right", data: { element_focus_target: "reveal" } do
span.glyphicon.glyphicon-pencil
' Edit
.form-actions.text-right
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link if-no-js-hide"
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link if-no-js-hide", data: { element_focus_target: "dismiss" }
= link_to "Save", "#", class: "btn btn-primary form-save-link pull-right"
.clear
12 changes: 6 additions & 6 deletions app/views/admin/form_answers/_assessors_section.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
= resource.lead_assessors
.clear

li.form-group.primary-assessor-assignment
li.form-group.primary-assessor-assignment[data-controller="element-focus"]
label[for='assessor_assignment_primary_assessor_id']
' Primary:
.form-value class=("ellipsis edit-value" if policy(resource).assign_assessor?)
Expand All @@ -35,18 +35,18 @@
id: 'assessor_assignment_primary_assessor_id',
class: 'form-control custom-select'

= link_to "#", class: "form-edit-link"
= link_to "#", class: "form-edit-link", data: { element_focus_target: "reveal" }
span.glyphicon.glyphicon-pencil
' Edit
.clear
.form-actions.text-right
span.if-no-js-hide
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link"
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link", data: { element_focus_target: "dismiss" }
= form.submit "Save", class: "btn btn-primary form-save-button"
.errors-holder
.clear

li.form-group.secondary-assessor-assignment
li.form-group.secondary-assessor-assignment[data-controller="element-focus"]
label[for='assessor_assignment_secondary_assessor_id']
span.assessor-label Secondary:
.form-value class=("ellipsis edit-value" if policy(resource).assign_assessor?)
Expand All @@ -69,13 +69,13 @@
id: 'assessor_assignment_secondary_assessor_id',
class: 'form-control custom-select'

= link_to "#", class: "form-edit-link"
= link_to "#", class: "form-edit-link", data: { element_focus_target: "reveal" }
span.glyphicon.glyphicon-pencil
' Edit
.clear
.form-actions.text-right
span.if-no-js-hide
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link"
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link", data: { element_focus_target: "dismiss" }
= form.submit "Save", class: "btn btn-primary form-save-button"
.errors-holder
.clear
8 changes: 4 additions & 4 deletions app/views/admin/form_answers/_financials_review.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

= f.input :form_answer_id, as: :hidden

.form-group
.form-group[data-controller="element-focus"]
= render "admin/form_answers/financial_summary/audit_certificate"
.input.form-group.form-fields
.radio
Expand All @@ -27,12 +27,12 @@
.input.form-group.form-fields.form-block
= f.input :changes_description, as: :text, input_html: { rows: 5, class: "form-control" }, label: "Changes made"
.clear
= link_to "#", class: "form-edit-link pull-right edit-review-audit"
= link_to "#", class: "form-edit-link pull-right edit-review-audit", data: { element_focus_target: "reveal" }
span.glyphicon.glyphicon-pencil
' Edit
.form-actions.text-left
.form-fields
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link if-no-js-hide"
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link if-no-js-hide", data: { element_focus_target: "dismiss" }
= link_to "Save", "#", class: "btn btn-primary save-review-audit pull-right", style: "display: none"

.clear
Expand All @@ -53,7 +53,7 @@

= f.input :form_answer_id, as: :hidden

.form-group
.form-group[data-controller="element-focus"]
= render "admin/form_answers/financial_summary/shortlisted_financial_docs", docs_wrapper: resource.shortlisted_documents_wrapper
.input.form-group.form-fields
.radio
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.panel.panel-default
.panel.panel-default[data-controller="element-focus"]
.panel-heading#admin-comments-heading
h3.panel-title
a data-toggle="collapse" data-parent="#panel-application-info" href="#section-admin-comments" aria-expanded="true" aria-controls="section-admin-comments"
a data-toggle="collapse" data-parent="#panel-application-info" href="#section-admin-comments" aria-expanded="true" aria-controls="section-admin-comments" data-element-focus-target="reveal"
' Admin Comments
span.comments-number
- if @form_answer.comments.admin.any?
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
.panel.panel-default
.panel.panel-default[data-controller="element-focus"]
.panel-heading#company-details-heading
h3.panel-title
a data-toggle="collapse" data-parent="#panel-application-info" href="#section-company-details" aria-expanded="true" aria-controls="section-company-details"
a data-toggle="collapse" data-parent="#panel-application-info" href="#section-company-details" aria-expanded="true" aria-controls="section-company-details" data-element-focus-target="reveal"
= @form_answer.promotion? ? "Nominee Details" : "Company Details"
- if resource.company_details_updated_at && resource.company_details_editable
small
= "Updated by: #{resource.company_details_editable.decorate.full_name} - #{format_date(resource.company_details_updated_at)}"


#section-company-details.section-company-details.panel-collapse.collapse aria-labelledby="company-details-heading"
.panel-body.company-details-forms
= render "admin/form_answers/company_details/company_name_form"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.panel.panel-default
.panel.panel-default[data-controller="element-focus"]
.panel-heading#critical-comments-heading
h3.panel-title
a data-toggle="collapse" data-parent="#panel-assessment" href="#section-critical-comments" aria-expanded="true" aria-controls="section-critical-comments"
a data-toggle="collapse" data-parent="#panel-assessment" href="#section-critical-comments" aria-expanded="true" aria-controls="section-critical-comments" data-element-focus-target="reveal"
' Critical Comments
span.comments-number
- if @form_answer.comments.critical.any?
Expand Down
10 changes: 5 additions & 5 deletions app/views/admin/form_answers/_section_draft_notes.html.slim
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
- @form_answer.draft_note || @form_answer.build_draft_note

.panel.panel-default
.panel.panel-default[data-controller="element-focus"]
.panel-heading#draft-notes-heading
h3.panel-title
a data-toggle="collapse" data-parent="#panel-assessment" href="#section-draft-notes" aria-expanded="true" aria-controls="section-draft-notes"
a data-toggle="collapse" data-parent="#panel-assessment" href="#section-draft-notes" aria-expanded="true" aria-controls="section-draft-notes" data-element-focus-target="reveal"
' Draft Notes
- if @form_answer.draft_note.decorate.try(:last_updated_by).present?
small= @form_answer.draft_note.decorate.try(:last_updated_by)
Expand All @@ -14,7 +14,7 @@
authenticity_token: true,
html: { "data-type" => "json" }) do |f|

.form-group class="#{'form-edit' if f.object.content.blank?}"
.form-group[class="#{'form-edit' if f.object.content.blank?}" data-controller="element-focus"]
.form-container
.form-value
p
Expand All @@ -28,11 +28,11 @@
as: :text,
label: false

= link_to "#", class: "form-edit-link pull-right"
= link_to "#", class: "form-edit-link pull-right", data: { element_focus_target: "reveal" }
span.glyphicon.glyphicon-pencil
' Edit
.form-actions.text-right
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link if-no-js-hide"
= link_to "Cancel", "#", class: "btn btn-default form-cancel-link if-no-js-hide", data: { element_focus_target: "dismiss" }
= f.submit "Save", class: "btn btn-primary form-save-link pull-right if-js-hide"
= link_to "Save", "#", class: "btn btn-primary form-save-link pull-right if-no-js-hide"
.clear
4 changes: 2 additions & 2 deletions app/views/admin/form_answers/_section_press_summary.html.slim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.panel.panel-default
.panel.panel-default[data-controller="element-focus"]
.panel-heading#press-summary-heading
h3.panel-title
a data-toggle="collapse" data-parent="#panel-winners" href="#section-press-summary" aria-expanded="true" aria-controls="section-press-summary"
a data-toggle="collapse" data-parent="#panel-winners" href="#section-press-summary" aria-expanded="true" aria-controls="section-press-summary" data-element-focus-target="reveal"
' Press Book Notes
small.updated-by
- if @form_answer.press_summary_updated_by
Expand Down
Loading

0 comments on commit d412689

Please sign in to comment.