diff --git a/app/assets/stylesheets/_main.scss b/app/assets/stylesheets/_main.scss index 0598ac942..39451bee5 100644 --- a/app/assets/stylesheets/_main.scss +++ b/app/assets/stylesheets/_main.scss @@ -11,6 +11,7 @@ $govuk-page-width: 1100px; // Imports from engines @import "bops_core/components/side_navigation"; @import "bops_core/components/task_accordion"; +@import "bops_core/components/ticket_panel"; // Imports from the application @import "components/buttons"; diff --git a/engines/bops_core/app/assets/stylesheets/bops_core/components/_ticket_panel.scss b/engines/bops_core/app/assets/stylesheets/bops_core/components/_ticket_panel.scss new file mode 100644 index 000000000..8e8b58a18 --- /dev/null +++ b/engines/bops_core/app/assets/stylesheets/bops_core/components/_ticket_panel.scss @@ -0,0 +1,80 @@ +@use "sass:map"; + +.bops-ticket-panel { + $ticket_colours: ( + "grey": ( + "color": govuk-shade(govuk-colour("dark-grey"), 50%), + "background-color": govuk-tint(govuk-colour("dark-grey"), 85%), + "border-color": govuk-colour("dark-grey") + ), + "green": ( + "color": govuk-shade(govuk-colour("green"), 20%), + "background-color": govuk-tint(govuk-colour("green"), 80%), + "border-color": govuk-colour("green") + ), + "turquoise": ( + "color": govuk-shade(govuk-colour("turquoise"), 60%), + "background-color": govuk-tint(govuk-colour("turquoise"), 80%), + "border-color": govuk-colour("turquoise") + ), + "blue": ( + "color": govuk-shade(govuk-colour("blue"), 60%), + "background-color": govuk-tint(govuk-colour("blue"), 70%), + "border-color": govuk-colour("blue") + ), + "red": ( + "color": govuk-shade(govuk-colour("red"), 80%), + "background-color": govuk-tint(govuk-colour("red"), 75%), + "border-color": govuk-colour("red") + ), + "purple": ( + "color": govuk-shade(govuk-colour("bright-purple"), 50%), + "background-color": govuk-tint(govuk-colour("bright-purple"), 85%), + "border-color": govuk-colour("bright-purple") + ), + "pink": ( + "color": govuk-shade(govuk-colour("pink"), 50%), + "background-color": govuk-tint(govuk-colour("pink"), 85%), + "border-color": govuk-colour("pink") + ), + "orange": ( + "color": govuk-shade(govuk-colour("orange"), 55%), + "background-color": govuk-tint(govuk-colour("orange"), 70%), + "border-color": govuk-colour("orange") + ), + "yellow": ( + "color": govuk-shade(govuk-colour("yellow"), 65%), + "background-color": govuk-tint(govuk-colour("yellow"), 75%), + "border-color": govuk-colour("yellow") + ), + ); + + $default_colour: map.get($ticket_colours, "blue"); + + color: map.get($default_colour, "color"); + background-color: map.get($default_colour, "background-color"); + border-left: 5px solid map.get($default_colour, "border-color"); + padding: govuk-spacing(2); + + &__body { + @include govuk-font($size: 19); + margin-bottom: govuk-spacing(1); + } + + &__footer { + @include govuk-font($size: 16); + color: govuk-tint(map.get($default_colour, "color"), 25%); + } + + @each $name, $attributes in $ticket_colours { + &--#{$name} { + color: map.get($attributes, "color"); + background-color: map.get($attributes, "background-color"); + border-left-color: map.get($attributes, "border-color"); + + .bops-ticket-panel__footer { + color: govuk-tint(map.get($attributes, "color"), 25%); + } + } + } +} diff --git a/engines/bops_core/app/components/bops_core/ticket_panel_component.rb b/engines/bops_core/app/components/bops_core/ticket_panel_component.rb new file mode 100644 index 000000000..e18cb5dcc --- /dev/null +++ b/engines/bops_core/app/components/bops_core/ticket_panel_component.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module BopsCore + class TicketPanelComponent < GovukComponent::Base + COLOURS = %w[grey green turquoise blue red purple pink orange yellow].freeze + + renders_one :body + renders_one :footer + + attr_reader :id, :colour + + def initialize(colour: nil, id: nil, classes: [], html_attributes: {}) + @id = id + @colour = colour + + super(classes:, html_attributes:) + end + + def call + tag.div(**default_attributes) do + safe_join([body_wrapper, footer_wrapper].compact_blank) + end + end + + private + + def body_wrapper + tag.div(body, class: "bops-ticket-panel__body") + end + + def footer_wrapper + tag.div(footer, class: "bops-ticket-panel__footer") + end + + def default_attributes + {id: id, class: ["bops-ticket-panel", colour_class]} + end + + def colour_class + return nil if colour.blank? + + fail(ArgumentError, colour_error_message) unless valid_colour? + + "bops-ticket-panel--#{colour}" + end + + def valid_colour? + colour.in?(COLOURS) + end + + def colour_error_message + "invalid ticket panel colour #{colour}, supported colours are #{COLOURS.to_sentence}" + end + end +end diff --git a/engines/bops_core/app/helpers/bops_core/application_helper.rb b/engines/bops_core/app/helpers/bops_core/application_helper.rb index 05ea6231c..e14e6fa92 100644 --- a/engines/bops_core/app/helpers/bops_core/application_helper.rb +++ b/engines/bops_core/app/helpers/bops_core/application_helper.rb @@ -8,7 +8,8 @@ module ApplicationHelper bops_secondary_navigation: "BopsCore::SecondaryNavigationComponent", bops_side_navigation: "BopsCore::SideNavigationComponent", bops_sub_navigation: "BopsCore::SubNavigationComponent", - bops_task_accordion: "BopsCore::TaskAccordionComponent" + bops_task_accordion: "BopsCore::TaskAccordionComponent", + bops_ticket_panel: "BopsCore::TicketPanelComponent" }.each do |name, klass| define_method(name) do |*args, **kwargs, &block| capture do diff --git a/engines/bops_core/spec/components/bops_core/ticket_panel_component_spec.rb b/engines/bops_core/spec/components/bops_core/ticket_panel_component_spec.rb new file mode 100644 index 000000000..3275d9e12 --- /dev/null +++ b/engines/bops_core/spec/components/bops_core/ticket_panel_component_spec.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +require "bops_core_helper" + +RSpec.describe(BopsCore::TicketPanelComponent, type: :component) do + context "without a colour" do + subject! do + render_inline(described_class.new(id: "ticket")) do |ticket| + ticket.with_body { "This is the body" } + ticket.with_footer { "This is the footer" } + end + end + + it "renders the ticket panel component" do + within "#ticket" do + expect(element["id"]).to eq("ticket") + expect(element["class"]).to eq("bops-ticket-panel") + + within "> div:nth-child(1)" do + expect(element["class"]).to eq("bops-ticket-panel__body") + expect(element.text).to eq("This is the body") + end + + within "> div:nth-child(2)" do + expect(element["class"]).to eq("bops-ticket-panel__footer") + expect(element.text).to eq("This is the footer") + end + end + end + end + + context "with HTML content" do + subject! do + render_inline(described_class.new(id: "ticket")) do |ticket| + ticket.with_body do + <<~HTML.html_safe +

This is the first paragraph

+

This is the second paragraph

+ HTML + end + + ticket.with_footer { "This is the footer" } + end + end + + it "renders the ticket panel component" do + within "#ticket" do + expect(element["id"]).to eq("ticket") + expect(element["class"]).to eq("bops-ticket-panel") + + within "> div:nth-child(1)" do + expect(element["class"]).to eq("bops-ticket-panel__body") + + within "> p:nth-child(1)" do + expect(element.text).to eq("This is the first paragraph") + end + + within "> p:nth-child(2)" do + expect(element.text).to eq("This is the second paragraph") + end + end + + within "> div:nth-child(2)" do + expect(element["class"]).to eq("bops-ticket-panel__footer") + expect(element.text).to eq("This is the footer") + end + end + end + end + + described_class::COLOURS.each do |colour| + context "with the colour: #{colour.inspect}" do + subject! do + render_inline(described_class.new(id: "ticket", colour: colour)) do |ticket| + ticket.with_body { "This is the body" } + ticket.with_footer { "This is the footer" } + end + end + + it "renders the ticket panel component" do + within "#ticket" do + expect(element["id"]).to eq("ticket") + expect(element["class"]).to eq("bops-ticket-panel bops-ticket-panel--#{colour}") + + within "> div:nth-child(1)" do + expect(element["class"]).to eq("bops-ticket-panel__body") + expect(element.text).to eq("This is the body") + end + + within "> div:nth-child(2)" do + expect(element["class"]).to eq("bops-ticket-panel__footer") + expect(element.text).to eq("This is the footer") + end + end + end + end + end +end