From abece9f7c304e21b4550094e22eda63d0e6b151a Mon Sep 17 00:00:00 2001 From: AshGDS <8880610+AshGDS@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:55:57 +0100 Subject: [PATCH] Add password input component --- .../component_guide/application.js | 1 + .../components/password-input.js | 5 ++ .../_all_components.scss | 1 + .../components/_password-input.scss | 2 + .../components/_password_input.html.erb | 78 +++++++++++++++++++ .../components/docs/password_input.yml | 45 +++++++++++ config/locales/en.yml | 5 ++ spec/components/password_input_spec.rb | 47 +++++++++++ .../app_helpers/asset_helper_spec.rb | 2 +- 9 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/govuk_publishing_components/components/password-input.js create mode 100644 app/assets/stylesheets/govuk_publishing_components/components/_password-input.scss create mode 100644 app/views/govuk_publishing_components/components/_password_input.html.erb create mode 100644 app/views/govuk_publishing_components/components/docs/password_input.yml create mode 100644 spec/components/password_input_spec.rb diff --git a/app/assets/javascripts/component_guide/application.js b/app/assets/javascripts/component_guide/application.js index dd6716cbaa..9073b429ac 100644 --- a/app/assets/javascripts/component_guide/application.js +++ b/app/assets/javascripts/component_guide/application.js @@ -3,4 +3,5 @@ //= require ../govuk_publishing_components/dependencies //= require ../govuk_publishing_components/components/accordion //= require ../govuk_publishing_components/components/intervention +//= require ../govuk_publishing_components/components/password-input //= require ../govuk_publishing_components/components/tabs diff --git a/app/assets/javascripts/govuk_publishing_components/components/password-input.js b/app/assets/javascripts/govuk_publishing_components/components/password-input.js new file mode 100644 index 0000000000..958ba7102c --- /dev/null +++ b/app/assets/javascripts/govuk_publishing_components/components/password-input.js @@ -0,0 +1,5 @@ +// This component relies on JavaScript from GOV.UK Frontend +// = require govuk/components/password-input/password-input.bundle.js +window.GOVUK = window.GOVUK || {} +window.GOVUK.Modules = window.GOVUK.Modules || {} +window.GOVUK.Modules.GovukPasswordInput = window.GOVUKFrontend.PasswordInput diff --git a/app/assets/stylesheets/govuk_publishing_components/_all_components.scss b/app/assets/stylesheets/govuk_publishing_components/_all_components.scss index 1097adff09..33b51fda5e 100644 --- a/app/assets/stylesheets/govuk_publishing_components/_all_components.scss +++ b/app/assets/stylesheets/govuk_publishing_components/_all_components.scss @@ -59,6 +59,7 @@ @import "components/option-select"; @import "components/organisation-logo"; @import "components/panel"; +@import "components/password-input"; @import "components/phase-banner"; @import "components/previous-and-next-navigation"; @import "components/print-link"; diff --git a/app/assets/stylesheets/govuk_publishing_components/components/_password-input.scss b/app/assets/stylesheets/govuk_publishing_components/components/_password-input.scss new file mode 100644 index 0000000000..10c27389ff --- /dev/null +++ b/app/assets/stylesheets/govuk_publishing_components/components/_password-input.scss @@ -0,0 +1,2 @@ +@import "govuk_publishing_components/individual_component_support"; +@import "govuk/components/password-input/password-input"; diff --git a/app/views/govuk_publishing_components/components/_password_input.html.erb b/app/views/govuk_publishing_components/components/_password_input.html.erb new file mode 100644 index 0000000000..4eed60acbb --- /dev/null +++ b/app/views/govuk_publishing_components/components/_password_input.html.erb @@ -0,0 +1,78 @@ +<% + add_gem_component_stylesheet("password-input") + + label_text ||= t("components.password_input.label") + + error_text ||= nil + error_text_prefix ||= t("components.password_input.error_text_prefix") + + shared_helper = GovukPublishingComponents::Presenters::SharedHelper.new(local_assigns) + component_helper = GovukPublishingComponents::Presenters::ComponentWrapperHelper.new(local_assigns) + component_helper.add_data_attribute({ module: "govuk-password-input" }) + component_helper.add_class('govuk-form-group') + component_helper.add_class('govuk-form-group--error') if error_text + component_helper.add_class('govuk-password-input') + component_helper.add_class('gem-c-password-input') + component_helper.add_class(shared_helper.get_margin_bottom) + + uid = SecureRandom.hex(4) + + label_for = uid + '-password-input' + + input_name = 'password' + input_id = uid + '-password-input' + + aria_controls = uid + '-password-input' + + input_classes = %w(govuk-input govuk-password-input__input govuk-js-password-input-input) + + if error_text + label_for << '-with-error-message' + input_id << '-with-error-message' + input_name << '-input-with-error-message' + input_classes << 'govuk-input--error' + aria_controls << '-with-error-message' + paragraph_id = uid + '-password-input-with-error-message-error' + end + +%> + +<%= tag.div(**component_helper.all_attributes) do %> + <%= render "govuk_publishing_components/components/label", { + text: label_text, + html_for: label_for + } %> + <% if error_text %> +

+ <%= error_text_prefix %>: <%= error_text %> +

+ <% end %> + +
+ <%= tag.input( + name: input_name, + type: "password", + class: input_classes, + id: input_id, + spellcheck: false, + autocomplete: "current-password", + autocapitalize: "none", + aria: { + describedby: paragraph_id + }) %> + + <%= tag.button( + type: "button", + class: "govuk-button govuk-button--secondary govuk-password-input__toggle govuk-js-password-input-toggle", + data: { + module: "govuk-button" + }, + hidden: true, + aria: { + controls: aria_controls, + label: t("components.password_input.button_aria_label") + }) do %> + <%= t("components.password_input.button") %> + <% end %> +
+<% end %> diff --git a/app/views/govuk_publishing_components/components/docs/password_input.yml b/app/views/govuk_publishing_components/components/docs/password_input.yml new file mode 100644 index 0000000000..9103fbb5b2 --- /dev/null +++ b/app/views/govuk_publishing_components/components/docs/password_input.yml @@ -0,0 +1,45 @@ +name: Password input +description: The password input component helps users to create and enter passwords. +govuk_frontend_components: + - password-input + +accessibility_criteria: | + The password input component must: + + * accept focus + * be focusable with a keyboard + * be usable with a keyboard + * be usable with touch + * indicate when they have focus + * be recognisable as form input elements + * have correctly associated labels + * have the appropriate type of 'password' + * have an appropriate validation message if there was an error + * allow users to interact with any 'show password' button + * allow users to use `autocomplete` to securely create and enter passwords + * allow users to copy and paste into the password input + + When CSS and / or JavaScript is unavailable, the component must: + + * fallback to a regular input element, with the show/hide button hidden + +uses_component_wrapper_helper: true + +examples: + default: + data: + with_error: + description: If there is a validation error, passing error text will style the password input component with error styles, and semantically state that there was a validation error. + data: + error_text: Enter a password + with_custom_label_and_error_text: + description: For translations or other purposes, the text presented to the user in this component can be changed. Note that the error text prefix is hidden visually and used by assistive tools when there is an error. By default, the prefix is "Error". + data: + label_text: Secret number + error_text_prefix: Incompatible + error_text: 6 is scared of 7, so they can't be next to each other. + with_margin_bottom: + description: | + The component accepts a number for margin bottom from `0` to `9` (`0px` to `60px`) using the [GOV.UK Frontend spacing scale](https://design-system.service.gov.uk/styles/spacing/#the-responsive-spacing-scale). It defaults to having a margin bottom of `30px`. + data: + margin_bottom: 5 diff --git a/config/locales/en.yml b/config/locales/en.yml index 63ddecffb4..3e7f9926c0 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -252,6 +252,11 @@ en: selected: selected organisation_schema: all_content_search_description: Find all content from %{organisation} + password_input: + label: "Password" + button: "Show" + button_aria_label: "Show password" + error_text_prefix: "Error" previous_and_next_navigation: next: Next pagination: Pagination diff --git a/spec/components/password_input_spec.rb b/spec/components/password_input_spec.rb new file mode 100644 index 0000000000..06e78cd7f4 --- /dev/null +++ b/spec/components/password_input_spec.rb @@ -0,0 +1,47 @@ +require "rails_helper" + +describe "Password Input", type: :view do + def component_name + "password_input" + end + + before { allow(SecureRandom).to receive(:hex).and_return("1234") } + + it "renders a password input component correctly by default" do + render_component({}) + assert_select 'div.govuk-password-input[class~="govuk-form-group govuk-password-input gem-c-password-input govuk-!-margin-bottom-3"]' + assert_select "div.govuk-password-input[data-module=govuk-password-input]" + assert_select "div.govuk-password-input input.govuk-input.govuk-password-input__input.govuk-js-password-input-input" + assert_select "div.govuk-password-input input[name=password][type=password][autocomplete=current-password][autocapitalize=none][spellcheck=false][id='1234-password-input']" + assert_select "div.govuk-password-input label[for='1234-password-input']", "Password" + assert_select "div.govuk-password-input button[aria-controls='1234-password-input']", "Show" + assert_select "p#1234-password-input-with-error-message-error", false + assert_select "div.govuk-password-input input.govuk-input--error", false + assert_select "div.govuk-form-group--error", false + end + + it "renders a password input with extra classes and a paragraph when error_text is passed" do + render_component({ error_text: "Password must contain at least 8 characters" }) + assert_select "div.govuk-form-group--error" + assert_select "div.govuk-password-input label[for='1234-password-input-with-error-message']", "Password" + assert_select "div.govuk-password-input input.govuk-input--error[name=password-input-with-error-message]" + assert_select "div.govuk-password-input input.govuk-input--error[aria-describedby='1234-password-input-with-error-message-error']" + assert_select "div.govuk-password-input button[aria-controls='1234-password-input-with-error-message']", "Show" + assert_select "p#1234-password-input-with-error-message-error", "Error: Password must contain at least 8 characters" + end + + it "renders a password input with custom text" do + render_component({ + label_text: "Secret number", + error_text_prefix: "Incompatible", + error_text: "6 is scared of 7, so they can't be next to each other.", + }) + assert_select "div.govuk-password-input label", "Secret number" + assert_select "p#1234-password-input-with-error-message-error", "Incompatible: 6 is scared of 7, so they can't be next to each other." + end + + it "accepts margin_bottom values" do + render_component({ margin_bottom: 9 }) + assert_select 'div.govuk-password-input[class~="govuk-form-group govuk-password-input gem-c-password-input govuk-!-margin-bottom-9"]' + end +end diff --git a/spec/lib/govuk_publishing_components/app_helpers/asset_helper_spec.rb b/spec/lib/govuk_publishing_components/app_helpers/asset_helper_spec.rb index 477f4d787f..f829af6442 100644 --- a/spec/lib/govuk_publishing_components/app_helpers/asset_helper_spec.rb +++ b/spec/lib/govuk_publishing_components/app_helpers/asset_helper_spec.rb @@ -19,7 +19,7 @@ def request end it "detect the total number of stylesheet paths" do - expect(get_component_css_paths.count).to eql(75) + expect(get_component_css_paths.count).to eql(76) end it "initialize empty asset helper" do