@ministryofjustice/frontend
Advanced tools
Comparing version 0.0.1-alpha to 0.0.2-alpha
328
all.js
var MOJFrontend = {}; | ||
MOJFrontend.AddAnother = function(container) { | ||
this.container = $(container); | ||
this.container.on('click', '.moj-add-another__remove-button', $.proxy(this, 'onRemoveButtonClick')); | ||
this.container.on('click', '.moj-add-another__add-button', $.proxy(this, 'onAddButtonClick')); | ||
this.container.find('.moj-add-another__add-button, moj-add-another__remove-button').prop('type', 'button'); | ||
MOJFrontend.removeAttributeValue = function(el, attr, value) { | ||
var re, m; | ||
if (el.getAttribute(attr)) { | ||
if (el.getAttribute(attr) == value) { | ||
el.removeAttribute(attr); | ||
} else { | ||
re = new RegExp('(^|\\s)' + value + '(\\s|$)'); | ||
m = el.getAttribute(attr).match(re); | ||
if (m && m.length == 3) { | ||
el.setAttribute(attr, el.getAttribute(attr).replace(re, (m[1] && m[2])?' ':'')) | ||
} | ||
} | ||
} | ||
} | ||
MOJFrontend.addAttributeValue = function(el, attr, value) { | ||
var re; | ||
if (!el.getAttribute(attr)) { | ||
el.setAttribute(attr, value); | ||
} | ||
else { | ||
re = new RegExp('(^|\\s)' + value + '(\\s|$)'); | ||
if (!re.test(el.getAttribute(attr))) { | ||
el.setAttribute(attr, el.getAttribute(attr) + ' ' + value); | ||
} | ||
} | ||
}; | ||
MOJFrontend.AddAnother.prototype.onAddButtonClick = function(e) { | ||
var item = this.getNewItem(); | ||
this.updateAttributes(this.getItems().length, item); | ||
this.resetItem(item); | ||
var firstItem = this.getItems().first(); | ||
if(!this.hasRemoveButton(firstItem)) { | ||
this.createRemoveButton(firstItem); | ||
MOJFrontend.FormValidator = function(form, options) { | ||
this.form = form; | ||
this.errors = []; | ||
this.validators = []; | ||
$(this.form).on('submit', $.proxy(this, 'onSubmit')); | ||
this.summary = (options && options.summary) ? $(options.summary) : $('.govuk-error-summary'); | ||
this.originalTitle = document.title; | ||
}; | ||
MOJFrontend.FormValidator.entityMap = { | ||
'&': '&', | ||
'<': '<', | ||
'>': '>', | ||
'"': '"', | ||
"'": ''', | ||
'/': '/', | ||
'`': '`', | ||
'=': '=' | ||
}; | ||
MOJFrontend.FormValidator.prototype.escapeHtml = function(string) { | ||
return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap (s) { | ||
return MOJFrontend.FormValidator.entityMap[s]; | ||
}); | ||
}; | ||
MOJFrontend.FormValidator.prototype.resetTitle = function() { | ||
document.title = this.originalTitle; | ||
}; | ||
MOJFrontend.FormValidator.prototype.updateTitle = function() { | ||
document.title = "" + this.errors.length + " errors - " + document.title; | ||
}; | ||
MOJFrontend.FormValidator.prototype.showSummary = function () { | ||
this.summary.html(this.getSummaryHtml()); | ||
this.summary.removeClass('moj-hidden'); | ||
this.summary.attr('aria-labelledby', 'errorSummary-heading'); | ||
this.summary.focus(); | ||
}; | ||
MOJFrontend.FormValidator.prototype.getSummaryHtml = function() { | ||
var html = '<h2 id="error-summary-title" class="govuk-error-summary__title">There is a problem</h2>'; | ||
html += '<div class="govuk-error-summary__body">'; | ||
html += '<ul class="govuk-list govuk-error-summary__list">'; | ||
for (var i = 0, j = this.errors.length; i < j; i++) { | ||
var error = this.errors[i]; | ||
html += '<li>'; | ||
html += '<a href="#' + this.escapeHtml(error.fieldName) + '">'; | ||
html += this.escapeHtml(error.message); | ||
html += '</a>'; | ||
html += '</li>'; | ||
} | ||
html += '</ul>'; | ||
html += '</div>'; | ||
return html; | ||
}; | ||
MOJFrontend.FormValidator.prototype.hideSummary = function() { | ||
this.summary.addClass('moj-hidden'); | ||
this.summary.removeAttr('aria-labelledby'); | ||
}; | ||
MOJFrontend.FormValidator.prototype.onSubmit = function (e) { | ||
this.removeInlineErrors(); | ||
this.hideSummary(); | ||
this.resetTitle(); | ||
if(!this.validate()) { | ||
e.preventDefault(); | ||
this.updateTitle(); | ||
this.showSummary(); | ||
this.showInlineErrors(); | ||
} | ||
}; | ||
MOJFrontend.FormValidator.prototype.showInlineErrors = function() { | ||
for (var i = 0, j = this.errors.length; i < j; i++) { | ||
this.showInlineError(this.errors[i]); | ||
} | ||
}; | ||
MOJFrontend.FormValidator.prototype.showInlineError = function (error) { | ||
var errorSpanId = error.fieldName + '-error'; | ||
var errorSpan = '<span class="govuk-error-message" id="'+ errorSpanId +'">'+this.escapeHtml(error.message)+'</span>'; | ||
var control = $("#" + error.fieldName); | ||
var fieldContainer = control.parents(".govuk-form-group"); | ||
var label = fieldContainer.find('label'); | ||
var legend = fieldContainer.find("legend"); | ||
var fieldset = fieldContainer.find("fieldset"); | ||
fieldContainer.addClass('govuk-form-group--error'); | ||
if(legend.length) { | ||
legend.after(errorSpan); | ||
fieldContainer.attr('aria-invalid', 'true'); | ||
MOJFrontend.addAttributeValue(fieldset[0], 'aria-describedby', errorSpanId); | ||
} else { | ||
label.after(errorSpan); | ||
control.attr('aria-invalid', 'true'); | ||
MOJFrontend.addAttributeValue(control[0], 'aria-describedby', errorSpanId); | ||
} | ||
}; | ||
MOJFrontend.FormValidator.prototype.removeInlineErrors = function() { | ||
var error; | ||
var i; | ||
for (var i = 0; i < this.errors.length; i++) { | ||
this.removeInlineError(this.errors[i]); | ||
} | ||
}; | ||
MOJFrontend.FormValidator.prototype.removeInlineError = function(error) { | ||
var control = $("#" + error.fieldName); | ||
var fieldContainer = control.parents(".govuk-form-group"); | ||
fieldContainer.find('.govuk-error-message').remove(); | ||
fieldContainer.removeClass('govuk-form-group--error'); | ||
fieldContainer.find("[aria-invalid]").attr('aria-invalid', 'false'); | ||
var errorSpanId = error.fieldName + '-error'; | ||
MOJFrontend.removeAttributeValue(fieldContainer.find('[aria-describedby]')[0], 'aria-describedby', errorSpanId); | ||
}; | ||
MOJFrontend.FormValidator.prototype.addValidator = function(fieldName, rules) { | ||
this.validators.push({ | ||
fieldName: fieldName, | ||
rules: rules, | ||
field: this.form.elements[fieldName] | ||
}); | ||
}; | ||
MOJFrontend.FormValidator.prototype.validate = function() { | ||
this.errors = []; | ||
var validator = null, | ||
validatorReturnValue = true, | ||
i, | ||
j; | ||
for (i = 0; i < this.validators.length; i++) { | ||
validator = this.validators[i]; | ||
for (j = 0; j < validator.rules.length; j++) { | ||
validatorReturnValue = validator.rules[j].method(validator.field, | ||
validator.rules[j].params); | ||
if (typeof validatorReturnValue === 'boolean' && !validatorReturnValue) { | ||
this.errors.push({ | ||
fieldName: validator.fieldName, | ||
message: validator.rules[j].message | ||
}); | ||
break; | ||
} else if(typeof validatorReturnValue === 'string') { | ||
this.errors.push({ | ||
fieldName: validatorReturnValue, | ||
message: validator.rules[j].message | ||
}); | ||
break; | ||
} | ||
} | ||
} | ||
return this.errors.length === 0; | ||
}; | ||
MOJFrontend.SortableTable = function(params) { | ||
this.table = $(params.table); | ||
this.setupOptions(params); | ||
this.body = this.table.find('tbody'); | ||
this.createHeadingButtons(); | ||
this.createStatusBox(); | ||
this.table.on('click', 'th button', $.proxy(this, 'onSortButtonClick')); | ||
}; | ||
MOJFrontend.SortableTable.prototype.setupOptions = function(params) { | ||
params = params || {}; | ||
this.statusMessage = params.statusMessage || 'Sort by %heading% (%direction%)'; | ||
this.ascendingText = params.ascendingText || 'ascending'; | ||
this.descendingText = params.descendingText || 'descending'; | ||
}; | ||
MOJFrontend.SortableTable.prototype.createHeadingButtons = function() { | ||
var headings = this.table.find('thead th'); | ||
var heading; | ||
for(var i = 0; i < headings.length; i++) { | ||
heading = $(headings[i]); | ||
if(heading.attr('aria-sort')) { | ||
this.createHeadingButton(heading, i); | ||
} | ||
} | ||
this.getItems().last().after(item); | ||
item.find('input, textarea, select').first().focus(); | ||
}; | ||
MOJFrontend.AddAnother.prototype.hasRemoveButton = function(item) { | ||
return item.find('.moj-add-another__remove-button').length; | ||
MOJFrontend.SortableTable.prototype.createHeadingButton = function(heading, i) { | ||
var text = heading.text(); | ||
var button = $('<button type="button" data-index="'+i+'">'+text+'</button>'); | ||
heading.text(''); | ||
heading.append(button); | ||
}; | ||
MOJFrontend.AddAnother.prototype.getItems = function() { | ||
return this.container.find('.moj-add-another__item'); | ||
MOJFrontend.SortableTable.prototype.createStatusBox = function() { | ||
this.status = $('<div aria-live="polite" role="status" aria-atomic="true" class="govuk-visually-hidden" />'); | ||
this.table.parent().append(this.status); | ||
}; | ||
MOJFrontend.AddAnother.prototype.getNewItem = function() { | ||
var item = this.getItems().first().clone(); | ||
if(!this.hasRemoveButton(item)) { | ||
this.createRemoveButton(item); | ||
MOJFrontend.SortableTable.prototype.onSortButtonClick = function(e) { | ||
var columnNumber = e.currentTarget.getAttribute('data-index'); | ||
var sortDirection = $(e.currentTarget).parent().attr('aria-sort'); | ||
var newSortDirection; | ||
if(sortDirection === 'none' || sortDirection === 'descending') { | ||
newSortDirection = 'ascending'; | ||
} else { | ||
newSortDirection = 'descending'; | ||
} | ||
return item; | ||
var rows = this.getTableRowsArray(); | ||
var sortedRows = this.sort(rows, columnNumber, newSortDirection); | ||
this.addRows(sortedRows); | ||
this.removeButtonStates(); | ||
this.updateButtonState($(e.currentTarget), newSortDirection); | ||
}; | ||
MOJFrontend.AddAnother.prototype.updateAttributes = function(index, item) { | ||
item.find('[data-name]').each(function(i, el) { | ||
el.name = $(el).attr('data-name').replace(/%index%/, index); | ||
el.id = $(el).attr('data-id').replace(/%index%/, index); | ||
($(el).prev('label')[0] || $(el).parents('label')[0]).htmlFor = el.id; | ||
}); | ||
MOJFrontend.SortableTable.prototype.updateButtonState = function(button, direction) { | ||
button.parent().attr('aria-sort', direction); | ||
var message = this.statusMessage; | ||
message = message.replace(/%heading%/, button.text()); | ||
message = message.replace(/%direction%/, this[direction+'Text']); | ||
this.status.text(message); | ||
}; | ||
MOJFrontend.AddAnother.prototype.createRemoveButton = function(item) { | ||
item.append('<button type="button" class="govuk-button moj-button--secondary moj-add-another__remove-button">Remove</button>'); | ||
MOJFrontend.SortableTable.prototype.removeButtonStates = function() { | ||
this.table.find('thead th').attr('aria-sort', 'none'); | ||
}; | ||
MOJFrontend.AddAnother.prototype.resetItem = function(item) { | ||
item.find('[data-name], [data-id]').each(function(index, el) { | ||
if(el.type == 'checkbox' || el.type == 'radio') { | ||
el.checked = false; | ||
} else { | ||
el.value = ''; | ||
} | ||
}); | ||
MOJFrontend.SortableTable.prototype.addRows = function(rows) { | ||
for(var i = 0; i < rows.length; i++) { | ||
this.body.append(rows[i]); | ||
} | ||
}; | ||
MOJFrontend.AddAnother.prototype.onRemoveButtonClick = function(e) { | ||
$(e.currentTarget).parents('.moj-add-another__item').remove(); | ||
var items = this.getItems(); | ||
if(items.length === 1) { | ||
items.find('.moj-add-another__remove-button').remove(); | ||
MOJFrontend.SortableTable.prototype.getTableRowsArray = function() { | ||
var rows = []; | ||
var trs = this.body.find('tr'); | ||
for (var i = 0; i < trs.length; i++) { | ||
rows.push(trs[i]); | ||
} | ||
items.each($.proxy(function(index, el) { | ||
this.updateAttributes(index, $(el)); | ||
return rows; | ||
}; | ||
MOJFrontend.SortableTable.prototype.sort = function(rows, columnNumber, sortDirection) { | ||
var newRows = rows.sort($.proxy(function(rowA, rowB) { | ||
var tdA = $(rowA).find('td').eq(columnNumber); | ||
var tdB = $(rowB).find('td').eq(columnNumber); | ||
var valueA = this.getCellValue(tdA); | ||
var valueB = this.getCellValue(tdB); | ||
if(sortDirection === 'ascending') { | ||
if(valueA < valueB) { | ||
return -1; | ||
} | ||
if(valueA > valueB) { | ||
return 1; | ||
} | ||
return 0; | ||
} else { | ||
if(valueB < valueA) { | ||
return -1; | ||
} | ||
if(valueB > valueA) { | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
}, this)); | ||
this.focusHeading(); | ||
return newRows; | ||
}; | ||
MOJFrontend.AddAnother.prototype.focusHeading = function() { | ||
this.container.find('.moj-add-another__heading').focus(); | ||
MOJFrontend.SortableTable.prototype.getCellValue = function(cell) { | ||
var val = cell.attr('data-sort-value'); | ||
val = val || cell.html(); | ||
if($.isNumeric(val)) { | ||
val = parseInt(val, 10); | ||
} | ||
return val; | ||
}; |
@@ -1,1 +0,29 @@ | ||
var MOJFrontend = {}; | ||
var MOJFrontend = {}; | ||
MOJFrontend.removeAttributeValue = function(el, attr, value) { | ||
var re, m; | ||
if (el.getAttribute(attr)) { | ||
if (el.getAttribute(attr) == value) { | ||
el.removeAttribute(attr); | ||
} else { | ||
re = new RegExp('(^|\\s)' + value + '(\\s|$)'); | ||
m = el.getAttribute(attr).match(re); | ||
if (m && m.length == 3) { | ||
el.setAttribute(attr, el.getAttribute(attr).replace(re, (m[1] && m[2])?' ':'')) | ||
} | ||
} | ||
} | ||
} | ||
MOJFrontend.addAttributeValue = function(el, attr, value) { | ||
var re; | ||
if (!el.getAttribute(attr)) { | ||
el.setAttribute(attr, value); | ||
} | ||
else { | ||
re = new RegExp('(^|\\s)' + value + '(\\s|$)'); | ||
if (!re.test(el.getAttribute(attr))) { | ||
el.setAttribute(attr, el.getAttribute(attr) + ' ' + value); | ||
} | ||
} | ||
}; |
{ | ||
"name": "@ministryofjustice/frontend", | ||
"description": "The MOJ Frontend contains the code you need to start building user interfaces for UK Ministry of Justice government services.", | ||
"version": "0.0.1-alpha", | ||
"version": "0.0.2-alpha", | ||
"main": "all.js", | ||
@@ -6,0 +6,0 @@ "sass": "all.scss", |
@@ -5,3 +5,3 @@ # Ministry of Justice Frontend | ||
See live examples of MOJ Frontend components, and guidance on when to use them in your service, in the [MoJ Design System](https://mojdt-design-system-beta.herokuapp.com/). | ||
See live examples of MOJ Frontend components, and guidance on when to use them in your service, in the [MoJ Design System](https://mojdt-design-system.herokuapp.com/). | ||
@@ -16,3 +16,3 @@ ## Contact the team | ||
Once installed, you will be able to use the code from the examples in the [MoJ Design System](https://mojdt-design-system-beta.herokuapp.com/) | ||
Once installed, you will be able to use the code from the examples in the [MoJ Design System](https://mojdt-design-system.herokuapp.com/) | ||
in your service. | ||
@@ -19,0 +19,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
355695
59
8502
0