katalyst-govuk-formbuilder 1.30.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +141 -15
  3. data/app/assets/builds/katalyst/govuk/formbuilder.css +77 -0
  4. data/app/assets/builds/katalyst/govuk/formbuilder.js +573 -10
  5. data/app/assets/builds/katalyst/govuk/formbuilder.min.js +8 -8
  6. data/app/assets/builds/katalyst/govuk/formbuilder.min.js.map +1 -1
  7. data/app/assets/stylesheets/katalyst/govuk/components/_index.scss +1 -0
  8. data/app/assets/stylesheets/katalyst/govuk/components/attachment/_index.scss +3 -0
  9. data/app/assets/stylesheets/katalyst/govuk/components/attachment/_mixin.scss +89 -0
  10. data/app/helpers/katalyst/govuk/form_builder/builder.rb +504 -0
  11. data/app/helpers/katalyst/govuk/form_builder/containers/fieldset_context.rb +19 -0
  12. data/app/helpers/katalyst/govuk/form_builder/elements/attachment.rb +80 -0
  13. data/app/helpers/katalyst/govuk/form_builder/elements/combobox.rb +85 -0
  14. data/app/helpers/katalyst/govuk/form_builder/elements/document.rb +68 -0
  15. data/app/helpers/katalyst/govuk/form_builder/elements/image.rb +65 -0
  16. data/app/helpers/katalyst/govuk/form_builder/elements/label.rb +38 -0
  17. data/app/helpers/katalyst/govuk/form_builder/elements/legend.rb +32 -0
  18. data/app/helpers/katalyst/govuk/form_builder/elements/rich_textarea.rb +70 -0
  19. data/app/helpers/katalyst/govuk/form_builder/frontend.rb +34 -0
  20. data/app/helpers/katalyst/govuk/form_builder/traits/attachment.rb +215 -0
  21. data/app/helpers/katalyst/govuk/form_builder/traits/file.rb +104 -0
  22. data/app/helpers/katalyst/govuk/form_builder/traits/label.rb +33 -0
  23. data/config/importmap.rb +1 -0
  24. data/config/locales/en.yml +16 -0
  25. data/lib/katalyst/govuk/form_builder/config.rb +20 -0
  26. data/lib/katalyst/govuk/form_builder/engine.rb +1 -1
  27. metadata +31 -1
@@ -1,3 +1,4 @@
1
+ @use "attachment";
1
2
  @use "govuk-frontend/dist/govuk/components/character-count";
2
3
  @use "govuk-frontend/dist/govuk/components/checkboxes";
3
4
  @use "combobox";
@@ -0,0 +1,3 @@
1
+ @use "mixin";
2
+
3
+ @include mixin.styles;
@@ -0,0 +1,89 @@
1
+ @use "govuk-frontend/dist/govuk/base";
2
+ @use "govuk-frontend/dist/govuk/core/typography";
3
+
4
+ $attachment-background-colour: base.govuk-colour("black", $variant: "tint-95");
5
+ $attachment-border-width: 2px;
6
+
7
+ @mixin styles {
8
+ :where(.govuk-attachment) {
9
+ display: grid;
10
+ grid-template-areas: "preview caption actions";
11
+ grid-template-columns: auto 1fr auto;
12
+ margin: 0;
13
+ padding: base.govuk-spacing(3) base.govuk-spacing(3);
14
+ grid-gap: base.govuk-spacing(2);
15
+ background-color: $attachment-background-colour;
16
+ border: $attachment-border-width solid
17
+ base.govuk-functional-colour("border");
18
+
19
+ .preview {
20
+ grid-area: preview;
21
+ max-width: 4rem;
22
+ aspect-ratio: 1/1;
23
+ object-fit: cover;
24
+ }
25
+
26
+ .caption {
27
+ display: flex;
28
+ flex-direction: column;
29
+ grid-area: caption;
30
+ @include base.govuk-font($size: 16, $line-height: 1.25);
31
+ }
32
+
33
+ .filename {
34
+ @include base.govuk-typography-weight-bold;
35
+ }
36
+
37
+ .size {
38
+ color: base.govuk-functional-colour(secondary-text);
39
+ }
40
+
41
+ .actions {
42
+ grid-area: actions;
43
+ }
44
+
45
+ // Buttons require JavaScript
46
+ .actions button {
47
+ display: none;
48
+ }
49
+
50
+ &[data-state="upload-successful"] {
51
+ .status {
52
+ color: base.govuk-functional-colour("success");
53
+ }
54
+ }
55
+
56
+ &[data-state="upload-failed"] {
57
+ border-color: base.govuk-functional-colour("error");
58
+
59
+ .status {
60
+ color: base.govuk-functional-colour("error");
61
+ }
62
+ }
63
+ }
64
+
65
+ .govuk-attachment {
66
+ margin-bottom: base.govuk-spacing(2);
67
+ }
68
+
69
+ // With JavaScript running the button is the figure's only interactive
70
+ // control; the select still carries the submitted value but leaves the
71
+ // display, tab order, and accessibility tree.
72
+ .govuk-frontend-supported :where(.govuk-attachment) {
73
+ .actions select {
74
+ display: none;
75
+ }
76
+
77
+ .actions button {
78
+ display: revert;
79
+ }
80
+ }
81
+
82
+ // Hide duplicate inputs when multiple is not enabled, this allows reverting
83
+ // but the value from the last select will overwrite the others in save.
84
+ .govuk-file-upload-wrapper:has(input[type="file"]:not([multiple])) {
85
+ .govuk-attachment:has(+ .govuk-attachment) {
86
+ display: none;
87
+ }
88
+ }
89
+ }
@@ -0,0 +1,504 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module GOVUK
5
+ module FormBuilder
6
+ module Builder
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ # Delegate image_tag for attachment previews
11
+ delegate :image_tag, to: :@template
12
+
13
+ # Overwrite GOVUK default to set small to true
14
+ # @see GOVUKDesignSystemFormBuilder::Builder#govuk_collection_radio_buttons
15
+ def govuk_collection_radio_buttons(attribute_name, collection, value_method, text_method = nil,
16
+ hint_method = nil, hint: {}, legend: {}, caption: {}, inline: false,
17
+ small: true, bold_labels: nil,
18
+ include_hidden: config.default_collection_radio_buttons_include_hidden,
19
+ form_group: {}, **, &)
20
+ GOVUKDesignSystemFormBuilder::Elements::Radios::Collection.new(
21
+ self,
22
+ object_name,
23
+ attribute_name,
24
+ collection,
25
+ value_method:,
26
+ text_method:,
27
+ hint_method:,
28
+ hint:,
29
+ legend:,
30
+ caption:,
31
+ inline:,
32
+ small:,
33
+ bold_labels:,
34
+ form_group:,
35
+ include_hidden:,
36
+ **,
37
+ &
38
+ ).html
39
+ end
40
+
41
+ # Overwrite GOVUK default to set small to true
42
+ # @see GOVUKDesignSystemFormBuilder::Builder#govuk_radio_buttons_fieldset
43
+ def govuk_radio_buttons_fieldset(attribute_name, hint: {}, legend: {}, caption: {}, inline: false,
44
+ small: true, form_group: {}, **, &)
45
+ GOVUKDesignSystemFormBuilder::Containers::RadioButtonsFieldset.new(
46
+ self, object_name, attribute_name,
47
+ hint:, legend:, caption:, inline:, small:, form_group:,
48
+ **, &
49
+ ).html
50
+ end
51
+
52
+ # Overwrite GOVUK default to set small to true
53
+ # @see GOVUKDesignSystemFormBuilder::Builder#govuk_collection_check_boxes
54
+ def govuk_collection_check_boxes(attribute_name, collection, value_method, text_method, hint_method = nil,
55
+ hint: {}, legend: {}, caption: {}, small: true, form_group: {},
56
+ include_hidden: config.default_collection_check_boxes_include_hidden,
57
+ **, &)
58
+ GOVUKDesignSystemFormBuilder::Elements::CheckBoxes::Collection.new(
59
+ self,
60
+ object_name,
61
+ attribute_name,
62
+ collection,
63
+ value_method:,
64
+ text_method:,
65
+ hint_method:,
66
+ hint:,
67
+ legend:,
68
+ caption:,
69
+ small:,
70
+ form_group:,
71
+ include_hidden:,
72
+ **,
73
+ &
74
+ ).html
75
+ end
76
+
77
+ # Overwrite GOVUK default to set small to true
78
+ # @see GOVUKDesignSystemFormBuilder::Builder#govuk_check_boxes_fieldset
79
+ def govuk_check_boxes_fieldset(attribute_name, legend: {}, caption: {}, hint: {}, small: true, form_group: {},
80
+ multiple: true, **, &)
81
+ GOVUKDesignSystemFormBuilder::Containers::CheckBoxesFieldset.new(
82
+ self,
83
+ object_name,
84
+ attribute_name,
85
+ hint:,
86
+ legend:,
87
+ caption:,
88
+ small:,
89
+ form_group:,
90
+ multiple:,
91
+ **,
92
+ &
93
+ ).html
94
+ end
95
+ end
96
+
97
+ # Generates a check box within a fieldset to be used as a boolean toggle for a single attribute.
98
+ # The values are 1 (toggled on), and 0 (toggled off).
99
+ #
100
+ # @param attribute_name [Symbol] The name of the attribute
101
+ # @param small [Boolean] controls whether small check boxes are used instead of regular-sized ones
102
+ # @param hint [Hash,Proc] The content of the hint. No hint will be added if 'text' is left +nil+. When a +Proc+
103
+ # is supplied the hint will be wrapped in a +div+ instead of a +span+
104
+ # @option hint text [String] the hint text
105
+ # @option hint kwargs [Hash] additional arguments are applied as attributes to the hint
106
+ # @param link_errors [Boolean] controls whether this checkbox should be linked to from {#govuk_error_summary}
107
+ # @option label text [String] the label text
108
+ # @option label size [String] the size of the label font, can be +xl+, +l+, +m+, +s+ or nil
109
+ # @option label tag [Symbol,String] the label's wrapper tag, intended to allow labels to act as page headings
110
+ # @option label hidden [Boolean] control the visibility of the label. Hidden labels will be read by
111
+ # screenreaders
112
+ # @option label kwargs [Hash] additional arguments are applied as attributes on the +label+ element
113
+ # @option kwargs [Hash] kwargs additional arguments are applied as attributes to the +input+ element
114
+ # @param block [Block] any HTML passed in will form the contents of the fieldset
115
+ # @return [ActiveSupport::SafeBuffer] HTML output
116
+ #
117
+ # @example A single check box for terms and conditions
118
+ # = f.govuk_check_box_field :terms_agreed,
119
+ # link_errors: true,
120
+ # label: { text: 'Do you agree with our terms and conditions?' },
121
+ # hint: { text: 'You will not be able to proceed unless you do' }
122
+ #
123
+ def govuk_check_box_field(attribute_name, value = 1, unchecked_value = 0,
124
+ small: true, hint: {}, label: {}, link_errors: false, **, &)
125
+ govuk_check_boxes_fieldset(attribute_name, legend: nil, multiple: false, small:) do
126
+ fieldset_context.pop # undo push from fieldset extension, labels should be bold unless already nested
127
+ checkbox = govuk_check_box(attribute_name, value, unchecked_value,
128
+ hint:,
129
+ label:,
130
+ link_errors:,
131
+ multiple: false,
132
+ exclusive: false,
133
+ **, &)
134
+ fieldset_context.push attribute_name # restore push from fieldset
135
+ checkbox
136
+ end
137
+ end
138
+
139
+ # Generates a select for an enum defined in the model.
140
+ # @see GOVUKDesignSystemFormBuilder::Builder#govuk_collection_select
141
+ def govuk_enum_select(attribute_name, **, &)
142
+ govuk_collection_select(attribute_name, enum_values(attribute_name),
143
+ :itself, enum_labels_for(attribute_name), **, &)
144
+ end
145
+
146
+ # Generates a checkbox fieldset for an enum defined in the model.
147
+ #
148
+ # @api internal
149
+ # @see GOVUKDesignSystemFormBuilder::Builder#govuk_collection_check_boxes
150
+ def govuk_enum_check_boxes(attribute_name, **, &)
151
+ govuk_collection_check_boxes(attribute_name, enum_values(attribute_name),
152
+ :itself, enum_labels_for(attribute_name), **, &)
153
+ end
154
+
155
+ # Generates a radio buttons fieldset for an enum defined in the model.
156
+ # @see GOVUKDesignSystemFormBuilder::Builder#govuk_collection_radio_buttons
157
+ def govuk_enum_radio_buttons(attribute_name, **, &)
158
+ govuk_collection_radio_buttons(attribute_name, enum_values(attribute_name),
159
+ :itself, enum_labels_for(attribute_name), **, &)
160
+ end
161
+
162
+ # Generates a pair of +trix-toolbar+ and +trix-editor+ elements with a label, optional hint.
163
+ # Requires action-text to be correctly setup in the application
164
+ #
165
+ # @param attribute_name [Symbol] The name of the attribute
166
+ # @param hint [Hash,Proc] The content of the hint. No hint will be added if 'text' is left +nil+. When a +Proc+
167
+ # is supplied the hint will be wrapped in a +div+ instead of a +span+
168
+ # @option hint text [String] the hint text
169
+ # @option hint kwargs [Hash] additional arguments are applied as attributes to the hint
170
+ # @param label [Hash,Proc] configures or sets the associated label content
171
+ # @option label text [String] the label text
172
+ # @option label size [String] the size of the label font, can be +xl+, +l+, +m+, +s+ or nil
173
+ # @option label tag [Symbol,String] the label's wrapper tag, intended to allow labels to act as page headings
174
+ # @option label hidden [Boolean] control the visibility of the label. Hidden labels will still be read by screen
175
+ # readers
176
+ # @option label kwargs [Hash] additional arguments are applied as attributes on the +label+ element
177
+ # @param caption [Hash] configures or sets the caption content which is inserted above the label
178
+ # @option caption text [String] the caption text
179
+ # @option caption size [String] the size of the caption, can be +xl+, +l+ or +m+. Defaults to +m+
180
+ # @option caption kwargs [Hash] additional arguments are applied as attributes on the caption +span+ element
181
+ # @option kwargs [Hash] kwargs additional arguments are applied as attributes to the +trix-editor+ element.
182
+ # This is picked up and handled by the action-text gem
183
+ # @param form_group [Hash] configures the form group
184
+ # @option form_group classes [Array,String] sets the form group's classes
185
+ # @option form_group kwargs [Hash] additional attributes added to the form group
186
+ # @param & [Block] arbitrary HTML that will be rendered between the hint and the input
187
+ # @return [ActiveSupport::SafeBuffer] HTML output
188
+ #
189
+ # @example A rich text area with injected content
190
+ # = f.govuk_rich_text_area :description,
191
+ # label: { text: 'Where did the incident take place?' } do
192
+ #
193
+ # p.govuk-inset-text
194
+ # | If you don't know exactly leave this section blank
195
+ #
196
+ # @example A rich text area with the label supplied as a proc
197
+ # = f.govuk_rich_text_area :instructions,
198
+ # label: -> { tag.h3("How do you set it up?") }
199
+ #
200
+ def govuk_rich_textarea(attribute_name, hint: {}, label: {}, caption: {}, form_group: {}, **, &)
201
+ Elements::RichTextarea.new(
202
+ self, object_name, attribute_name,
203
+ hint:, label:, caption:, form_group:, **, &
204
+ ).html
205
+ end
206
+ alias_method :govuk_rich_text_area, :govuk_rich_textarea
207
+
208
+ # Generates a +combobox+ element that uses Hotwire Combobox to generate a combobox selection element.
209
+ # @see https://hotwirecombobox.com
210
+ # @see https://github.com/josefarias/hotwire_combobox
211
+ #
212
+ # @param attribute_name [Symbol] The name of the attribute
213
+ # @param options_or_src [Array] The +option+ values or a source path for async combobox
214
+ # @param options [Hash] Options hash passed through to the +combobox+ helper
215
+ # @param hint [Hash,Proc] The content of the hint. No hint will be added if 'text' is left +nil+.
216
+ # When a +Proc+ is supplied the hint will be wrapped in a +div+ instead of a +span+
217
+ # @option hint text [String] the hint text
218
+ # @option hint kwargs [Hash] additional arguments are applied as attributes to the hint
219
+ # @param label [Hash,Proc] configures or sets the associated label content
220
+ # @option label text [String] the label text
221
+ # @option label size [String] the size of the label font, can be +xl+, +l+, +m+, +s+ or nil
222
+ # @option label tag [Symbol,String] the label's wrapper tag, intended to allow labels to act as page headings
223
+ # @option label hidden [Boolean] control the visibility of the label.
224
+ # Hidden labels will still be read by screenreaders
225
+ # @option label kwargs [Hash] additional arguments are applied as attributes on the +label+ element
226
+ # @param form_group [Hash] configures the form group
227
+ # @option form_group kwargs [Hash] additional attributes added to the form group
228
+ # @param before_input [String,Proc] the content injected before the input. No content will be added if +nil+
229
+ # @param after_input [String,Proc] the content injected after the input. No content will be added if +nil+
230
+ # @param & [Block] build the contents of the select element manually for exact control
231
+ # @see https://hotwirecombobox.com Hotwire Combobox's +combobox+ (called by govuk_combobox)
232
+ # @return [ActiveSupport::SafeBuffer] HTML output
233
+ #
234
+ # @example A combobox that allows the user to choose from a list of states
235
+ #
236
+ # = f.combobox "state", State.all
237
+ #
238
+ # @example A combobox that allows the user to choose from an asynchronous states endpoint
239
+ #
240
+ # = f.combobox "state", states_path
241
+ #
242
+ # @example A multi-select combobox that allows the user to choose multiple states
243
+ #
244
+ # = f.combobox "state", State.all, multiselect_chip_src: states_chips_path
245
+ #
246
+ def govuk_combobox(attribute_name, options_or_src = [], options: {}, label: {}, hint: {}, form_group: {},
247
+ caption: {}, before_input: nil, after_input: nil, **, &)
248
+ Elements::Combobox.new(
249
+ self, object_name, attribute_name, options_or_src,
250
+ options:, label:, hint:, form_group:, caption:, before_input:, after_input:, **, &
251
+ ).html
252
+ end
253
+
254
+ # Generates an input of type +file+ with active storage and preview support.
255
+ #
256
+ # @param attribute_name [Symbol] The name of the attribute
257
+ # @option label text [String] the label text
258
+ # @option label tag [Symbol,String] the label's wrapper tag, intended to allow labels to act as page headings
259
+ # @option label size [String] the size of the label font, can be +xl+, +l+, +m+, +s+ or nil
260
+ # @option label hidden [Boolean] control the visability of the label. Hidden labels will stil be read by
261
+ # screenreaders
262
+ # @option label kwargs [Hash] additional arguments are applied as attributes on the +label+ element
263
+ # @param caption [Hash] configures or sets the caption content which is inserted above the label
264
+ # @option caption text [String] the caption text
265
+ # @option caption size [String] the size of the caption, can be +xl+, +l+ or +m+. Defaults to +m+
266
+ # @option caption kwargs [Hash] additional arguments are applied as attributes on the caption +span+ element
267
+ # @param hint [Hash,Proc] The content of the hint. No hint will be added if 'text' is left +nil+. When a
268
+ # +Proc+ is supplied the hint will be wrapped in a +div+ instead of a +span+
269
+ # @option hint text [String] the hint text
270
+ # @option hint kwargs [Hash] additional arguments are applied as attributes to the hint
271
+ # @option kwargs [Hash] kwargs additional arguments are applied as attributes to the +input+ element
272
+ # @param form_group [Hash] configures the form group
273
+ # @option form_group kwargs [Hash] additional attributes added to the form group
274
+ # @param before_input [String,Proc] the content injected before the input. No content will be added if left
275
+ # +nil+
276
+ # @param after_input [String,Proc] the content injected after the input. No content will be added if left
277
+ # +nil+
278
+ # @param choose_files_button_text [String] The text of the button that opens the file picker. Default is
279
+ # "Choose file". If javascript is not provided, this option will be ignored.
280
+ # @param drop_instruction_text [String] The text informing users they can drop files. Default is
281
+ # "or drop file". If javascript is not provided, this option will be ignored.
282
+ # @param multiple_files_chosen_text [Hash] The text displayed when multiple files have been chosen by the
283
+ # user. The component will replace the %{count} placeholder with the number of files selected. This uses
284
+ # the govuk-frontend pluralisation rules. If javascript is not provided, this option will be ignored.
285
+ # @param multiple_files_chosen_one_text [String] The text displayed when JavaScript is enabled and one file
286
+ # has been chosen by the user. The component will replace the %{count} placeholder with the number of files
287
+ # selected. This can also be set by passing a hash with key +one:+ to +multiple_files_chosen_text+.
288
+ # @param multiple_files_chosen_other_text [String] The text displayed when JavaScript is enabled and multiple
289
+ # files have been chosen by the user. The component will replace the %{count} placeholder with the number of
290
+ # files selected. This can also be set by passing a hash with key +other:+ to +multiple_files_chosen_text+.
291
+ # @param no_file_chosen_text [String] The text displayed when no file has been chosen by the user. Default is
292
+ # "No file chosen". If javascript is not provided, this option will be ignored.
293
+ # @param entered_drop_zone_text [String] The text announced by assistive technology when user drags files and
294
+ # enters the drop zone. Default is "Entered drop zone". If javascript is not provided, this option will be
295
+ # ignored.
296
+ # @param left_drop_zone_text [String] The text announced by assistive technology when user drags files and
297
+ # leaves the drop zone without dropping. Default is "Left drop zone". If javascript is not provided, this
298
+ # option will be ignored.
299
+ # @param upload_succeeded_text [String] The status shown in a figure's caption when its direct upload
300
+ # completes. Default is "Uploaded successfully". If javascript is not provided, this option will be ignored.
301
+ # @param upload_failed_text [String] The status shown in a figure's caption when its direct upload fails.
302
+ # Default is "Upload failed — try again". If javascript is not provided, this option will be ignored.
303
+ # @param retry_button_text [String] The label of the retry control offered on a failed upload. Default is
304
+ # "Try again". If javascript is not provided, this option will be ignored.
305
+ # @param file_removed_text [String] The text announced by assistive technology when a figure is removed. The
306
+ # component will replace the %{filename} placeholder with the removed file's name. Default is
307
+ # "%{filename} removed". If javascript is not provided, this option will be ignored.
308
+ # @param remove_button_text [String] The accessible name of each figure's remove control and the text of its
309
+ # no-JavaScript remove option. The component will replace the %{filename} placeholder with the figure's
310
+ # file name. Default is "Remove %{filename}".
311
+ # @param remove_button_content_text [String] The visible content of each figure's remove button. Default is
312
+ # "Remove".
313
+ # @param & [Block] arbitrary HTML that will be rendered between the hint and the input
314
+ #
315
+ # @example A photo upload field with file type specifier and injected content
316
+ # = f.govuk_attachment_field :photo, label: { text: 'Upload your photo' }, accept: 'image/*' do
317
+ #
318
+ # p.govuk-inset-text
319
+ # | Explicit images will result in account termination
320
+ #
321
+ # @example A CV upload field with label as a proc
322
+ # = f.govuk_attachment_field :cv, label: -> { tag.h3('Upload your CV') }
323
+ #
324
+ # @see https://design-system.service.gov.uk/components/file-upload/ GOV.UK file upload
325
+ # @see https://design-system.service.gov.uk/styles/typography/#headings-with-captions Headings with captions
326
+ # @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file MDN documentation for file upload
327
+ def govuk_attachment_field(
328
+ attribute_name,
329
+ label: {},
330
+ caption: {},
331
+ hint: {},
332
+ form_group: {},
333
+ before_input: nil,
334
+ after_input: nil,
335
+ choose_files_button_text: nil,
336
+ drop_instruction_text: nil,
337
+ multiple_files_chosen_text: nil,
338
+ multiple_files_chosen_one_text: nil,
339
+ multiple_files_chosen_other_text: nil,
340
+ no_file_chosen_text: nil,
341
+ entered_drop_zone_text: nil,
342
+ left_drop_zone_text: nil,
343
+ upload_succeeded_text: nil,
344
+ upload_failed_text: nil,
345
+ retry_button_text: nil,
346
+ file_removed_text: nil,
347
+ remove_button_text: nil,
348
+ remove_button_content_text: nil,
349
+ direct_upload: true,
350
+ direct_upload_url: (self.direct_upload_url if direct_upload),
351
+ **,
352
+ &
353
+ )
354
+ Elements::Attachment.new(
355
+ self,
356
+ object_name,
357
+ attribute_name,
358
+ label:,
359
+ caption:,
360
+ hint:,
361
+ form_group:,
362
+ before_input:,
363
+ after_input:,
364
+ direct_upload_url:,
365
+ choose_files_button_text:,
366
+ drop_instruction_text:,
367
+ multiple_files_chosen_text:,
368
+ multiple_files_chosen_one_text:,
369
+ multiple_files_chosen_other_text:,
370
+ no_file_chosen_text:,
371
+ entered_drop_zone_text:,
372
+ left_drop_zone_text:,
373
+ upload_succeeded_text:,
374
+ upload_failed_text:,
375
+ retry_button_text:,
376
+ file_removed_text:,
377
+ remove_button_text:,
378
+ remove_button_content_text:,
379
+ **,
380
+ &
381
+ ).html
382
+ end
383
+
384
+ # Generates a file input element for uploading documents.
385
+ #
386
+ # @example A upload field with label as a proc
387
+ # = f.govuk_document_field :data, label: -> { tag.h3('Upload your document') }
388
+ #
389
+ def govuk_document_field(attribute_name,
390
+ label: {},
391
+ caption: {},
392
+ hint: {},
393
+ form_group: {},
394
+ mime_types: config.document_mime_types,
395
+ **,
396
+ &)
397
+ if config.use_legacy_file_fields
398
+ Elements::Document.new(
399
+ self, object_name, attribute_name, label:, caption:, hint:, form_group:, mime_types:, **, &
400
+ ).html
401
+ else
402
+ govuk_attachment_field(
403
+ attribute_name, label:, caption:, hint:, form_group:, accept: mime_types&.join(","), **, &
404
+ )
405
+ end
406
+ end
407
+
408
+ # Generates a file input element with a preview for uploading images.
409
+ #
410
+ # @param attribute_name [Symbol] The name of the attribute
411
+ # @param hint [Hash,Proc] The content of the hint. No hint will be added if 'text' is left +nil+.
412
+ # When a +Proc+ is supplied the hint will be wrapped in a +div+ instead of a +span+
413
+ # @option hint text [String] the hint text
414
+ # @option hint kwargs [Hash] additional arguments are applied as attributes to the hint
415
+ # @param label [Hash,Proc] configures or sets the associated label content
416
+ # @option label text [String] the label text
417
+ # @option label size [String] the size of the label font, can be +xl+, +l+, +m+, +s+ or nil
418
+ # @option label tag [Symbol,String] the label's wrapper tag, intended to allow labels to act as page headings
419
+ # @option label hidden [Boolean] control the visibility of the label. Hidden labels will still be read by screen
420
+ # readers
421
+ # @option label kwargs [Hash] additional arguments are applied as attributes on the +label+ element
422
+ # @param caption [Hash] configures or sets the caption content which is inserted above the label
423
+ # @option caption text [String] the caption text
424
+ # @option caption size [String] the size of the caption, can be +xl+, +l+ or +m+. Defaults to +m+
425
+ # @option caption kwargs [Hash] additional arguments are applied as attributes on the caption +span+ element
426
+ # @option kwargs [Hash] kwargs additional arguments are applied as attributes to the +input+ element.
427
+ # @param form_group [Hash] configures the form group
428
+ # @option form_group classes [Array,String] sets the form group's classes
429
+ # @option form_group kwargs [Hash] additional attributes added to the form group
430
+ # @param & [Block] arbitrary HTML that will be rendered between the hint and the input
431
+ # @return [ActiveSupport::SafeBuffer] HTML output
432
+ #
433
+ # @example An image field with injected content
434
+ # = f.govuk_image_field :incident_image,
435
+ # label: { text: 'Attach a picture of the incident' } do
436
+ #
437
+ # p.govuk-inset-text
438
+ # | If you don't know exactly leave this section blank
439
+ #
440
+ # @example A image upload field with label as a proc
441
+ # = f.govuk_image_field :image, label: -> { tag.h3('Upload your image') }
442
+ #
443
+ def govuk_image_field(attribute_name,
444
+ label: {},
445
+ caption: {},
446
+ hint: {},
447
+ form_group: {},
448
+ mime_types: config.image_mime_types,
449
+ **,
450
+ &)
451
+ if config.use_legacy_file_fields
452
+ Elements::Image.new(
453
+ self, object_name, attribute_name, label:, caption:, hint:, form_group:, mime_types:, **, &
454
+ ).html
455
+ else
456
+ govuk_attachment_field(
457
+ attribute_name, label:, caption:, hint:, form_group:, accept: mime_types&.join(","), **, &
458
+ )
459
+ end
460
+ end
461
+
462
+ # Keep track of whether we are inside a fieldset
463
+ # This allows labels to default to bold ("s") normally but use the default otherwise
464
+ def fieldset_context
465
+ @fieldset_context ||= []
466
+ end
467
+
468
+ # URL for an attachment preview. ActiveStorage's representation route
469
+ # lives in the application's route set, so engine-mounted forms
470
+ # resolve it through main_app. Returns nil when no route is
471
+ # available, in which case the figure renders without a preview.
472
+ #
473
+ # @param [ActiveStorage::Variant,ActiveStorage::VariantWithRecord,ActiveStorage::Preview] representation
474
+ # @return [String,nil]
475
+ def attachment_preview_url(representation)
476
+ if @template.respond_to?(:rails_representation_path)
477
+ @template.rails_representation_path(representation)
478
+ elsif @template.respond_to?(:main_app) && @template.main_app.respond_to?(:rails_representation_path)
479
+ @template.main_app.rails_representation_path(representation)
480
+ end
481
+ end
482
+
483
+ private
484
+
485
+ def direct_upload_url
486
+ if @template.respond_to?(:rails_direct_uploads_url)
487
+ @template.rails_direct_uploads_url
488
+ elsif @template.respond_to?(:main_app) && @template.main_app.respond_to?(:rails_direct_uploads_url)
489
+ @template.main_app.rails_direct_uploads_url
490
+ end
491
+ end
492
+
493
+ def enum_values(attribute_name)
494
+ object.class.defined_enums[attribute_name.to_s].keys
495
+ end
496
+
497
+ def enum_labels_for(attribute_name)
498
+ model = object.class
499
+ ->(value) { model.human_attribute_name("#{attribute_name}.#{value}") }
500
+ end
501
+ end
502
+ end
503
+ end
504
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Katalyst
4
+ module GOVUK
5
+ module FormBuilder
6
+ module Containers
7
+ module FieldsetContext
8
+ def initialize(builder, object_name = nil, attribute_name = nil, &)
9
+ builder.fieldset_context << attribute_name
10
+
11
+ super
12
+
13
+ builder.fieldset_context.pop
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end