Skip to content

Commit dff5512

Browse files
GDSNewtChrisBAshton
authored andcommitted
WIP: Social media links
1 parent efe9faf commit dff5512

8 files changed

Lines changed: 258 additions & 1 deletion

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
module ConfigurableContentBlocks
2+
class ArrayOfHashes
3+
def to_partial_path
4+
"admin/configurable_content_blocks/array_of_hashes"
5+
end
6+
7+
8+
def publishing_api_payload(schema, content)
9+
return [] if content.blank?
10+
11+
social_media_items = content.is_a?(Hash) ? content.values : Array(content)
12+
social_media_items.filter_map do |item|
13+
next if item["_destroy"] == "1"
14+
build_api_output(schema, item)
15+
end
16+
end
17+
18+
private
19+
20+
21+
def build_api_output(schema, item)
22+
properties = schema.dig("items", "properties") || {}
23+
output = {}
24+
25+
properties.each do |field, field_schema|
26+
value = item[field]
27+
key = (field_schema["output_key"] || field).to_sym
28+
output[key] = value.present? ? value : nil
29+
end
30+
31+
output.compact
32+
end
33+
end
34+
end

app/models/configurable_content_blocks/factory.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def build_block(block)
1212
"image_select" => ->(page) { ConfigurableContentBlocks::ImageSelect.new(page.valid_images) },
1313
"lead_image_select" => ->(page) { ConfigurableContentBlocks::LeadImageSelect.new(page.valid_lead_images, default_lead_image: page.default_lead_image, placeholder_image_url: page.placeholder_image_url) },
1414
"default_object" => ->(_page) { ConfigurableContentBlocks::DefaultObject.new(self) },
15+
"array_of_hashes" => ->(_page) { ConfigurableContentBlocks::ArrayOfHashes.new },
1516
}.freeze
1617

1718
raise "Block #{block} is not defined" if blocks[block].nil?
@@ -34,6 +35,9 @@ def blocks
3435
"object" => {
3536
"default" => ->(_page) { ConfigurableContentBlocks::DefaultObject.new(self) },
3637
},
38+
"array" => {
39+
"hash" => ->(_page) { ConfigurableContentBlocks::ArrayOfHashes.new },
40+
},
3741
"date" => {
3842
"default" => ->(_page) { ConfigurableContentBlocks::DefaultDate.new },
3943
},

app/models/configurable_document_types/topical_event.json

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,27 @@
2323
"block": "default_date"
2424
}
2525
}
26+
},
27+
"social_media_links": {
28+
"title": "Social media accounts",
29+
"block": "array_of_hashes",
30+
"items": {
31+
"type": "object",
32+
"properties": {
33+
"social_media_service_id": {
34+
"title": "Service",
35+
"type": "integer",
36+
"input_type": "social_media_service_select",
37+
"output_key": "service_type",
38+
"transform": "social_media_service_name"
39+
},
40+
"url": {
41+
"title": "URL",
42+
"type": "string",
43+
"output_key": "href"
44+
}
45+
}
46+
}
2647
}
2748
}
2849
}
@@ -37,6 +58,9 @@
3758
},
3859
"end_date": {
3960
"type": "date"
61+
},
62+
"social_media_links": {
63+
"type": "array"
4064
}
4165
},
4266
"validations": {
@@ -67,7 +91,8 @@
6791
"publishing_api": {
6892
"body": "govspeak",
6993
"start_date": "rfc3339_date",
70-
"end_date": "rfc3339_date"
94+
"end_date": "rfc3339_date",
95+
"social_media_links": "array_of_hashes"
7196
}
7297
},
7398
"associations": [

app/models/standard_edition/block_content.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ def attributes_class_for(attribute_config)
7474
case property_schema["type"]
7575
when "object"
7676
attribute key
77+
when "array"
78+
attribute key, default: -> { {} }
7779
else
7880
attribute key, property_schema["type"].to_sym
7981
end

app/presenters/publishing_api/payload_builder/block_content.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,35 @@ def lead_image(attribute)
8080
def string(attribute)
8181
item.block_content&.public_send(attribute)
8282
end
83+
84+
def array_of_hashes(attribute)
85+
content = item.block_content&.public_send(attribute)
86+
return [] if content.blank?
87+
88+
content.map { |item| process_social_media_item(item) }.compact
89+
end
90+
91+
def process_social_media_item(item)
92+
hash = case item
93+
when Hash
94+
item.transform_keys(&:to_sym)
95+
when Array
96+
{
97+
social_media_service_id: item[0],
98+
url: item[1],
99+
title: item[2] || ''
100+
}
101+
else
102+
return nil
103+
end
104+
105+
service = SocialMediaService.find_by(id: hash[:social_media_service_id])
106+
{
107+
service_type: service&.name&.downcase || 'other',
108+
href: hash[:url].to_s,
109+
title: hash[:title] || ''
110+
}
111+
end
83112
end
84113
end
85114
end
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<% required ||= false
2+
content ||= []
3+
errors ||= []
4+
items = content.is_a?(Hash) ? content.values : Array(content)
5+
properties = schema.dig("items", "properties") || {}
6+
social_media_services = SocialMediaService.all
7+
8+
render_fields = ->(item, item_path, item_errors) do
9+
capture do
10+
properties.each do |key, prop|
11+
field_path = item_path.push(key)
12+
field_errors = item_errors.select { |e| e.attribute.to_s.end_with?(".#{key}") }
13+
14+
if prop["input_type"] == "social_media_service_select"
15+
concat(render("govuk_publishing_components/components/select", {
16+
id: field_path.form_control_id,
17+
label: prop["title"],
18+
name: "#{item_path.form_control_name}[#{key}]",
19+
heading_size: "s",
20+
options: [{ text: "", value: "" }] +
21+
social_media_services.map do |service|
22+
{
23+
text: service.name,
24+
value: service.id,
25+
selected: item[key].to_s == service.id.to_s,
26+
}
27+
end,
28+
full_width: true,
29+
error_items: field_errors.map { |e| { text: e.message } },
30+
}))
31+
else
32+
concat(render("govuk_publishing_components/components/input", {
33+
id: field_path.form_control_id,
34+
label: { text: prop["title"] },
35+
name: "#{item_path.form_control_name}[#{key}]",
36+
value: item[key],
37+
error_items: field_errors.map { |e| { text: e.message } },
38+
}))
39+
end
40+
end
41+
end
42+
end %>
43+
44+
<%= render "govuk_publishing_components/components/fieldset", {
45+
legend_text: "#{schema['title']}#{required ? ' (required)' : ''}",
46+
heading_size: "l",
47+
} do %>
48+
<div class="app-c-array-of-hashes">
49+
<%= render "govuk_publishing_components/components/add_another", {
50+
fieldset_legend: "Account",
51+
add_button_text: "Add account",
52+
empty_fields: true,
53+
items: items.each_with_index.filter_map do |item, index|
54+
next if item["_destroy"] == "1"
55+
56+
item_path = path.push(index.to_s)
57+
item_errors = errors.select { |e| e.attribute.to_s.start_with?(item_path.validation_error_attribute) }
58+
59+
{
60+
fields: render_fields.call(item, item_path, item_errors),
61+
destroy_checkbox: render("govuk_publishing_components/components/checkboxes", {
62+
name: "#{item_path.form_control_name}[_destroy]",
63+
items: [{ label: "Remove", value: "1" }],
64+
}),
65+
}
66+
end,
67+
empty: render_fields.call({}, path.push(items.length.to_s), []),
68+
} %>
69+
</div>
70+
<style>
71+
.app-c-array-of-hashes .js-add-another__add-button {
72+
margin-top: 20px;
73+
}
74+
</style>
75+
<% end %>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<% item ||= {}
2+
errors ||= []
3+
url_errors = errors.select { |e| e.attribute.to_s.end_with?(".url") }
4+
service_errors = errors.select { |e| e.attribute.to_s.end_with?(".social_media_service_id") } %>
5+
6+
<%= render "govuk_publishing_components/components/select", {
7+
id: path.push("social_media_service_id").form_control_id,
8+
label: "Service (required)",
9+
name: "#{path.form_control_name}[social_media_service_id]",
10+
heading_size: "s",
11+
options: [{ text: "", value: "" }] +
12+
social_media_services.map do |service|
13+
{
14+
text: service.name,
15+
value: service.id,
16+
selected: item["social_media_service_id"].to_s == service.id.to_s,
17+
}
18+
end,
19+
full_width: true,
20+
error_items: service_errors.map { |e| { text: e.message } },
21+
} %>
22+
23+
<%= render "govuk_publishing_components/components/input", {
24+
label: {
25+
text: "URL (required)",
26+
},
27+
name: "#{path.form_control_name}[url]",
28+
id: path.push("url").form_control_id,
29+
value: item["url"],
30+
heading_size: "s",
31+
error_items: url_errors.map { |e| { text: e.message } },
32+
} %>
33+
34+
<%= render "govuk_publishing_components/components/input", {
35+
label: {
36+
text: "Title (optional)",
37+
},
38+
hint: "If left blank, the service name will be used",
39+
name: "#{path.form_control_name}[title]",
40+
id: path.push("title").form_control_id,
41+
value: item["title"],
42+
heading_size: "s",
43+
} %>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<% required ||= false
2+
content ||= {}
3+
errors ||= []
4+
items = content.is_a?(Hash) ? content.values : Array(content)
5+
items = [{}] if items.empty?
6+
social_media_services = SocialMediaService.all %>
7+
8+
<%= render "govuk_publishing_components/components/fieldset", {
9+
legend_text: "#{schema['title']}#{required ? ' (required)' : ''}",
10+
heading_size: "l",
11+
} do %>
12+
<%= render "govuk_publishing_components/components/add_another", {
13+
fieldset_legend: "Account",
14+
add_button_text: "Add account",
15+
items: items.each_with_index.filter_map do |item, index|
16+
next if item["_destroy"] == "1"
17+
18+
item_path = path.push(index.to_s)
19+
item_errors = errors.select { |e| e.attribute.to_s.start_with?(item_path.validation_error_attribute) }
20+
21+
{
22+
fields: render(partial: "admin/configurable_content_blocks/social_media_account_fields", locals: {
23+
item: item,
24+
path: item_path,
25+
social_media_services: social_media_services,
26+
errors: item_errors,
27+
}),
28+
destroy_checkbox: render("govuk_publishing_components/components/checkboxes", {
29+
name: "#{item_path.form_control_name}[_destroy]",
30+
items: [{ label: "Delete", value: "1" }],
31+
}),
32+
}
33+
end,
34+
empty: begin
35+
empty_index = items.length
36+
empty_path = path.push(empty_index.to_s)
37+
render(partial: "admin/configurable_content_blocks/social_media_account_fields", locals: {
38+
item: {},
39+
path: empty_path,
40+
social_media_services: social_media_services,
41+
errors: [],
42+
})
43+
end,
44+
} %>
45+
<% end %>

0 commit comments

Comments
 (0)