dsfr-view-components 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|