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.
- checksums.yaml +4 -4
- data/README.md +141 -15
- data/app/assets/builds/katalyst/govuk/formbuilder.css +77 -0
- data/app/assets/builds/katalyst/govuk/formbuilder.js +573 -10
- data/app/assets/builds/katalyst/govuk/formbuilder.min.js +8 -8
- data/app/assets/builds/katalyst/govuk/formbuilder.min.js.map +1 -1
- data/app/assets/stylesheets/katalyst/govuk/components/_index.scss +1 -0
- data/app/assets/stylesheets/katalyst/govuk/components/attachment/_index.scss +3 -0
- data/app/assets/stylesheets/katalyst/govuk/components/attachment/_mixin.scss +89 -0
- data/app/helpers/katalyst/govuk/form_builder/builder.rb +504 -0
- data/app/helpers/katalyst/govuk/form_builder/containers/fieldset_context.rb +19 -0
- data/app/helpers/katalyst/govuk/form_builder/elements/attachment.rb +80 -0
- data/app/helpers/katalyst/govuk/form_builder/elements/combobox.rb +85 -0
- data/app/helpers/katalyst/govuk/form_builder/elements/document.rb +68 -0
- data/app/helpers/katalyst/govuk/form_builder/elements/image.rb +65 -0
- data/app/helpers/katalyst/govuk/form_builder/elements/label.rb +38 -0
- data/app/helpers/katalyst/govuk/form_builder/elements/legend.rb +32 -0
- data/app/helpers/katalyst/govuk/form_builder/elements/rich_textarea.rb +70 -0
- data/app/helpers/katalyst/govuk/form_builder/frontend.rb +34 -0
- data/app/helpers/katalyst/govuk/form_builder/traits/attachment.rb +215 -0
- data/app/helpers/katalyst/govuk/form_builder/traits/file.rb +104 -0
- data/app/helpers/katalyst/govuk/form_builder/traits/label.rb +33 -0
- data/config/importmap.rb +1 -0
- data/config/locales/en.yml +16 -0
- data/lib/katalyst/govuk/form_builder/config.rb +20 -0
- data/lib/katalyst/govuk/form_builder/engine.rb +1 -1
- metadata +31 -1
|
@@ -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
|