Skip to content

Commit

Permalink
Add the govuk password field
Browse files Browse the repository at this point in the history
  • Loading branch information
peteryates committed Mar 27, 2024
1 parent 0d0db42 commit 1ddf5e2
Show file tree
Hide file tree
Showing 7 changed files with 395 additions and 3 deletions.
7 changes: 6 additions & 1 deletion lib/govuk_design_system_formbuilder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,12 @@ module GOVUKDesignSystemFormBuilder
default_collection_radio_buttons_include_hidden: true,
default_collection_radio_buttons_auto_bold_labels: true,
default_submit_validate: false,

default_show_password_text: "Show",
default_hide_password_text: "Hide",
default_show_password_aria_label_text: "Show password",
default_hide_password_aria_label_text: "Hide password",
default_password_shown_announcement_text: "Your password is visible",
default_password_hidden_announcement_text: "Your password is hidden",
localisation_schema_fallback: %i(helpers __context__),
localisation_schema_label: nil,
localisation_schema_hint: nil,
Expand Down
64 changes: 64 additions & 0 deletions lib/govuk_design_system_formbuilder/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,70 @@ def govuk_number_field(attribute_name, hint: {}, label: {}, caption: {}, width:
Elements::Inputs::Number.new(self, object_name, attribute_name, hint:, label:, caption:, width:, extra_letter_spacing:, form_group:, prefix_text:, suffix_text:, **kwargs, &block).html
end

# Generates a password input
#
# @param attribute_name [Symbol] The name of the attribute
# @param hint [Hash,Proc] The content of the hint. No hint will be added if 'text' is left +nil+. When a +Proc+ is
# supplied the hint will be wrapped in a +div+ instead of a +span+
# @option hint text [String] the hint text
# @option hint kwargs [Hash] additional arguments are applied as attributes to the hint
# @param label [Hash,Proc] configures or sets the associated label content
# @option label text [String] the label text
# @option label size [String] the size of the label font, can be +xl+, +l+, +m+, +s+ or nil
# @option label tag [Symbol,String] the label's wrapper tag, intended to allow labels to act as page headings
# @option label hidden [Boolean] control the visability of the label. Hidden labels will stil be read by screenreaders
# @option label kwargs [Hash] additional arguments are applied as attributes on the +label+ element
# @param caption [Hash] configures or sets the caption content which is inserted above the label
# @option caption text [String] the caption text
# @option caption size [String] the size of the caption, can be +xl+, +l+ or +m+. Defaults to +m+
# @option caption kwargs [Hash] additional arguments are applied as attributes on the caption +span+ element
# @option kwargs [Hash] kwargs additional arguments are applied as attributes to the +input+ element
# @param form_group [Hash] configures the form group
# @option form_group kwargs [Hash] additional attributes added to the form group
# @param show_password_text [String] button text when the password is hidden. Defaults to "Show"
# @param hide_password_text [String] button text when the password is shown. Defaults to "Hide"
# @param show_password_aria_label_text [String] button text exposed to assistive technologies, like screen readers, when the password is hidden. Defaults to "Show password"
# @param hide_password_aria_label_text [String] button text exposed to assistive technologies, like screen readers, when the password is visible. Defaults to "Hide password"
# @param password_shown_announcement_text [String] Announcement made to screen reader users when their password has become visible in plain text. Defaults to "Your password is visible"
# @param password_hidden_announcement_text [String] Announcement made to screen reader users when their password has been obscured and is not visible. Defaults to "Your password is hidden"
#
# @example A password field
# = f.govuk_password_field :password
#
def govuk_password_field(
attribute_name,
hint: {},
label: {},
caption: {},
form_group: {},
show_password_text: config.default_show_password_text,
hide_password_text: config.default_hide_password_text,
show_password_aria_label_text: config.default_show_password_aria_label_text,
hide_password_aria_label_text: config.default_hide_password_aria_label_text,
password_shown_announcement_text: config.default_password_shown_announcement_text,
password_hidden_announcement_text: config.default_password_hidden_announcement_text,
**kwargs,
&block
)
Elements::Password.new(
self,
object_name,
attribute_name,
hint:,
label:,
caption:,
form_group:,
show_password_text:,
hide_password_text:,
show_password_aria_label_text:,
hide_password_aria_label_text:,
password_shown_announcement_text:,
password_hidden_announcement_text:,
**kwargs,
&block
).html
end

# Generates a +textarea+ element with a label, optional hint. Also offers
# the ability to add the GOV.UK character and word counting components
# automatically
Expand Down
106 changes: 106 additions & 0 deletions lib/govuk_design_system_formbuilder/elements/password.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
module GOVUKDesignSystemFormBuilder
module Elements
class Password < Base
using PrefixableArray

include Traits::Error
include Traits::Hint
include Traits::Label
include Traits::HTMLAttributes
include Traits::HTMLClasses

I18nAttr = Struct.new(:key, :text, :default)

def initialize(builder, object_name, attribute_name, label:, caption:, hint:, form_group:, show_password_text:, hide_password_text:, show_password_aria_label_text:, hide_password_aria_label_text:, password_hidden_announcement_text:, password_shown_announcement_text:, **kwargs, &block)
super(builder, object_name, attribute_name, &block)

@label = label
@caption = caption
@hint = hint
@form_group = form_group
@html_attributes = kwargs

@show_password_text = show_password_text
@hide_password_text = hide_password_text

@show_password_aria_label_text = show_password_aria_label_text
@hide_password_aria_label_text = hide_password_aria_label_text

@password_shown_announcement_text = password_shown_announcement_text
@password_hidden_announcement_text = password_hidden_announcement_text
end

def html
Containers::FormGroup.new(*bound, **form_group_options).html do
safe_join([label_element, hint_element, error_element, password_input_and_button])
end
end

private

def password_input_and_button
tag.div(class: wrapper_classes) do
safe_join([password_input, button])
end
end

def options
{
id: field_id(link_errors: true),
class: classes,
spellcheck: "false",
autocomplete: "current-password",
autocapitalize: "none",
aria: { describedby: combine_references(hint_id, error_id) }
}
end

def form_group_options
{
**@form_group,
**i18n_data,
class: %(#{brand}-password-input),
data: { module: %(#{brand}-password-input) },
}
end

def password_input
@builder.password_field(@attribute_name, attributes(@html_attributes))
end

def classes
build_classes('input', 'password-input__input', 'js-password-input-input', %(password-input--error) => has_errors?).prefix(brand)
end

def wrapper_classes
%w(input__wrapper password-input__wrapper).prefix(brand)
end

def button
tag.button(@show_password_text, **button_options)
end

def button_options
{
data: { module: %(#{brand}-button) },
aria: { label: "Show password", controls: field_id(link_errors: true) },
type: 'button',
class: %w(button button--secondary password-input__toggle js-password-input-toggle).prefix(brand)
}
end

def i18n_data
[
I18nAttr.new("data-i18n.show-password", @show_password_text, config.default_show_password_text),
I18nAttr.new("data-i18n.hide-password", @hide_password_text, config.default_hide_password_text),
I18nAttr.new("data-i18n.show-password-aria-label", @show_password_aria_label_text, config.default_show_password_aria_label_text),
I18nAttr.new("data-i18n.hide-password-aria-label", @hide_password_aria_label_text, config.default_hide_password_aria_label_text),
I18nAttr.new("data-i18n.password-shown-announcement", @password_shown_announcement_text, config.default_password_shown_announcement_text),
I18nAttr.new("data-i18n.password-hidden-announcement", @password_hidden_announcement_text, config.default_password_hidden_announcement_text),
].each_with_object({}) do |attr, h|
h[attr.key] = attr.text unless attr.text == attr.default
end
end
end
end
end
2 changes: 1 addition & 1 deletion spec/govuk_design_system_formbuilder/builder/date_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
describe GOVUKDesignSystemFormBuilder::FormBuilder do
include_context 'setup builder'

describe '#date_input_group' do
describe '#govuk_date_field' do
let(:method) { :govuk_date_field }
let(:attribute) { :born_on }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@
specify "errors are displayed in the order they're defined in the model" do
expect(object.name).to be_present

expect(actual_order).to eql(%w(favourite_colour projects cv))
expect(actual_order).to eql(%w(favourite_colour projects cv password))
end
end

Expand Down
Loading

0 comments on commit 1ddf5e2

Please sign in to comment.