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

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.
@@ -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