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

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.



54
55
56
57
58
59
60
61
62
63
64
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
# File 'lib/primer/forms/dsl/input.rb', line 54

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)

  # 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.
  #
  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.



50
51
52
# File 'lib/primer/forms/dsl/input.rb', line 50

def base_id
  @base_id
end

#builderObject (readonly)

Returns the value of attribute builder.



50
51
52
# File 'lib/primer/forms/dsl/input.rb', line 50

def builder
  @builder
end

#captionObject (readonly)

Returns the value of attribute caption.



50
51
52
# File 'lib/primer/forms/dsl/input.rb', line 50

def caption
  @caption
end

#formObject (readonly)

Returns the value of attribute form.



50
51
52
# File 'lib/primer/forms/dsl/input.rb', line 50

def form
  @form
end

#form_controlObject (readonly) Also known as: form_control?

Returns the value of attribute form_control.



50
51
52
# File 'lib/primer/forms/dsl/input.rb', line 50

def form_control
  @form_control
end

#idsObject (readonly)

Returns the value of attribute ids.



50
51
52
# File 'lib/primer/forms/dsl/input.rb', line 50

def ids
  @ids
end

#input_argumentsObject (readonly)

Returns the value of attribute input_arguments.



50
51
52
# File 'lib/primer/forms/dsl/input.rb', line 50

def input_arguments
  @input_arguments
end

#label_argumentsObject (readonly)

Returns the value of attribute label_arguments.



50
51
52
# File 'lib/primer/forms/dsl/input.rb', line 50

def label_arguments
  @label_arguments
end

#validation_messageObject (readonly)

Returns the value of attribute validation_message.



50
51
52
# File 'lib/primer/forms/dsl/input.rb', line 50

def validation_message
  @validation_message
end

Instance Method Details

#add_input_aria(key, value) ⇒ Object



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

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



124
125
126
127
128
# File 'lib/primer/forms/dsl/input.rb', line 124

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

#add_input_data(key, value) ⇒ Object



146
147
148
# File 'lib/primer/forms/dsl/input.rb', line 146

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

#add_label_classes(*class_names) ⇒ Object



130
131
132
133
134
# File 'lib/primer/forms/dsl/input.rb', line 130

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

#autofocus!Object



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

def autofocus!
  input_arguments[:autofocus] = true
end

#caption?Boolean

Returns:

  • (Boolean)


183
184
185
# File 'lib/primer/forms/dsl/input.rb', line 183

def caption?
  caption.present?
end

#caption_idObject



179
180
181
# File 'lib/primer/forms/dsl/input.rb', line 179

def caption_id
  ids[:caption]
end

#caption_template?Boolean

Returns:

  • (Boolean)


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

def caption_template?
  return false unless form

  form.caption_template?(caption_template_name)
end

#disabled?Boolean

Returns:

  • (Boolean)


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

def disabled?
  !!input_arguments[:disabled]
end

#focusable?Boolean

:nocov:

Returns:

  • (Boolean)


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

def focusable?
  false
end

#full_width?Boolean

Returns:

  • (Boolean)


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

def full_width?
  @full_width
end

#hidden?Boolean

Returns:

  • (Boolean)


205
206
207
# File 'lib/primer/forms/dsl/input.rb', line 205

def hidden?
  !!input_arguments[:hidden]
end

#input?Boolean

Returns:

  • (Boolean)


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

def input?
  true
end

#invalid?Boolean

Returns:

  • (Boolean)


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

def invalid?
  !valid?
end

#labelObject



248
249
250
# File 'lib/primer/forms/dsl/input.rb', line 248

def label
  raise_for_abstract_method!(__method__)
end

#merge_input_arguments!(arguments) ⇒ Object

:nocov:



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/primer/forms/dsl/input.rb', line 156

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:



244
245
246
# File 'lib/primer/forms/dsl/input.rb', line 244

def name
  raise_for_abstract_method!(__method__)
end

#remove_input_data(key) ⇒ Object

:nocov:



151
152
153
# File 'lib/primer/forms/dsl/input.rb', line 151

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

#render_caption_templateObject



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

def render_caption_template
  form.render_caption_template(caption_template_name)
end

#required?Boolean

Returns:

  • (Boolean)


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

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

#sizeObject



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

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

#to_componentObject



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

def to_component
  raise_for_abstract_method!(__method__)
end

#typeObject



252
253
254
# File 'lib/primer/forms/dsl/input.rb', line 252

def type
  raise_for_abstract_method!(__method__)
end

#valid?Boolean

Returns:

  • (Boolean)


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

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

#validation_argumentsObject



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

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

#validation_error_icon_targetObject



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

def validation_error_icon_target
  ""
end

#validation_idObject



175
176
177
# File 'lib/primer/forms/dsl/input.rb', line 175

def validation_id
  ids[:validation]
end

#validation_message_argumentsObject



277
278
279
# File 'lib/primer/forms/dsl/input.rb', line 277

def validation_message_arguments
  {}
end

#validation_messagesObject



228
229
230
231
232
233
234
235
236
237
# File 'lib/primer/forms/dsl/input.rb', line 228

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

#validation_success_icon_targetObject



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

def validation_success_icon_target
  ""
end