Skip to content

Commit

Permalink
Rewrite the cookie banner with slots
Browse files Browse the repository at this point in the history
This now works in a very similar fashion to the Nunjucks macros. Instead
of passing in actions with arguments it supports the passing in of any
arbitrary HTML, the intention is for developers to pass in an array of
buttons or links generated by `govuk_link_to` or similar. These will
then automatically be wrapped in a `govuk-button-group`.
  • Loading branch information
peteryates committed Jul 31, 2021
1 parent be928a5 commit 682eaae
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 69 deletions.
17 changes: 0 additions & 17 deletions app/components/govuk_component/cookie_banner_component.html.erb

This file was deleted.

15 changes: 10 additions & 5 deletions app/components/govuk_component/cookie_banner_component.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
class GovukComponent::CookieBannerComponent < GovukComponent::Base
renders_one :body
renders_one :actions
renders_many :messages, GovukComponent::CookieBannerComponent::MessageComponent

attr_accessor :title, :aria_label
attr_accessor :aria_label, :hidden

def initialize(title: nil, aria_label: "Cookie banner", classes: [], html_attributes: {})
def initialize(aria_label: "Cookie banner", hidden: false, classes: [], html_attributes: {})
super(classes: classes, html_attributes: html_attributes)

@title = title
@aria_label = aria_label
@hidden = hidden
end

def call
tag.div(class: classes, role: "region", aria: { label: aria_label }, hidden: hidden, **html_attributes) do
safe_join(messages)
end
end

private
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
class GovukComponent::CookieBannerComponent::MessageComponent < GovukComponent::Base
attr_reader :heading_text, :text, :hidden, :role

renders_many :actions
renders_one :heading_html

def initialize(heading_text: nil, text: nil, hidden: false, role: nil, classes: [], html_attributes: {})
super(classes: classes, html_attributes: html_attributes)

@heading_text = heading_text
@text = text
@hidden = hidden
@role = role
end

def call
tag.div(class: classes, role: role, hidden: hidden, **html_attributes) do
tag.div(class: "govuk-grid-row") do
tag.div(class: "govuk-grid-column-two-thirds") do
safe_join([heading_element, message_element, actions_element])
end
end
end
end

private

def default_classes
%w(govuk-cookie-banner__message govuk-width-container)
end

def heading_element
tag.h2(heading_content, class: "govuk-cookie-banner__heading")
end

def heading_content
heading_html || heading_text || fail(ArgumentError, "no heading_text or heading_html")
end

def message_element
tag.div(message_content, class: "govuk-cookie-banner__content")
end

def message_content
content || text || fail(ArgumentError, "no text or content")
end

def actions_element
return if actions.none?

tag.div(class: "govuk-button-group") { safe_join(actions) }
end
end
170 changes: 123 additions & 47 deletions spec/components/govuk_component/cookie_banner_component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,75 +5,151 @@
include_context 'helpers'

let(:component_css_class) { 'govuk-cookie-banner' }

let(:title) { "Cookies on a service" }
let(:body) do
helper.safe_join(
[
helper.tag.p("An introductory paragraph."),
helper.tag.p("A second paragraph."),
]
)
end
let(:actions) do
helper.safe_join(
[
helper.button_to("Accept", "/accept-path"),
helper.button_to("Reject", "/reject-path"),
helper.link_to("View", "/view-path"),
]
)
end
let(:kwargs) { { title: title } }
let(:kwargs) { {} }

subject! do
render_inline(described_class.new(**kwargs)) do |component|
component.body { body }
component.actions { actions }
end
render_inline(described_class.new(**kwargs))
end

specify "renders a cookie banner div with the right attributes" do
expected_attributes = { class: component_css_class, role: "region", "aria-label" => "Cookie banner" }

expect(rendered_component).to have_tag("div", with: expected_attributes) do
with_tag("h2", with: { class: %w(govuk-cookie-banner__heading govuk-heading-m) }, text: title)
end
expect(rendered_component).to have_tag("div", with: expected_attributes)
end

specify "renders the cookie banner content" do
expect(rendered_component).to have_tag('div', with: { class: 'govuk-cookie-banner__content' }) do
with_tag('p', 'An introductory paragraph.')
with_tag('p', 'A second paragraph.')
context "when hidden: true" do
subject! { render_inline(described_class.new(**kwargs.merge(hidden: true))) }

specify "the cookie banner has a hidden attribute" do
expect(rendered_component).to have_tag("div", with: { class: component_css_class, hidden: "hidden" })
end
end

specify "renders the buttons and links" do
expect(rendered_component).to have_tag('div', with: { class: 'govuk-button-group' }) do
with_tag('input', with: { value: 'Accept', type: 'submit' })
with_tag('input', with: { value: 'Reject', type: 'submit' })
with_tag('a', text: 'View', with: { href: '/view-path' })
context "when the aria-label is overridden" do
let(:custom_label) { "Privacy information" }
subject! { render_inline(described_class.new(**kwargs.merge(aria_label: custom_label))) }

specify "the cookie banner has the custom aria-label value" do
expect(rendered_component).to have_tag("div", with: { class: component_css_class, "aria-label" => custom_label })
end
end

context "custom aria labels" do
let(:aria_label) { "Cookie section" }
let(:kwargs) { { aria_label: aria_label } }
context "with messages and actions" do
let(:message_selector) do
".govuk-cookie-banner > .govuk-cookie-banner__message > .govuk-grid-row > .govuk-grid-column-two-thirds"
end

let(:custom_heading_text) { "What a nice heading" }
let(:custom_message_text) { "A really important message" }
let(:custom_role) { "alert" }

specify "sets the aria-label correctly" do
expect(rendered_component).to have_tag('div', with: { class: 'govuk-cookie-banner', 'aria-label' => aria_label })
subject! do
render_inline(described_class.new(**kwargs)) do |cookie_banner|
cookie_banner.message(heading_text: custom_heading_text, role: custom_role, text: custom_message_text) do |message|
message.action { helper.govuk_button_to("/accept") { "Accept" } }
message.action { helper.govuk_link_to("View cookie policy", "/cookie-policy") }
end
end
end

specify "renders the message heading" do
expect(rendered_component).to have_tag(message_selector) do
with_tag("h2", text: custom_heading_text)
end
end

specify "applies the custom role" do
expect(rendered_component).to have_tag(".govuk-cookie-banner") do
with_tag("div", with: { class: "govuk-cookie-banner__message", role: custom_role })
end
end
end

context "when there is no title" do
let(:kwargs) { {} }
specify "renders the message text" do
expect(rendered_component).to have_tag(message_selector) do
with_tag("div", text: custom_message_text, with: { class: "govuk-cookie-banner__content" })
end
end

specify "renders the cookie banner without a title" do
expect(rendered_component).to have_tag("div", with: { class: %w(govuk-cookie-banner) })
expect(rendered_component).not_to have_tag(".govuk-cookie-banner__heading")
specify "renders the actions" do
expect(rendered_component).to have_tag(message_selector) do
with_tag("div", with: { class: "govuk-button-group" }) do
with_tag("button", count: 1)
with_tag("a", count: 1)
end
end
end
end

it_behaves_like "a component that accepts custom classes"
it_behaves_like "a component that accepts custom HTML attributes"
end

RSpec.describe(GovukComponent::CookieBannerComponent::MessageComponent, type: :component) do
include_context 'setup'
include_context 'helpers'

let(:component_css_class) { "govuk-cookie-banner__message" }
let(:custom_heading) { "Some heading" }
let(:custom_text) { "Some message" }
let(:kwargs) { { heading_text: custom_heading, text: custom_text } }

it_behaves_like 'a component that accepts custom classes'
it_behaves_like 'a component that accepts custom HTML attributes'

context "when there is no heading_text or heading_html" do
specify "raises an appropriate error" do
expect { render_inline(described_class.new(**kwargs.except(:heading_text))) }.to raise_error(ArgumentError, "no heading_text or heading_html")
end
end

context "when there is no text or block" do
specify "raises an appropriate error" do
expect { render_inline(described_class.new(**kwargs.except(:text))) }.to raise_error(ArgumentError, "no text or content")
end
end

context "when hidden: true" do
subject! { render_inline(described_class.new(**kwargs.merge(hidden: true))) }

specify "the message has a hidden attribute" do
expect(rendered_component).to have_tag("div", with: { class: component_css_class, hidden: "hidden" })
end
end


context "when there are blocks of HTML" do
let(:custom_role) { "spam" }

let(:custom_message_text) { "We need to track you!" }
let(:custom_message_tag) { "em" }
let(:custom_message_html) { helper.content_tag(custom_message_tag, custom_message_text) }

let(:custom_heading_text) { "Wait a minute" }
let(:custom_heading_tag) { "marquee" }
let(:custom_heading_html) { helper.content_tag(custom_heading_tag, custom_heading_text) }

subject! do
render_inline(described_class.new(role: custom_role)) do |message|
message.heading_html { custom_heading_html }

helper.content_tag(custom_message_tag, custom_text)
end
end

specify "the custom heading HTML is rendered" do
expect(rendered_component).to have_tag("div", with: { class: component_css_class }) do
with_tag("h2", class: "govuk-cookie-banner__heading") do
with_tag(custom_heading_tag, text: custom_heading_text)
end
end
end

specify "the custom message HTML is rendered" do
expect(rendered_component).to have_tag("div", with: { class: component_css_class }) do
with_tag("div", class: "govuk-cookie-banner__content") do
with_tag(custom_message_tag, text: custom_text)
end
end
end
end
end

0 comments on commit 682eaae

Please sign in to comment.