From 3a309771a80463dddf637df07c2e64e911a86973 Mon Sep 17 00:00:00 2001 From: Kevin Perez Date: Mon, 26 Feb 2024 15:17:59 -0600 Subject: [PATCH] Add basic specs for all components --- Gemfile | 6 ++ Gemfile.lock | 42 +++++++++++- app/components/mcm/banner_component.rb | 4 +- .../banner_component.html+admin.haml | 55 ++++++++-------- app/components/mcm/base_component.rb | 11 +++- app/components/mcm/columns_component.rb | 4 -- .../columns_component.html+admin.haml | 27 ++++---- .../columns_component.html.haml | 30 ++------- .../hero_image_component.html+admin.haml | 7 +- .../hero_image_component.html.haml | 4 +- ...itle_content_cta_component.html+admin.haml | 65 ++++++++++--------- ...mage_title_content_cta_component.html.haml | 8 +-- .../hero_slider_component.html+admin.haml | 7 +- .../hero_slider_component.html.haml | 32 +++------ ..._description_cta_component.html+admin.haml | 46 ++++++------- .../rich_text_component.html+admin.haml | 9 +-- .../square_image_component.html+admin.haml | 13 ++-- ...itle_content_cta_component.html+admin.haml | 47 +++++++------- .../title_content_cta_component.html.haml | 6 +- app/components/mcm/youtube_video_component.rb | 2 +- .../youtube_video_component.html+admin.haml | 7 +- .../youtube_video_component.html.haml | 2 +- app/models/mcm/components_page.rb | 17 +---- app/views/mcm/_child_component.html.haml | 7 ++ app/views/mcm/_component_form.html.haml | 5 ++ spec/components/mcm/banner_component_spec.rb | 20 ++++++ spec/components/mcm/columns_component_spec.rb | 17 +++++ .../mcm/hero_image_component_spec.rb | 29 +++++++++ ..._image_title_content_cta_component_spec.rb | 29 +++++++++ .../mcm/hero_slider_component_spec.rb | 17 +++++ ...ge_title_description_cta_component_spec.rb | 19 ++++++ .../mcm/rich_text_component_spec.rb | 16 +++++ .../mcm/square_image_component_spec.rb | 19 ++++++ .../mcm/title_content_cta_component_spec.rb | 16 +++++ .../mcm/youtube_video_component_spec.rb | 16 +++++ spec/spec_helper.rb | 13 ++++ spec/support/mcm/failing_component.rb | 3 + spec/support/shared_examples/component.rb | 11 ++++ .../shared_examples/container_component.rb | 45 +++++++++++++ 39 files changed, 512 insertions(+), 221 deletions(-) create mode 100644 app/views/mcm/_child_component.html.haml create mode 100644 app/views/mcm/_component_form.html.haml create mode 100644 spec/components/mcm/banner_component_spec.rb create mode 100644 spec/components/mcm/columns_component_spec.rb create mode 100644 spec/components/mcm/hero_image_component_spec.rb create mode 100644 spec/components/mcm/hero_image_title_content_cta_component_spec.rb create mode 100644 spec/components/mcm/hero_slider_component_spec.rb create mode 100644 spec/components/mcm/image_title_description_cta_component_spec.rb create mode 100644 spec/components/mcm/rich_text_component_spec.rb create mode 100644 spec/components/mcm/square_image_component_spec.rb create mode 100644 spec/components/mcm/title_content_cta_component_spec.rb create mode 100644 spec/components/mcm/youtube_video_component_spec.rb create mode 100644 spec/support/mcm/failing_component.rb create mode 100644 spec/support/shared_examples/component.rb create mode 100644 spec/support/shared_examples/container_component.rb diff --git a/Gemfile b/Gemfile index adabff0..064e9b4 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,13 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } gemspec group :development, :test do + # gems needed by the dummy app + gem "sprockets-rails" + gem "importmap-rails" + gem "sqlite3" + gem "rspec-rails" gem "silent_stream" gem "debug", ">= 1.0.0" + gem "capybara" end diff --git a/Gemfile.lock b/Gemfile.lock index 984fd0c..d1c6771 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,11 +2,12 @@ PATH remote: . specs: mcm (0.1.0) + activestorage (>= 6.0.0) bootstrap_form (~> 4.0) haml-rails - image_processing + image_processing (~> 1.2) rails (>= 6.0.0) - view_component + view_component (>= 2.7.0) GEM remote: https://rubygems.org/ @@ -85,12 +86,23 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) + addressable (2.8.6) + public_suffix (>= 2.0.2, < 6.0) base64 (0.2.0) bigdecimal (3.1.5) bootstrap_form (4.5.0) actionpack (>= 5.2) activemodel (>= 5.2) builder (3.2.4) + capybara (3.40.0) + addressable + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.11) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) concurrent-ruby (1.2.2) connection_pool (2.4.1) crass (1.0.6) @@ -119,6 +131,10 @@ GEM image_processing (1.12.2) mini_magick (>= 4.9.5, < 5) ruby-vips (>= 2.0.17, < 3) + importmap-rails (2.0.1) + actionpack (>= 6.0.0) + activesupport (>= 6.0.0) + railties (>= 6.0.0) io-console (0.7.1) irb (1.11.1) rdoc @@ -132,6 +148,7 @@ GEM net-pop net-smtp marcel (1.0.2) + matrix (0.4.2) method_source (1.0.0) mini_magick (4.12.0) mini_mime (1.1.5) @@ -161,6 +178,7 @@ GEM racc (~> 1.4) psych (5.1.2) stringio + public_suffix (5.0.4) racc (1.7.3) rack (3.0.8) rack-session (2.0.0) @@ -202,6 +220,7 @@ GEM rake (13.1.0) rdoc (6.6.2) psych (>= 4.0.0) + regexp_parser (2.9.0) reline (0.4.2) io-console (~> 0.5) rspec-core (3.12.2) @@ -225,6 +244,19 @@ GEM ffi (~> 1.12) ruby2_keywords (0.0.5) silent_stream (1.0.6) + sprockets (4.2.1) + concurrent-ruby (~> 1.0) + rack (>= 2.2.4, < 4) + sprockets-rails (3.4.2) + actionpack (>= 5.2) + activesupport (>= 5.2) + sprockets (>= 3.0.0) + sqlite3 (1.7.2-aarch64-linux) + sqlite3 (1.7.2-arm-linux) + sqlite3 (1.7.2-arm64-darwin) + sqlite3 (1.7.2-x86-linux) + sqlite3 (1.7.2-x86_64-darwin) + sqlite3 (1.7.2-x86_64-linux) stringio (3.1.0) temple (0.10.3) thor (1.3.0) @@ -240,6 +272,8 @@ GEM websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) zeitwerk (2.6.12) PLATFORMS @@ -251,10 +285,14 @@ PLATFORMS x86_64-linux DEPENDENCIES + capybara debug (>= 1.0.0) + importmap-rails mcm! rspec-rails silent_stream + sprockets-rails + sqlite3 BUNDLED WITH 2.5.4 diff --git a/app/components/mcm/banner_component.rb b/app/components/mcm/banner_component.rb index 0b58cca..ccf1833 100644 --- a/app/components/mcm/banner_component.rb +++ b/app/components/mcm/banner_component.rb @@ -14,11 +14,11 @@ def variant_for(index) end def background_image - @component.assets.first.attachment + @component.assets.first&.attachment end def cta_image - @component.assets.second.attachment + @component.assets.second&.attachment end end end diff --git a/app/components/mcm/banner_component/banner_component.html+admin.haml b/app/components/mcm/banner_component/banner_component.html+admin.haml index 590dd9c..d4f3647 100644 --- a/app/components/mcm/banner_component/banner_component.html+admin.haml +++ b/app/components/mcm/banner_component/banner_component.html+admin.haml @@ -1,30 +1,31 @@ -- @component_form.fields_for :metadata, @component.metadata do |metadata_form| - .row - .col-6= metadata_form.text_field :title, required: true - .col-6= metadata_form.select :title_color, available_text_colors, required: true +- render "mcm/component_form", component: @component do |component_form| + - component_form.fields_for :metadata, @component.metadata do |metadata_form| + .row + .col-6= metadata_form.text_field :title, required: true + .col-6= metadata_form.select :title_color, available_text_colors, required: true - = metadata_form.label :content - = metadata_form.hidden_field :content, required: true - %trix-editor{ input: 'components_page_metadata_content' } + = metadata_form.label :content + = metadata_form.hidden_field :content, required: true + %trix-editor{ input: 'components_page_metadata_content' } - .row.mt-3 - .col-6 - = metadata_form.select :content_color, available_text_colors - .col-6 - = metadata_form.select :content_alignment, text_alignments, required: true - .row - .col-6 - = metadata_form.text_field :cta, required: true, label: 'CTA' - .col-6 - = metadata_form.text_field :cta_url, required: true, label: 'URL' - .row - .col-6 - = metadata_form.select :button_color, available_button_colors + .row.mt-3 + .col-6 + = metadata_form.select :content_color, available_text_colors + .col-6 + = metadata_form.select :content_alignment, text_alignments, required: true + .row + .col-6 + = metadata_form.text_field :cta, required: true, label: 'CTA' + .col-6 + = metadata_form.text_field :cta_url, required: true, label: 'URL' + .row + .col-6 + = metadata_form.select :button_color, available_button_colors -- 2.times { @component_form.object.assets.build } if @component_form.object.assets.empty? -- @component_form.fields_for :assets do |assets_form| - = assets_form.hidden_field :position, value: assets_form.index + 1 - = render Mcm::ImageComponent.new(form: assets_form, - styles: image_preview_styles(variant_for(assets_form.index)), - variant: variant_for(assets_form.index), - label: t("custom_pages.components.banner.image_#{assets_form.index + 1}")) + - 2.times { @component.assets.build } if @component.assets.empty? + - component_form.fields_for :assets do |assets_form| + = assets_form.hidden_field :position, value: assets_form.index + 1 + = render Mcm::ImageComponent.new(form: assets_form, + styles: image_preview_styles(variant_for(assets_form.index)), + variant: variant_for(assets_form.index), + label: t("custom_pages.components.banner.image_#{assets_form.index + 1}")) diff --git a/app/components/mcm/base_component.rb b/app/components/mcm/base_component.rb index 43703b3..ee1ff6d 100644 --- a/app/components/mcm/base_component.rb +++ b/app/components/mcm/base_component.rb @@ -4,9 +4,8 @@ def self.component_type "content" end - def initialize(component:, component_form: nil) + def initialize(component:) @component = component - @component_form = component_form end def defaults @@ -18,6 +17,14 @@ def image_preview_styles "max-width:400px;max-height:400px;" end + def height + @component.metadata.height.to_i.positive? ? "#{@component.metadata.height}px" : 'auto' + end + + def background_color + @component.metadata.background_color + end + def title_classes @component.metadata.values(:title_color, :title_alignment).join(' ') end diff --git a/app/components/mcm/columns_component.rb b/app/components/mcm/columns_component.rb index b1e0b2f..9e56985 100644 --- a/app/components/mcm/columns_component.rb +++ b/app/components/mcm/columns_component.rb @@ -8,10 +8,6 @@ def full_width? @component.metadata.full_width.eql?('1') end - def height - @component.metadata.height.to_i.positive? ? "#{@component.metadata.height}px" : 'auto' - end - def title_classes @component.metadata.values(:section_title_color, :section_title_alignment).join(' ') end diff --git a/app/components/mcm/columns_component/columns_component.html+admin.haml b/app/components/mcm/columns_component/columns_component.html+admin.haml index 5e40096..042a161 100644 --- a/app/components/mcm/columns_component/columns_component.html+admin.haml +++ b/app/components/mcm/columns_component/columns_component.html+admin.haml @@ -1,14 +1,15 @@ -- @component_form.fields_for :metadata, @component.metadata do |metadata_form| - = metadata_form.text_field :section_title, help: t('custom_pages.messages.leave_empty') - .row - .col-6 - = metadata_form.select :section_title_alignment, text_alignments, required: true - .col-6 - = metadata_form.select :section_title_color, available_text_colors, required: true +- render "mcm/component_form", component: @component do |component_form| + - component_form.fields_for :metadata, @component.metadata do |metadata_form| + = metadata_form.text_field :section_title, help: t('custom_pages.messages.leave_empty') + .row + .col-6 + = metadata_form.select :section_title_alignment, text_alignments, required: true + .col-6 + = metadata_form.select :section_title_color, available_text_colors, required: true - .row - .col-6 - = metadata_form.text_field :background_color, label: t('custom_pages.components.background_color'), help: '#ffffff' - .col-6 - = metadata_form.number_field :height, label: t('custom_pages.components.height.label'), help: t('custom_pages.components.height.help') - = metadata_form.check_box :full_width, label: t('custom_pages.components.full_width'), custom: :switch + .row + .col-6 + = metadata_form.text_field :background_color, label: t('custom_pages.components.background_color'), help: '#ffffff' + .col-6 + = metadata_form.number_field :height, label: t('custom_pages.components.height.label'), help: t('custom_pages.components.height.help') + = metadata_form.check_box :full_width, label: t('custom_pages.components.full_width'), custom: :switch diff --git a/app/components/mcm/columns_component/columns_component.html.haml b/app/components/mcm/columns_component/columns_component.html.haml index 3df9826..0acec94 100644 --- a/app/components/mcm/columns_component/columns_component.html.haml +++ b/app/components/mcm/columns_component/columns_component.html.haml @@ -1,26 +1,8 @@ -.py-5{ class: @component.full_width? ? 'container-fluid' : 'container' } +%section{ class: @component.full_width? ? "container-fluid" : "container", style: "background:#{background_color}" } - if @component.metadata.section_title.present? - %h2{ class: title_classes } - = @component.section_title - .row.align-content-center.d-flex{ id: "row-#{@component.id}" } - - @component.children.active.each do |component| - .col-12.col-md.align-items-center.d-flex.flex-wrap - - begin - = render component.view_component - - rescue ActionView::Template::Error => e - - Rails.logger.error(e) - .container - %h4= t('custom_pages.messages.something_went_wrong') - = link_to t('custom_pages.actions.edit_component'), - main_app.edit_admin_custom_page_component_component_path(@component.page, - @component, component) -- if @component.metadata.background_color.present? - :css - #section-#{@component.id} { - background-color: #{@component.metadata.background_color}; - } + %h2{ class: title_classes }= @component.metadata.section_title -:css - #row-#{@component.id} { - height: #{height}; - } + .d-flex.align-content-center{ style: "height:#{height}px" } + - @component.children.active.each do |component| + .d-flex.align-items-center.flex-wrap + = render "mcm/child_component", component: component diff --git a/app/components/mcm/hero_image_component/hero_image_component.html+admin.haml b/app/components/mcm/hero_image_component/hero_image_component.html+admin.haml index c943184..83322cf 100644 --- a/app/components/mcm/hero_image_component/hero_image_component.html+admin.haml +++ b/app/components/mcm/hero_image_component/hero_image_component.html+admin.haml @@ -1,3 +1,4 @@ -- @component.assets.first_or_initialize -- @component_form.fields_for :assets do |assets_form| - = render Mcm::ImageComponent.new(form: assets_form, variant: :desktop, styles: image_preview_styles) +- render "mcm/component_form", component: @component do |component_form| + - @component.assets.first_or_initialize + - component_form.fields_for :assets do |assets_form| + = render Mcm::ImageComponent.new(form: assets_form, variant: :desktop, styles: image_preview_styles) diff --git a/app/components/mcm/hero_image_component/hero_image_component.html.haml b/app/components/mcm/hero_image_component/hero_image_component.html.haml index 43bbdbb..dec1f29 100644 --- a/app/components/mcm/hero_image_component/hero_image_component.html.haml +++ b/app/components/mcm/hero_image_component/hero_image_component.html.haml @@ -1,2 +1,4 @@ .p-0.w-100{ class: "image_hero_#{@component.assets.first.id}" } -= render Mcm::ImageComponent.new(asset: @component.assets.first, class_name: "image_hero_#{@component.assets.first.id}", height: @component.parent.height) += render Mcm::ImageComponent.new(asset: @component.assets.first, + class_name: "image_hero_#{@component.assets.first.id}", + height: @component.parent.metadata.height) diff --git a/app/components/mcm/hero_image_title_content_cta_component/hero_image_title_content_cta_component.html+admin.haml b/app/components/mcm/hero_image_title_content_cta_component/hero_image_title_content_cta_component.html+admin.haml index 8883734..731fa78 100644 --- a/app/components/mcm/hero_image_title_content_cta_component/hero_image_title_content_cta_component.html+admin.haml +++ b/app/components/mcm/hero_image_title_content_cta_component/hero_image_title_content_cta_component.html+admin.haml @@ -1,35 +1,36 @@ -- @component.assets.first_or_initialize -- @component_form.fields_for :assets do |assets_form| - = render Mcm::ImageComponent.new(form: assets_form, styles: image_preview_styles) +- render "mcm/component_form", component: @component do |component_form| + - @component.assets.first_or_initialize + - component_form.fields_for :assets do |assets_form| + = render Mcm::ImageComponent.new(form: assets_form, styles: image_preview_styles) -- @component_form.fields_for :metadata, @component.metadata do |metadata_form| - .row - .col-6 - = metadata_form.text_field :title, required: true - .col-6 - = metadata_form.select :title_color, available_text_colors, required: true + - component_form.fields_for :metadata, @component.metadata do |metadata_form| + .row + .col-6 + = metadata_form.text_field :title, required: true + .col-6 + = metadata_form.select :title_color, available_text_colors, required: true - = metadata_form.label :content - = metadata_form.hidden_field :content, required: true - %trix-editor{ input: 'components_page_metadata_content' } + = metadata_form.label :content + = metadata_form.hidden_field :content, required: true + %trix-editor{ input: 'components_page_metadata_content' } - .row.mt-3 - .col-6 - = metadata_form.select :content_alignment, text_alignments, required: true - .col-6 - = metadata_form.select :content_vertical_alignment, vertical_alignment_options, required: true - .row - .col-6 - = metadata_form.select :content_color, available_text_colors, required: true - .col-6 - = metadata_form.select :content_horizontal_alignment, horizontal_alignment_options, required: true - .row - .col-6 - = metadata_form.text_field :cta - .col-6 - = metadata_form.text_field :cta_url - .row - .col-6 - = metadata_form.select :button_color, available_button_colors - .col-6 - = metadata_form.select :button_alignment, text_alignments, required: true + .row.mt-3 + .col-6 + = metadata_form.select :content_alignment, text_alignments, required: true + .col-6 + = metadata_form.select :content_vertical_alignment, vertical_alignment_options, required: true + .row + .col-6 + = metadata_form.select :content_color, available_text_colors, required: true + .col-6 + = metadata_form.select :content_horizontal_alignment, horizontal_alignment_options, required: true + .row + .col-6 + = metadata_form.text_field :cta + .col-6 + = metadata_form.text_field :cta_url + .row + .col-6 + = metadata_form.select :button_color, available_button_colors + .col-6 + = metadata_form.select :button_alignment, text_alignments, required: true diff --git a/app/components/mcm/hero_image_title_content_cta_component/hero_image_title_content_cta_component.html.haml b/app/components/mcm/hero_image_title_content_cta_component/hero_image_title_content_cta_component.html.haml index a4475fc..b45b5ee 100644 --- a/app/components/mcm/hero_image_title_content_cta_component/hero_image_title_content_cta_component.html.haml +++ b/app/components/mcm/hero_image_title_content_cta_component/hero_image_title_content_cta_component.html.haml @@ -1,12 +1,12 @@ - @component.assets.each do |asset| .p-0.w-100{ class: "image_hero_#{asset.id}" } - = render Mcm::ImageComponent.new(asset: asset, class_name: "image_hero_#{asset.id}", height: @component.parent.height) + = render Mcm::ImageComponent.new(asset: asset, class_name: "image_hero_#{asset.id}", height: @component.parent.metadata.height) .carousel-caption.d-flex.h-100{ class: content_alignment_classes } %div - %h2{ class: title_classes }= @component.title + %h2{ class: title_classes }= @component.metadata.title %div{ class: content_classes } - != @component.content + != @component.metadata.content - if @component.metadata.cta.present? %div{ class: @component.metadata.button_alignment } - = link_to @component.cta, @component.cta_url, + = link_to @component.metadata.cta, @component.metadata.cta_url, class: "btn #{@component.metadata.button_color}" diff --git a/app/components/mcm/hero_slider_component/hero_slider_component.html+admin.haml b/app/components/mcm/hero_slider_component/hero_slider_component.html+admin.haml index 44fad07..9c29129 100644 --- a/app/components/mcm/hero_slider_component/hero_slider_component.html+admin.haml +++ b/app/components/mcm/hero_slider_component/hero_slider_component.html+admin.haml @@ -1,3 +1,4 @@ -- @component_form.fields_for :metadata, @component.metadata do |metadata_form| - = metadata_form.number_field :height, required: true - = metadata_form.check_box :full_width, label: t('custom_pages.components.full_width'), custom: :switch +- render "mcm/component_form", component: @component do |component_form| + - component_form.fields_for :metadata, @component.metadata do |metadata_form| + = metadata_form.number_field :height, required: true + = metadata_form.check_box :full_width, label: t('custom_pages.components.full_width'), custom: :switch diff --git a/app/components/mcm/hero_slider_component/hero_slider_component.html.haml b/app/components/mcm/hero_slider_component/hero_slider_component.html.haml index cabd442..4c04684 100644 --- a/app/components/mcm/hero_slider_component/hero_slider_component.html.haml +++ b/app/components/mcm/hero_slider_component/hero_slider_component.html.haml @@ -1,33 +1,17 @@ --# haml-lint:disable InlineStyles %div{ class: @component.full_width? ? 'container-fluid p-0' : 'container' } - .carousel.slide.w-100{ data: { ride: 'carousel' }, - id: "hero-slide-#{@component.id}", - style: "height: #{@component.metadata.height}px;" } - - if @component.children.active.size > 1 - %ol.carousel-indicators - - @component.children.active.each_with_index do |_slide, index| - %li{ class: index.zero? ? 'active' : '', - data: { target: "#hero-slide-#{@component.id}", 'slide-to': index } } + .carousel.slide.w-100{ data: { ride: 'carousel' }, id: "hero-slide-#{@component.id}", style: "height: #{height}px;" } .carousel-inner - @component.children.active.each_with_index do |slide, index| - .carousel-item{ class: index.zero? ? 'active' : '' } - - begin - = render slide.view_component - - rescue ActionView::Template::Error => e - - Rails.logger.error(e) - .container - %h4= t('custom_pages.messages.something_went_wrong') - = link_to t('custom_pages.actions.edit_component'), - main_app.edit_admin_custom_page_component_component_path(@component.page, - @component, slide) + .carousel-item{ class: index.zero? ? 'active' : '' }= render "mcm/child_component", component: slide - if @component.children.active.size > 1 - = link_to "#hero-slide-#{@component.id}", role: 'buton', - data: { slide: 'prev' }, class: 'carousel-control-prev' do + %ol.carousel-indicators + - @component.children.active.each_with_index do |_slide, index| + %li{ class: index.zero? ? 'active' : '', data: { target: "#hero-slide-#{@component.id}", 'slide-to': index } } + + = link_to "#hero-slide-#{@component.id}", role: 'buton', data: { slide: 'prev' }, class: 'carousel-control-prev' do %span.carousel-control-prev-icon{ aria: { hidden: true } } %span.sr-only= t('views.pagination.previous') - = link_to "#hero-slide-#{@component.id}", role: 'buton', - data: { slide: 'next' }, class: 'carousel-control-next' do + = link_to "#hero-slide-#{@component.id}", role: 'buton', data: { slide: 'next' }, class: 'carousel-control-next' do %span.carousel-control-next-icon{ aria: { hidden: true } } %span.sr-only= t('views.pagination.next') --# haml-lint:enable InlineStyles diff --git a/app/components/mcm/image_title_description_cta_component/image_title_description_cta_component.html+admin.haml b/app/components/mcm/image_title_description_cta_component/image_title_description_cta_component.html+admin.haml index 678675a..ca52d3d 100644 --- a/app/components/mcm/image_title_description_cta_component/image_title_description_cta_component.html+admin.haml +++ b/app/components/mcm/image_title_description_cta_component/image_title_description_cta_component.html+admin.haml @@ -1,24 +1,24 @@ -%h5 Image -- @component.assets.first_or_initialize -- @component_form.fields_for :assets do |assets_form| - = render Mcm::ImageComponent.new(form: assets_form, styles: image_preview_styles, variant: :small) +- render "mcm/component_form", component: @component do |component_form| + - @component.assets.first_or_initialize + - component_form.fields_for :assets do |assets_form| + = render Mcm::ImageComponent.new(form: assets_form, styles: image_preview_styles, variant: :small) -- @component_form.fields_for :metadata, @component.metadata do |metadata_form| - = metadata_form.text_field :title, required: true, help: 'Always centered' - = metadata_form.select :title_color, available_text_colors, required: true - = metadata_form.text_area :content, required: true - .row - .col-6 - = metadata_form.select :content_color, available_text_colors, required: true - .col-6 - = metadata_form.select :content_alignment, text_alignments, required: true - .row - .col-6 - = metadata_form.text_field :cta, label: "Call to Action" - .col-6 - = metadata_form.text_field :cta_url, label: "Call to Action URL" - .row - .col-6 - = metadata_form.select :button_color, available_button_colors - .col-6 - = metadata_form.select :button_alignment, text_alignments, required: true + - component_form.fields_for :metadata, @component.metadata do |metadata_form| + = metadata_form.text_field :title, required: true, help: 'Always centered' + = metadata_form.select :title_color, available_text_colors, required: true + = metadata_form.text_area :content, required: true + .row + .col-6 + = metadata_form.select :content_color, available_text_colors, required: true + .col-6 + = metadata_form.select :content_alignment, text_alignments, required: true + .row + .col-6 + = metadata_form.text_field :cta, label: "Call to Action" + .col-6 + = metadata_form.text_field :cta_url, label: "Call to Action URL" + .row + .col-6 + = metadata_form.select :button_color, available_button_colors + .col-6 + = metadata_form.select :button_alignment, text_alignments, required: true diff --git a/app/components/mcm/rich_text_component/rich_text_component.html+admin.haml b/app/components/mcm/rich_text_component/rich_text_component.html+admin.haml index 84dc307..eabfcdd 100644 --- a/app/components/mcm/rich_text_component/rich_text_component.html+admin.haml +++ b/app/components/mcm/rich_text_component/rich_text_component.html+admin.haml @@ -1,4 +1,5 @@ -- @component_form.fields_for :metadata, @component.metadata do |metadata_form| - = metadata_form.hidden_field :body - %trix-editor{ input: 'components_page_metadata_body' } - = metadata_form.select :content_color, available_text_colors, required: true +- render "mcm/component_form", component: @component do |component_form| + - component_form.fields_for :metadata, @component.metadata do |metadata_form| + %trix-editor{ input: 'components_page_metadata_body' } + = metadata_form.hidden_field :body + = metadata_form.select :content_color, available_text_colors, required: true diff --git a/app/components/mcm/square_image_component/square_image_component.html+admin.haml b/app/components/mcm/square_image_component/square_image_component.html+admin.haml index 64b80bc..5adb8b2 100644 --- a/app/components/mcm/square_image_component/square_image_component.html+admin.haml +++ b/app/components/mcm/square_image_component/square_image_component.html+admin.haml @@ -1,7 +1,8 @@ -- @component_form.fields_for :metadata, @component.metadata do |metadata_form| - = metadata_form.select :vertical_alignment, vertical_alignment_options, required: true - = metadata_form.select :horizontal_alignment, horizontal_alignment_options, required: true +- render "mcm/component_form", component: @component do |component_form| + - component_form.fields_for :metadata, @component.metadata do |metadata_form| + = metadata_form.select :vertical_alignment, vertical_alignment_options, required: true + = metadata_form.select :horizontal_alignment, horizontal_alignment_options, required: true -- @component.assets.first_or_initialize -- @component_form.fields_for :assets do |assets_form| - = render Mcm::ImageComponent.new(form: assets_form, styles: image_preview_styles, variant: :small) + - @component.assets.first_or_initialize + - component_form.fields_for :assets do |assets_form| + = render Mcm::ImageComponent.new(form: assets_form, styles: image_preview_styles, variant: :small) diff --git a/app/components/mcm/title_content_cta_component/title_content_cta_component.html+admin.haml b/app/components/mcm/title_content_cta_component/title_content_cta_component.html+admin.haml index 0f84f7c..4c05a2a 100644 --- a/app/components/mcm/title_content_cta_component/title_content_cta_component.html+admin.haml +++ b/app/components/mcm/title_content_cta_component/title_content_cta_component.html+admin.haml @@ -1,25 +1,26 @@ -- @component_form.fields_for :metadata, @component.metadata do |metadata_form| - = metadata_form.text_field :title, required: true - .row - .col-6 - = metadata_form.select :title_alignment, text_alignments, required: true - .col-6 - = metadata_form.select :title_color, available_text_colors, required: true +- render "mcm/component_form", component: @component do |component_form| + - component_form.fields_for :metadata, @component.metadata do |metadata_form| + = metadata_form.text_field :title, required: true + .row + .col-6 + = metadata_form.select :title_alignment, text_alignments, required: true + .col-6 + = metadata_form.select :title_color, available_text_colors, required: true - = metadata_form.hidden_field :content, required: true - %trix-editor{ input: 'components_page_metadata_content' } + = metadata_form.hidden_field :content, required: true + %trix-editor{ input: 'components_page_metadata_content' } - .row.mt-3 - .col-6 - = metadata_form.select :content_alignment, text_alignments, required: true - .col-6 - = metadata_form.select :content_color, available_text_colors, required: true - .row - .col-6 - = metadata_form.text_field :cta_title, label: t('.cta.title') - = metadata_form.text_field :cta_url, label: t('.cta.url') - = metadata_form.select :button_color, available_button_colors, label: t('.cta.button') - = metadata_form.select :button_alignment, text_alignments, required: true - .col-6 - = metadata_form.select :content_vertical_alignment, vertical_alignment_options, required: true, label: t('.position.vertical') - = metadata_form.select :content_horizontal_alignment, horizontal_alignment_options, required: true, label: t('.position.horizontal') + .row.mt-3 + .col-6 + = metadata_form.select :content_alignment, text_alignments, required: true + .col-6 + = metadata_form.select :content_color, available_text_colors, required: true + .row + .col-6 + = metadata_form.text_field :cta_title, label: t('.cta.title') + = metadata_form.text_field :cta_url, label: t('.cta.url') + = metadata_form.select :button_color, available_button_colors, label: t('.cta.button') + = metadata_form.select :button_alignment, text_alignments, required: true + .col-6 + = metadata_form.select :content_vertical_alignment, vertical_alignment_options, required: true, label: t('.position.vertical') + = metadata_form.select :content_horizontal_alignment, horizontal_alignment_options, required: true, label: t('.position.horizontal') diff --git a/app/components/mcm/title_content_cta_component/title_content_cta_component.html.haml b/app/components/mcm/title_content_cta_component/title_content_cta_component.html.haml index e81e744..62c8c79 100644 --- a/app/components/mcm/title_content_cta_component/title_content_cta_component.html.haml +++ b/app/components/mcm/title_content_cta_component/title_content_cta_component.html.haml @@ -1,9 +1,9 @@ .row.p-1.p-md-2.p-lg-3.h-100 .col-12.d-flex{ class: component_classes } %div - %h2{ class: title_classes }= @component.title + %h2{ class: title_classes }= @component.metadata.title %div{ class: content_classes } - != @component.content + != @component.metadata.content - if @component.metadata.cta_title.present? %div{ class: @component.metadata.button_alignment } - = link_to @component.cta_title, @component.cta_url, class: "btn #{@component.metadata.button_color}" + = link_to @component.metadata.cta_title, @component.metadata.cta_url, class: "btn #{@component.metadata.button_color}" diff --git a/app/components/mcm/youtube_video_component.rb b/app/components/mcm/youtube_video_component.rb index 07501af..29a6aa3 100644 --- a/app/components/mcm/youtube_video_component.rb +++ b/app/components/mcm/youtube_video_component.rb @@ -5,7 +5,7 @@ def defaults end def youtube_url - "https://www.youtube.com/embed/#{@component.youtube_video_id}" + "https://www.youtube.com/embed/#{@component.metadata.youtube_video_id}" end end end diff --git a/app/components/mcm/youtube_video_component/youtube_video_component.html+admin.haml b/app/components/mcm/youtube_video_component/youtube_video_component.html+admin.haml index 3d800a9..097b98f 100644 --- a/app/components/mcm/youtube_video_component/youtube_video_component.html+admin.haml +++ b/app/components/mcm/youtube_video_component/youtube_video_component.html+admin.haml @@ -1,3 +1,4 @@ -- @component_form.fields_for :metadata, @component.metadata do |metadata_form| - = metadata_form.number_field :height, required: true - = metadata_form.text_field :youtube_video_id, placeholder: 'vlDzyIIMYma', help: 'Youtube video id', required: true +- render "mcm/component_form", component: @component do |component_form| + - component_form.fields_for :metadata, @component.metadata do |metadata_form| + = metadata_form.number_field :height, required: true + = metadata_form.text_field :youtube_video_id, placeholder: 'vlDzyIIMYma', help: 'Youtube video id', required: true diff --git a/app/components/mcm/youtube_video_component/youtube_video_component.html.haml b/app/components/mcm/youtube_video_component/youtube_video_component.html.haml index 7d0148b..86c918a 100644 --- a/app/components/mcm/youtube_video_component/youtube_video_component.html.haml +++ b/app/components/mcm/youtube_video_component/youtube_video_component.html.haml @@ -1,6 +1,6 @@ %iframe.w-100{ allow: 'accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture', allowfullscreen: '', frameborder: '0', - height: @component.height, + height: @component.metadata.height, src: youtube_url, width: '560' } diff --git a/app/models/mcm/components_page.rb b/app/models/mcm/components_page.rb index d5903cd..8181fec 100644 --- a/app/models/mcm/components_page.rb +++ b/app/models/mcm/components_page.rb @@ -33,22 +33,7 @@ class ComponentsPage < Mcm::ApplicationRecord before_destroy :update_related_positions def view_component(component_form: nil) - "::Mcm::#{component_name.camelize}Component".constantize.new( - component: self, - component_form: component_form - ) - end - - def respond_to_missing?(method, *args) - metadata.respond_to?(method) || super(method, args) - end - - def method_missing(method, *args) - if respond_to_missing?(method) - metadata.send(method, *args) - else - super - end + "::Mcm::#{component_name.camelize}Component".constantize.new(component: self) end # rubocop:disable Rails/SkipsModelValidations diff --git a/app/views/mcm/_child_component.html.haml b/app/views/mcm/_child_component.html.haml new file mode 100644 index 0000000..c3b8d00 --- /dev/null +++ b/app/views/mcm/_child_component.html.haml @@ -0,0 +1,7 @@ +- begin + = render component.view_component +- rescue ViewComponent::TemplateError => e + - Rails.logger.error(e) + .alert.alert-danger + %p= t('custom_pages.messages.something_went_wrong') + = link_to t('custom_pages.actions.edit_component'), main_app.edit_admin_component_path(component) diff --git a/app/views/mcm/_component_form.html.haml b/app/views/mcm/_component_form.html.haml new file mode 100644 index 0000000..baf5f51 --- /dev/null +++ b/app/views/mcm/_component_form.html.haml @@ -0,0 +1,5 @@ += bootstrap_form_for component, url: main_app.admin_component_path(component), method: :put do |component_form| + = component_form.text_field :name, required: true + = component_form.check_box :active, label: t('custom_pages.components.status'), custom: :switch + + = yield component_form diff --git a/spec/components/mcm/banner_component_spec.rb b/spec/components/mcm/banner_component_spec.rb new file mode 100644 index 0000000..09f23c6 --- /dev/null +++ b/spec/components/mcm/banner_component_spec.rb @@ -0,0 +1,20 @@ +require "spec_helper" + +RSpec.describe Mcm::BannerComponent, type: :component do + let(:component) do + Mcm::ComponentsPage.create!( + page: Mcm::Page.create!(name: "Test Page"), + component: Mcm::Component.find_by!(name: "banner"), + name: "Test component", + position: 1, + active: true, + metadata: { content: "Foo Bar" }, + assets_attributes: [ + { attachment: fixture_file_upload("hero_image.png") }, + { attachment: fixture_file_upload("hero_image.png") } + ] + ) + end + + it_behaves_like "a component" +end diff --git a/spec/components/mcm/columns_component_spec.rb b/spec/components/mcm/columns_component_spec.rb new file mode 100644 index 0000000..bc41053 --- /dev/null +++ b/spec/components/mcm/columns_component_spec.rb @@ -0,0 +1,17 @@ +require "spec_helper" + +RSpec.describe Mcm::ColumnsComponent, type: :component do + let(:component) do + Mcm::ComponentsPage.create!( + page: Mcm::Page.create!(name: "Test Page"), + component: Mcm::Component.find_by!(name: "columns"), + name: "Test component", + position: 1, + active: true, + metadata: { section_title: "Section 1" } + ) + end + + it_behaves_like "a component" + it_behaves_like "a container component" + end diff --git a/spec/components/mcm/hero_image_component_spec.rb b/spec/components/mcm/hero_image_component_spec.rb new file mode 100644 index 0000000..06b51e1 --- /dev/null +++ b/spec/components/mcm/hero_image_component_spec.rb @@ -0,0 +1,29 @@ +require "spec_helper" + +RSpec.describe Mcm::HeroImageComponent, type: :component do + let(:component) do + Mcm::ComponentsPage.create!( + page: Mcm::Page.create!(name: "Test Page"), + component: Mcm::Component.find_by!(name: "hero_image"), + name: "Test component", + position: 1, + active: true, + metadata: { }, + assets_attributes: [ + { attachment: fixture_file_upload("hero_image.png") } + ] + ) + end + + before do + # This component needs access to its parent's metadata + component.create_parent!( + page: component.page, + component: Mcm::Component.find_by!(name: "hero_slider"), + name: "Parent component", + metadata: { height: 400 } + ) + end + + it_behaves_like "a component" + end diff --git a/spec/components/mcm/hero_image_title_content_cta_component_spec.rb b/spec/components/mcm/hero_image_title_content_cta_component_spec.rb new file mode 100644 index 0000000..451f2e6 --- /dev/null +++ b/spec/components/mcm/hero_image_title_content_cta_component_spec.rb @@ -0,0 +1,29 @@ +require "spec_helper" + +RSpec.describe Mcm::HeroImageTitleContentCtaComponent, type: :component do + let(:component) do + Mcm::ComponentsPage.create!( + page: Mcm::Page.create!(name: "Test Page"), + component: Mcm::Component.find_by!(name: "hero_image_title_content_cta"), + name: "Test component", + position: 1, + active: true, + metadata: { title: "Section 1" }, + assets_attributes: [ + { attachment: fixture_file_upload("hero_image.png") } + ] + ) + end + + before do + # This component needs access to its parent's metadata + component.create_parent!( + page: component.page, + component: Mcm::Component.find_by!(name: "hero_slider"), + name: "Parent component", + metadata: { height: 400 } + ) + end + + it_behaves_like "a component" + end diff --git a/spec/components/mcm/hero_slider_component_spec.rb b/spec/components/mcm/hero_slider_component_spec.rb new file mode 100644 index 0000000..322e257 --- /dev/null +++ b/spec/components/mcm/hero_slider_component_spec.rb @@ -0,0 +1,17 @@ +require "spec_helper" + +RSpec.describe Mcm::HeroSliderComponent, type: :component do + let(:component) do + Mcm::ComponentsPage.create!( + page: Mcm::Page.create!(name: "Test Page"), + component: Mcm::Component.find_by!(name: "hero_slider"), + name: "Test component", + position: 1, + active: true, + metadata: { } + ) + end + + it_behaves_like "a component" + it_behaves_like "a container component" + end diff --git a/spec/components/mcm/image_title_description_cta_component_spec.rb b/spec/components/mcm/image_title_description_cta_component_spec.rb new file mode 100644 index 0000000..91104ba --- /dev/null +++ b/spec/components/mcm/image_title_description_cta_component_spec.rb @@ -0,0 +1,19 @@ +require "spec_helper" + +RSpec.describe Mcm::ImageTitleDescriptionCtaComponent, type: :component do + let(:component) do + Mcm::ComponentsPage.create!( + page: Mcm::Page.create!(name: "Test Page"), + component: Mcm::Component.find_by!(name: "image_title_description_cta"), + name: "Test component", + position: 1, + active: true, + metadata: { title: "Section 1" }, + assets_attributes: [ + { attachment: fixture_file_upload("hero_image.png") } + ] + ) + end + + it_behaves_like "a component" + end diff --git a/spec/components/mcm/rich_text_component_spec.rb b/spec/components/mcm/rich_text_component_spec.rb new file mode 100644 index 0000000..45fe1f4 --- /dev/null +++ b/spec/components/mcm/rich_text_component_spec.rb @@ -0,0 +1,16 @@ +require "spec_helper" + +RSpec.describe Mcm::RichTextComponent, type: :component do + let(:component) do + Mcm::ComponentsPage.create!( + page: Mcm::Page.create!(name: "Test Page"), + component: Mcm::Component.find_by!(name: "rich_text"), + name: "Test component", + position: 1, + active: true, + metadata: { body: "

Lorem Ipsum

" } + ) + end + + it_behaves_like "a component" +end diff --git a/spec/components/mcm/square_image_component_spec.rb b/spec/components/mcm/square_image_component_spec.rb new file mode 100644 index 0000000..cdf400a --- /dev/null +++ b/spec/components/mcm/square_image_component_spec.rb @@ -0,0 +1,19 @@ +require "spec_helper" + +RSpec.describe Mcm::SquareImageComponent, type: :component do + let(:component) do + Mcm::ComponentsPage.create!( + page: Mcm::Page.create!(name: "Test Page"), + component: Mcm::Component.find_by!(name: "square_image"), + name: "Test component", + position: 1, + active: true, + metadata: { }, + assets_attributes: [ + { attachment: fixture_file_upload("hero_image.png") } + ] + ) + end + + it_behaves_like "a component" + end diff --git a/spec/components/mcm/title_content_cta_component_spec.rb b/spec/components/mcm/title_content_cta_component_spec.rb new file mode 100644 index 0000000..1cb00d8 --- /dev/null +++ b/spec/components/mcm/title_content_cta_component_spec.rb @@ -0,0 +1,16 @@ +require "spec_helper" + +RSpec.describe Mcm::TitleContentCtaComponent, type: :component do + let(:component) do + Mcm::ComponentsPage.create!( + page: Mcm::Page.create!(name: "Test Page"), + component: Mcm::Component.find_by!(name: "title_content_cta"), + name: "Test component", + position: 1, + active: true, + metadata: { title: "Test", content: "Foo Bar", cta_title: "Test", cta_url: "http://test.com" } + ) + end + + it_behaves_like "a component" + end diff --git a/spec/components/mcm/youtube_video_component_spec.rb b/spec/components/mcm/youtube_video_component_spec.rb new file mode 100644 index 0000000..914c362 --- /dev/null +++ b/spec/components/mcm/youtube_video_component_spec.rb @@ -0,0 +1,16 @@ +require "spec_helper" + +RSpec.describe Mcm::YoutubeVideoComponent, type: :component do + let(:component) do + Mcm::ComponentsPage.create!( + page: Mcm::Page.create!(name: "Test Page"), + component: Mcm::Component.find_by!(name: "youtube_video"), + name: "Test component", + position: 1, + active: true, + metadata: { } + ) + end + + it_behaves_like "a component" + end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7a880e9..dd8d1d0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -16,6 +16,12 @@ require dummy_env require "#{__dir__}/dummy/config/environment" require "rspec/rails" +require "view_component/test_helpers" +require "view_component/system_test_helpers" +require "capybara/rspec" + +# Load shared examples +Dir["#{__dir__}/support/shared_examples/*.rb"].sort.each { |f| require f } # Configure mcm Mcm.controller_parent = "ActionController::Base" @@ -24,6 +30,9 @@ # Configure rspec RSpec.configure do |config| config.include SilentStream + config.include ViewComponent::TestHelpers, type: :component + config.include ViewComponent::SystemTestHelpers, type: :component + config.include Capybara::RSpecMatchers, type: :component config.order = "random" config.use_transactional_fixtures = true @@ -33,4 +42,8 @@ Rails.application.load_tasks silence_stream(STDOUT) { Rake::Task['mcm:sync_components'].invoke } end + + config.before type: :component do + ActiveStorage::Current.url_options = { protocol: :http, host: "localhost", port: 3000 } + end end diff --git a/spec/support/mcm/failing_component.rb b/spec/support/mcm/failing_component.rb new file mode 100644 index 0000000..3c4a2a2 --- /dev/null +++ b/spec/support/mcm/failing_component.rb @@ -0,0 +1,3 @@ +# This component is missing its template since it's meant to raise an exception +class Mcm::FailingComponent < ViewComponent::Base +end diff --git a/spec/support/shared_examples/component.rb b/spec/support/shared_examples/component.rb new file mode 100644 index 0000000..d93419b --- /dev/null +++ b/spec/support/shared_examples/component.rb @@ -0,0 +1,11 @@ +shared_examples "a component" do + it "renders a frontend version" do + expect { render_inline described_class.new(component: component) }.not_to raise_error + end + + it "renders an admin version" do + with_variant :admin do + expect { render_inline described_class.new(component: component) }.not_to raise_error + end + end +end diff --git a/spec/support/shared_examples/container_component.rb b/spec/support/shared_examples/container_component.rb new file mode 100644 index 0000000..606e773 --- /dev/null +++ b/spec/support/shared_examples/container_component.rb @@ -0,0 +1,45 @@ +require_relative "../mcm/failing_component" + +shared_examples "a container component" do + context "when rendering children components" do + before do + component.children.create!( + page: component.page, + component: Mcm::Component.find_by!(name: "rich_text"), + name: "Child component", + active: true, + metadata: { body: "Foo Bar" } + ) + end + + it "renders child components" do + render_inline Mcm::ColumnsComponent.new(component: component) + + expect(page).to have_content "Foo Bar" + end + + context "when something goes wrong with a child component" do + before do + failing_component = Mcm::Component.create! name: "failing", component_type: "content" + component.children.create!( + page: component.page, + component: failing_component, + name: "Child component", + active: true + ) + end + + it "renders an error message" do + render_inline Mcm::ColumnsComponent.new(component: component) + + expect(page).to have_content I18n.t('custom_pages.messages.something_went_wrong') + end + + it "renders the rest of the child components" do + render_inline Mcm::ColumnsComponent.new(component: component) + + expect(page).to have_content "Foo Bar" + end + end + end +end