DFE-Digital/govuk-formbuilder

View on GitHub
lib/govuk_design_system_formbuilder.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
require 'active_support/configurable'
require 'html_attributes_utils'

[%w(presenters *.rb), %w(refinements *.rb), %w(traits *.rb), %w(*.rb), %w(elements ** *.rb), %w(containers ** *.rb)]
  .flat_map { |matcher| Dir.glob(File.join(__dir__, 'govuk_design_system_formbuilder', *matcher)) }
  .each { |file| require file }

module GOVUKDesignSystemFormBuilder
  include ActiveSupport::Configurable

  # @!group Defaults

  # Default form builder configuration
  #
  # * +:brand+ sets the value used to prefix all classes, used to allow the
  #   builder to be branded for alternative (similar) design systems.
  #
  # * +:default_caption_size+ controls the default size of caption text.
  #   Can be either +xl+, +l+ or +m+.
  #
  # * +:default_legend_size+ controls the default size of legend text.
  #   Can be either +xl+, +l+, +m+ or +s+.
  #
  # * +:default_legend_tag+ controls the default tag that legends are
  #   wrapped in. Defaults to +nil+.
  #
  # * +:default_submit_button_text+ sets the value assigned to +govuk_submit+,
  #   defaults to 'Continue'.
  #
  # * +:default_date_segments+ allows the date segments used by Rails'
  #   multiparameter attributes to be configured. This is useful if you want to
  #   override the standard behaviour where Rails tries to cast the values
  #   into a +Date+. Defaults to +{ day: '3i', month: '2i', year: '1i' }+
  #
  # * +:default_radio_divider_text+ sets the text automatically added to the
  #   radio button divider, defaults to 'or'
  #
  # * +:default_check_box_divider_text+ sets the text automatically added to the
  #   checkbox divider, defaults to 'or'
  #
  # * +:default_collection_check_boxes_include_hidden+ controls whether or not
  #   a hidden field is added when rendering a collection of check boxes
  #
  # * +:default_collection_radio_buttons_include_hidden+ controls whether or not
  #   a hidden field is added when rendering a collection of radio buttons
  #
  # * +:default_collection_radio_buttons_auto_bold_labels+ will automatically
  #   make labels on {#govuk_collection_radio_buttons} bold when a +:hint_method+
  #   is present. The default can be overridden using the +bold_labels:+ argument.
  #   The default value is 'true'.
  #
  # * +:default_error_summary_title+ sets the text used in error summary
  #   blocks. As per the GOV.UK Design System spec, it defaults to
  #   'There is a problem'.
  #
  # * +:default_error_summary_presenter+ the class that's instantiated when
  #   rendering an error summary and formats the messages for each attribute
  #
  # * +:default_error_summary_error_order_method+ is the method that the library
  #   will check for on the bound object to see whether or not to try ordering the
  #   error messages
  #
  # * +:default_error_summary_turbo_prefix+ is used to disable turbo/turbolinks from
  #   handling clicks on links in the error summary. When it's a string (eg,
  #   turbo), that will result in links with the attribute 'data-turbo=false'.
  #   When nil, no data attribute will be added. Defaults to turbo since Rails 7,
  #   change to 'turbolinks' for Rails 6.1
  #
  # * +:localisation_schema_fallback+ sets the prefix elements for the array
  #   used to build the localisation string. The final two elements are always
  #   are the object name and attribute name. The _special_ value +__context__+,
  #   is used as a placeholder for the context (label, fieldset or hint).
  #
  # * +:localisation_schema_legend+, +:localisation_schema_hint+ and
  #   +:localisation_schema_label+ each override the schema root for their
  #   particular context, allowing them to be independently customised.
  #
  # * +:enable_logger+ controls whether or not the library will emit log
  #   messages via Rails.logger.warn, defaults to +true+
  #
  # * +:trust_error_messages+ call html_safe on error messages before they're
  #   rendered. This allows formatting markup to be used to change the display
  #   of the error message. Defaults to +false+
  #
  # * +:enable_nested_localisation+ scan the object name before building the
  #   localisation schema path to see if it's nested or not. The old behaviour
  #   can be restored by setting this option to +false+. Defaults to +true+
  # ===
  DEFAULTS = {
    brand: 'govuk',

    default_legend_size: 'm',
    default_legend_tag: nil,
    default_caption_size: 'm',
    default_submit_button_text: 'Continue',
    default_date_segments: { day: '3i', month: '2i', year: '1i' },
    default_radio_divider_text: 'or',
    default_check_box_divider_text: 'or',
    default_error_summary_title: 'There is a problem',
    default_error_summary_presenter: Presenters::ErrorSummaryPresenter,
    default_error_summary_error_order_method: nil,
    default_error_summary_turbo_prefix: 'turbo',
    default_collection_check_boxes_include_hidden: true,
    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,
    localisation_schema_legend: nil,
    localisation_schema_caption: nil,

    enable_logger: true,
    trust_error_messages: false,

    # temporarily allow the new nested localisation functionality to be
    # disabled
    enable_nested_localisation: true,
  }.freeze

  DEFAULTS.each_key { |k| config_accessor(k) { DEFAULTS[k] } }
  # @!endgroup

  class << self
    # Configure the form builder in the usual manner. All of the
    # keys in {DEFAULTS} can be configured as per the example below
    #
    # @example
    #   GOVUKDesignSystemFormBuilder.configure do |conf|
    #     conf.default_legend_size = 'xl'
    #     conf.default_error_summary_title = 'OMG'
    #   end
    def configure
      yield(config)
    end

    # Resets each of the configurable values to its default
    #
    # @note This method is only really intended for use to clean up
    #   during testing
    def reset!
      configure do |c|
        DEFAULTS.each { |k, v| c.send("#{k}=", v) }
      end
    end
  end

  class FormBuilder < ActionView::Helpers::FormBuilder
    delegate :content_tag, :tag, :safe_join, :link_to, :capture, to: :@template

    include GOVUKDesignSystemFormBuilder::Builder
  end

  class FormBuilderProxy < FormBuilder; end

  # Disable Rails' div.field_with_error wrapper
  ActionView::Base.field_error_proc = ->(html_tag, _instance) { html_tag }
end