design_system 0.7.0 → 0.8.0
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 +4 -4
- data/README.md +3 -1
- data/app/controllers/concerns/design_system/branded.rb +7 -0
- data/app/helpers/design_system_helper.rb +0 -2
- data/app/views/layouts/govuk/application.html.erb +14 -25
- data/app/views/layouts/nhsuk/application.html.erb +24 -37
- data/app/views/nhsuk/_navigation.html.erb +9 -12
- data/lib/design_system/engine.rb +0 -3
- data/lib/design_system/generic/builders/base.rb +2 -1
- data/lib/design_system/generic/builders/button.rb +0 -2
- data/lib/design_system/generic/builders/fixed_elements.rb +0 -5
- data/lib/design_system/generic/builders/link.rb +0 -2
- data/lib/design_system/generic/builders/summary_list.rb +0 -3
- data/lib/design_system/generic/builders/tab.rb +0 -3
- data/lib/design_system/generic/builders/table.rb +0 -3
- data/lib/design_system/generic/form_builder.rb +2 -12
- data/lib/design_system/govuk/builders/button.rb +0 -2
- data/lib/design_system/govuk/builders/fixed_elements.rb +0 -4
- data/lib/design_system/govuk/builders/link.rb +0 -2
- data/lib/design_system/govuk/builders/summary_list.rb +0 -2
- data/lib/design_system/govuk/builders/tab.rb +0 -2
- data/lib/design_system/govuk/builders/table.rb +0 -2
- data/lib/design_system/govuk/form_builder.rb +0 -1
- data/lib/design_system/govuk/test_helpers/form_builder_assertions_helper.rb +99 -0
- data/lib/design_system/govuk/test_helpers/form_builder_testable.rb +1300 -0
- data/lib/design_system/helpers/css_helper.rb +20 -0
- data/lib/design_system/nhsuk/builders/fixed_elements.rb +0 -3
- data/lib/design_system/nhsuk/builders/summary_list.rb +0 -2
- data/lib/design_system/nhsuk/form_builder.rb +0 -2
- data/lib/design_system/version.rb +1 -1
- data/lib/design_system.rb +9 -0
- metadata +21 -5
- data/app/helpers/css_helper.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2df0ff381525e15513457d76f8d2a44852ee4e64fcca301ae96cdbfb207a98fc
|
4
|
+
data.tar.gz: 2a8c01fee4df51855a47a996f1061132f01335e7df72fe9911867a4c5f01a668
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fabfc45353d5c2148d201e98464277692146da0eecc46417aa7ad2ff46719fe00030823746cbeecbf63021702a85d5aafddfa12e267d671e8b3b786f4cd21a97
|
7
|
+
data.tar.gz: aa747e752e7f645c15c4dcd62c18005a0144a1530ddb16b99b2aeb1208d7f816c7dec27fedc4fd85acf975c14d76fe9da8b94a0d76c5879a481ec7208dc10bdb
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# DesignSystem
|
2
2
|
|
3
|
-
|
3
|
+
[](https://badge.fury.io/rb/design_system)
|
4
|
+
|
5
|
+
DesignSystem is an extensible Ruby on Rails engine that enables consistent, compliant web applications across design systems. It includes GOV.UK and NHS UK design systems and plugins are being developed for the National Disease Registration Service (NDRS), and Health Data Insight (HDI).
|
4
6
|
|
5
7
|
The gem provides unified form builders, UI components (tables, buttons, navigation, tabs), layouts, and styling that automatically adapt to each design system's specific requirements and branding guidelines.
|
6
8
|
|
@@ -5,6 +5,8 @@ module DesignSystem
|
|
5
5
|
|
6
6
|
included do
|
7
7
|
attr_reader :navigation_items
|
8
|
+
attr_reader :footer_links
|
9
|
+
attr_accessor :copyright_notice
|
8
10
|
|
9
11
|
helper DesignSystemHelper
|
10
12
|
end
|
@@ -17,5 +19,10 @@ module DesignSystem
|
|
17
19
|
@navigation_items ||= []
|
18
20
|
@navigation_items << { label:, path:, options: }
|
19
21
|
end
|
22
|
+
|
23
|
+
def add_footer_link(name, href, options = {})
|
24
|
+
@footer_links ||= []
|
25
|
+
@footer_links << { name:, href:, options: }
|
26
|
+
end
|
20
27
|
end
|
21
28
|
end
|
@@ -133,26 +133,15 @@
|
|
133
133
|
<div class="govuk-footer__meta-item govuk-footer__meta-item--grow">
|
134
134
|
<h2 class="govuk-visually-hidden">Support links</h2>
|
135
135
|
<ul class="govuk-footer__inline-list">
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
</li>
|
146
|
-
<li class="govuk-footer__inline-list-item">
|
147
|
-
<a class="govuk-footer__link" href="#">
|
148
|
-
Contact
|
149
|
-
</a>
|
150
|
-
</li>
|
151
|
-
<li class="govuk-footer__inline-list-item">
|
152
|
-
<a class="govuk-footer__link" href="#">
|
153
|
-
Terms and conditions
|
154
|
-
</a>
|
155
|
-
</li>
|
136
|
+
<% if controller.footer_links.present? %>
|
137
|
+
<% controller.footer_links.each do |link| %>
|
138
|
+
<li class="govuk-footer__inline-list-item">
|
139
|
+
<%= link_to link[:name],
|
140
|
+
link[:href],
|
141
|
+
{ class: "govuk-footer__link" }.merge(link[:options] || {}) %>
|
142
|
+
</li>
|
143
|
+
<% end %>
|
144
|
+
<% end %>
|
156
145
|
</ul>
|
157
146
|
<svg
|
158
147
|
aria-hidden="true"
|
@@ -175,11 +164,11 @@
|
|
175
164
|
</span>
|
176
165
|
</div>
|
177
166
|
<div class="govuk-footer__meta-item">
|
178
|
-
|
179
|
-
class="govuk-footer__link govuk-footer__copyright-logo"
|
180
|
-
|
181
|
-
|
182
|
-
|
167
|
+
<% if controller.copyright_notice.present? %>
|
168
|
+
<span class="govuk-footer__link govuk-footer__copyright-logo">
|
169
|
+
<%= controller.copyright_notice %>
|
170
|
+
</span>
|
171
|
+
<% end %>
|
183
172
|
</div>
|
184
173
|
</div>
|
185
174
|
</div>
|
@@ -86,46 +86,33 @@
|
|
86
86
|
</main>
|
87
87
|
</div>
|
88
88
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
<
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
<li class="nhsuk-footer__list-item nhsuk-footer-default__list-item">
|
112
|
-
<a class="nhsuk-footer__list-item-link" href="#">Accessibility statement</a>
|
113
|
-
</li>
|
114
|
-
<li class="nhsuk-footer__list-item nhsuk-footer-default__list-item">
|
115
|
-
<a class="nhsuk-footer__list-item-link" href="#">Our policies</a>
|
116
|
-
</li>
|
117
|
-
<li class="nhsuk-footer__list-item nhsuk-footer-default__list-item">
|
118
|
-
<a class="nhsuk-footer__list-item-link" href="#">Cookies</a>
|
119
|
-
</li>
|
120
|
-
</ul>
|
121
|
-
<div>
|
122
|
-
<p class="nhsuk-footer__copyright">© NHS England</p>
|
123
|
-
</div>
|
89
|
+
<footer role="contentinfo">
|
90
|
+
<div class="nhsuk-footer-container">
|
91
|
+
<div class="nhsuk-width-container">
|
92
|
+
<h2 class="nhsuk-u-visually-hidden">Support links</h2>
|
93
|
+
<div class="nhsuk-footer">
|
94
|
+
<ul class="nhsuk-footer__list">
|
95
|
+
<% if controller.footer_links.present? %>
|
96
|
+
<% controller.footer_links.each do |link| %>
|
97
|
+
<li class="nhsuk-footer__list-item">
|
98
|
+
<%= link_to link[:name],
|
99
|
+
link[:href],
|
100
|
+
{ class: "nhsuk-footer__list-item-link" }.merge(link[:options] || {}) %>
|
101
|
+
</li>
|
102
|
+
<% end %>
|
103
|
+
<% end %>
|
104
|
+
</ul>
|
105
|
+
<div>
|
106
|
+
<p class="nhsuk-footer__copyright">
|
107
|
+
<% if controller.copyright_notice.present? %>
|
108
|
+
<%= controller.copyright_notice %>
|
109
|
+
<% end %>
|
110
|
+
</p>
|
124
111
|
</div>
|
125
|
-
|
126
112
|
</div>
|
127
113
|
</div>
|
128
|
-
</
|
114
|
+
</div>
|
115
|
+
</footer>
|
129
116
|
|
130
117
|
</body>
|
131
118
|
|
@@ -7,18 +7,15 @@
|
|
7
7
|
<%= link_to(item[:label], item[:path], item[:options].merge(class: 'nhsuk-header__navigation-link')) %>
|
8
8
|
</li>
|
9
9
|
<% end %>
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
</svg>
|
20
|
-
</button>
|
21
|
-
</li>
|
10
|
+
<li class="nhsuk-mobile-menu-container">
|
11
|
+
<button class="nhsuk-header__menu-toggle nhsuk-header__navigation-link" id="toggle-menu" aria-expanded="false">
|
12
|
+
<span class="nhsuk-u-visually-hidden">Browse</span>
|
13
|
+
More
|
14
|
+
<svg class="nhsuk-icon nhsuk-icon__chevron-down" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
15
|
+
<path d="M15.5 12a1 1 0 0 1-.29.71l-5 5a1 1 0 0 1-1.42-1.42l4.3-4.29-4.3-4.29a1 1 0 0 1 1.42-1.42l5 5a1 1 0 0 1 .29.71z"></path>
|
16
|
+
</svg>
|
17
|
+
</button>
|
18
|
+
</li>
|
22
19
|
</ul>
|
23
20
|
</nav>
|
24
21
|
</div>
|
data/lib/design_system/engine.rb
CHANGED
@@ -4,9 +4,6 @@ require 'stimulus-rails'
|
|
4
4
|
module DesignSystem
|
5
5
|
# This is the main engine class for the design system.
|
6
6
|
class Engine < ::Rails::Engine
|
7
|
-
# Allow changes to the design system to be reloaded in development.
|
8
|
-
config.autoload_paths << File.expand_path('..', __dir__) if Rails.env.development?
|
9
|
-
|
10
7
|
initializer 'design_system.importmap', before: 'importmap' do |app|
|
11
8
|
app.config.importmap.paths << Engine.root.join('config/importmap.rb')
|
12
9
|
end
|
@@ -5,8 +5,9 @@ module DesignSystem
|
|
5
5
|
module Builders
|
6
6
|
# This is the base class for design system builders.
|
7
7
|
class Base
|
8
|
-
include CssHelper
|
9
8
|
include DesignSystem::Generic::Builders::Concerns::BrandDerivable
|
9
|
+
include DesignSystem::Helpers::CssHelper
|
10
|
+
|
10
11
|
delegate :button_tag, :capture, :content_for, :content_tag, :link_to, :link_to_unless_current, to: :@context
|
11
12
|
|
12
13
|
def initialize(context)
|
@@ -2,6 +2,8 @@ module DesignSystem
|
|
2
2
|
module Generic
|
3
3
|
# The generic version of the form builder
|
4
4
|
class FormBuilder < ActionView::Helpers::FormBuilder
|
5
|
+
include DesignSystem::Helpers::CssHelper
|
6
|
+
|
5
7
|
delegate :content_tag, :tag, :safe_join, :link_to, :capture, to: :@template
|
6
8
|
|
7
9
|
def self.brand
|
@@ -10,18 +12,6 @@ module DesignSystem
|
|
10
12
|
|
11
13
|
private
|
12
14
|
|
13
|
-
# Helper copied from https://github.com/NHSDigital/ndr_ui/blob/main/app/helpers/ndr_ui/css_helper.rb with thanks.
|
14
|
-
# This method merges the specified css_classes into the options hash
|
15
|
-
def css_class_options_merge(options, css_classes = [])
|
16
|
-
options = options.symbolize_keys
|
17
|
-
css_classes += options[:class].split if options.include?(:class)
|
18
|
-
yield(css_classes) if block_given?
|
19
|
-
options[:class] = css_classes.join(' ') unless css_classes.empty?
|
20
|
-
raise "Multiple css class definitions: #{css_classes.inspect}" unless css_classes == css_classes.uniq
|
21
|
-
|
22
|
-
options
|
23
|
-
end
|
24
|
-
|
25
15
|
# This method exposes some useful Rails magic as a helper method
|
26
16
|
def separate_content_or_options(content_or_options = nil, options = nil)
|
27
17
|
content = nil
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module DesignSystem
|
2
|
+
module Govuk
|
3
|
+
module TestHelpers
|
4
|
+
# This module provides assertion methods for form elements using the GOV.UK Design System
|
5
|
+
module FormBuilderAssertionsHelper
|
6
|
+
# Asserts the presence and attributes of a file upload input
|
7
|
+
def assert_file_upload(field = nil, type: nil, value: nil, classes: [], attributes: {}, model: 'assistant')
|
8
|
+
assert_form_element('input', 'file-upload', field, type:, value:, classes:, attributes:, model:)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Assert the presence of a form group
|
12
|
+
def assert_form_group(classes = [])
|
13
|
+
assert_select('form') do
|
14
|
+
form_group = assert_select("div.#{@brand}-form-group#{classes.map { |c| ".#{c}" }.join}").first
|
15
|
+
assert form_group, "Form group not found with classes: #{classes.join(', ')}"
|
16
|
+
yield(form_group) if block_given?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Asserts the presence and attributes of a hint
|
21
|
+
def assert_hint(field = nil, value = nil, text = nil, classes: [], model: 'assistant')
|
22
|
+
base_class = "div.#{@brand}-hint"
|
23
|
+
class_selector = base_class + classes.map { |c| ".#{c}" }.join
|
24
|
+
|
25
|
+
selector = if value
|
26
|
+
"#{class_selector}[id='#{model}_#{field}_#{value}_hint']"
|
27
|
+
else
|
28
|
+
"#{class_selector}[id='#{model}_#{field}_hint']"
|
29
|
+
end
|
30
|
+
|
31
|
+
assert_select(selector, text)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Asserts the presence and attributes of an input field
|
35
|
+
def assert_input(field = nil, type: nil, value: nil, classes: [], attributes: {}, model: 'assistant')
|
36
|
+
assert_form_element('input', 'input', field, type:, value:, classes:, attributes:, model:)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Asserts the presence and attributes of a label
|
40
|
+
# TODO: support special labels like checkbox_label?
|
41
|
+
def assert_label(field = nil, value = nil, text = nil, model: 'assistant', classes: [])
|
42
|
+
selector = if value
|
43
|
+
"label.#{@brand}-label[for='#{model}_#{field}_#{value}']"
|
44
|
+
else
|
45
|
+
"label.#{@brand}-label[for='#{model}_#{field}']"
|
46
|
+
end
|
47
|
+
selector << classes.map { |c| ".#{c}" }.join
|
48
|
+
assert_select(selector, text)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Asserts the presence and attributes of a text area
|
52
|
+
def assert_text_area(field = nil, value: nil, classes: [], attributes: {}, model: 'assistant')
|
53
|
+
assert_form_element('textarea', 'textarea', field, value:, classes:, attributes:, model:)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
# Asserts the presence and attributes of a form element
|
59
|
+
#
|
60
|
+
# @param element_type [Symbol] The type of HTML element to assert (:input, :textarea, :file_upload)
|
61
|
+
# @param base_class [String] The class name of the element in the design system, prefixed by the brand name (e.g., 'input', 'textarea', 'file-upload')
|
62
|
+
# @param field [Symbol] The form field name (e.g., :title, :description)
|
63
|
+
# @param options [Hash] Additional options for the element
|
64
|
+
# @option options [String] :type The HTML input type (e.g., 'text', 'email', 'tel')
|
65
|
+
# @option options [String] :value The expected value of the field
|
66
|
+
# @option options [Array<String>] :classes Additional CSS classes to check for
|
67
|
+
# @option options [Hash] :attributes Additional HTML attributes to verify
|
68
|
+
# @option options [String] :model The model name for the field (defaults to 'assistant')
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# assert_form_element(:input, :title, type: :text, value: 'Lorem ipsum dolor sit amet')
|
72
|
+
# assert_form_element(:textarea, :description, classes: ['custom-class'])
|
73
|
+
# assert_form_element(:file_upload, :cv, type: :file, attributes: { accept: 'application/pdf' })
|
74
|
+
def assert_form_element(element_type, base_class, field, options = {})
|
75
|
+
base_classes = ["#{@brand}-#{base_class}"]
|
76
|
+
classes = (base_classes + Array(options[:classes])).flatten.compact
|
77
|
+
|
78
|
+
attributes = {
|
79
|
+
id: "#{options[:model] || 'assistant'}_#{field}",
|
80
|
+
name: "#{options[:model] || 'assistant'}[#{field}]"
|
81
|
+
}.merge(options[:attributes] || {})
|
82
|
+
|
83
|
+
# Build the selector with each class as a separate class attribute
|
84
|
+
class_selector = classes.map { |c| ".#{c}" }.join
|
85
|
+
element = assert_select("#{element_type}#{class_selector}").first
|
86
|
+
assert element,
|
87
|
+
"#{element_type.capitalize} not found with type: #{options[:type]} and classes: #{classes.join(', ')}"
|
88
|
+
|
89
|
+
attributes.each do |key, expected_value|
|
90
|
+
assert_equal expected_value.to_s, element[key.to_s],
|
91
|
+
"Expected #{key} to be '#{expected_value}' but was '#{element[key.to_s]}'"
|
92
|
+
end
|
93
|
+
|
94
|
+
assert_equal options[:value], element['value'] if options[:value]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|