Class: Primer::Forms::Dsl::Input

Inherits:
Object
  • Object
show all
Includes:
ClassNameHelper
Defined in:
lib/primer/forms/dsl/input.rb

Overview

:nodoc:

Constant Summary collapse

SPACE_DELIMITED_ARIA_ATTRIBUTES =
%i[describedby].freeze
DEFAULT_SIZE =
:medium
SIZE_MAPPINGS =
{
  :small => "FormControl-small",
  DEFAULT_SIZE => "FormControl-medium",
  :large => "FormControl-large"
}.freeze
SIZE_OPTIONS =
SIZE_MAPPINGS.keys
DEFAULT_INPUT_WIDTH =
:auto
INPUT_WIDTH_MAPPINGS =
{
  DEFAULT_INPUT_WIDTH => "FormControl-input-width--auto",
  :small => "FormControl-input-width--small",
  :medium => "FormControl-input-width--medium",
  :large => "FormControl-input-width--large",
  :xlarge => "FormControl-input-width--xlarge",
  :xxlarge => "FormControl-input-width--xxlarge"
}.freeze
INPUT_WIDTH_OPTIONS =
INPUT_WIDTH_MAPPINGS.keys

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ClassNameHelper

#class_names

Constructor Details

#initialize(builder:, form:, **system_arguments) ⇒ Input

Returns a new instance of Input.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/primer/forms/dsl/input.rb', line 65

def initialize(builder:, form:, **system_arguments)
  @builder = builder
  @form = form

  @input_arguments = system_arguments
  @label_arguments = @input_arguments.delete(:label_arguments) || {}

  @label_arguments[:class] = class_names(
    @label_arguments[:class],
    @input_arguments.fetch(:visually_hide_label, false) ? "sr-only" : nil
  )

  @input_arguments.delete(:visually_hide_label)

  @input_arguments.delete(:class) if @input_arguments[:class].blank?
  @label_arguments.delete(:class) if @label_arguments[:class].blank?

  @caption = @input_arguments.delete(:caption)
  @validation_message = @input_arguments.delete(:validation_message)
  @invalid = @input_arguments.delete(:invalid)
  @full_width = @input_arguments.delete(:full_width)
  @size = @input_arguments.delete(:size)
  @input_width = @input_arguments.delete(:input_width)

  # If scope_name_to_model is false, the name of the input for eg. `my_field`
  # will be `my_field` instead of the Rails default of `model[my_field]`.
  #
  # We achieve this by passing the `name` option to Rails form builder
  # methods. These methods will use the passed name if provided instead
  # of generating a scoped one.
  #
  # rubocop:disable Style/IfUnlessModifier
  unless @input_arguments.delete(:scope_name_to_model) { true }
    @input_arguments[:name] = name
  end
  # rubocop:enable Style/IfUnlessModifier

  # If scope_id_to_model is false, the name of the input for eg. `my_field`
  # will be `my_field` instead of the Rails default of `model_my_field`.
  #
  # We achieve this by passing the `id` option to Rails form builder
  # methods. These methods will use the passed id if provided instead
  # of generating a scoped one. The id is the name of the field unless
  # explicitly provided in system_arguments.
  #
  # rubocop:disable Style/IfUnlessModifier
  unless @input_arguments.delete(:scope_id_to_model) { true }
    @input_arguments[:id] = @input_arguments.delete(:id) { name }
  end
  # rubocop:enable Style/IfUnlessModifier

  # Whether or not to wrap the component in a FormControl, which renders a
  # label above and validation message beneath the input.
  @form_control = @input_arguments.delete(:form_control) { true }

  @input_arguments[:invalid] = "true" if invalid?

  @base_id = SecureRandom.uuid

  @ids = {}.tap do |id_map|
    id_map[:validation] = "validation-#{@base_id}"
    id_map[:caption] = "caption-#{@base_id}" if caption? || caption_template?
  end

  add_input_aria(:required, true) if required?
  add_input_aria(:invalid, true) if invalid?
  add_input_aria(:describedby, ids.values) if ids.any?

  # avoid browser-native validation, which doesn't match Primer's style
  input_arguments.delete(:required)
end

Instance Attribute Details

#base_idObject (readonly)

Returns the value of attribute base_id.



61
62
63
# File 'lib/primer/forms/dsl/input.rb', line 61

def base_id
  @base_id
end

#builderObject (readonly)

Returns the value of attribute builder.



61
62
63
# File 'lib/primer/forms/dsl/input.rb', line 61

def builder
  @builder
end

#captionObject (readonly)

Returns the value of attribute caption.



61
62
63
# File 'lib/primer/forms/dsl/input.rb', line 61

def caption
  @caption
end

#formObject (readonly)

Returns the value of attribute form.



61
62
63
# File 'lib/primer/forms/dsl/input.rb', line 61

def form
  @form
end

#form_controlObject (readonly) Also known as: form_control?

Returns the value of attribute form_control.



61
62
63
# File 'lib/primer/forms/dsl/input.rb', line 61

def form_control
  @form_control
end

#idsObject (readonly)

Returns the value of attribute ids.



61
62
63
# File 'lib/primer/forms/dsl/input.rb', line 61

def ids
  @ids
end

#input_argumentsObject (readonly)

Returns the value of attribute input_arguments.



61
62
63
# File 'lib/primer/forms/dsl/input.rb', line 61

def input_arguments
  @input_arguments
end

#label_argumentsObject (readonly)

Returns the value of attribute label_arguments.



61
62
63
# File 'lib/primer/forms/dsl/input.rb', line 61

def label_arguments
  @label_arguments
end

#validation_messageObject (readonly)

Returns the value of attribute validation_message.



61
62
63
# File 'lib/primer/forms/dsl/input.rb', line 61

def validation_message
  @validation_message
end

Instance Method Details

#add_input_aria(key, value) ⇒ Object



149
150
151
152
153
154
155
156
157
# File 'lib/primer/forms/dsl/input.rb', line 149

def add_input_aria(key, value)
  @input_arguments[:aria] ||= {}

  @input_arguments[:aria][key] = if space_delimited_aria_attribute?(key)
                                   aria_join(@input_arguments[:aria][key], *Array(value))
                                 else
                                   value
                                 end
end

#add_input_classes(*class_names) ⇒ Object



137
138
139
140
141
# File 'lib/primer/forms/dsl/input.rb', line 137

def add_input_classes(*class_names)
  input_arguments[:class] = class_names(
    input_arguments[:class], *class_names
  )
end

#add_input_data(key, value) ⇒ Object



159
160
161
# File 'lib/primer/forms/dsl/input.rb', line 159

def add_input_data(key, value)
  input_data[key] = value
end

#add_label_classes(*class_names) ⇒ Object



143
144
145
146
147
# File 'lib/primer/forms/dsl/input.rb', line 143

def add_label_classes(*class_names)
  label_arguments[:class] = class_names(
    label_arguments[:class], *class_names
  )
end

#autofocus!Object



257
258
259
# File 'lib/primer/forms/dsl/input.rb', line 257

def autofocus!
  input_arguments[:autofocus] = true
end

#caption?Boolean

Returns:

  • (Boolean)


197
198
199
# File 'lib/primer/forms/dsl/input.rb', line 197

def caption?
  caption.present?
end

#caption_idObject



193
194
195
# File 'lib/primer/forms/dsl/input.rb', line 193

def caption_id
  ids[:caption]
end

#caption_template?Boolean

Returns:

  • (Boolean)


201
202
203
204
205
# File 'lib/primer/forms/dsl/input.rb', line 201

def caption_template?
  return false unless form

  form.caption_template?(caption_template_name)
end

#disabled?Boolean

Returns:

  • (Boolean)


230
231
232
# File 'lib/primer/forms/dsl/input.rb', line 230

def disabled?
  !!input_arguments[:disabled]
end

#focusable?Boolean

:nocov:

Returns:

  • (Boolean)


280
281
282
# File 'lib/primer/forms/dsl/input.rb', line 280

def focusable?
  false
end

#full_width?Boolean

Returns:

  • (Boolean)


234
235
236
# File 'lib/primer/forms/dsl/input.rb', line 234

def full_width?
  @full_width
end

#hidden?Boolean

Returns:

  • (Boolean)


219
220
221
# File 'lib/primer/forms/dsl/input.rb', line 219

def hidden?
  !!input_arguments[:hidden]
end

#input?Boolean

Returns:

  • (Boolean)


284
285
286
# File 'lib/primer/forms/dsl/input.rb', line 284

def input?
  true
end

#input_widthObject



242
243
244
# File 'lib/primer/forms/dsl/input.rb', line 242

def input_width
  @input_width ||= INPUT_WIDTH_MAPPINGS.include?(@input_width) ? @input_width : DEFAULT_INPUT_WIDTH
end

#invalid?Boolean

Returns:

  • (Boolean)


215
216
217
# File 'lib/primer/forms/dsl/input.rb', line 215

def invalid?
  !valid?
end

#labelObject



266
267
268
# File 'lib/primer/forms/dsl/input.rb', line 266

def label
  raise_for_abstract_method!(__method__)
end

#merge_input_arguments!(arguments) ⇒ Object

:nocov:



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/primer/forms/dsl/input.rb', line 170

def merge_input_arguments!(arguments)
  arguments.each do |k, v|
    case k
    when :class, :classes, "class", "classes"
      add_input_classes(v)
    when :aria, "aria"
      v.each do |aria_k, aria_v|
        add_input_aria(aria_k, aria_v)
      end
    when :data, "data"
      v.each do |data_k, data_v|
        add_input_data(data_k, data_v)
      end
    else
      @input_arguments[k] = v
    end
  end
end

#nameObject

:nocov:



262
263
264
# File 'lib/primer/forms/dsl/input.rb', line 262

def name
  raise_for_abstract_method!(__method__)
end

#remove_input_data(key) ⇒ Object

:nocov:



164
165
166
# File 'lib/primer/forms/dsl/input.rb', line 164

def remove_input_data(key)
  input_data.delete(key)
end

#render_caption_templateObject



207
208
209
# File 'lib/primer/forms/dsl/input.rb', line 207

def render_caption_template
  form.render_caption_template(caption_template_name)
end

#required?Boolean

Returns:

  • (Boolean)


223
224
225
226
227
228
# File 'lib/primer/forms/dsl/input.rb', line 223

def required?
  input_arguments[:required] ||
    input_arguments[:aria_required] ||
    input_arguments[:"aria-required"] ||
    input_arguments.dig(:aria, :required)
end

#sizeObject



238
239
240
# File 'lib/primer/forms/dsl/input.rb', line 238

def size
  @size ||= SIZE_MAPPINGS.include?(@size) ? @size : DEFAULT_SIZE
end

#to_componentObject



274
275
276
# File 'lib/primer/forms/dsl/input.rb', line 274

def to_component
  raise_for_abstract_method!(__method__)
end

#typeObject



270
271
272
# File 'lib/primer/forms/dsl/input.rb', line 270

def type
  raise_for_abstract_method!(__method__)
end

#valid?Boolean

Returns:

  • (Boolean)


211
212
213
# File 'lib/primer/forms/dsl/input.rb', line 211

def valid?
  validation_messages.empty? && !@invalid
end

#validation_argumentsObject



288
289
290
291
292
293
294
# File 'lib/primer/forms/dsl/input.rb', line 288

def validation_arguments
  {
    class: "FormControl-inlineValidation",
    id: validation_id,
    hidden: valid? || validation_messages.empty?
  }
end

#validation_idObject



189
190
191
# File 'lib/primer/forms/dsl/input.rb', line 189

def validation_id
  ids[:validation]
end

#validation_message_argumentsObject



296
297
298
# File 'lib/primer/forms/dsl/input.rb', line 296

def validation_message_arguments
  {}
end

#validation_messagesObject



246
247
248
249
250
251
252
253
254
255
# File 'lib/primer/forms/dsl/input.rb', line 246

def validation_messages
  @validation_messages ||=
    if validation_message
      [validation_message]
    elsif builder.object.respond_to?(:errors)
      name ? builder.object.errors.full_messages_for(name) : []
    else
      []
    end
end