Merge pull request #4367 from consul/sdg_header
Allow to add header cards to SDG homepage
This commit is contained in:
@@ -47,4 +47,8 @@
|
||||
@include grid-row-nest;
|
||||
}
|
||||
}
|
||||
|
||||
.background-header {
|
||||
margin-bottom: $line-height;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
.sdg-header,
|
||||
.phase-cards {
|
||||
> header {
|
||||
align-items: flex-start;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<%= back_link_to index_path %>
|
||||
<%= header %>
|
||||
<%= render "admin/widget/cards/form", card: card %>
|
||||
<%= render "admin/widget/cards/form", card: card, url: form_path %>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
class Admin::Widget::Cards::EditComponent < ApplicationComponent
|
||||
include Header
|
||||
attr_reader :card, :index_path
|
||||
attr_reader :card, :index_path, :form_path
|
||||
|
||||
def initialize(card, index_path:)
|
||||
def initialize(card, index_path:, form_path: nil)
|
||||
@card = card
|
||||
@index_path = index_path
|
||||
@form_path = form_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<%= back_link_to index_path %>
|
||||
<%= header %>
|
||||
<%= render "admin/widget/cards/form", card: card %>
|
||||
<%= render "admin/widget/cards/form", card: card, url: form_path %>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
class Admin::Widget::Cards::NewComponent < ApplicationComponent
|
||||
include Header
|
||||
attr_reader :card, :index_path
|
||||
attr_reader :card, :index_path, :form_path
|
||||
|
||||
def initialize(card, index_path:)
|
||||
def initialize(card, index_path:, form_path: nil)
|
||||
@card = card
|
||||
@index_path = index_path
|
||||
@form_path = form_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
<% end %>
|
||||
</td>
|
||||
<td>
|
||||
<%= render Admin::TableActionsComponent.new(card) %>
|
||||
<%= render Admin::TableActionsComponent.new(card, options) %>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
class Admin::Widget::Cards::RowComponent < ApplicationComponent
|
||||
attr_reader :card
|
||||
attr_reader :card, :options
|
||||
|
||||
def initialize(card)
|
||||
def initialize(card, **options)
|
||||
@card = card
|
||||
@options = options
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<% cards.each do |card| %>
|
||||
<%= render Admin::Widget::Cards::RowComponent.new(card) %>
|
||||
<%= render Admin::Widget::Cards::RowComponent.new(card, options) %>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
class Admin::Widget::Cards::TableComponent < ApplicationComponent
|
||||
attr_reader :cards, :no_cards_message
|
||||
attr_reader :cards, :no_cards_message, :options
|
||||
|
||||
def initialize(cards, no_cards_message:)
|
||||
def initialize(cards, no_cards_message:, **options)
|
||||
@cards = cards
|
||||
@no_cards_message = no_cards_message
|
||||
@options = options
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<% provide(:title) { title } %>
|
||||
|
||||
<main class="sdg-goals-index">
|
||||
<header class="section-header">
|
||||
<h1><%= title %></h1>
|
||||
</header>
|
||||
<% if header.present? %>
|
||||
<%= render "shared/header", header: header %>
|
||||
<% else %>
|
||||
<header class="section-header">
|
||||
<h1><%= title %></h1>
|
||||
</header>
|
||||
<% end %>
|
||||
|
||||
<%= render Shared::BannerComponent.new("sdg") %>
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
class SDG::Goals::IndexComponent < ApplicationComponent
|
||||
attr_reader :goals, :phases
|
||||
attr_reader :goals, :header, :phases
|
||||
delegate :link_list, to: :helpers
|
||||
|
||||
def initialize(goals, phases)
|
||||
def initialize(goals, header:, phases:)
|
||||
@goals = goals
|
||||
@header = header
|
||||
@phases = phases
|
||||
end
|
||||
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
<%= header %>
|
||||
|
||||
<section class="sdg-header">
|
||||
<header>
|
||||
<h3><%= t("sdg_management.homepage.header.title") %></h3>
|
||||
|
||||
<% unless header_card %>
|
||||
<%= link_to t("sdg_management.homepage.header.create"), new_sdg_management_homepage_header_path %>
|
||||
<% end %>
|
||||
</header>
|
||||
|
||||
<%= render Admin::Widget::Cards::TableComponent.new(
|
||||
[header_card],
|
||||
edit_path: edit_sdg_management_homepage_header_path,
|
||||
destroy_path: sdg_management_homepage_header_path,
|
||||
no_cards_message: t("sdg_management.homepage.header.no_cards")
|
||||
) %>
|
||||
</section>
|
||||
|
||||
<% phases.each do |phase| %>
|
||||
<section class="phase-cards <%= phase.kind %>-cards">
|
||||
<header>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
class SDGManagement::Homepage::ShowComponent < ApplicationComponent
|
||||
include Header
|
||||
|
||||
attr_reader :phases
|
||||
attr_reader :header_card, :phases
|
||||
|
||||
def initialize(phases)
|
||||
def initialize(header_card, phases)
|
||||
@header_card = header_card
|
||||
@phases = phases
|
||||
end
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@ module Admin::Widget::CardsActions
|
||||
include Translatable
|
||||
include ImageAttributes
|
||||
|
||||
included do
|
||||
helper_method :form_path
|
||||
end
|
||||
|
||||
def new
|
||||
@card.header = header_card?
|
||||
render template: "#{cards_view_path}/new"
|
||||
@@ -56,4 +60,8 @@ module Admin::Widget::CardsActions
|
||||
def cards_view_path
|
||||
"admin/widget/cards"
|
||||
end
|
||||
|
||||
def form_path
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,6 +6,7 @@ class SDG::GoalsController < ApplicationController
|
||||
def index
|
||||
@goals = @goals.order(:code)
|
||||
@phases = SDG::Phase.accessible_by(current_ability).order(:kind)
|
||||
@header = WebSection.find_by!(name: "sdg").header
|
||||
end
|
||||
|
||||
def show
|
||||
|
||||
29
app/controllers/sdg_management/header_controller.rb
Normal file
29
app/controllers/sdg_management/header_controller.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
class SDGManagement::HeaderController < SDGManagement::BaseController
|
||||
include Admin::Widget::CardsActions
|
||||
helper_method :index_path
|
||||
|
||||
before_action :load_cardable
|
||||
load_and_authorize_resource :header,
|
||||
class: "Widget::Card",
|
||||
through: :cardable,
|
||||
singleton: true,
|
||||
instance_name: :card
|
||||
|
||||
private
|
||||
|
||||
def load_cardable
|
||||
@cardable = WebSection.find_by!(name: "sdg")
|
||||
end
|
||||
|
||||
def index_path
|
||||
sdg_management_homepage_path
|
||||
end
|
||||
|
||||
def form_path
|
||||
sdg_management_homepage_header_path
|
||||
end
|
||||
|
||||
def header_params
|
||||
card_params
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,6 @@
|
||||
class SDGManagement::HomepageController < SDGManagement::BaseController
|
||||
def show
|
||||
@phases = SDG::Phase.accessible_by(current_ability).order(:kind)
|
||||
@card = WebSection.find_by!(name: "sdg").header
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,9 @@ class Abilities::SDG::Manager
|
||||
|
||||
can :read, ::SDG::Target
|
||||
can :manage, ::SDG::LocalTarget
|
||||
can [:read, :update, :destroy], Widget::Card, cardable_type: "SDG::Phase"
|
||||
can(:create, Widget::Card) { |card| card.cardable_type == "SDG::Phase" }
|
||||
can :read, WebSection, name: "sdg"
|
||||
can [:create, :update, :destroy], Widget::Card do |card|
|
||||
card.cardable_type == "SDG::Phase" || card.cardable&.name == "sdg"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
class WebSection < ApplicationRecord
|
||||
has_many :sections
|
||||
has_many :banners, through: :sections
|
||||
has_one :header, class_name: "Widget::Card", as: :cardable, dependent: :destroy
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<%= render "shared/globalize_locales", resource: card %>
|
||||
|
||||
<%= translatable_form_for [namespace, card.cardable, card] do |f| %>
|
||||
<%= translatable_form_for [namespace, card.cardable, card], url: local_assigns[:url] do |f| %>
|
||||
<%= render "shared/errors", resource: card %>
|
||||
|
||||
<div class="row">
|
||||
|
||||
@@ -1 +1 @@
|
||||
<%= render Admin::Widget::Cards::EditComponent.new(@card, index_path: index_path) %>
|
||||
<%= render Admin::Widget::Cards::EditComponent.new(@card, index_path: index_path, form_path: form_path) %>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<%= render Admin::Widget::Cards::NewComponent.new(@card, index_path: index_path) %>
|
||||
<%= render Admin::Widget::Cards::NewComponent.new(@card, index_path: index_path, form_path: form_path) %>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<%= render SDG::Goals::IndexComponent.new(@goals, @phases) %>
|
||||
<%= render SDG::Goals::IndexComponent.new(@goals, header: @header, phases: @phases) %>
|
||||
|
||||
@@ -1 +1 @@
|
||||
<%= render SDGManagement::Homepage::ShowComponent.new(@phases) %>
|
||||
<%= render SDGManagement::Homepage::ShowComponent.new(@card, @phases) %>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
social_url: root_url %>
|
||||
<% end %>
|
||||
|
||||
<%= render "header", header: @header %>
|
||||
<%= render "shared/header", header: @header %>
|
||||
|
||||
<main>
|
||||
<%= render "feeds" %>
|
||||
|
||||
@@ -6,6 +6,10 @@ en:
|
||||
title: "SDG content"
|
||||
homepage:
|
||||
title: "Homepage configuration"
|
||||
header:
|
||||
create: "Create header"
|
||||
no_cards: "There is no header"
|
||||
title: "Header"
|
||||
create_card: "Create %{phase} card"
|
||||
no_cards: "There are no cards for this phase"
|
||||
menu:
|
||||
|
||||
@@ -6,6 +6,10 @@ es:
|
||||
title: "Contenido ODS"
|
||||
homepage:
|
||||
title: "Configuración de la página de inicio"
|
||||
header:
|
||||
create: "Crear cabecera"
|
||||
no_cards: "No hay cabecera"
|
||||
title: "Cabecera"
|
||||
create_card: "Crear tarjeta de %{phase}"
|
||||
no_cards: "No hay tarjetas para esta fase"
|
||||
menu:
|
||||
|
||||
@@ -4,7 +4,9 @@ namespace :sdg_management do
|
||||
resources :goals, only: [:index]
|
||||
resources :targets, only: [:index]
|
||||
resources :local_targets, except: [:show]
|
||||
resource :homepage, controller: :homepage, only: [:show]
|
||||
resource :homepage, controller: :homepage, only: [:show] do
|
||||
resource :header, controller: :header, only: [:new, :create, :edit, :update, :destroy]
|
||||
end
|
||||
|
||||
resources :phases, only: [], as: :sdg_phases do
|
||||
resources :cards, except: [:index, :show], as: :widget_cards
|
||||
|
||||
@@ -3,16 +3,29 @@ require "rails_helper"
|
||||
describe SDG::Goals::IndexComponent, type: :component do
|
||||
let!(:goals) { SDG::Goal.all }
|
||||
let!(:phases) { SDG::Phase.all }
|
||||
let!(:component) { SDG::Goals::IndexComponent.new(goals, phases) }
|
||||
let!(:component) { SDG::Goals::IndexComponent.new(goals, header: nil, phases: phases) }
|
||||
|
||||
before do
|
||||
Setting["feature.sdg"] = true
|
||||
end
|
||||
|
||||
it "renders a heading" do
|
||||
render_inline component
|
||||
describe "header" do
|
||||
it "renders the default header when a custom one is not defined" do
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_css "h1", exact_text: "Sustainable Development Goals"
|
||||
expect(page).to have_css "h1", exact_text: "Sustainable Development Goals"
|
||||
end
|
||||
|
||||
it "renders a custom header" do
|
||||
sdg_web_section = WebSection.find_by!(name: "sdg")
|
||||
header = create(:widget_card, cardable: sdg_web_section)
|
||||
component = SDG::Goals::IndexComponent.new(goals, header: header, phases: phases)
|
||||
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_content header.title
|
||||
expect(page).not_to have_css "h1", exact_text: "Sustainable Development Goals"
|
||||
end
|
||||
end
|
||||
|
||||
it "renders phases" do
|
||||
|
||||
@@ -9,13 +9,19 @@ describe "Abilities::SDG::Manager" do
|
||||
|
||||
it { should be_able_to(:read, SDG::Target) }
|
||||
it { should be_able_to(:manage, SDG::LocalTarget) }
|
||||
it { should be_able_to(:read, WebSection.find_by!(name: "sdg")) }
|
||||
|
||||
it { should_not be_able_to(:read, SDG::Manager) }
|
||||
it { should_not be_able_to(:create, SDG::Manager) }
|
||||
it { should_not be_able_to(:delete, SDG::Manager) }
|
||||
it { should_not be_able_to(:read, WebSection.find_by!(name: "homepage")) }
|
||||
|
||||
it { should_not be_able_to(:update, create(:widget_card)) }
|
||||
it { should be_able_to(:update, create(:widget_card, cardable: SDG::Phase.sample)) }
|
||||
it { should be_able_to(:update, create(:widget_card, cardable: WebSection.find_by!(name: "sdg"))) }
|
||||
it { should_not be_able_to(:update, create(:widget_card, cardable: WebSection.find_by!(name: "homepage"))) }
|
||||
it { should_not be_able_to(:create, build(:widget_card)) }
|
||||
it { should be_able_to(:create, build(:widget_card, cardable: SDG::Phase.sample)) }
|
||||
it { should be_able_to(:create, build(:widget_card, cardable: WebSection.find_by!(name: "sdg"))) }
|
||||
it { should_not be_able_to(:create, build(:widget_card, cardable: WebSection.find_by!(name: "homepage"))) }
|
||||
end
|
||||
|
||||
@@ -48,5 +48,42 @@ describe "SDG homepage configuration", :js do
|
||||
expect(page).not_to have_content "My monitoring card"
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Create header card" do
|
||||
visit sdg_management_homepage_path
|
||||
click_link "Create header"
|
||||
|
||||
within(".translatable-fields") { fill_in "Title", with: "My header" }
|
||||
click_button "Create card"
|
||||
|
||||
within(".sdg-header") do
|
||||
expect(page).to have_content "My header"
|
||||
expect(page).not_to have_content "Create header"
|
||||
end
|
||||
end
|
||||
|
||||
scenario "Update header card" do
|
||||
create(:widget_card, cardable: WebSection.find_by!(name: "sdg"))
|
||||
visit sdg_management_homepage_path
|
||||
within ".sdg-header" do
|
||||
click_link "Edit"
|
||||
end
|
||||
|
||||
within(".translatable-fields") { fill_in "Title", with: "My header update" }
|
||||
click_button "Save card"
|
||||
|
||||
expect(page).to have_content "My header update"
|
||||
end
|
||||
|
||||
scenario "Remove header card" do
|
||||
create(:widget_card, title: "SDG Header", cardable: WebSection.find_by!(name: "sdg"))
|
||||
visit sdg_management_homepage_path
|
||||
|
||||
within ".sdg-header" do
|
||||
accept_confirm { click_link "Delete" }
|
||||
end
|
||||
|
||||
expect(page).not_to have_content "SDG Header"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user