dsfr-view-components 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +214 -0
- data/Rakefile +22 -0
- data/app/components/dsfr_component/accordion_component/section_component.html.erb +11 -0
- data/app/components/dsfr_component/accordion_component/section_component.rb +39 -0
- data/app/components/dsfr_component/accordion_component.html.erb +5 -0
- data/app/components/dsfr_component/accordion_component.rb +33 -0
- data/app/components/dsfr_component/alert_component.rb +22 -0
- data/app/components/dsfr_component/back_link_component.rb +24 -0
- data/app/components/dsfr_component/base.rb +28 -0
- data/app/components/dsfr_component/breadcrumbs_component.html.erb +7 -0
- data/app/components/dsfr_component/breadcrumbs_component.rb +51 -0
- data/app/components/dsfr_component/cookie_banner_component/message_component.rb +62 -0
- data/app/components/dsfr_component/cookie_banner_component.rb +35 -0
- data/app/components/dsfr_component/details_component.rb +42 -0
- data/app/components/dsfr_component/footer_component.html.erb +49 -0
- data/app/components/dsfr_component/footer_component.rb +87 -0
- data/app/components/dsfr_component/header_component.html.erb +51 -0
- data/app/components/dsfr_component/header_component.rb +145 -0
- data/app/components/dsfr_component/inset_text_component.rb +28 -0
- data/app/components/dsfr_component/notification_banner_component.html.erb +14 -0
- data/app/components/dsfr_component/notification_banner_component.rb +93 -0
- data/app/components/dsfr_component/pagination_component/adjacent_page.rb +59 -0
- data/app/components/dsfr_component/pagination_component/item.rb +77 -0
- data/app/components/dsfr_component/pagination_component/next_page.rb +27 -0
- data/app/components/dsfr_component/pagination_component/previous_page.rb +21 -0
- data/app/components/dsfr_component/pagination_component.rb +129 -0
- data/app/components/dsfr_component/panel_component.rb +56 -0
- data/app/components/dsfr_component/phase_banner_component.html.erb +6 -0
- data/app/components/dsfr_component/phase_banner_component.rb +25 -0
- data/app/components/dsfr_component/section_break_component.rb +49 -0
- data/app/components/dsfr_component/start_button_component.rb +55 -0
- data/app/components/dsfr_component/summary_list_component/action_component.rb +42 -0
- data/app/components/dsfr_component/summary_list_component/key_component.rb +23 -0
- data/app/components/dsfr_component/summary_list_component/row_component.rb +57 -0
- data/app/components/dsfr_component/summary_list_component/value_component.rb +23 -0
- data/app/components/dsfr_component/summary_list_component.html.erb +5 -0
- data/app/components/dsfr_component/summary_list_component.rb +49 -0
- data/app/components/dsfr_component/tab_component.html.erb +11 -0
- data/app/components/dsfr_component/tab_component.rb +61 -0
- data/app/components/dsfr_component/table_component/body_component.html.erb +5 -0
- data/app/components/dsfr_component/table_component/body_component.rb +21 -0
- data/app/components/dsfr_component/table_component/caption_component.rb +37 -0
- data/app/components/dsfr_component/table_component/cell_component.rb +57 -0
- data/app/components/dsfr_component/table_component/head_component.html.erb +5 -0
- data/app/components/dsfr_component/table_component/head_component.rb +23 -0
- data/app/components/dsfr_component/table_component/row_component.html.erb +5 -0
- data/app/components/dsfr_component/table_component/row_component.rb +30 -0
- data/app/components/dsfr_component/table_component.html.erb +7 -0
- data/app/components/dsfr_component/table_component.rb +35 -0
- data/app/components/dsfr_component/tag_component.rb +44 -0
- data/app/components/dsfr_component/traits/custom_html_attributes.rb +7 -0
- data/app/components/dsfr_component/traits.rb +1 -0
- data/app/components/dsfr_component/warning_text_component.rb +37 -0
- data/app/components/dsfr_component.rb +1 -0
- data/app/helpers/dsfr_back_to_top_link_helper.rb +14 -0
- data/app/helpers/dsfr_components_helper.rb +33 -0
- data/app/helpers/dsfr_link_helper.rb +123 -0
- data/app/helpers/dsfr_skip_link_helper.rb +13 -0
- data/config/routes.rb +2 -0
- data/lib/dsfr/components/engine.rb +110 -0
- data/lib/dsfr/components/helpers/css_utilities.rb +22 -0
- data/lib/dsfr/components/version.rb +5 -0
- data/lib/dsfr/components.rb +6 -0
- data/lib/tasks/dsfr/components_tasks.rake +4 -0
- metadata +482 -0
@@ -0,0 +1,129 @@
|
|
1
|
+
class DsfrComponent::PaginationComponent < DsfrComponent::Base
|
2
|
+
include Pagy::UrlHelpers
|
3
|
+
|
4
|
+
attr_reader :pagy,
|
5
|
+
:next_text,
|
6
|
+
:previous_text,
|
7
|
+
:page_items,
|
8
|
+
:previous_content,
|
9
|
+
:next_content,
|
10
|
+
:block_mode,
|
11
|
+
:landmark_label
|
12
|
+
|
13
|
+
alias_method :block_mode?, :block_mode
|
14
|
+
|
15
|
+
renders_many :items, "DsfrComponent::PaginationComponent::Item"
|
16
|
+
|
17
|
+
renders_one :next_page, ->(href:, text: default_adjacent_text(:next), label_text: nil, classes: [], html_attributes: {}) do
|
18
|
+
DsfrComponent::PaginationComponent::NextPage.new(
|
19
|
+
text: text,
|
20
|
+
href: href,
|
21
|
+
label_text: label_text,
|
22
|
+
block_mode: block_mode?,
|
23
|
+
classes: classes,
|
24
|
+
html_attributes: html_attributes
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
renders_one :previous_page, ->(href:, text: default_adjacent_text(:prev), label_text: nil, classes: [], html_attributes: {}) do
|
29
|
+
DsfrComponent::PaginationComponent::PreviousPage.new(
|
30
|
+
text: text,
|
31
|
+
href: href,
|
32
|
+
label_text: label_text,
|
33
|
+
block_mode: block_mode?,
|
34
|
+
classes: classes,
|
35
|
+
html_attributes: html_attributes
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(pagy: nil,
|
40
|
+
next_text: nil,
|
41
|
+
previous_text: nil,
|
42
|
+
block_mode: false,
|
43
|
+
landmark_label: config.default_pagination_landmark_label,
|
44
|
+
classes: [],
|
45
|
+
html_attributes: {})
|
46
|
+
@pagy = pagy
|
47
|
+
@next_text = next_text
|
48
|
+
@previous_text = previous_text
|
49
|
+
@block_mode = block_mode
|
50
|
+
@landmark_label = landmark_label
|
51
|
+
|
52
|
+
super(classes: classes, html_attributes: html_attributes)
|
53
|
+
end
|
54
|
+
|
55
|
+
def before_render
|
56
|
+
@page_items = if pagy.present?
|
57
|
+
build_items
|
58
|
+
elsif items.any?
|
59
|
+
items
|
60
|
+
else
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
|
64
|
+
@previous_content = previous_page || build_previous
|
65
|
+
@next_content = next_page || build_next
|
66
|
+
end
|
67
|
+
|
68
|
+
def call
|
69
|
+
attributes = html_attributes.tap { |ha| (ha[:class] << "govuk-pagination--block") if items.empty? }
|
70
|
+
|
71
|
+
tag.nav(**attributes) do
|
72
|
+
safe_join([
|
73
|
+
previous_content,
|
74
|
+
tag.ul(class: "govuk-pagination__list") { safe_join(page_items) },
|
75
|
+
next_content
|
76
|
+
])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def render?
|
81
|
+
# probably isn't any point rendering if there's only one page
|
82
|
+
(pagy.present? && pagy.series.size > 1) || @previous_content.present? || @next_content.present?
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def default_attributes
|
88
|
+
{ role: "navigation", aria: { label: landmark_label }, class: %w(govuk-pagination) }
|
89
|
+
end
|
90
|
+
|
91
|
+
def build_previous
|
92
|
+
return unless pagy&.prev
|
93
|
+
|
94
|
+
kwargs = {
|
95
|
+
href: pagy_url_for(pagy, pagy.prev),
|
96
|
+
text: @previous_text,
|
97
|
+
}
|
98
|
+
|
99
|
+
previous_page(**kwargs.compact)
|
100
|
+
end
|
101
|
+
|
102
|
+
def build_next
|
103
|
+
return unless pagy&.next
|
104
|
+
|
105
|
+
kwargs = {
|
106
|
+
href: pagy_url_for(pagy, pagy.next),
|
107
|
+
text: @next_text,
|
108
|
+
}
|
109
|
+
|
110
|
+
next_page(**kwargs.compact)
|
111
|
+
end
|
112
|
+
|
113
|
+
def build_items
|
114
|
+
pagy.series.map { |i| item(number: i, href: pagy_url_for(pagy, i), from_pagy: true) }
|
115
|
+
end
|
116
|
+
|
117
|
+
def default_adjacent_text(side)
|
118
|
+
visible, hidden = *case side
|
119
|
+
when :next
|
120
|
+
config.default_pagination_next_text
|
121
|
+
when :prev
|
122
|
+
config.default_pagination_previous_text
|
123
|
+
end
|
124
|
+
|
125
|
+
return visible if hidden.blank?
|
126
|
+
|
127
|
+
(visible + tag.span(" #{hidden}", class: "govuk-visually-hidden")).html_safe
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class DsfrComponent::PanelComponent < DsfrComponent::Base
|
2
|
+
attr_reader :id, :title_text, :text, :heading_level
|
3
|
+
|
4
|
+
renders_one :title_html
|
5
|
+
|
6
|
+
def initialize(title_text: nil, text: nil, heading_level: 1, id: nil, classes: [], html_attributes: {})
|
7
|
+
@heading_level = heading_level
|
8
|
+
@title_text = title_text
|
9
|
+
@text = text
|
10
|
+
@id = id
|
11
|
+
|
12
|
+
super(classes: classes, html_attributes: html_attributes)
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
tag.div(id: id, **html_attributes) do
|
17
|
+
safe_join([panel_title, panel_body].compact)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def default_attributes
|
24
|
+
{ class: %w(govuk-panel govuk-panel--confirmation) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def heading_tag
|
28
|
+
"h#{heading_level}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def panel_content
|
32
|
+
content || text
|
33
|
+
end
|
34
|
+
|
35
|
+
def title
|
36
|
+
title_html || title_text
|
37
|
+
end
|
38
|
+
|
39
|
+
def panel_title
|
40
|
+
return if title.blank?
|
41
|
+
|
42
|
+
content_tag(heading_tag, title, class: "govuk-panel__title")
|
43
|
+
end
|
44
|
+
|
45
|
+
def panel_body
|
46
|
+
return if panel_content.blank?
|
47
|
+
|
48
|
+
tag.div(class: "govuk-panel__body") do
|
49
|
+
panel_content
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def render?
|
54
|
+
title.present? || panel_content.present?
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class DsfrComponent::PhaseBannerComponent < DsfrComponent::Base
|
2
|
+
attr_reader :text, :phase_tag
|
3
|
+
|
4
|
+
def initialize(
|
5
|
+
tag: { text: config.default_phase_banner_tag },
|
6
|
+
text: config.default_phase_banner_text,
|
7
|
+
classes: [],
|
8
|
+
html_attributes: {}
|
9
|
+
)
|
10
|
+
@phase_tag = tag
|
11
|
+
@text = text
|
12
|
+
|
13
|
+
super(classes: classes, html_attributes: html_attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
def phase_tag_component
|
17
|
+
DsfrComponent::TagComponent.new(**phase_tag.deep_merge(classes: "govuk-phase-banner__content__tag"))
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def default_attributes
|
23
|
+
{ class: %w(govuk-phase-banner) }
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class DsfrComponent::SectionBreakComponent < DsfrComponent::Base
|
2
|
+
SIZES = %w(m l xl).freeze
|
3
|
+
|
4
|
+
def initialize(
|
5
|
+
visible: config.default_section_break_visible,
|
6
|
+
size: config.default_section_break_size,
|
7
|
+
classes: [],
|
8
|
+
html_attributes: {}
|
9
|
+
)
|
10
|
+
@visible = visible
|
11
|
+
@size = size
|
12
|
+
|
13
|
+
super(classes: classes, html_attributes: html_attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
tag.hr(**html_attributes)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :size, :visible
|
23
|
+
|
24
|
+
def default_attributes
|
25
|
+
{ class: default_classes }
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_classes
|
29
|
+
class_names(
|
30
|
+
"govuk-section-break",
|
31
|
+
size_class,
|
32
|
+
"govuk-section-break--visible" => visible?
|
33
|
+
).split
|
34
|
+
end
|
35
|
+
|
36
|
+
def size_class
|
37
|
+
if size.blank?
|
38
|
+
""
|
39
|
+
elsif size.in?(SIZES)
|
40
|
+
"govuk-section-break--#{size}"
|
41
|
+
else
|
42
|
+
raise ArgumentError, "invalid size #{size}, supported sizes are #{SIZES.to_sentence}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def visible?
|
47
|
+
visible
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
class DsfrComponent::StartButtonComponent < DsfrComponent::Base
|
2
|
+
BUTTON_ATTRIBUTES = {
|
3
|
+
draggable: 'false',
|
4
|
+
data: { module: 'fr-btn' }
|
5
|
+
}.freeze
|
6
|
+
|
7
|
+
LINK_ATTRIBUTES = BUTTON_ATTRIBUTES.merge({ role: 'button' }).freeze
|
8
|
+
|
9
|
+
attr_reader :text, :href, :as_button
|
10
|
+
|
11
|
+
def initialize(text:, href:, as_button: config.default_start_button_as_button, classes: [], html_attributes: {})
|
12
|
+
@text = text
|
13
|
+
@href = href
|
14
|
+
@as_button = as_button
|
15
|
+
|
16
|
+
super(classes: classes, html_attributes: html_attributes)
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
if as_button
|
21
|
+
button_to(href, **html_attributes) do
|
22
|
+
safe_join([text, icon])
|
23
|
+
end
|
24
|
+
else
|
25
|
+
link_to(href, **html_attributes) do
|
26
|
+
safe_join([text, icon])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def default_attributes
|
34
|
+
(as_button ? BUTTON_ATTRIBUTES : LINK_ATTRIBUTES)
|
35
|
+
.merge({ class: %w(fr-btn fr-btn--start) })
|
36
|
+
end
|
37
|
+
|
38
|
+
def icon
|
39
|
+
tag.svg(**svg_attributes) do
|
40
|
+
tag.path(fill: "currentColor", d: "M0 0h13l20 20-20 20H0l20-20z")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def svg_attributes
|
45
|
+
{
|
46
|
+
class: "fr-btn__start-icon",
|
47
|
+
xmlns: "http://www.w3.org/2000/svg",
|
48
|
+
width: "17.5",
|
49
|
+
height: "19",
|
50
|
+
viewBox: "0 0 33 40",
|
51
|
+
focusable: "false",
|
52
|
+
aria: { hidden: "true" }
|
53
|
+
}
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class DsfrComponent::SummaryListComponent::ActionComponent < DsfrComponent::Base
|
2
|
+
attr_reader :href, :text, :visually_hidden_text, :attributes, :classes
|
3
|
+
|
4
|
+
def initialize(href: nil, text: 'Change', visually_hidden_text: false, classes: [], html_attributes: {})
|
5
|
+
@visually_hidden_text = visually_hidden_text
|
6
|
+
|
7
|
+
if config.require_summary_list_action_visually_hidden_text && visually_hidden_text == false
|
8
|
+
fail(ArgumentError, "missing keyword: visually_hidden_text")
|
9
|
+
end
|
10
|
+
|
11
|
+
super(classes: classes, html_attributes: html_attributes)
|
12
|
+
|
13
|
+
@href = href
|
14
|
+
@text = text
|
15
|
+
end
|
16
|
+
|
17
|
+
def render?
|
18
|
+
href.present?
|
19
|
+
end
|
20
|
+
|
21
|
+
def call
|
22
|
+
link_to(href, **html_attributes) do
|
23
|
+
safe_join([action_text, visually_hidden_span].compact, " ")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def default_attributes
|
30
|
+
link_classes = dsfr_link_classes.append(classes).flatten
|
31
|
+
|
32
|
+
{ class: link_classes }
|
33
|
+
end
|
34
|
+
|
35
|
+
def action_text
|
36
|
+
content || text || fail(ArgumentError, "no text or content")
|
37
|
+
end
|
38
|
+
|
39
|
+
def visually_hidden_span
|
40
|
+
tag.span(visually_hidden_text, class: "govuk-visually-hidden") if visually_hidden_text.present?
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class DsfrComponent::SummaryListComponent::KeyComponent < DsfrComponent::Base
|
2
|
+
attr_reader :text
|
3
|
+
|
4
|
+
def initialize(text: nil, classes: [], html_attributes: {})
|
5
|
+
@text = text
|
6
|
+
|
7
|
+
super(classes: classes, html_attributes: html_attributes)
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
tag.dt(key_content, **html_attributes)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def default_attributes
|
17
|
+
{ class: %w(govuk-summary-list__key) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def key_content
|
21
|
+
content || text || fail(ArgumentError, "no text or content")
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class DsfrComponent::SummaryListComponent::RowComponent < DsfrComponent::Base
|
2
|
+
attr_reader :href, :visually_hidden_text, :show_actions_column
|
3
|
+
|
4
|
+
renders_one :key, DsfrComponent::SummaryListComponent::KeyComponent
|
5
|
+
renders_one :value, DsfrComponent::SummaryListComponent::ValueComponent
|
6
|
+
renders_many :actions, DsfrComponent::SummaryListComponent::ActionComponent
|
7
|
+
|
8
|
+
def initialize(show_actions_column: nil, classes: [], html_attributes: {})
|
9
|
+
@show_actions_column = show_actions_column
|
10
|
+
|
11
|
+
super(classes: classes, html_attributes: html_attributes)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
tag.div(**html_attributes) do
|
16
|
+
safe_join([key, value, actions_content])
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def before_render
|
21
|
+
if show_actions_column && actions.none?
|
22
|
+
html_attributes[:class] << no_actions_class
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def actions_content
|
29
|
+
return unless show_actions_column && actions.any?
|
30
|
+
|
31
|
+
(actions.one?) ? single_action : actions_list
|
32
|
+
end
|
33
|
+
|
34
|
+
def single_action
|
35
|
+
tag.dd(class: actions_class) { safe_join(actions) }
|
36
|
+
end
|
37
|
+
|
38
|
+
def actions_list
|
39
|
+
tag.dd(class: actions_class) do
|
40
|
+
tag.ul(class: "govuk-summary-list__actions-list") do
|
41
|
+
safe_join(actions.map { |action| tag.li(action, class: "govuk-summary-list__actions-list-item") })
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def default_attributes
|
47
|
+
{ class: %w(govuk-summary-list__row) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def actions_class
|
51
|
+
"govuk-summary-list__actions"
|
52
|
+
end
|
53
|
+
|
54
|
+
def no_actions_class
|
55
|
+
"govuk-summary-list__row--no-actions"
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class DsfrComponent::SummaryListComponent::ValueComponent < DsfrComponent::Base
|
2
|
+
attr_reader :text
|
3
|
+
|
4
|
+
def initialize(text: nil, classes: [], html_attributes: {})
|
5
|
+
super(classes: classes, html_attributes: html_attributes)
|
6
|
+
|
7
|
+
@text = text
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
tag.dd(value_content, **html_attributes)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def default_attributes
|
17
|
+
{ class: %w(govuk-summary-list__value) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def value_content
|
21
|
+
content || text || ""
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module DsfrComponent
|
2
|
+
class SummaryListComponent < DsfrComponent::Base
|
3
|
+
attr_reader :borders, :actions
|
4
|
+
|
5
|
+
renders_many :rows, ->(classes: [], html_attributes: {}, &block) do
|
6
|
+
DsfrComponent::SummaryListComponent::RowComponent.new(
|
7
|
+
show_actions_column: @show_actions_column,
|
8
|
+
classes: classes,
|
9
|
+
html_attributes: html_attributes,
|
10
|
+
&block
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(rows: nil, actions: true, borders: config.default_summary_list_borders, classes: [], html_attributes: {})
|
15
|
+
@borders = borders
|
16
|
+
@show_actions_column = actions
|
17
|
+
|
18
|
+
super(classes: classes, html_attributes: html_attributes)
|
19
|
+
|
20
|
+
return unless rows.presence
|
21
|
+
|
22
|
+
build(rows)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def borders_class
|
28
|
+
%(govuk-summary-list--no-border) unless borders
|
29
|
+
end
|
30
|
+
|
31
|
+
def default_attributes
|
32
|
+
{ class: ["govuk-summary-list", borders_class].compact }
|
33
|
+
end
|
34
|
+
|
35
|
+
def build(rows)
|
36
|
+
@show_actions_column = rows.any? { |r| r.key?(:actions) }
|
37
|
+
|
38
|
+
rows.each do |data|
|
39
|
+
k, v, a = data.values_at(:key, :value, :actions)
|
40
|
+
|
41
|
+
row(**data.slice(:classes, :html_attributes)) do |r|
|
42
|
+
r.key(**k)
|
43
|
+
r.value(**v)
|
44
|
+
Array.wrap(a).each { |ad| r.action(**ad) }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%= tag.div(**html_attributes) do %>
|
2
|
+
<%= tag.h2(title, class: "govuk-tabs__title") %>
|
3
|
+
<ul class="govuk-tabs__list">
|
4
|
+
<% tabs.each.with_index do |tab, i| %>
|
5
|
+
<%= tag.li(tab.li_link, class: tab.li_classes(i)) %>
|
6
|
+
<% end %>
|
7
|
+
</ul>
|
8
|
+
<% tabs.each.with_index do |tab, i| %>
|
9
|
+
<%= tag.div(tab, **tab.combined_attributes(i)) %>
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class DsfrComponent::TabComponent < DsfrComponent::Base
|
2
|
+
using HTMLAttributesUtils
|
3
|
+
|
4
|
+
renders_many :tabs, "Tab"
|
5
|
+
|
6
|
+
attr_reader :title, :id
|
7
|
+
|
8
|
+
def initialize(title:, id: nil, classes: [], html_attributes: {})
|
9
|
+
@title = title
|
10
|
+
@id = id
|
11
|
+
|
12
|
+
super(classes: classes, html_attributes: html_attributes)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def default_attributes
|
18
|
+
{ id: id, class: %w(govuk-tabs), data: { module: 'govuk-tabs' } }
|
19
|
+
end
|
20
|
+
|
21
|
+
class Tab < DsfrComponent::Base
|
22
|
+
attr_reader :label, :text
|
23
|
+
|
24
|
+
def initialize(label:, text: nil, classes: [], html_attributes: {})
|
25
|
+
@label = label
|
26
|
+
@text = text
|
27
|
+
|
28
|
+
super(classes: classes, html_attributes: html_attributes)
|
29
|
+
end
|
30
|
+
|
31
|
+
def id(prefix: nil)
|
32
|
+
[prefix, label.parameterize].join
|
33
|
+
end
|
34
|
+
|
35
|
+
def hidden_class(i = nil)
|
36
|
+
return [] if i&.zero?
|
37
|
+
|
38
|
+
%w(fr-tabs__panel--hidden)
|
39
|
+
end
|
40
|
+
|
41
|
+
def li_classes(i = nil)
|
42
|
+
class_names("govuk-tabs__list-item", "govuk-tabs__list-item--selected" => i&.zero?).split
|
43
|
+
end
|
44
|
+
|
45
|
+
def li_link
|
46
|
+
link_to(label, id(prefix: '#'), class: "govuk-tabs__tab")
|
47
|
+
end
|
48
|
+
|
49
|
+
def default_attributes
|
50
|
+
{ id: id, class: %w(fr-tabs__panel) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def combined_attributes(i)
|
54
|
+
html_attributes.deep_merge_html_attributes({ class: hidden_class(i) }).deep_tidy_html_attributes
|
55
|
+
end
|
56
|
+
|
57
|
+
def call
|
58
|
+
content || text || fail(ArgumentError, "no text or content")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class DsfrComponent::TableComponent::BodyComponent < DsfrComponent::Base
|
2
|
+
renders_many :rows, "DsfrComponent::TableComponent::RowComponent"
|
3
|
+
|
4
|
+
def initialize(rows: nil, first_cell_is_header: false, classes: [], html_attributes: {})
|
5
|
+
super(classes: classes, html_attributes: html_attributes)
|
6
|
+
|
7
|
+
build_rows_from_row_data(rows, first_cell_is_header)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def build_rows_from_row_data(data, first_cell_is_header)
|
13
|
+
return if data.blank?
|
14
|
+
|
15
|
+
data.each { |d| row(cell_data: d, first_cell_is_header: first_cell_is_header) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def default_attributes
|
19
|
+
{ class: %w(govuk-table__body) }
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class DsfrComponent::TableComponent::CaptionComponent < DsfrComponent::Base
|
2
|
+
attr_reader :text, :size
|
3
|
+
|
4
|
+
SIZES = %w(s m l xl).freeze
|
5
|
+
|
6
|
+
def initialize(text: nil, id: nil, size: 'm', classes: [], html_attributes: {})
|
7
|
+
@id = id
|
8
|
+
@text = text
|
9
|
+
@size = size
|
10
|
+
|
11
|
+
super(classes: classes, html_attributes: html_attributes)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
tag.caption(caption_content, **html_attributes)
|
16
|
+
end
|
17
|
+
|
18
|
+
def render?
|
19
|
+
caption_content.present?
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def caption_content
|
25
|
+
@caption_content ||= (content || text)
|
26
|
+
end
|
27
|
+
|
28
|
+
def default_attributes
|
29
|
+
{ class: class_names("govuk-table__caption", caption_size_class => size).split }
|
30
|
+
end
|
31
|
+
|
32
|
+
def caption_size_class
|
33
|
+
fail(ArgumentError, "bad size #{size}, must be in #{SIZES}") unless size.in?(SIZES)
|
34
|
+
|
35
|
+
%(govuk-table__caption--#{size})
|
36
|
+
end
|
37
|
+
end
|