mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-02-08 12:23:54 -05:00
Split module out into objects for each component
Is clearer than just having one large hierarchical object and makes the relationships between the module and its components clearer.
This commit is contained in:
@@ -1,150 +1,164 @@
|
||||
(function (Modules) {
|
||||
"use strict";
|
||||
|
||||
Modules.CollapsibleCheckboxes = function() {
|
||||
const _focusTextElement = ($el) => {
|
||||
$el
|
||||
.attr('tabindex', '-1')
|
||||
.focus();
|
||||
};
|
||||
function Summary (module) {
|
||||
this.module = module;
|
||||
this.$el = module.$formGroup.find('.selection-summary');
|
||||
this.fieldLabel = module.fieldLabel;
|
||||
this.total = module.total;
|
||||
this.addContent();
|
||||
this.update(module.getSelection());
|
||||
}
|
||||
Summary.prototype.templates = {
|
||||
all: (selection, total, field) => `All ${field}s`,
|
||||
some: (selection, total, field) => `${selection} of ${total} ${field}s`,
|
||||
none: (selection, total, field) => ({
|
||||
"folder": "No folders (only templates outside a folder)",
|
||||
"team member": "No team members (only you)"
|
||||
}[field] || `No ${field}s`)
|
||||
};
|
||||
Summary.prototype.addContent = function() {
|
||||
this.$text = $(`<p class="selection-summary__text" />`);
|
||||
|
||||
this.start = function(component) {
|
||||
this.$formGroup = $(component);
|
||||
this.$fieldset = this.$formGroup.find('fieldset');
|
||||
this.$checkboxes = this.$fieldset.find('input[type=checkbox]');
|
||||
this.fieldLabel = this.$formGroup.data('fieldLabel');
|
||||
this.total = this.$checkboxes.length;
|
||||
this.legendText = this.$fieldset.find('legend').text().trim();
|
||||
this.expanded = false;
|
||||
if (this.fieldLabel === 'folder') { this.$text.addClass('selection-summary__text--folders'); }
|
||||
|
||||
this.addHeadingHideLegend();
|
||||
this.$el.append(this.$text);
|
||||
};
|
||||
Summary.prototype.update = function(selection) {
|
||||
let template;
|
||||
|
||||
// generate summary and footer
|
||||
this.summary.$el = this.$formGroup.find('.selection-summary');
|
||||
this.footer.$el = this.footer.getEl(this);
|
||||
this.footer.update(this);
|
||||
if (selection === this.total) {
|
||||
template = 'all';
|
||||
} else if (selection > 0) {
|
||||
template = 'some';
|
||||
} else {
|
||||
template = 'none';
|
||||
}
|
||||
|
||||
// create summary from component pieces and match text to current selection
|
||||
this.summary.addContent(this.legendText, this.fieldLabel);
|
||||
this.summary.update(this.getSelection(), this.total, this.fieldLabel);
|
||||
this.$fieldset.before(this.summary.$el);
|
||||
|
||||
// add custom classes
|
||||
this.$formGroup.addClass('selection-wrapper');
|
||||
this.$fieldset.addClass('selection-content');
|
||||
|
||||
// hide checkboxes
|
||||
this.$fieldset.hide();
|
||||
|
||||
this.bindEvents();
|
||||
};
|
||||
this.getSelection = function() { return this.$checkboxes.filter(':checked').length; };
|
||||
this.addHeadingHideLegend = function() {
|
||||
const headingLevel = this.$formGroup.data('heading-level') || '2';
|
||||
|
||||
this.$heading = $(`<h${headingLevel} class="heading-small">${this.legendText}</h${headingLevel}>`);
|
||||
this.$fieldset.before(this.$heading);
|
||||
|
||||
this.$fieldset.find('legend').addClass('visuallyhidden');
|
||||
};
|
||||
this.summary = {
|
||||
templates: {
|
||||
all: (selection, total, field) => `All ${field}s`,
|
||||
some: (selection, total, field) => `${selection} of ${total} ${field}s`,
|
||||
none: (selection, total, field) => ({
|
||||
"folder": "No folders (only templates outside a folder)",
|
||||
"team member": "No team members (only you)"
|
||||
}[field] || `No ${field}s`)
|
||||
},
|
||||
addContent: function(legendText, fieldLabel) {
|
||||
this.$text = $(`<p class="selection-summary__text" />`);
|
||||
|
||||
if (fieldLabel === 'folder') { this.$text.addClass('selection-summary__text--folders'); }
|
||||
|
||||
this.$el.append(this.$text);
|
||||
},
|
||||
update: function(selection, total, field) {
|
||||
let template;
|
||||
|
||||
if (selection === total) {
|
||||
template = 'all';
|
||||
} else if (selection > 0) {
|
||||
template = 'some';
|
||||
} else {
|
||||
template = 'none';
|
||||
}
|
||||
|
||||
this.$text.html(this.templates[template](selection, total, field));
|
||||
}
|
||||
};
|
||||
this.footer = {
|
||||
buttonContent: {
|
||||
change: (fieldLabel) => `Choose ${fieldLabel}s`,
|
||||
done: (fieldLabel) => `Done<span class="visuallyhidden"> choosing ${fieldLabel}s</span>`
|
||||
},
|
||||
getEl: function (module) {
|
||||
const buttonState = module.expanded ? 'done' : 'change';
|
||||
const buttonContent = this.buttonContent[buttonState](module.fieldLabel);
|
||||
|
||||
return $(`<div class="selection-footer">
|
||||
<button
|
||||
class="button button-secondary"
|
||||
aria-expanded="${module.expanded ? 'true' : 'false'}"
|
||||
aria-controls="${module.$fieldset.attr('id')}">
|
||||
${buttonContent}
|
||||
</button>
|
||||
</div>`);
|
||||
},
|
||||
update: function (module) {
|
||||
this.$el.remove();
|
||||
this.$el = this.getEl(module);
|
||||
|
||||
module.$formGroup.append(this.$el);
|
||||
}
|
||||
};
|
||||
this.expand = function(e) {
|
||||
if (e !== undefined) { e.preventDefault(); }
|
||||
|
||||
if (!this.expanded) {
|
||||
this.$fieldset.show();
|
||||
this.expanded = true;
|
||||
this.footer.update(this);
|
||||
}
|
||||
|
||||
// shift focus whether expanded or not
|
||||
_focusTextElement(this.$fieldset);
|
||||
};
|
||||
this.collapse = function(e) {
|
||||
if (e !== undefined) { e.preventDefault(); }
|
||||
|
||||
if (this.expanded) {
|
||||
this.$fieldset.hide();
|
||||
this.expanded = false;
|
||||
this.footer.update(this);
|
||||
}
|
||||
|
||||
// shift focus whether expanded or not
|
||||
_focusTextElement(this.summary.$text);
|
||||
};
|
||||
this.handleClick = function(e) {
|
||||
if (this.expanded) {
|
||||
this.collapse(e);
|
||||
} else {
|
||||
this.expand(e);
|
||||
}
|
||||
};
|
||||
this.handleSelection = function(e) {
|
||||
this.summary.update(this.getSelection(), this.total, this.fieldLabel);
|
||||
};
|
||||
this.bindEvents = function() {
|
||||
const self = this;
|
||||
|
||||
this.$formGroup.on('click', '.button', this.handleClick.bind(this));
|
||||
this.$checkboxes.on('click', this.handleSelection.bind(this));
|
||||
|
||||
// take summary out of tab order when focus moves
|
||||
this.summary.$el.on('blur', (e) => $(this).attr('tabindex', '-1'));
|
||||
};
|
||||
this.$text.html(this.templates[template](selection, this.total, this.fieldLabel));
|
||||
};
|
||||
Summary.prototype.bindEvents = function () {
|
||||
// take summary out of tab order when focus moves
|
||||
this.$el.on('blur', (e) => $(this).attr('tabindex', '-1'));
|
||||
};
|
||||
|
||||
function Footer (module) {
|
||||
this.module = module;
|
||||
this.fieldLabel = module.fieldLabel;
|
||||
this.fieldsetId = module.$fieldset.attr('id');
|
||||
this.$el = this.getEl(this.module.expanded);
|
||||
this.module.$formGroup.append(this.$el);
|
||||
}
|
||||
Footer.prototype.buttonContent = {
|
||||
change: (fieldLabel) => `Choose ${fieldLabel}s`,
|
||||
done: (fieldLabel) => `Done<span class="visuallyhidden"> choosing ${fieldLabel}s</span>`
|
||||
};
|
||||
Footer.prototype.getEl = function (expanded) {
|
||||
const buttonState = expanded ? 'done' : 'change';
|
||||
const buttonContent = this.buttonContent[buttonState](this.fieldLabel);
|
||||
|
||||
return $(`<div class="selection-footer">
|
||||
<button
|
||||
class="button button-secondary"
|
||||
aria-expanded="${expanded ? 'true' : 'false'}"
|
||||
aria-controls="${this.fieldsetId}">
|
||||
${buttonContent}
|
||||
</button>
|
||||
</div>`);
|
||||
};
|
||||
Footer.prototype.update = function (expanded) {
|
||||
this.$el.remove();
|
||||
this.$el = this.getEl(expanded);
|
||||
|
||||
this.module.$formGroup.append(this.$el);
|
||||
};
|
||||
|
||||
function CollapsibleCheckboxes () {}
|
||||
CollapsibleCheckboxes.prototype._focusTextElement = ($el) => {
|
||||
$el
|
||||
.attr('tabindex', '-1')
|
||||
.focus();
|
||||
};
|
||||
CollapsibleCheckboxes.prototype.start = function(component) {
|
||||
this.$formGroup = $(component);
|
||||
this.$fieldset = this.$formGroup.find('fieldset');
|
||||
this.$checkboxes = this.$fieldset.find('input[type=checkbox]');
|
||||
this.fieldLabel = this.$formGroup.data('fieldLabel');
|
||||
this.total = this.$checkboxes.length;
|
||||
this.legendText = this.$fieldset.find('legend').text().trim();
|
||||
this.expanded = false;
|
||||
|
||||
this.addHeadingHideLegend();
|
||||
|
||||
// generate summary and footer
|
||||
this.footer = new Footer(this);
|
||||
this.summary = new Summary(this);
|
||||
|
||||
this.$fieldset.before(this.summary.$el);
|
||||
|
||||
// add custom classes
|
||||
this.$formGroup.addClass('selection-wrapper');
|
||||
this.$fieldset.addClass('selection-content');
|
||||
|
||||
// hide checkboxes
|
||||
this.$fieldset.hide();
|
||||
|
||||
this.bindEvents();
|
||||
};
|
||||
CollapsibleCheckboxes.prototype.getSelection = function() { return this.$checkboxes.filter(':checked').length; };
|
||||
CollapsibleCheckboxes.prototype.addHeadingHideLegend = function() {
|
||||
const headingLevel = this.$formGroup.data('heading-level') || '2';
|
||||
|
||||
this.$heading = $(`<h${headingLevel} class="heading-small">${this.legendText}</h${headingLevel}>`);
|
||||
this.$fieldset.before(this.$heading);
|
||||
|
||||
this.$fieldset.find('legend').addClass('visuallyhidden');
|
||||
};
|
||||
CollapsibleCheckboxes.prototype.expand = function(e) {
|
||||
if (e !== undefined) { e.preventDefault(); }
|
||||
|
||||
if (!this.expanded) {
|
||||
this.$fieldset.show();
|
||||
this.expanded = true;
|
||||
this.summary.update(this.getSelection());
|
||||
this.footer.update(this.expanded);
|
||||
}
|
||||
|
||||
// shift focus whether expanded or not
|
||||
this._focusTextElement(this.$fieldset);
|
||||
};
|
||||
CollapsibleCheckboxes.prototype.collapse = function(e) {
|
||||
if (e !== undefined) { e.preventDefault(); }
|
||||
|
||||
if (this.expanded) {
|
||||
this.$fieldset.hide();
|
||||
this.expanded = false;
|
||||
this.summary.update(this.getSelection());
|
||||
this.footer.update(this.expanded);
|
||||
}
|
||||
|
||||
// shift focus whether expanded or not
|
||||
this._focusTextElement(this.summary.$text);
|
||||
};
|
||||
CollapsibleCheckboxes.prototype.handleClick = function(e) {
|
||||
if (this.expanded) {
|
||||
this.collapse(e);
|
||||
} else {
|
||||
this.expand(e);
|
||||
}
|
||||
};
|
||||
CollapsibleCheckboxes.prototype.handleSelection = function(e) {
|
||||
this.summary.update(this.getSelection(), this.total, this.fieldLabel);
|
||||
};
|
||||
CollapsibleCheckboxes.prototype.bindEvents = function() {
|
||||
const self = this;
|
||||
|
||||
this.$formGroup.on('click', '.button', this.handleClick.bind(this));
|
||||
this.$checkboxes.on('click', this.handleSelection.bind(this));
|
||||
|
||||
this.summary.bindEvents(this);
|
||||
};
|
||||
|
||||
Modules.CollapsibleCheckboxes = CollapsibleCheckboxes;
|
||||
|
||||
}(window.GOVUK.Modules));
|
||||
|
||||
Reference in New Issue
Block a user