Module: Katalyst::GOVUK::FormBuilder::Traits::Attachment
- Extended by:
- ActiveSupport::Concern
- Includes:
- ActionView::Helpers::NumberHelper
- Included in:
- Elements::Attachment
- Defined in:
- app/helpers/katalyst/govuk/form_builder/traits/attachment.rb
Overview
Generates attachment inputs and previews for ActiveStorage associations.
Constant Summary collapse
- I18N_SCOPE =
The attachment strings' canonical home (config/locales).
%i[katalyst govuk attachment].freeze
- BUNDLED_DEFAULTS =
The gem's own en strings, read straight from its locale file: resolving them through I18n would absorb a host app's en overrides, and this table is the baseline those overrides are detected against.
YAML.load_file( Engine.root.join("config/locales/en.yml"), ).dig("en", *I18N_SCOPE.map(&:to_s)).freeze
Instance Method Summary collapse
- #attachment ⇒ ActiveSupport::SafeBuffer?
- #attachment? ⇒ Boolean
-
#attachment_actions_for(blob) ⇒ ActiveSupport::SafeBuffer|nil
A
with form elements for managing the attachment.- #attachment_caption_for(blob) ⇒ ActiveSupport::SafeBuffer?
The caption is a polite atomic live region: JS writes upload status into the status span, and the announcement reads the whole caption so the user hears which file the status belongs to.
- #attachment_for(blob) ⇒ ActiveSupport::SafeBuffer?
- #attachment_id_for(blob, *suffixes) ⇒ String?
- #attachment_input_for(blob) ⇒ ActiveSupport::SafeBuffer?
A
- #attachment_preview_for(blob) ⇒ ActiveSupport::SafeBuffer?
The representation is rendered lazily: the variant is processed when the browser requests it, never during the form render.
- #attachment_remove_for(blob) ⇒ ActiveSupport::SafeBuffer?
A
- #attachment_translation(key) ⇒ String
The current locale's translation, falling back to the gem's en defaults rather than a "translation missing" marker.
- #many? ⇒ Boolean
- #one? ⇒ Boolean
- #remove_button_content ⇒ String
- #remove_button_label(blob) ⇒ String
The remove strings render here and in the JS figure template, so both draw from the same options (or matching defaults) — the two figure sources must stay string-identical.
- #value ⇒ Object
Instance Method Details
#attachment ⇒ ActiveSupport::SafeBuffer?
45 46 47 48 49 50 51 52 53 54
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 45 def return unless attached? # Preserve unsaved multi-part form uploads before rendering # mimics direct-upload for non-js consumers. persist_pending_blobs blobs = (one? ? [value.blob] : value.blobs).select(&:persisted?) safe_join(blobs.map { |blob| (blob) }) end
#attachment? ⇒ Boolean
26 27 28
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 26 def value.is_a?(ActiveStorage::Attached) end
#attachment_actions_for(blob) ⇒ ActiveSupport::SafeBuffer|nil
A
with form elements for managing the attachment.73 74 75 76 77 78 79 80 81 82
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 73 def (blob) return if blob.nil? tag.div(class: "actions") do safe_join([ (blob), (blob), ]) end end
#attachment_caption_for(blob) ⇒ ActiveSupport::SafeBuffer?
The caption is a polite atomic live region: JS writes upload status into the status span, and the announcement reads the whole caption so the user hears which file the status belongs to.
164 165 166 167 168 169 170 171 172 173 174
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 164 def (blob) tag.figcaption(class: "caption", aria: { atomic: true, live: "polite" }) do safe_join([ tag.span(blob.filename, id: (blob, :filename), class: "filename"), " ", tag.span(number_to_human_size(blob.byte_size), class: "size"), " ", tag.span(class: "status"), ]) end end
#attachment_for(blob) ⇒ ActiveSupport::SafeBuffer?
58 59 60 61 62 63 64 65 66 67 68
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 58 def (blob) tag.figure(class: "#{brand}-attachment", aria: { labelledby: (blob, :filename) }, data: { controller: "govuk-attachment" }) do safe_join([ (blob), (blob), (blob), ]) end end
#attachment_id_for(blob, *suffixes) ⇒ String?
178 179 180 181 182
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 178 def (blob, *suffixes) return nil if @html_attributes.fetch(:skip_default_ids, false) @builder.field_id(@attribute_name, :attachment, blob.id, *suffixes) end
#attachment_input_for(blob) ⇒ ActiveSupport::SafeBuffer?
A
87 88 89 90 91 92 93 94 95 96 97
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 87 def (blob) @builder.select( @attribute_name, [[blob.filename.to_s, blob.signed_id], [(blob), ""]], { selected: blob.signed_id }, id: (blob, :input), name: @builder.field_name(@attribute_name, multiple: many?), aria: { labelledby: (blob, :filename) }, ) end
#attachment_preview_for(blob) ⇒ ActiveSupport::SafeBuffer?
The representation is rendered lazily: the variant is processed when the browser requests it, never during the form render. If the blob's bytes turn out to be missing or unprocessable the preview simply fails to load — validating attachment content is the model's responsibility, not the form's.
147 148 149 150 151 152 153 154 155 156 157
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 147 def (blob) return unless blob.representable? url = @builder.( blob.representation(config.), ) return if url.nil? # Setting alt to "" as the details already describe the attachment, equivalent to role="presentation" @builder.image_tag(url, alt: "", class: "preview") end
#attachment_remove_for(blob) ⇒ ActiveSupport::SafeBuffer?
A
102 103 104 105 106 107 108
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 102 def (blob) tag.(, type: "button", class: "#{brand}-button #{brand}-button--secondary #{brand}-attachment__remove", aria: { label: (blob) }, data: { action: "govuk-attachment#destroy", module: "govuk-button" }) end
#attachment_translation(key) ⇒ String
The current locale's translation, falling back to the gem's en defaults rather than a "translation missing" marker.
135 136 137 138
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 135 def (key, **) I18n.t(key, scope: I18N_SCOPE, default: nil, **) || I18n.t(key, scope: I18N_SCOPE, locale: :en, **) end
#many? ⇒ Boolean
34 35 36
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 34 def many? value.is_a?(ActiveStorage::Attached::Many) end
#one? ⇒ Boolean
30 31 32
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 30 def one? value.is_a?(ActiveStorage::Attached::One) end
#remove_button_content ⇒ String
128 129 130
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 128 def @remove_button_content_text || (:remove_button_content) end
#remove_button_label(blob) ⇒ String
The remove strings render here and in the JS figure template, so both draw from the same options (or matching defaults) — the two figure sources must stay string-identical.
115 116 117 118 119 120 121 122 123 124 125
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 115 def (blob) if @remove_button_text # %{filename} is the option placeholder (govuk-frontend's i18n # convention), substituted directly — not a Ruby format token. # rubocop:disable Style/FormatStringToken @remove_button_text.gsub("%{filename}", blob.filename.to_s) # rubocop:enable Style/FormatStringToken else (:remove_button, filename: blob.filename.to_s) end end
#value ⇒ Object
38 39 40
# File 'app/helpers/katalyst/govuk/form_builder/traits/attachment.rb', line 38 def value @builder.object.send(@attribute_name) end
- #attachment_caption_for(blob) ⇒ ActiveSupport::SafeBuffer?