crown_marketplace_utils 0.1.0.beta.2 → 0.1.0.beta.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view'
4
+
5
+ module CrownMarketplaceUtils
6
+ module GovUkHelper
7
+ # = GOV.UK Header
8
+ #
9
+ # This helper is used for generating the header component from the
10
+ # {https://design-system.service.gov.uk/components/header GDS - Components - Header}
11
+
12
+ module Header
13
+ include ActionView::Context
14
+ include ActionView::Helpers::FormTagHelper
15
+ include ActionView::Helpers::TagHelper
16
+ include ActionView::Helpers::TextHelper
17
+ include ActionView::Helpers::UrlHelper
18
+
19
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
20
+
21
+ # Generates the HTML for the GOV.UK Header component
22
+ #
23
+ # @param govuk_header_options [Hash] options that will be used in customising the HTML
24
+ #
25
+ # @option govuk_header_options [String] :classes additional CSS classes for the header HTML
26
+ # @option govuk_header_options [Hash] :container_options (see: {govuk_header_container})
27
+ # @option govuk_header_options [Hash] :service_options (see: {govuk_header_service_name})
28
+ # @option govuk_header_options [Hash] :navigation_options (see: {govuk_header_navigation})
29
+ # @option govuk_header_options [Hash] :attributes ({data: { module: 'govuk-header' }})
30
+ # any additional attributes that will added as part of the HTML
31
+ #
32
+ # @return [ActiveSupport::SafeBuffer] the HTML for the GOV.UK Header
33
+ # which can then be rendered on the page
34
+
35
+ def govuk_header(**govuk_header_options)
36
+ govuk_header_classes = ['govuk-header']
37
+ govuk_header_classes << govuk_header_options[:classes]
38
+ govuk_header_options[:attributes] ||= {}
39
+ (govuk_header_options[:attributes][:data] ||= {}).merge!({ module: 'govuk-header' })
40
+
41
+ tag.header(class: govuk_header_classes, role: 'banner', **govuk_header_options[:attributes]) do
42
+ capture do
43
+ concat(govuk_header_container(govuk_header_options[:container_options] || {}))
44
+ if govuk_header_options[:service_options].present? || govuk_header_options[:navigation_options].present?
45
+ concat(tag.div(class: 'govuk-header__content') do
46
+ capture do
47
+ concat(govuk_header_service_name(service_options)) if govuk_header_options[:service_options].present?
48
+ concat(govuk_header_navigation(navigation_options)) if govuk_header_options[:navigation_options].present?
49
+ end
50
+ end)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ # rubocop:enable Metrics/CyclomaticComplexity
57
+
58
+ private
59
+
60
+ # Generates the header container with the logo for {govuk_header}
61
+ #
62
+ # @param container_options [Hash] options that will be used in customising the HTML
63
+ #
64
+ # @option container_options [String] :classes additional CSS classes for the container HTML
65
+ # @option container_options [String] :home_url the URL of the homepage. Defaults to +/+
66
+ # @option container_options [String] :product_name used when the product name follows on directly from ‘GOV.UK’
67
+ # @option container_options [Hash] :attributes ({}) any additional attributes that will added as part of the HTML
68
+ #
69
+ # @return [ActiveSupport::SafeBuffer] the HTML for the header container which is used in {govuk_header}
70
+
71
+ def govuk_header_container(container_options)
72
+ govuk_header_classes = ['govuk-header__container']
73
+ govuk_header_classes << (container_options[:classes] || 'govuk-width-container')
74
+
75
+ tag.div(class: govuk_header_classes) do
76
+ tag.div(class: 'govuk-header__logo') do
77
+ link_to(container_options[:home_url] || '/', class: 'govuk-header__link govuk-header__link--homepage') do
78
+ capture do
79
+ concat(tag.span(class: 'govuk-header__logotype') do
80
+ capture do
81
+ concat(tag.svg(class: 'govuk-header__logotype-crown', xmlns: 'http://www.w3.org/2000/svg', height: '30', width: '36', aria: { hidden: 'true' }, focusable: 'false', viewBox: '0 0 132 97') do
82
+ tag.path(fill: 'currentColor', 'fill-rule': 'evenodd', d: 'M25 30.2c3.5 1.5 7.7-.2 9.1-3.7 1.5-3.6-.2-7.8-3.9-9.2-3.6-1.4-7.6.3-9.1 3.9-1.4 3.5.3 7.5 3.9 9zM9 39.5c3.6 1.5 7.8-.2 9.2-3.7 1.5-3.6-.2-7.8-3.9-9.1-3.6-1.5-7.6.2-9.1 3.8-1.4 3.5.3 7.5 3.8 9zM4.4 57.2c3.5 1.5 7.7-.2 9.1-3.8 1.5-3.6-.2-7.7-3.9-9.1-3.5-1.5-7.6.3-9.1 3.8-1.4 3.5.3 7.6 3.9 9.1zm38.3-21.4c3.5 1.5 7.7-.2 9.1-3.8 1.5-3.6-.2-7.7-3.9-9.1-3.6-1.5-7.6.3-9.1 3.8-1.3 3.6.4 7.7 3.9 9.1zm64.4-5.6c-3.6 1.5-7.8-.2-9.1-3.7-1.5-3.6.2-7.8 3.8-9.2 3.6-1.4 7.7.3 9.2 3.9 1.3 3.5-.4 7.5-3.9 9zm15.9 9.3c-3.6 1.5-7.7-.2-9.1-3.7-1.5-3.6.2-7.8 3.7-9.1 3.6-1.5 7.7.2 9.2 3.8 1.5 3.5-.3 7.5-3.8 9zm4.7 17.7c-3.6 1.5-7.8-.2-9.2-3.8-1.5-3.6.2-7.7 3.9-9.1 3.6-1.5 7.7.3 9.2 3.8 1.3 3.5-.4 7.6-3.9 9.1zM89.3 35.8c-3.6 1.5-7.8-.2-9.2-3.8-1.4-3.6.2-7.7 3.9-9.1 3.6-1.5 7.7.3 9.2 3.8 1.4 3.6-.3 7.7-3.9 9.1zM69.7 17.7l8.9 4.7V9.3l-8.9 2.8c-.2-.3-.5-.6-.9-.9L72.4 0H59.6l3.5 11.2c-.3.3-.6.5-.9.9l-8.8-2.8v13.1l8.8-4.7c.3.3.6.7.9.9l-5 15.4v.1c-.2.8-.4 1.6-.4 2.4 0 4.1 3.1 7.5 7 8.1h.2c.3 0 .7.1 1 .1.4 0 .7 0 1-.1h.2c4-.6 7.1-4.1 7.1-8.1 0-.8-.1-1.7-.4-2.4V34l-5.1-15.4c.4-.2.7-.6 1-.9zM66 92.8c16.9 0 32.8 1.1 47.1 3.2 4-16.9 8.9-26.7 14-33.5l-9.6-3.4c1 4.9 1.1 7.2 0 10.2-1.5-1.4-3-4.3-4.2-8.7L108.6 76c2.8-2 5-3.2 7.5-3.3-4.4 9.4-10 11.9-13.6 11.2-4.3-.8-6.3-4.6-5.6-7.9 1-4.7 5.7-5.9 8-.5 4.3-8.7-3-11.4-7.6-8.8 7.1-7.2 7.9-13.5 2.1-21.1-8 6.1-8.1 12.3-4.5 20.8-4.7-5.4-12.1-2.5-9.5 6.2 3.4-5.2 7.9-2 7.2 3.1-.6 4.3-6.4 7.8-13.5 7.2-10.3-.9-10.9-8-11.2-13.8 2.5-.5 7.1 1.8 11 7.3L80.2 60c-4.1 4.4-8 5.3-12.3 5.4 1.4-4.4 8-11.6 8-11.6H55.5s6.4 7.2 7.9 11.6c-4.2-.1-8-1-12.3-5.4l1.4 16.4c3.9-5.5 8.5-7.7 10.9-7.3-.3 5.8-.9 12.8-11.1 13.8-7.2.6-12.9-2.9-13.5-7.2-.7-5 3.8-8.3 7.1-3.1 2.7-8.7-4.6-11.6-9.4-6.2 3.7-8.5 3.6-14.7-4.6-20.8-5.8 7.6-5 13.9 2.2 21.1-4.7-2.6-11.9.1-7.7 8.8 2.3-5.5 7.1-4.2 8.1.5.7 3.3-1.3 7.1-5.7 7.9-3.5.7-9-1.8-13.5-11.2 2.5.1 4.7 1.3 7.5 3.3l-4.7-15.4c-1.2 4.4-2.7 7.2-4.3 8.7-1.1-3-.9-5.3 0-10.2l-9.5 3.4c5 6.9 9.9 16.7 14 33.5 14.8-2.1 30.8-3.2 47.7-3.2z')
83
+ end)
84
+ concat(tag.span('GOV.UK', class: 'govuk-header__logotype-text'))
85
+ end
86
+ end)
87
+ concat(tag.span(container_options[:product_name], class: 'govuk-header__product-name')) if container_options[:product_name]
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ # rubocop:enable Metrics/AbcSize
95
+
96
+ # Generates the service name for {govuk_header}
97
+ #
98
+ # @param service_options [Hash] options that will be used in customising the HTML
99
+ #
100
+ # @option service_options [String] :service_name the name of your service, included in the header
101
+ # @option service_options [String] :service_url URL for the service name anchor
102
+ #
103
+ # @return [ActiveSupport::SafeBuffer] the HTML for the service name which is used in {govuk_header}
104
+
105
+ def govuk_header_service_name(service_options)
106
+ if service_options[:service_url]
107
+ link_to(service_options[:service_name], service_options[:service_url], class: 'govuk-header__link govuk-header__service-name')
108
+ else
109
+ tag.span(service_options[:service_name], class: 'govuk-header__service-name')
110
+ end
111
+ end
112
+
113
+ # Generates the navigation section for {govuk_header}
114
+ #
115
+ # @param navigation_options [Hash] options that will be used in customising the HTML
116
+ #
117
+ # @option navigation_options [String] :classes additional CSS classes for the navigation HTML
118
+ # @option navigation_options [String] :menu_button_text text of the button that opens the mobile navigation menu.
119
+ # By default, this is set to +'Menu'+.
120
+ # @option navigation_options [String] :menu_button_label text for the aria-label attribute of the button that opens the mobile navigation.
121
+ # Defaults to +'Show or hide menu'+.
122
+ # @option navigation_options [Array] :items the navigation items that will be rendered on the page (see {govuk_header_navigation_item})
123
+ #
124
+ # @return [ActiveSupport::SafeBuffer] the HTML for the navigation section which is used in {govuk_header}
125
+
126
+ def govuk_header_navigation(navigation_options)
127
+ menu_button_text = navigation_options[:menu_button_text] || 'Menu'
128
+ menu_button_label = navigation_options[:menu_button_label] || 'Show or hide menu'
129
+
130
+ govuk_header_navigation_classes = ['govuk-header__navigation']
131
+ govuk_header_navigation_classes << navigation_options[:classes]
132
+
133
+ tag.nav(class: govuk_header_navigation_classes, aria: { label: menu_button_text }) do
134
+ capture do
135
+ concat(button_tag(menu_button_text, class: 'govuk-header__menu-button govuk-js-header-toggle', type: 'button', hidden: true, aria: { controls: 'navigation', label: menu_button_label, }))
136
+ concat(tag.ul(id: 'navigation', class: 'govuk-header__navigation-list') do
137
+ capture do
138
+ navigation_options[:items].each do |navigation_item|
139
+ concat(govuk_header_navigation_item(navigation_item))
140
+ end
141
+ end
142
+ end)
143
+ end
144
+ end
145
+ end
146
+
147
+ # Generates a navigation item for {govuk_header_navigation}
148
+ #
149
+ # @param navigation_item [Hash] options that will be used in customising the HTML
150
+ #
151
+ # @option navigation_item [Boolean] :active flag to mark the navigation item as active or not
152
+ # @option navigation_item [String] :text text for the navigation item
153
+ # @option navigation_item [String] :href URL of the navigation item anchor
154
+ # @option navigation_item [Hash] :attributes ({}) any additional attributes that will added as part of the HTML
155
+ #
156
+ # @return [ActiveSupport::SafeBuffer] the HTML for a navigation item which is used in {govuk_header_navigation}
157
+
158
+ def govuk_header_navigation_item(navigation_item)
159
+ govuk_header_navigation_item_classes = ['govuk-header__navigation-item']
160
+ govuk_header_navigation_item_classes << 'govuk-header__navigation-item--active' if navigation_item[:active]
161
+
162
+ tag.li(class: govuk_header_navigation_item_classes) do
163
+ if navigation_item[:href]
164
+ link_to(navigation_item[:text], navigation_item[:href], class: 'govuk-header__link', **(navigation_item[:attributes] || {}))
165
+ else
166
+ concat(navigation_item[:text])
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view'
4
+
5
+ module CrownMarketplaceUtils
6
+ module GovUkHelper
7
+ # = GOV.UK label
8
+ #
9
+ # This helper is used for generating the label component from the Government Design Systems
10
+
11
+ module Label
12
+ include ActionView::Context
13
+ include ActionView::Helpers::TagHelper
14
+ include ActionView::Helpers::TextHelper
15
+ include ActionView::Helpers::FormTagHelper
16
+
17
+ # Generates the HTML for the GOV.UK label component
18
+ #
19
+ # @param attribute [String, Symbol] the attribute of the input that requires a label
20
+ # @param label_text [String] the label text, it is ignored if a block is given
21
+ # @param govuk_label_options [Hash] options that will be used in customising the HTML
22
+ #
23
+ # @option govuk_label_options [String] :classes additional CSS classes for the label HTML
24
+ # @option govuk_label_options [Boolean] :is_page_heading (false) if the legend is also the heading it will rendered in a h1
25
+ # @option govuk_label_options [Hash] :attributes ({}) any additional attributes that will added as part of the HTML
26
+ #
27
+ # @yield HTML that will be contained within the 'govuk-label' label
28
+ #
29
+ # @return [ActiveSupport::SafeBuffer] the HTML for the GOV.UK Label
30
+ # which can then be rendered on the page
31
+
32
+ def govuk_label(attribute, label_text = nil, **govuk_label_options)
33
+ _govuk_label(**govuk_label_options) do |govuk_label_classes, govuk_label_attributes|
34
+ label_tag(attribute, class: govuk_label_classes, **govuk_label_attributes) do
35
+ if block_given?
36
+ yield
37
+ else
38
+ concat(label_text)
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ # Generates the HTML for the GOV.UK label component using an ActionView::Helpers::FormBuilder
45
+ #
46
+ # @param form [ActionView::Helpers::FormBuilder] :form the form builder used to create the label
47
+ # @param attribute [String, Symbol] the attribute of the input that requires a label
48
+ # @param label_text [String] the label text, it is ignored if a block is given
49
+ # @param govuk_label_options [Hash] options that will be used in customising the HTML
50
+ #
51
+ # @option (see govuk_label)
52
+ #
53
+ # @yield (see govuk_label)
54
+ #
55
+ # @return (see govuk_label)
56
+
57
+ def govuk_label_with_form(form, attribute, label_text = nil, **govuk_label_options, &block)
58
+ _govuk_label(**govuk_label_options) do |govuk_label_classes, govuk_label_attributes|
59
+ if block_given?
60
+ form.label(attribute, class: govuk_label_classes, **govuk_label_attributes, &block)
61
+ else
62
+ form.label(attribute, label_text, class: govuk_label_classes, **govuk_label_attributes)
63
+ end
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ # Wrapper method used by {#govuk_label} and {#govuk_label_with_form} to generate the Label HTML
70
+ #
71
+ # @param govuk_label_options [Hash] options that will be used in customising the HTML
72
+ #
73
+ # @option (see govuk_label)
74
+ #
75
+ # @yield the label HTML generated by {#govuk_label} or {#govuk_label_with_form}
76
+ #
77
+ # @yieldparam govuk_label_classes [Array] the classes for the label HTML
78
+ # @yieldparam govuk_label_attributes [Hash] additional attributes that will added as part of the HTML
79
+ #
80
+ # @return (see govuk_label)
81
+
82
+ def _govuk_label(**govuk_label_options)
83
+ govuk_label_classes = ['govuk-label']
84
+ govuk_label_classes << govuk_label_options[:classes]
85
+ govuk_label_options[:attributes] ||= {}
86
+
87
+ label_html = yield(govuk_label_classes, govuk_label_options[:attributes])
88
+
89
+ if govuk_label_options[:is_page_heading]
90
+ tag.h1(label_html, class: 'govuk-label-wrapper')
91
+ else
92
+ label_html
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view'
4
+
5
+ module CrownMarketplaceUtils
6
+ module GovUkHelper
7
+ # = GOV.UK Notification Banner
8
+ #
9
+ # This helper is used for generating the notification banner component from the
10
+ # {https://design-system.service.gov.uk/components/notification-banner GDS - Components - Notification banner}
11
+
12
+ module NotificationBanner
13
+ include ActionView::Context
14
+ include ActionView::Helpers::TagHelper
15
+ include ActionView::Helpers::TextHelper
16
+
17
+ # Generates the HTML for the GOV.UK Notification banner component
18
+ #
19
+ # @param text [String] the text that will be used for the heading in the content section of the banner.
20
+ # It is ignored if a block is given
21
+ # @param success_banner [Boolean] will use the success banner options if this is set to true
22
+ # @param govuk_notification_banner_options [Hash] options that will be used in customising the HTML
23
+ #
24
+ # @option govuk_notification_banner_options [String] :classes additional CSS classes for the notification banner HTML
25
+ # @option govuk_notification_banner_options [String] :title_text ('Important') the title text shown at the top of the banner
26
+ # @option govuk_notification_banner_options [String] :title_id ('govuk-notification-banner-title') the ID for the title text
27
+ # @option govuk_notification_banner_options [String] :role ('region') the role for the banner
28
+ # @option govuk_notification_banner_options [String] :heading_level (2) the heading level for the title text
29
+ # @option govuk_notification_banner_options [Hash] :attributes ({data: { module: 'govuk-notification-banner' }, aria: { labelledby: 'govuk-notification-banner-title' }})
30
+ # any additional attributes that will added as part of the HTML
31
+ #
32
+ # @yield HTML that will be contained within the 'govuk-notification-banner__content' div
33
+ #
34
+ # @return [ActiveSupport::SafeBuffer] the HTML for the GOV.UK Notification banner
35
+ # which can then be rendered on the page
36
+
37
+ def govuk_notification_banner(text = nil, success_banner = false, **govuk_notification_banner_options)
38
+ banner_options = fetch_banner_options(success_banner, govuk_notification_banner_options)
39
+ govuk_notification_banner_classes = fetch_banner_classes(banner_options, govuk_notification_banner_options)
40
+ govuk_notification_banner_attributes = fetch_banner_attributes(banner_options, govuk_notification_banner_options)
41
+
42
+ tag.div(class: govuk_notification_banner_classes, role: banner_options[:role], **govuk_notification_banner_attributes) do
43
+ capture do
44
+ concat(tag.div(class: 'govuk-notification-banner__header') do
45
+ tag.send(:"h#{govuk_notification_banner_options[:heading_level] || 2}", banner_options[:title_text], class: 'govuk-notification-banner__title', id: banner_options[:title_id])
46
+ end)
47
+ concat(tag.div(class: 'govuk-notification-banner__content') do
48
+ if block_given?
49
+ yield
50
+ else
51
+ tag.p(text, class: 'govuk-notification-banner__heading')
52
+ end
53
+ end)
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ # Determines the banner options to be used for {#govuk_notification_banner}
61
+ #
62
+ # @param success_banner [Boolean] will use the success banner options if this is set to true
63
+ # @param govuk_notification_banner_options [Hash] options that will be used in customising the HTML
64
+ #
65
+ # @option govuk_notification_banner_options [String] :title_text ('Important') the title text shown at the top of the banner
66
+ # @option govuk_notification_banner_options [String] :title_id ('govuk-notification-banner-title') the ID for the title text
67
+ # @option govuk_notification_banner_options [String] :role ('region') the role for the banner
68
+ #
69
+ # @return [Hash] contains the following options used in {#govuk_notification_banner}:
70
+ # - +classes+
71
+ # - +title_text+
72
+ # - +title_id+
73
+ # - +role+
74
+
75
+ def fetch_banner_options(success_banner, govuk_notification_banner_options)
76
+ banner_options = DEFAULT_OPTIONS.dup
77
+
78
+ banner_options.merge!(SUCCESS_BANNER_OPTIONS) if success_banner
79
+
80
+ govuk_notification_banner_options.each do |key, value|
81
+ banner_options[key] = value if banner_options.key?(key)
82
+ end
83
+
84
+ banner_options
85
+ end
86
+
87
+ # Determines the banner classes to be used for {#govuk_notification_banner}
88
+ #
89
+ # @param banner_options [Hash] this is the return value from {#fetch_banner_options}
90
+ # @param govuk_notification_banner_options [Hash] options that will be used in customising the HTML
91
+ #
92
+ # @option govuk_notification_banner_options [String] :classes additional CSS classes for the notification banner HTML
93
+ #
94
+ # @return [Array<String>] an array of classes to be used in {#govuk_notification_banner}
95
+
96
+ def fetch_banner_classes(banner_options, govuk_notification_banner_options)
97
+ govuk_notification_banner_classes = ['govuk-notification-banner']
98
+ govuk_notification_banner_classes << govuk_notification_banner_options[:classes]
99
+ govuk_notification_banner_classes << banner_options[:classes]
100
+
101
+ govuk_notification_banner_classes
102
+ end
103
+
104
+ # Default HTML attributes for {#govuk_notification_banner}
105
+ #
106
+ # @param banner_options [Hash] this is the return value from {#fetch_banner_options}
107
+ # @param govuk_notification_banner_options [Hash] options that will be used in customising the HTML
108
+ #
109
+ # @option govuk_notification_banner_options [Hash] :attributes ({data: { module: 'govuk-notification-banner' }, aria: { labelledby: 'govuk-notification-banner-title' }})
110
+ # any additional attributes that will added as part of the HTML
111
+ #
112
+ # @return [Hash] contains the default attributes for {#govuk_notification_banner} and any additional attributes that were passed
113
+
114
+ def fetch_banner_attributes(banner_options, govuk_notification_banner_options)
115
+ govuk_notification_banner_attributes = govuk_notification_banner_options[:attributes] || {}
116
+ (govuk_notification_banner_attributes[:data] ||= {}).merge!({ module: 'govuk-notification-banner' })
117
+ (govuk_notification_banner_attributes[:aria] ||= {}).merge!({ labelledby: banner_options[:title_id] })
118
+
119
+ govuk_notification_banner_attributes
120
+ end
121
+
122
+ # Default options used in normal versions of {#govuk_notification_banner}
123
+
124
+ DEFAULT_OPTIONS = {
125
+ title_text: 'Important',
126
+ title_id: 'govuk-notification-banner-title',
127
+ role: 'region'
128
+ }.freeze
129
+
130
+ # Options specific for the success version of {#govuk_notification_banner}
131
+
132
+ SUCCESS_BANNER_OPTIONS = {
133
+ classes: 'govuk-notification-banner--success',
134
+ title_text: 'Success',
135
+ role: 'alert'
136
+ }.freeze
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_view'
4
+
5
+ module CrownMarketplaceUtils
6
+ module GovUkHelper
7
+ # = GOV.UK Pagination
8
+ #
9
+ # This helper is used for generating the pagination component from the
10
+ # {https://design-system.service.gov.uk/components/pagination GDS - Components - Pagination}
11
+
12
+ module Pagination
13
+ include ActionView::Context
14
+ include ActionView::Helpers::TagHelper
15
+ include ActionView::Helpers::TextHelper
16
+ include ActionView::Helpers::UrlHelper
17
+
18
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
19
+
20
+ # Generates the HTML for the GOV.UK Pagination component
21
+ #
22
+ # @param govuk_pagination_options [Hash] options that will be used in customising the HTML
23
+ #
24
+ # @option govuk_pagination_options [String] :classes additional CSS classes for the pagination HTML
25
+ # @option govuk_pagination_options [Array] :items an array of items for the pagination (see: {govuk_pagination_items})
26
+ # @option govuk_pagination_options [Hash] :previous the previous link (see: {govuk_pagination_previous})
27
+ # @option govuk_pagination_options [Hash] :next the next link (see: {govuk_pagination_next})
28
+ # @option govuk_pagination_options [Hash] :attributes ({role: 'navigation', aria: { label: 'results' }})
29
+ # any additional attributes that will added as part of the HTML
30
+ #
31
+ # @return [ActiveSupport::SafeBuffer] the HTML for the GOV.UK Pagination
32
+ # which can then be rendered on the page
33
+
34
+ def govuk_pagination(**govuk_pagination_options)
35
+ govuk_pagination_classes = ['govuk-pagination']
36
+ govuk_pagination_classes << govuk_pagination_options[:classes]
37
+
38
+ block_is_level = govuk_pagination_options[:items].blank? && (govuk_pagination_options[:next].present? || govuk_pagination_options[:previous].present?)
39
+
40
+ govuk_pagination_classes << 'govuk-pagination--block' if block_is_level
41
+
42
+ (govuk_pagination_options[:attributes] ||= {}).merge({ role: 'navigation' })
43
+ (govuk_pagination_options[:attributes][:aria] ||= {})[:label] ||= 'results'
44
+
45
+ tag.nav(class: govuk_pagination_classes, **govuk_pagination_options[:attributes]) do
46
+ capture do
47
+ concat(govuk_pagination_previous(block_is_level, **govuk_pagination_options[:previous])) if govuk_pagination_options[:previous]
48
+ concat(govuk_pagination_items(govuk_pagination_options[:items])) if govuk_pagination_options[:items]
49
+ concat(govuk_pagination_next(block_is_level, **govuk_pagination_options[:next])) if govuk_pagination_options[:next]
50
+ end
51
+ end
52
+ end
53
+
54
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
55
+
56
+ private
57
+
58
+ # Generates the previous link for {govuk_pagination}
59
+ #
60
+ # @param block_is_level [Boolean] when there are no items, this will be true and will add extra classe
61
+ # to the link to make the next and previous pagination links level
62
+ # @param govuk_pagination_previous_options [Hash] options that will be used in customising the HTML
63
+ #
64
+ # @option govuk_pagination_previous_options [String] :href the URL for the link
65
+ # @option govuk_pagination_previous_options [String] :text ('Previous') the text for the link
66
+ # @option govuk_pagination_previous_options [String] :lable_text additional text for the link when the pagination block is level
67
+ # @option govuk_pagination_previous_options [Hash] :attributes ({}) any additional attributes that will added as part of the HTML
68
+ #
69
+ # @return [ActiveSupport::SafeBuffer] the HTML for the previous link which is used in {govuk_pagination}
70
+
71
+ def govuk_pagination_previous(block_is_level, **govuk_pagination_previous_options)
72
+ govuk_pagination_previous_classes = ['govuk-pagination__link-title']
73
+ govuk_pagination_previous_classes << 'govuk-pagination__link-title--decorated' if block_is_level && !govuk_pagination_previous_options[:lable_text]
74
+
75
+ tag.div(class: 'govuk-pagination__prev') do
76
+ link_to(govuk_pagination_previous_options[:href], class: 'govuk-link govuk-pagination__link', rel: 'prev', **(govuk_pagination_previous_options[:attributes] || {})) do
77
+ capture do
78
+ concat(govuk_pagination_icon(:prev))
79
+ concat(tag.span(govuk_pagination_previous_options[:text] || 'Previous', class: govuk_pagination_previous_classes))
80
+ concat(govuk_pagination_icon_label_text(block_is_level, govuk_pagination_previous_options[:lable_text]))
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ # Generates the next link for {govuk_pagination}
87
+ #
88
+ # @param block_is_level [Boolean] when there are no items, this will be true and will add extra classe
89
+ # to the link to make the next and previous pagination links level
90
+ # @param govuk_pagination_next_options [Hash] options that will be used in customising the HTML
91
+ #
92
+ # @option govuk_pagination_next_options [String] :href the URL for the link
93
+ # @option govuk_pagination_next_options [String] :text ('Next') the text for the link
94
+ # @option govuk_pagination_next_options [String] :lable_text additional text for the link when the pagination block is level
95
+ # @option govuk_pagination_next_options [Hash] :attributes ({}) any additional attributes that will added as part of the HTML
96
+ #
97
+ # @return [ActiveSupport::SafeBuffer] the HTML for the next link which is used in {govuk_pagination}
98
+
99
+ def govuk_pagination_next(block_is_level, **govuk_pagination_next_options)
100
+ govuk_pagination_next_classes = ['govuk-pagination__link-title']
101
+ govuk_pagination_next_classes << 'govuk-pagination__link-title--decorated' if block_is_level && !govuk_pagination_next_options[:lable_text]
102
+
103
+ tag.div(class: 'govuk-pagination__next') do
104
+ link_to(govuk_pagination_next_options[:href], class: 'govuk-link govuk-pagination__link', rel: 'next', **(govuk_pagination_next_options[:attributes] || {})) do
105
+ capture do
106
+ concat(govuk_pagination_icon(:next)) if block_is_level
107
+ concat(tag.span(govuk_pagination_next_options[:text] || 'Next', class: govuk_pagination_next_classes))
108
+ concat(govuk_pagination_icon_label_text(block_is_level, govuk_pagination_next_options[:lable_text]))
109
+ concat(govuk_pagination_icon(:next)) unless block_is_level
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ # Generates the item links for {govuk_pagination}
116
+ #
117
+ # @param govuk_pagination_items [Array(Hash)] an array of item hashes for the pagination.
118
+ # There are two types of item:
119
+ # - {govuk_pagination_item_ellipsis ellipsis item}
120
+ # - {govuk_pagination_item_number number item}
121
+ #
122
+ # @return [ActiveSupport::SafeBuffer] the HTML for the lits of items which is used in {govuk_pagination}
123
+
124
+ def govuk_pagination_items(govuk_pagination_items)
125
+ tag.ul(class: 'govuk-pagination__list') do
126
+ capture do
127
+ govuk_pagination_items.each do |govuk_pagination_item|
128
+ case govuk_pagination_item[:type]
129
+ when :ellipsis
130
+ concat(govuk_pagination_item_ellipsis)
131
+ when :number
132
+ concat(govuk_pagination_item_number(govuk_pagination_item))
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ # Generates the icon for:
140
+ # - {govuk_pagination_previous}
141
+ # - {govuk_pagination_next}
142
+ #
143
+ # @param type [Symbol] the type of the pagination icon (+:prev+ or +:next+)
144
+ #
145
+ # @return [ActiveSupport::SafeBuffer]
146
+
147
+ def govuk_pagination_icon(type)
148
+ tag.svg(class: "govuk-pagination__icon govuk-pagination__icon--#{type}", xmlns: 'http://www.w3.org/2000/svg', height: '13', width: '15', aria: { hidden: 'true' }, focusable: 'false', viewBox: '0 0 15 13') do
149
+ tag.path(d: PAGINATION_ICON_PATHS[type])
150
+ end
151
+ end
152
+
153
+ # Generates the label text for:
154
+ # - {govuk_pagination_previous}
155
+ # - {govuk_pagination_next}
156
+ #
157
+ # @param block_is_level [Boolean] when there are no items, this will be true
158
+ # @param label_text [String] the additional text for the link
159
+ #
160
+ # @return [ActiveSupport::SafeBuffer]
161
+
162
+ def govuk_pagination_icon_label_text(block_is_level, label_text)
163
+ return unless block_is_level && label_text
164
+
165
+ capture do
166
+ concat(tag.span(':', class: 'govuk-visually-hidden'))
167
+ concat(tag.span(label_text, class: 'govuk-pagination__link-label'))
168
+ end
169
+ end
170
+
171
+ # Generates the ellipsis HTML for {govuk_pagination_items}
172
+ #
173
+ # @return [ActiveSupport::SafeBuffer] the HTML for an ellipsis item which is used in {govuk_pagination_items}
174
+
175
+ def govuk_pagination_item_ellipsis
176
+ tag.li('⋯', class: 'govuk-pagination__item govuk-pagination__item--ellipses')
177
+ end
178
+
179
+ # Generates the number item HTML for {govuk_pagination_items}
180
+ #
181
+ # @param govuk_pagination_item [Hash] options that will be used in customising the HTML
182
+ #
183
+ # @option govuk_pagination_item [String] :href the URL for the link
184
+ # @option govuk_pagination_item [String] :number the number for the link
185
+ # @option govuk_pagination_item [String] :current is this item the current page
186
+ # @option govuk_pagination_item [Hash] :attributes ({aria: { label: 'Page <NUMBER>' } })
187
+ # any additional attributes that will added as part of the HTML
188
+ #
189
+ # @return [ActiveSupport::SafeBuffer] the HTML for an number item which is used in {govuk_pagination_items}
190
+
191
+ def govuk_pagination_item_number(govuk_pagination_item)
192
+ govuk_pagination_item_classes = ['govuk-pagination__item']
193
+ (govuk_pagination_item[:attributes] ||= {})[:aria] ||= {}
194
+ govuk_pagination_item[:attributes][:aria][:label] ||= "Page #{govuk_pagination_item[:number]}"
195
+
196
+ if govuk_pagination_item[:current]
197
+ govuk_pagination_item_classes << 'govuk-pagination__item--current'
198
+ govuk_pagination_item[:attributes][:aria][:current] = 'page'
199
+ end
200
+
201
+ tag.li(class: govuk_pagination_item_classes) do
202
+ link_to(govuk_pagination_item[:href], govuk_pagination_item[:number], class: 'govuk-link govuk-pagination__link', **govuk_pagination_item[:attributes])
203
+ end
204
+ end
205
+
206
+ # The paths for the pagination next and previous icons
207
+
208
+ PAGINATION_ICON_PATHS = {
209
+ prev: 'm6.5938-0.0078125-6.7266 6.7266 6.7441 6.4062 1.377-1.449-4.1856-3.9768h12.896v-2h-12.984l4.2931-4.293-1.414-1.414z',
210
+ next: 'm8.107-0.0078125-1.4136 1.414 4.2926 4.293h-12.986v2h12.896l-4.1855 3.9766 1.377 1.4492 6.7441-6.4062-6.7246-6.7266z'
211
+ }.freeze
212
+ end
213
+ end
214
+ end