diff --git a/app/views/devise/registrations/_external_identifier_orcid.html.erb b/app/views/devise/registrations/_external_identifier_orcid.html.erb index 76c66499e8..b5e9acbe3f 100644 --- a/app/views/devise/registrations/_external_identifier_orcid.html.erb +++ b/app/views/devise/registrations/_external_identifier_orcid.html.erb @@ -1,15 +1,13 @@ -
- <% if id.nil? || id.identifier == '' %> - <%= link_to 'Create or connect your ORCID iD', Rails.application.routes.url_helpers.send("user_#{scheme.name.downcase}_omniauth_authorize_path"), id:"connect-orcid-button", target: '_blank', title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") %> +<% if id.nil? || id.identifier == '' %> + <%= link_to 'Create or connect your ORCID iD', Rails.application.routes.url_helpers.send("user_#{scheme.name.downcase}_omniauth_authorize_path"), id:"connect-orcid-button", target: '_blank', title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") %> +<% else %> + <% if scheme.user_landing_url.nil? %> + <%= _("Your account has been linked to #{scheme.description}.") %> <% else %> - <% if scheme.user_landing_url.nil? %> - <%= _("Your account has been linked to #{scheme.description}.") %> - <% else %> - <%= link_to (image_tag("#{scheme.logo_url}", id: 'orcid-id-logo', alt: scheme.description)) + "#{scheme.user_landing_url}/#{id.identifier}", "#{scheme.user_landing_url}/#{id.identifier}", id: 'orcid-id', target: '_blank', title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") %> - <% end %> - <% title = _("Unlink your account from #{scheme.description}. You can link again at any time.") %> - <%= link_to ''.html_safe, - destroy_user_identifier_path(id), method: :delete, title: title, 'aria-label': title, - data: {confirm: _("Are you sure you want to unlink #{scheme.description} ID?")} %> + <%= link_to (image_tag("#{scheme.logo_url}", id: 'orcid-id-logo', alt: scheme.description)) + "#{scheme.user_landing_url}/#{id.identifier}", "#{scheme.user_landing_url}/#{id.identifier}", id: 'orcid-id', target: '_blank', title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") %> <% end %> -
\ No newline at end of file + <% title = _("Unlink your account from #{scheme.description}. You can link again at any time.") %> + <%= link_to ''.html_safe, + destroy_user_identifier_path(id), method: :delete, title: title, 'aria-label': title, + data: {confirm: _("Are you sure you want to unlink #{scheme.description} ID?")} %> +<% end %> diff --git a/app/views/devise/registrations/_external_identifier_shibboleth.html.erb b/app/views/devise/registrations/_external_identifier_shibboleth.html.erb index 65ca8fb6fb..9daa7f1065 100644 --- a/app/views/devise/registrations/_external_identifier_shibboleth.html.erb +++ b/app/views/devise/registrations/_external_identifier_shibboleth.html.erb @@ -1,21 +1,19 @@ -
- <% if id.nil? || id.identifier == '' %> - <%= link_to "#{_("Link account with #{scheme.description} ID")}", - Rails.application.routes.url_helpers.send( - "user_#{scheme.name.downcase}_omniauth_authorize_path" - ), - title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") - %> +<% if id.nil? || id.identifier == '' %> + <%= link_to "#{_("Link account with #{scheme.description} ID")}", + Rails.application.routes.url_helpers.send( + "user_#{scheme.name.downcase}_omniauth_authorize_path" + ), + title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") + %> +<% else %> + <% if scheme.user_landing_url.nil? %> + <%= _("Your account has been linked to #{scheme.description}.") %> <% else %> - <% if scheme.user_landing_url.nil? %> - <%= _("Your account has been linked to #{scheme.description}.") %> - <% else %> - <%= link_to "#{_("Your account has been linked to #{scheme.description}.")}", "#{scheme.user_landing_url}/#{id.identifier}", target: '_blank', - title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") %> - <% end %> - <% title = _("Unlink your account from #{scheme.description}. You can link again at any time.") %> - <%= link_to ''.html_safe, - destroy_user_identifier_path(id), method: :delete, title: title, 'aria-label': title, - data: {confirm: _("Are you sure you want to unlink #{scheme.description} ID?")} %> + <%= link_to "#{_("Your account has been linked to #{scheme.description}.")}", "#{scheme.user_landing_url}/#{id.identifier}", target: '_blank', + title: t("identifier_schemes.schemes.#{scheme.name}.connect_tooltip", default: "") %> <% end %> -
\ No newline at end of file + <% title = _("Unlink your account from #{scheme.description}. You can link again at any time.") %> + <%= link_to ''.html_safe, + destroy_user_identifier_path(id), method: :delete, title: title, 'aria-label': title, + data: {confirm: _("Are you sure you want to unlink #{scheme.description} ID?")} %> +<% end %> diff --git a/app/views/devise/registrations/_password_details.html.erb b/app/views/devise/registrations/_password_details.html.erb index a5b7937253..26cf54b234 100644 --- a/app/views/devise/registrations/_password_details.html.erb +++ b/app/views/devise/registrations/_password_details.html.erb @@ -1,44 +1,43 @@ -<%= form_for(resource, as: resource_name, url: registration_path(resource_name, tab: 'password-details-tab'), html: {method: :put, class: "roadmap-form white_background"}) do |f| %> -
- <%= _('If you would like to change your password please complete the following fields.') %> +<%= form_for(resource, as: resource_name, url: registration_path(resource_name, tab: 'password-details-tab'), html: {method: :put, id: "password_details_registration_form", class: "form-horizontal"}) do |f| %> - <%= hidden_field_tag :skip_personal_details, "true" %> +

<%= _('If you would like to change your password please complete the following fields.') %>

-
- <%= f.label(:current_password, _('Current password'), class: 'required') %> - <%= f.password_field(:current_password, class: 'left-indent required input-medium') %> - + <%= hidden_field_tag :skip_personal_details, "true" %> + +
+ <%= f.label(:current_password, _('Current password'), class: 'col-sm-2 control-label') %> +
+ <%= f.password_field(:current_password, class: 'form-control', "aria-required": true ) %>
+
-
- <%= f.label(:password, _('New password'), class: 'required') %> - <%= f.password_field(:password, class: 'left-indent required input-medium', id: 'user_new_password') %> - +
+ <%= f.label(:password, _('New password'), class: 'col-sm-2 control-label') %> +
+ <%= f.password_field(:password, class: 'form-control', "aria-required": true , id: 'user_new_password') %>
+
-
- <%= f.label(:password_confirmation, _('Password confirmation'), class: 'required') %> - <%= f.password_field(:password_confirmation, class: 'left-indent required input-medium') %> - +
+ <%= f.label(:password_confirmation, _('Password confirmation'), class: 'col-sm-2 control-label') %> +
+ <%= f.password_field(:password_confirmation, class: 'form-control', "aria-required": true, ) %>
+
-
- <%= label_tag(:password_show, _('Show passwords')) %> - <%= check_box_tag(:password_show, "1", false, class: 'left-indent') %> +
+
+
+ +
- -
-
-
 
- <%= render partial: 'shared/accessible_submit_button', - locals: {id: 'update', - val: 'Save', - disabled_initially: false, - classes: 'small-input-button left-indent', - tooltip: _('Enter all of the required information above')} %> +
+ +
+
+ <%= f.button(_('Save'), class: "btn btn-default", type: "submit") %>
-
-<% end %> \ No newline at end of file + +<% end %> diff --git a/app/views/devise/registrations/_personal_details.html.erb b/app/views/devise/registrations/_personal_details.html.erb index 5ed5eb3252..e08b028118 100644 --- a/app/views/devise/registrations/_personal_details.html.erb +++ b/app/views/devise/registrations/_personal_details.html.erb @@ -1,121 +1,122 @@ -<%= form_for(resource, as: resource_name, url: registration_path(resource_name, tab: 'personal-details-tab'), html: {method: :put, class: "roadmap-form white_background"}) do |f| %> +<%= form_for(resource, as: resource_name, url: registration_path(resource_name, tab: 'personal-details-tab'), html: {method: :put, id: "personal_details_registration_form", class: "form-horizontal"}) do |f| %> +

+ <%= _("Please note that your email address is used as your username. + If you change this, remember to use your new email address on sign in.") %> +

-
-

- <%= _("Please note that your email address is used as your username. - If you change this, remember to use your new email address on sign in.") %> -

-
+

<%= _('You can edit any of the details below.') %>

+ <%= hidden_field_tag :unlink_flag, "false", id: "unlink_flag" %> + <%= hidden_field_tag :skip_personal_details, "false" %> -
- -
- <%= _('You can edit any of the details below.') %> +
+ <%= f.label(:email, _('Email'), class: "col-sm-2 control-label") %> +
+ <%= f.email_field(:email, class: "form-control", "aria-required": true, + title: _('Please enter your current password below when changing your email address.'), + value: @user.email) %> +
+
- <%= hidden_field_tag :unlink_flag, "false", id: "unlink_flag" %> - <%= hidden_field_tag :skip_personal_details, "false" %> +
+ <%= f.label(:firstname, _('First name'), class: "col-sm-2 control-label") %> +
+ <%= f.text_field(:firstname, class: 'form-control', "aria-required": true, value: @user.firstname) %> +
+
-
- <%= f.label(:email, _('Email'), class: 'required') %> - <%= f.email_field(:email, class: 'left-indent required input-small', - title: _('Please enter your current password below when changing your email address.'), - value: @user.email) %> - -
+
+ <%= f.label(:surname, _('Last name'), class: "col-sm-2 control-label") %> +
+ <%= f.text_field(:surname, class: 'form-control', "aria-required": true, value: @user.surname) %> +
+
-
- <%= f.label(:firstname, _('First name'), class: 'required') %> - <%= f.text_field(:firstname, class: 'input-small required left-indent', value: @user.firstname) %> -
-
- <%= f.label(:surname, _('Last name'), class: 'required') %> - <%= f.text_field(:surname, class: 'input-small required left-indent', value: @user.surname) %> -
+
+ <%= f.label(:recovery_email, _('Recovery email'), class: "col-sm-2 control-label") %> +
+ <%= f.email_field(:recovery_email, class: 'form-control', "aria-required": true, + title: _('This email will be used to recover your account if you change institutions'), + value: @user.recovery_email) %> +
+
-
- <%= f.label(:recovery_email, _('Recovery email'), class: 'required') %> - <%= f.email_field(:recovery_email, class: 'left-indent required input-small', - title: _('This email will be used to recover your account if you change institutions'), - value: @user.recovery_email) %> - -
+
+ <%= f.label(:password, _('Password'), class: "col-sm-2 control-label") %> +
+ <%= f.password_field(:password, class: 'form-control', "aria-required": true) %> +
+
-
- <%= f.label(:password, _('Password'), class: 'required') %> - <%= f.password_field(:password, class: 'left-indent required input-small') %> - -
+
+ <%= f.label(:org_name, _('Organisation'), class: "col-sm-2 control-label") %> +
+ <%= render partial: "shared/accessible_combobox", + locals: {name: "#{resource_name}[org_name]", + id: "#{resource_name}_org_name", + default_selection: @default_org, + models: @orgs, + attribute: 'name', + classes: 'form-control'} %> -
- <%= f.label(:org_name, _('Organisation')) %> - <%= render partial: "shared/accessible_combobox", - locals: {name: "#{resource_name}[org_name]", - id: "#{resource_name}_org_name", - default_selection: @default_org, - models: @orgs, - attribute: 'name', - classes: 'left-indent input-large'} %> +
+ +
<% other_organisations = Org.where("parent_id IS ? AND is_other = ?", nil, true).pluck(:id) %> -
- <%= f.label _('Organisation name'), for: :other_organisation %> - <%= f.text_field :other_organisation, :autocomplete => "off", :class => 'left-indent input-medium has-tooltip', 'data-toggle' => "tooltip", 'data-container' => "body", 'title' => _('Please enter the name of your organisation.') %> -
+
+ <%= f.label _('Organisation name'), for: :other_organisation, class: "col-sm-2 control-label" %> + <%= f.text_field :other_organisation, :autocomplete => "off", :class => 'form-control has-tooltip', 'data-toggle' => "tooltip", 'data-container' => "body", 'title' => _('Please enter the name of your organisation.') %>
+
+
- <% if MANY_LANGUAGES %> -
- <% lang_id = current_user.language.nil? ? Language.id_for(FastGettext.default_locale) : current_user.language.id %> - <%= f.label(:language_id, _('Language')) %> - <%= select_tag("user[language_id]", + <% if MANY_LANGUAGES %> +
+ <% lang_id = current_user.language.nil? ? Language.id_for(FastGettext.default_locale) : current_user.language.id %> + <%= f.label(:language_id, _('Language'), class: "col-sm-2 control-label") %> +
+ <%= select_tag("user[language_id]", options_from_collection_for_select(@languages, "id", "name", lang_id), - class: 'input-small') %> -
- <% end %> - - <% @identifier_schemes.each do |scheme| %> -
- <% if scheme.name == 'shibboleth' %> - - <% else %> - <%= label_tag(:scheme_name, scheme.name.capitalize ) %> - <% end %> -
- <%= render partial: "external_identifier_#{scheme.name}", - locals: { scheme: scheme, - id: current_user.identifier_for(scheme)} %> -
-
- <% end %> + class: "form-control") %> +
+
+ <% end %> - <% unless @user.api_token.blank? %> -
- <%= f.label(:api_token, _('API token')) %> -
<%= @user.api_token %>
- <%= label_tag(:api_information, _('API Information')) %> -
<%= link_to( _('How to use the API'), controller: "token_permission_types", action: "index")%>
-
+ <% @identifier_schemes.each do |scheme| %> +
+ <% if scheme.name == 'shibboleth' %> + + <% else %> + <%= label_tag(:scheme_name, scheme.name.capitalize, class: "col-sm-2 control-label") %> <% end %> -
-
-
 
- <%= render partial: 'shared/accessible_submit_button', - locals: {id: 'update', - val: 'Save', - disabled_initially: true, - classes: 'small-input-button', - tooltip: _('Enter all of the required information above')} %> +
+ <%= render partial: "external_identifier_#{scheme.name}", + locals: { scheme: scheme, + id: current_user.identifier_for(scheme)} %>
+
+ <% end %> + + <% unless @user.api_token.blank? %> +
+ <%= f.label(:api_token, _('API token'), class: "col-sm-2 control-label") %> +
<%= @user.api_token %>
+ <%= label_tag(:api_information, _('API Information'), class: "col-sm-2 control-label") %> +
<%= link_to( _('How to use the API'), controller: "token_permission_types", action: "index")%>
+
+ <% end %> -
+
+
+ <%= f.button(_('Save'), class: "btn btn-default", type: "submit") %> +
<% end %> + + diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index 219bee50f6..c93f4a1e45 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -6,35 +6,31 @@
-
-
    -
diff --git a/app/views/shared/_accessible_combobox.html.erb b/app/views/shared/_accessible_combobox.html.erb index 6b32337554..f2ef99f039 100644 --- a/app/views/shared/_accessible_combobox.html.erb +++ b/app/views/shared/_accessible_combobox.html.erb @@ -1,10 +1,13 @@ <% if !models.nil? %> + <% required = required ||= false %> + <% classes = classes ||= '' %> + <% error = error ||= _('Please select an item from the list.') %> + <% json = {} %> <% models.map{|m| json[m[attribute]] = m.id} %> - <% err_msg = error ||= _('Please select an item from the list.') %> + + + " name="<%= name.gsub("_#{attribute}]", "_id]") %>" - value="<%= default_selection.id unless default_selection.nil? %>" /> + value="<%= default_selection.id unless default_selection.nil? %>" aria-required="<%= required %>" + data-validation="js-combobox" data-validation-error="<%= error %>" /> - - <% else %> <%= _('No items available.') %> <% end %> \ No newline at end of file diff --git a/app/views/users/_notification_preferences.html.erb b/app/views/users/_notification_preferences.html.erb index 0e56a73ece..2f59817470 100644 --- a/app/views/users/_notification_preferences.html.erb +++ b/app/views/users/_notification_preferences.html.erb @@ -1,60 +1,67 @@ -

- <%= link_to _('Select all'), '#', id: 'select_all' %> | - <%= link_to _('Deselect all'), '#', id: 'deselect_all' %> -

+<%= form_tag( update_email_preferences_user_path(@user), method: :put, html: { id: "preferences_registration_form", class: "form-horizontal" }) do |f| %> +
+

+ <%= link_to _('Select all'), '#', id: 'select_all' %> | + <%= link_to _('Deselect all'), '#', id: 'deselect_all' %> +

-
- <%= form_tag( update_email_preferences_user_path(@user), method: :put, html: {class: "roadmap-form"}) do |f| %> + <%= hidden_field_tag :tab, 'notification-preferences-tab' %> - <%= hidden_field_tag :tab, 'notification-preferences-tab' %> -

All Users

-
+

All Users

+
+
-
+ <%= check_box_tag 'prefs[users][new_comment]', true, @prefs[:users][:new_comment] %><%= _('A new comment has been added to my DMP') %> + +
+
+
-
+ <%= check_box_tag 'prefs[users][added_as_coowner]', true, @prefs[:users][:added_as_coowner] %><%= _('A plan has been shared with me') %> + +
+
+
+ <%= check_box_tag 'prefs[users][admin_privileges]', true, @prefs[:users][:admin_privileges] %><%= _('Admin privileges granted to me') %> + +
-
-

DMP owners and co-owners

-
+

DMP owners and co-owners

+ +
+
+ <%= check_box_tag 'prefs[owners_and_coowners][visibility_changed]', true, @prefs[:owners_and_coowners][:visibility_changed] %><%= _("My DMP's visibility has changed") %> + +
+ +

DMP administrators

-
-

DMP administrators

-
+
+
-
+ <%= check_box_tag 'prefs[admins][template_published]', true, @prefs[:admins][:template_published] %><%= _('An organisational template is published') %> + +
+ +
+
-
+ <%= check_box_tag 'prefs[admins][template_unpublished]', true, @prefs[:admins][:template_unpublished] %><%= _('An organisational template is unpublished') %> + +
+ +
+
- -
-
-
 
- <%= submit_tag _('Save'), class: 'btn btn-primary', role: 'button' %> -
- <% end %> -
\ No newline at end of file + <%= check_box_tag 'prefs[admins][feedback_requested]', true, @prefs[:admins][:feedback_requested] %><%= _('A user has requested feedback on a DMP') %> + +
+ +
+ <%= submit_tag _('Save'), class: 'btn btn-default' %> +
+ +<% end %> diff --git a/lib/assets/javascripts/application.js b/lib/assets/javascripts/application.js index 0a2fc358b2..0ad26111d0 100644 --- a/lib/assets/javascripts/application.js +++ b/lib/assets/javascripts/application.js @@ -6,4 +6,6 @@ import './views/devise/passwords/new'; import './views/phases/edit'; import './views/plans/download'; import './views/plans/edit_details'; +import './views/plans/new'; import './views/plans/share'; +import './views/devise/registrations/edit'; diff --git a/lib/assets/javascripts/constants.js b/lib/assets/javascripts/constants.js index 1ac626714b..cd8245f7a9 100644 --- a/lib/assets/javascripts/constants.js +++ b/lib/assets/javascripts/constants.js @@ -7,6 +7,7 @@ export const VALIDATION_MESSAGE_NUMBER = 'Please enter a valid number.'; export const VALIDATION_MESSAGE_PASSWORD = 'The password must be between 8 and 128 characters.'; export const VALIDATION_MESSAGE_PASSWORDS_MATCH = 'The passwords must match.'; export const VALIDATION_MESSAGE_RADIO = 'Please choose one of the options.'; +export const VALIDATION_MESSAGE_SELECT = 'Please select a value from the list.'; export const VALIDATION_MESSAGE_TEXT = 'This field is required.'; export const SHOW_PASSWORD_MESSAGE = 'Show password'; diff --git a/lib/assets/javascripts/spec/autoCompleteSpec.js b/lib/assets/javascripts/spec/autoCompleteSpec.js new file mode 100644 index 0000000000..08965ea55d --- /dev/null +++ b/lib/assets/javascripts/spec/autoCompleteSpec.js @@ -0,0 +1,26 @@ +import initAutoComplete from '../utils/autoComplete'; + +describe('autoComplete test suite', () => { + beforeAll(() => fixture.setBase('javascripts/spec/fixtures')); + + beforeEach(() => { + $('body').html(fixture.load('autoComplete.html')); + initAutoComplete(); + // Override the form submission, we are just going to validate the ariatisation of the form + $('form').submit((e) => { e.preventDefault(); }); + }); + + afterEach(() => { + fixture.cleanup(); + $('body').html(''); + }); + + it('shows/hides the clear button correctly', () => { + + }); + + it('Selects the correct id based on the item selected in the combobox', () => { + + }); + +}); diff --git a/lib/assets/javascripts/spec/expandCollapseAllSpec.js b/lib/assets/javascripts/spec/expandCollapseAllSpec.js new file mode 100644 index 0000000000..71c129a3b3 --- /dev/null +++ b/lib/assets/javascripts/spec/expandCollapseAllSpec.js @@ -0,0 +1,48 @@ +import expandCollapseAll from '../utils/expandCollapseAll'; + +describe('expandCollapseAll test suite', () => { + beforeAll(() => fixture.setBase('javascripts/spec/fixtures')); + + beforeEach(() => { + this.form = fixture.load('accordion.html'); + expandCollapseAll({ selector: '#accordion' }); + }); + + afterEach(() => { + fixture.cleanup(); + }); + + it('should be able to expand all sections when all are either expanded or collapsed', () => { + // Collapse all of the sections + // - click on 'collapse all' should have no effect + // - click on 'expand all' should expand all sections + $('#accordion div.panel-collapse').collapse('hide'); + expect($('.in').length === 0); + $('a[data-toggle-direction="hide"]').click(); + expect($('.in').length === 0); + $('a[data-toggle-direction="show"]').click(); + expect($('.in').length === 3); + + // Expand all of the sections + // - click on 'expand all' should have no effect + // - click on 'collapse all' should collapse all sections + $('#accordion div.panel-collapse').collapse('show'); + expect($('.in').length === 3); + $('a[data-toggle-direction="show"]').click(); + expect($('.in').length === 3); + $('a[data-toggle-direction="hide"]').click(); + expect($('.in').length === 0); + }); + + it('should be able to expand all sections when some are open and some collapsed', () => { + // Expand 2 of the 3 sections - click 'collapse all' - verify that all are collapsed + $('#collapseA, #collapseC').collapse('show'); + $('a[data-toggle-direction="hide"]').click(); + expect($('.in').length === 0); + + // Expand 2 of the 3 sections - click 'expand all' - verify that all are expanded + $('#collapseA, #collapseC').collapse('show'); + $('a[data-toggle-direction="show"]').click(); + expect($('.in').length === 3); + }); +}); diff --git a/lib/assets/javascripts/spec/fixtures/accordion.html b/lib/assets/javascripts/spec/fixtures/accordion.html new file mode 100644 index 0000000000..78cb5aa72a --- /dev/null +++ b/lib/assets/javascripts/spec/fixtures/accordion.html @@ -0,0 +1,60 @@ +
+ + + + + +
+
+ +
+
+
+ This is test section A. +
+
+ +
+ +
+
+
+ This is test section B. +
+
+ +
+ +
+
+
+ This is test section C. +
+
+
+
\ No newline at end of file diff --git a/lib/assets/javascripts/spec/fixtures/autoComplete.html b/lib/assets/javascripts/spec/fixtures/autoComplete.html new file mode 100644 index 0000000000..7c3ce2fb8f --- /dev/null +++ b/lib/assets/javascripts/spec/fixtures/autoComplete.html @@ -0,0 +1,35 @@ +
+ +

JQuery Accessible Auto-complete Combobox

+
+ + + + + + + + + + + + + +
+
\ No newline at end of file diff --git a/lib/assets/javascripts/utils/ariatiseForm.js b/lib/assets/javascripts/utils/ariatiseForm.js index fdd417a562..2db61b3c45 100644 --- a/lib/assets/javascripts/utils/ariatiseForm.js +++ b/lib/assets/javascripts/utils/ariatiseForm.js @@ -103,12 +103,14 @@ const isValid = (type, value) => { return validator.isValidPassword(value); case 'radio': return validator.isValidText(value); + case 'js-combobox': + return validator.isValidText(value); default: return false; } }; -const getValidationMessage = (type) => { +const getDefaultValidationMessage = (type) => { switch (type) { case 'text': return constants.VALIDATION_MESSAGE_TEXT; @@ -120,11 +122,21 @@ const getValidationMessage = (type) => { return constants.VALIDATION_MESSAGE_PASSWORD; case 'radio': return constants.VALIDATION_MESSAGE_RADIO; + case 'js-combobox': + return constants.VALIDATION_MESSAGE_SELECT; default: return constants.VALIDATION_MESSAGE_DEFAULT; } }; +const getValidationMessage = (el) => { + if ($(el).attr('data-validation-error')) { + return $(el).attr('data-validation-error'); + } + // Use the default validation error message if none was specified + return getDefaultValidationMessage(getValidationTypeForElement(el)); +}; + const valid = (el) => { $(el).parent().removeClass(validationStates.hasError); $(el).attr(ariaInvalid(false)); @@ -142,9 +154,8 @@ export default (options) => { // Add validation error message sections for each validatable input element validatable.each((i, el) => { - const type = getValidationTypeForElement(el); $(el).attr(ariaDescribedBy(`help${i}`)); - $(el).after(blockHelp(`help${i}`, getValidationMessage(type))); + $(el).after(blockHelp(`help${i}`, getValidationMessage(el))); }); // Bind validations to the form's submit button @@ -160,7 +171,7 @@ export default (options) => { valid(el); } else { anyInvalid = true; - invalid(el, getValidationMessage(type)); + invalid(el); } } }); diff --git a/lib/assets/javascripts/utils/autoComplete.js b/lib/assets/javascripts/utils/autoComplete.js new file mode 100644 index 0000000000..44b77f1f97 --- /dev/null +++ b/lib/assets/javascripts/utils/autoComplete.js @@ -0,0 +1,58 @@ +import debounce from '../utils/debounce'; + +/* + * Looks up the id for the text selected by the user in the jquery autocomplete combobox and + * then sets updates the hidden id field with the id value so that its available on form submit. + * The id-text mappings are stored as JSON in the corresponding hidden crosswalk field + * @param the combobox element + */ +const updateIdField = (el) => { + const crosswalk = $(`#${$(el).attr('id')}_crosswalk`); + const idField = $(el).attr('id').replace(/_name/, '_id'); + + if (crosswalk && idField) { + const json = JSON.parse(`${$(crosswalk).val().replace(/\\"/g, '"').replace(/\\'/g, '\'')}`); + const selection = json[$(el).val()]; + $(idField).val(selection === 'undefined' ? '' : selection).change(); + } +}; + +/* + * Shows/hides the combobox's clear button based on whether or not text is present + * @param the combobox id + */ +const toggleClearButton = (el) => { + const clearButton = $(el).parent().find('.combobox-clear-button'); + if ($(el).val().trim().length <= 0) { + $(clearButton).addClass('hidden'); + } else { + $(clearButton).removeClass('hidden'); + } +}; + +/* + * Wires up the jquery autocomplete combobox so that it calls the above 2 functions when the + * user changes the text values in the combobox by typing or selecting a value + */ +export default () => { + $('.js-combobox').each((idx, el) => { + const debounced = debounce((e) => { + toggleClearButton(e); + updateIdField(e); + }, 500); + + // When the value in the combobox changes update the hidden id field + $(el).on('keyup', (e) => { + debounced($(e.currentTarget)); + }); + + // Clear the text and hide the button when the user clicks the clear button + $(el).parent().find('.combobox-clear-button').on('click', () => { + $(el).val('').focus(); + debounced($(el)); + }); + + // Show/hide the clear button on page load + toggleClearButton(el); + }); +}; diff --git a/lib/assets/javascripts/views/devise/registrations/edit.js b/lib/assets/javascripts/views/devise/registrations/edit.js new file mode 100644 index 0000000000..f8fe8100df --- /dev/null +++ b/lib/assets/javascripts/views/devise/registrations/edit.js @@ -0,0 +1,12 @@ +import 'bootstrap-sass/assets/javascripts/bootstrap/tooltip'; +import 'bootstrap-sass/assets/javascripts/bootstrap/popover'; +import ariatiseForm from '../../../utils/ariatiseForm'; +import { addMatchingPasswordValidator, togglisePasswords } from '../../../utils/passwordHelper'; + +$(() => { + ariatiseForm({ selector: '#personal_details_registration_form' }); + ariatiseForm({ selector: '#password_details_registration_form' }); + ariatiseForm({ selector: '#preferences_registration_form' }); + addMatchingPasswordValidator({ selector: '#password_details_registration_form' }); + togglisePasswords({ selector: '#password_details_registration_form' }); +}); diff --git a/lib/assets/javascripts/views/plans/new.js b/lib/assets/javascripts/views/plans/new.js index 05ea6c6a18..815c832938 100644 --- a/lib/assets/javascripts/views/plans/new.js +++ b/lib/assets/javascripts/views/plans/new.js @@ -1,75 +1,67 @@ -$(document).ready(function(){ - $("#available-templates").hide(); +import ariatiseForm from '../../utils/ariatiseForm'; +import initAutoComplete from '../../utils/autoComplete'; - var defaultVisibility = $("#plan_visibility").val(); +const handleCheckboxClick = (name, checked) => { + $(`#plan_${name}_name`).prop('disabled', checked); + $('#plan_template_id').val('').change(); + $('#available-templates').fadeOut(); - // retrieve the template options and toggle the submit button on page reload + if (checked) { + $(`#plan_${name}_name`).val(''); + $(`#plan_${name}_id`).val('-1').change(); + $(`#plan_${name}_name`).siblings('.combobox-clear-button').hide(); + } else { + $(`#plan_${name}_id`).val('').change(); + } +}; + +const handleComboboxChange = () => { + const validOrg = ($('#plan_org_id').val().trim().length > 0 || $('#plan_no_org').prop('checked')); + const validFunder = ($('#plan_funder_id').val().trim().length > 0 || $('#plan_no_funder').prop('checked')); + + if (!validOrg || !validFunder) { + $('#available-templates').fadeOut(); + $('#plan_template_id').val(''); + } +}; + +$().ready(() => { + initAutoComplete(); + ariatiseForm({ selector: '#create-plan' }); + + const defaultVisibility = $('#plan_visibility').val(); + + // Initialize the form handleComboboxChange(); - handleCheckboxClick("org", $("#plan_no_org").prop("checked")); - handleCheckboxClick("funder", $("#plan_no_funder").prop("checked")); - + handleCheckboxClick('org', $('#plan_no_org').prop('checked')); + handleCheckboxClick('funder', $('#plan_no_funder').prop('checked')); + // When the user checks the 'mock project' box we need to set the // visibility to 'is_test' - $("#is_test").click(function(){ - $("#plan_visibility").val(($(this)[0].checked ? 'is_test' : defaultVisibility)); + $('#is_test').click((e) => { + $('#plan_visibility').val(($(e.currentTarget)[0].checked ? 'is_test' : defaultVisibility)); }); // When the hidden org and funder id fields change toogle the submit button - $("#plan_org_id, #plan_funder_id").change(function(){ + $('#plan_org_id, #plan_funder_id').change(() => { handleComboboxChange(); }); // Make sure the checkbox is unchecked if we're entering text - $(".js-combobox").keyup(function(){ - var whichOne = $(this).prop('id').split('_')[1]; - $("#plan_no_" + whichOne).prop("checked", false); + $('.js-combobox').keyup((e) => { + const whichOne = $(e.currentTarget).prop('id').split('_')[1]; + $(`#plan_no_${whichOne}`).prop('checked', false); }); // If the user clicks the no Org/Funder checkbox disable the dropdown // and hide clear button - $("#plan_no_org, #plan_no_funder").click(function(){ - var whichOne = $(this).prop('id').split('_')[2]; - handleCheckboxClick(whichOne, this.checked); + $('#plan_no_org, #plan_no_funder').click((e) => { + const whichOne = $(e.currentTarget).prop('id').split('_')[2]; + handleCheckboxClick(whichOne, e.currentTarget.checked); }); - + // When the form receives a valid template id enable the button - $("#plan_template_id").change(function(){ - $("#create_plan_submit").attr('aria-disabled', ($(this).val().trim().length <= 0)); + $('#plan_template_id').change((e) => { + $('#create_plan_submit').attr('aria-disabled', ($(e.currentTarget).val().trim().length <= 0)); }); }); - -// Only display the submit button if the user has made each decision -// ------------------------------------------------------------- -function handleComboboxChange(){ - // If the (no_org checkbox is checked OR an org was selected) AND - // (no_funder checkbox is checked OR a funder was selected) AND - // (the template selector is not visible OR a template has been selected) - var retrieve = ($("#plan_no_org").prop("checked") || - ($("#plan_org_id").val() && $("#plan_org_id").val().trim().length > 0)) && - ($("#plan_no_funder").prop("checked") || - ($("#plan_org_id").val() && $("#plan_funder_id").val().trim().length > 0)); - - if(retrieve){ - if($("#plan_template_id").val().trim().length <= 0){ - $("form").submit(); - } - - }else{ - $("#available-templates").fadeOut(); - $("#plan_template_id").val(""); - } -} - -// Clear the combobox and disable it if the box was checked -// ------------------------------------------------------------- -function handleCheckboxClick(name, checked){ - $("#plan_" + name + "_name").prop("disabled", checked); - $("#plan_template_id").val("").change(); - $("#available-templates").fadeOut(); - - if(checked){ - $("#plan_" + name + "_name").val(""); - $("#plan_" + name + "_id").val("").change(); - $("#plan_" + name + "_name").siblings(".combobox-clear-button").hide(); - } -} diff --git a/lib/assets/javascripts/views/users/notification_preferences.js b/lib/assets/javascripts/views/users/notification_preferences.js index a1d54cef69..aaaed3a761 100644 --- a/lib/assets/javascripts/views/users/notification_preferences.js +++ b/lib/assets/javascripts/views/users/notification_preferences.js @@ -1,9 +1,9 @@ -$(document).ready(function() { - $("#select_all").click(function() { +$(() => { + $('#select_all').click(() => { $('.preferences').find('input[type="checkbox"]').prop('checked', true); }); - $("#deselect_all").click(function() { + $('#deselect_all').click(() => { $('.preferences').find('input[type="checkbox"]').prop('checked', false); }); -}); \ No newline at end of file +}); diff --git a/lib/assets/package-lock.json b/lib/assets/package-lock.json index 10cb86b9b4..d43dad43be 100644 --- a/lib/assets/package-lock.json +++ b/lib/assets/package-lock.json @@ -4652,9 +4652,7 @@ "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=" }, "jquery-accessible-autocomplete-list-aria": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/jquery-accessible-autocomplete-list-aria/-/jquery-accessible-autocomplete-list-aria-1.5.5.tgz", - "integrity": "sha1-2EG0wfBwSQaGcri1FsIkhUaPi4s=", + "version": "github:nico3333fr/jquery-accessible-autocomplete-list-aria#38a057140ccafa9a1c8a948b1bbf4a410c0181c6", "requires": { "jquery": "3.2.1" } diff --git a/lib/assets/webpack.config.js b/lib/assets/webpack.config.js index 6e205704cc..63fd3eb79c 100644 --- a/lib/assets/webpack.config.js +++ b/lib/assets/webpack.config.js @@ -15,7 +15,7 @@ module.exports = { context: __dirname, entry: { - vendor: ['jquery', 'timeago/jquery.timeago'], + vendor: ['jquery', 'timeago/jquery.timeago', 'jquery-accessible-autocomplete-list-aria/jquery-accessible-autocomplete-list-aria'], application: ['./javascripts/application.js', './stylesheets/application.scss'], },