primer_view_components 0.0.60 → 0.0.61
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/CHANGELOG.md +71 -1
- data/app/components/primer/alpha/layout.html.erb +5 -0
- data/app/components/primer/alpha/layout.rb +276 -0
- data/app/components/primer/base_button.rb +1 -5
- data/app/components/primer/base_component.rb +7 -2
- data/app/components/primer/beta/blankslate.html.erb +15 -0
- data/app/components/primer/beta/blankslate.rb +240 -0
- data/app/components/primer/blankslate_component.rb +1 -1
- data/app/components/primer/component.rb +2 -2
- data/app/components/primer/hellip_button.rb +39 -0
- data/app/components/primer/hidden_text_expander.rb +18 -6
- data/app/components/primer/subhead_component.rb +1 -1
- data/lib/primer/classify.rb +3 -3
- data/lib/primer/view_components/engine.rb +1 -1
- data/lib/primer/view_components/linters/base_linter.rb +3 -52
- data/lib/primer/view_components/linters/blankslate_api_migration.rb +146 -0
- data/lib/primer/view_components/linters/blankslate_component_migration_counter.rb +1 -1
- data/lib/primer/view_components/linters/close_button_component_migration_counter.rb +2 -4
- data/lib/primer/view_components/linters/helpers/rubocop_helpers.rb +14 -0
- data/lib/primer/view_components/linters/tag_tree_helpers.rb +61 -0
- data/lib/primer/view_components/linters/two_column_layout_migration_counter.rb +158 -0
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/tasks/docs.rake +50 -1
- data/static/arguments.yml +52 -67
- data/static/audited_at.json +5 -0
- data/static/classes.yml +20 -2
- data/static/constants.json +103 -0
- data/static/statuses.json +6 -1
- metadata +11 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61a72c86047d4820bb909bb7aba73505a1401868e362831ce3cfcd44f6c65da2
|
4
|
+
data.tar.gz: cedb04ce42976410ad380e5d39e448fc2078f07fb3d26b3d2ce4e7d79141f6ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 714f04473ef45b3a80c4fcad50dbcd575a981c93b990f770844e903d561dc016cc88a94b09567deaa9954680add33581bc002c6f80b3a3590d2e64faf9397c63
|
7
|
+
data.tar.gz: 8d982a43c2fd2a0d5fe32287c129c8d4559ae2441e3124f9f50857ccbf25e2a4a3b63426e6f5a1c24572d6d8804b90849a3e600e3680de3efdbc59c92de0293e
|
data/CHANGELOG.md
CHANGED
@@ -30,6 +30,76 @@ The category for changes related to documentation, testing and tooling. Also, fo
|
|
30
30
|
|
31
31
|
## main
|
32
32
|
|
33
|
+
## 0.0.61
|
34
|
+
|
35
|
+
### New
|
36
|
+
|
37
|
+
* Adding new Alpha component: `Layout` with `main` and `sidebar` slots
|
38
|
+
|
39
|
+
*Cameron Dutro*
|
40
|
+
|
41
|
+
* Add a two-column layout linter.
|
42
|
+
|
43
|
+
*Cameron Dutro*
|
44
|
+
|
45
|
+
* Add the `HellipButton` component
|
46
|
+
|
47
|
+
*Amélia Chavot*, *Owen Niblock*
|
48
|
+
|
49
|
+
### Updates
|
50
|
+
|
51
|
+
* Bump Storybook version to include Skip to Content links for keyboard auditors
|
52
|
+
|
53
|
+
*Katie Foster @inkblotty*
|
54
|
+
|
55
|
+
* Update the `HiddenTextExpander` component to use the `HellipButton`.
|
56
|
+
|
57
|
+
*Amélia Chavot*, *Owen Niblock*
|
58
|
+
|
59
|
+
### Misc
|
60
|
+
|
61
|
+
* Fix components not rendering in Storybook because of kebab case arguments.
|
62
|
+
|
63
|
+
*Amélia Chavot*, *Manuel Puyol*, *Owen Niblock*
|
64
|
+
|
65
|
+
* Fix a typo on a command on the contribution page.
|
66
|
+
|
67
|
+
*Amélia Chavot*, *Owen Niblock*
|
68
|
+
|
69
|
+
### Bug Fixes
|
70
|
+
|
71
|
+
* Fix issue where tags were not self-closing when they are void elements
|
72
|
+
|
73
|
+
*Owen Niblock*
|
74
|
+
|
75
|
+
### Deprecations
|
76
|
+
|
77
|
+
* Deprecate `Primer::BlankslateComponent` in favor of `Primer::Beta::Blankslate`.
|
78
|
+
|
79
|
+
*Manuel Puyol*
|
80
|
+
|
81
|
+
### Breaking Changes
|
82
|
+
|
83
|
+
* Require an `aria-label` to be provided for the `HiddenTextExpander` component.
|
84
|
+
|
85
|
+
*Amélia Chavot*, *Owen Niblock*
|
86
|
+
|
87
|
+
* Rename `force_system_arguments` to `raise_on_invalid_options` to better reflect its functionality
|
88
|
+
|
89
|
+
*Owen Niblock*
|
90
|
+
|
91
|
+
* Renamed `Blankslate` `title` slot to `heading`.
|
92
|
+
|
93
|
+
*Manuel Puyol*
|
94
|
+
|
95
|
+
* Removed `Blankslate` `large` variant.
|
96
|
+
|
97
|
+
*Manuel Puyol*
|
98
|
+
|
99
|
+
* Renamed `Blankslate` `graphic` slot to `visual`.
|
100
|
+
|
101
|
+
*Manuel Puyol*
|
102
|
+
|
33
103
|
## 0.0.60
|
34
104
|
|
35
105
|
### Updates
|
@@ -535,7 +605,7 @@ The category for changes related to documentation, testing and tooling. Also, fo
|
|
535
605
|
|
536
606
|
*Manuel Puyol*
|
537
607
|
|
538
|
-
*
|
608
|
+
* Add a changelog authoring guide to `CHANGELOG.md`.
|
539
609
|
|
540
610
|
*Amélia Chavot*
|
541
611
|
|
@@ -0,0 +1,276 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Alpha
|
5
|
+
# `Layout` provides foundational patterns for responsive pages.
|
6
|
+
# `Layout` can be used for simple two-column pages, or it can be nested to provide flexible 3-column experiences.
|
7
|
+
# On smaller screens, `Layout` uses vertically stacked rows to display content.
|
8
|
+
#
|
9
|
+
# `Layout` flows as both column, when there's enough horizontal space to render both `Main` and `Sidebar`side-by-side (on a desktop of tablet device, per instance);
|
10
|
+
# or it flows as a row, when `Main` and `Sidebar` are stacked vertically (e.g. on a mobile device).
|
11
|
+
# `Layout` should always work in any screen size.
|
12
|
+
#
|
13
|
+
# @accessibility
|
14
|
+
# Keyboard navigation follows the markup order. Decide carefully how the focus order should be be by deciding whether
|
15
|
+
# `main` or `sidebar` comes first in code. The code order won’t affect the visual position.
|
16
|
+
class Layout < Primer::Component
|
17
|
+
FIRST_IN_SOURCE_DEFAULT = :sidebar
|
18
|
+
FIRST_IN_SOURCE_OPTIONS = [FIRST_IN_SOURCE_DEFAULT, :main].freeze
|
19
|
+
|
20
|
+
SIDEBAR_COL_PLACEMENT_DEFAULT = :start
|
21
|
+
SIDEBAR_COL_PLACEMENT_OPTIONS = [SIDEBAR_COL_PLACEMENT_DEFAULT, :end].freeze
|
22
|
+
|
23
|
+
GUTTER_DEFAULT = :default
|
24
|
+
GUTTER_MAPPINGS = {
|
25
|
+
:none => "Layout--gutter-none",
|
26
|
+
:condensed => "Layout--gutter-condensed",
|
27
|
+
:spacious => "Layout--gutter-spacious",
|
28
|
+
GUTTER_DEFAULT => ""
|
29
|
+
}.freeze
|
30
|
+
GUTTER_OPTIONS = GUTTER_MAPPINGS.keys.freeze
|
31
|
+
|
32
|
+
STACKING_BREAKPOINT_DEFAULT = :md
|
33
|
+
STACKING_BREAKPOINT_MAPPINGS = {
|
34
|
+
:sm => "",
|
35
|
+
STACKING_BREAKPOINT_DEFAULT => "Layout--flowRow-until-md",
|
36
|
+
:lg => "Layout--flowRow-until-lg"
|
37
|
+
}.freeze
|
38
|
+
STACKING_BREAKPOINT_OPTIONS = STACKING_BREAKPOINT_MAPPINGS.keys.freeze
|
39
|
+
|
40
|
+
SIDEBAR_ROW_PLACEMENT_DEFAULT = :start
|
41
|
+
SIDEBAR_ROW_PLACEMENT_OPTIONS = [SIDEBAR_ROW_PLACEMENT_DEFAULT, :end, :none].freeze
|
42
|
+
|
43
|
+
SIDEBAR_WIDTH_DEFAULT = :default
|
44
|
+
SIDEBAR_WIDTH_MAPPINGS = {
|
45
|
+
SIDEBAR_WIDTH_DEFAULT => "",
|
46
|
+
:narrow => "Layout--sidebar-narrow",
|
47
|
+
:wide => "Layout--sidebar-wide"
|
48
|
+
}.freeze
|
49
|
+
SIDEBAR_WIDTH_OPTIONS = SIDEBAR_WIDTH_MAPPINGS.keys.freeze
|
50
|
+
|
51
|
+
# The layout's main content.
|
52
|
+
#
|
53
|
+
# @param width [Symbol] <%= one_of(Primer::Alpha::Layout::Main::WIDTH_OPTIONS) %>
|
54
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
55
|
+
renders_one :main, "Primer::Alpha::Layout::Main"
|
56
|
+
|
57
|
+
# The layout's sidebar.
|
58
|
+
#
|
59
|
+
# @param width [Symbol] <%= one_of(Primer::Alpha::Layout::SIDEBAR_WIDTH_OPTIONS) %>
|
60
|
+
# @param col_placement [Symbol] Sidebar placement when `Layout` is in column modes. <%= one_of(Primer::Alpha::Layout::SIDEBAR_COL_PLACEMENT_OPTIONS) %>
|
61
|
+
# @param row_placement [Symbol] Sidebar placement when `Layout` is in row mode. <%= one_of(Primer::Alpha::Layout::SIDEBAR_ROW_PLACEMENT_OPTIONS) %>
|
62
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
63
|
+
renders_one :sidebar, lambda { |
|
64
|
+
width: SIDEBAR_WIDTH_DEFAULT,
|
65
|
+
col_placement: SIDEBAR_COL_PLACEMENT_DEFAULT,
|
66
|
+
row_placement: SIDEBAR_ROW_PLACEMENT_DEFAULT,
|
67
|
+
**system_arguments
|
68
|
+
|
|
69
|
+
# These classes have to be set in the parent `Layout` element, so we modify its system arguments.
|
70
|
+
@system_arguments[:classes] = class_names(
|
71
|
+
@system_arguments[:classes],
|
72
|
+
"Layout--sidebarPosition-#{fetch_or_fallback(SIDEBAR_COL_PLACEMENT_OPTIONS, col_placement, SIDEBAR_COL_PLACEMENT_DEFAULT)}",
|
73
|
+
"Layout--sidebarPosition-flowRow-#{fetch_or_fallback(SIDEBAR_ROW_PLACEMENT_OPTIONS, row_placement, SIDEBAR_ROW_PLACEMENT_DEFAULT)}",
|
74
|
+
SIDEBAR_WIDTH_MAPPINGS[fetch_or_fallback(SIDEBAR_WIDTH_OPTIONS, width, SIDEBAR_WIDTH_DEFAULT)]
|
75
|
+
)
|
76
|
+
|
77
|
+
Primer::Alpha::Layout::Sidebar.new(**system_arguments)
|
78
|
+
}
|
79
|
+
|
80
|
+
# @example Default
|
81
|
+
#
|
82
|
+
# <%= render(Primer::Alpha::Layout.new) do |c| %>
|
83
|
+
# <% c.main(border: true) { "Main" } %>
|
84
|
+
# <% c.sidebar(border: true) { "Sidebar" } %>
|
85
|
+
# <% end %>
|
86
|
+
#
|
87
|
+
# @example Main widths
|
88
|
+
#
|
89
|
+
# @description
|
90
|
+
# When `full`, the main column will stretch to cover all the available width.
|
91
|
+
# Otherwise, the main column will try to be centered in the screen; it may appear aligned to the left when there isn't enough space.
|
92
|
+
#
|
93
|
+
# Use smaller maximum widths in the main column to facilitate interface scanning and reading.
|
94
|
+
#
|
95
|
+
# When flowing as a row, `Main` takes the full width.
|
96
|
+
#
|
97
|
+
# @code
|
98
|
+
# <%= render(Primer::Alpha::Layout.new) do |c| %>
|
99
|
+
# <% c.main(width: :full, border: true) { "Main" } %>
|
100
|
+
# <% c.sidebar(border: true) { "Sidebar" } %>
|
101
|
+
# <% end %>
|
102
|
+
# <%= render(Primer::Alpha::Layout.new(mt: 5)) do |c| %>
|
103
|
+
# <% c.main(width: :md, border: true) { "Main" } %>
|
104
|
+
# <% c.sidebar(border: true) { "Sidebar" } %>
|
105
|
+
# <% end %>
|
106
|
+
# <%= render(Primer::Alpha::Layout.new(mt: 5)) do |c| %>
|
107
|
+
# <% c.main(width: :lg, border: true) { "Main" } %>
|
108
|
+
# <% c.sidebar(border: true) { "Sidebar" } %>
|
109
|
+
# <% end %>
|
110
|
+
# <%= render(Primer::Alpha::Layout.new(mt: 5)) do |c| %>
|
111
|
+
# <% c.main(width: :xl, border: true) { "Main" } %>
|
112
|
+
# <% c.sidebar(border: true) { "Sidebar" } %>
|
113
|
+
# <% end %>
|
114
|
+
#
|
115
|
+
# @example Sidebar widths
|
116
|
+
#
|
117
|
+
# @description
|
118
|
+
# Sets the sidebar width. The width is predetermined according to the breakpoint instead of it being percentage-based.
|
119
|
+
#
|
120
|
+
# - `default`: [md: 256px, lg: 296px, xl: 320px]
|
121
|
+
# - `narrow`: [md: 240px, lg: 256px, xl: 296px]
|
122
|
+
# - `wide`: [md: 296px, lg: 320px, xl: 344px]
|
123
|
+
#
|
124
|
+
# When flowing as a row, `Sidebar` takes the full width.
|
125
|
+
#
|
126
|
+
# @code
|
127
|
+
# <%= render(Primer::Alpha::Layout.new) do |c| %>
|
128
|
+
# <% c.main(border: true) { "Main" } %>
|
129
|
+
# <% c.sidebar(width: :default, border: true) { "Sidebar" } %>
|
130
|
+
# <% end %>
|
131
|
+
# <%= render(Primer::Alpha::Layout.new(mt: 5)) do |c| %>
|
132
|
+
# <% c.main(border: true) { "Main" } %>
|
133
|
+
# <% c.sidebar(width: :narrow, border: true) { "Sidebar" } %>
|
134
|
+
# <% end %>
|
135
|
+
# <%= render(Primer::Alpha::Layout.new(mt: 5)) do |c| %>
|
136
|
+
# <% c.main(border: true) { "Main" } %>
|
137
|
+
# <% c.sidebar(width: :wide, border: true) { "Sidebar" } %>
|
138
|
+
# <% end %>
|
139
|
+
#
|
140
|
+
# @example Sidebar placement
|
141
|
+
#
|
142
|
+
# @description
|
143
|
+
# Use `start` for sidebars that manipulate local navigation, while right-aligned `end` is useful for metadata and other auxiliary information.
|
144
|
+
#
|
145
|
+
# @code
|
146
|
+
# <%= render(Primer::Alpha::Layout.new) do |c| %>
|
147
|
+
# <% c.main(border: true) { "Main" } %>
|
148
|
+
# <% c.sidebar(col_placement: :start, border: true) { "Sidebar" } %>
|
149
|
+
# <% end %>
|
150
|
+
# <%= render(Primer::Alpha::Layout.new( mt: 5)) do |c| %>
|
151
|
+
# <% c.main(border: true) { "Main" } %>
|
152
|
+
# <% c.sidebar(col_placement: :end, border: true) { "Sidebar" } %>
|
153
|
+
# <% end %>
|
154
|
+
#
|
155
|
+
# @example Sidebar placement as row
|
156
|
+
#
|
157
|
+
# @description
|
158
|
+
# When flowing as a row, whether the sidebar is rendered first or last in the layout, or, if it's entirely hidden from the user.
|
159
|
+
#
|
160
|
+
# When `hidden`, make sure the experience is not degraded on smaller screens, and the user can still access the sidebar content somehow.
|
161
|
+
# For instance, the user may not see a Settings navigation sidebar when drilled down on a page, but they can still navigate to the Settings
|
162
|
+
# landing page to interact with the local navigation.
|
163
|
+
#
|
164
|
+
# @code
|
165
|
+
# <%= render(Primer::Alpha::Layout.new) do |c| %>
|
166
|
+
# <% c.main(border: true) { "Main" } %>
|
167
|
+
# <% c.sidebar(row_placement: :start, border: true) { "Sidebar" } %>
|
168
|
+
# <% end %>
|
169
|
+
# <%= render(Primer::Alpha::Layout.new(mt: 5)) do |c| %>
|
170
|
+
# <% c.main(border: true) { "Main" } %>
|
171
|
+
# <% c.sidebar(row_placement: :end, border: true) { "Sidebar" } %>
|
172
|
+
# <% end %>
|
173
|
+
# <%= render(Primer::Alpha::Layout.new(mt: 5)) do |c| %>
|
174
|
+
# <% c.main(border: true) { "Main" } %>
|
175
|
+
# <% c.sidebar(row_placement: :none, border: true) { "Sidebar" } %>
|
176
|
+
# <% end %>
|
177
|
+
#
|
178
|
+
# @example Changing when to render `Layout` as columns
|
179
|
+
#
|
180
|
+
# @description
|
181
|
+
# You can specify when the `Layout` should change from rows into columns.
|
182
|
+
# Any screen size before this breakpoint will render the `Layout` in stacked rows.
|
183
|
+
#
|
184
|
+
# @code
|
185
|
+
# <%= render(Primer::Alpha::Layout.new(stacking_breakpoint: :sm)) do |c| %>
|
186
|
+
# <% c.main(border: true) { "Main" } %>
|
187
|
+
# <% c.sidebar(border: true) { "Sidebar" } %>
|
188
|
+
# <% end %>
|
189
|
+
# <%= render(Primer::Alpha::Layout.new(stacking_breakpoint: :md, mt: 5)) do |c| %>
|
190
|
+
# <% c.main(border: true) { "Main" } %>
|
191
|
+
# <% c.sidebar(border: true) { "Sidebar" } %>
|
192
|
+
# <% end %>
|
193
|
+
# <%= render(Primer::Alpha::Layout.new(stacking_breakpoint: :lg, mt: 5)) do |c| %>
|
194
|
+
# <% c.main(border: true) { "Main" } %>
|
195
|
+
# <% c.sidebar(border: true) { "Sidebar" } %>
|
196
|
+
# <% end %>
|
197
|
+
#
|
198
|
+
# @param stacking_breakpoint [Symbol] When the `Layout` should change from rows into columns. <%= one_of(Primer::Alpha::Layout::STACKING_BREAKPOINT_OPTIONS) %>
|
199
|
+
# @param first_in_source [Symbol] Which element to render first in the HTML. This will change the keyboard navigation order. <%= one_of(Primer::Alpha::Layout::FIRST_IN_SOURCE_OPTIONS) %>
|
200
|
+
# @param gutter [Symbol] The amount of space between the main section and the sidebar. <%= one_of(Primer::Alpha::Layout::GUTTER_OPTIONS) %>
|
201
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
202
|
+
def initialize(stacking_breakpoint: STACKING_BREAKPOINT_DEFAULT, first_in_source: FIRST_IN_SOURCE_DEFAULT, gutter: :default, **system_arguments)
|
203
|
+
@first_in_source = fetch_or_fallback(FIRST_IN_SOURCE_OPTIONS, first_in_source, FIRST_IN_SOURCE_OPTIONS)
|
204
|
+
|
205
|
+
@system_arguments = system_arguments
|
206
|
+
@system_arguments[:tag] = :div
|
207
|
+
@system_arguments[:classes] = class_names(
|
208
|
+
"Layout",
|
209
|
+
STACKING_BREAKPOINT_MAPPINGS[fetch_or_fallback(STACKING_BREAKPOINT_OPTIONS, stacking_breakpoint, STACKING_BREAKPOINT_DEFAULT)],
|
210
|
+
GUTTER_MAPPINGS[fetch_or_fallback(GUTTER_OPTIONS, gutter, GUTTER_DEFAULT)],
|
211
|
+
system_arguments[:classes]
|
212
|
+
)
|
213
|
+
end
|
214
|
+
|
215
|
+
def render?
|
216
|
+
main.present? && sidebar.present?
|
217
|
+
end
|
218
|
+
|
219
|
+
# The layout's main content.
|
220
|
+
class Main < Primer::Component
|
221
|
+
WIDTH_DEFAULT = :full
|
222
|
+
WIDTH_OPTIONS = [WIDTH_DEFAULT, :md, :lg, :xl].freeze
|
223
|
+
|
224
|
+
TAG_DEFAULT = :div
|
225
|
+
TAG_OPTIONS = [TAG_DEFAULT, :main].freeze
|
226
|
+
|
227
|
+
# @param width [Symbol] <%= one_of(Primer::Alpha::Layout::Main::WIDTH_OPTIONS) %>
|
228
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
229
|
+
def initialize(tag: TAG_DEFAULT, width: WIDTH_DEFAULT, **system_arguments)
|
230
|
+
@width = fetch_or_fallback(WIDTH_OPTIONS, width, WIDTH_DEFAULT)
|
231
|
+
|
232
|
+
@system_arguments = system_arguments
|
233
|
+
@system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, TAG_DEFAULT)
|
234
|
+
@system_arguments[:classes] = class_names(
|
235
|
+
"Layout-main",
|
236
|
+
system_arguments[:classes]
|
237
|
+
)
|
238
|
+
end
|
239
|
+
|
240
|
+
def call
|
241
|
+
render(Primer::BaseComponent.new(**@system_arguments)) do
|
242
|
+
if @width == :full
|
243
|
+
content
|
244
|
+
else
|
245
|
+
render(Primer::BaseComponent.new(tag: :div, classes: "Layout-main-centered-#{@width}")) do
|
246
|
+
render(Primer::BaseComponent.new(tag: :div, container: @width)) do
|
247
|
+
content
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
# The layout's sidebar content.
|
256
|
+
class Sidebar < Primer::Component
|
257
|
+
TAG_DEFAULT = :div
|
258
|
+
TAG_OPTIONS = [TAG_DEFAULT, :aside, :nav, :section].freeze
|
259
|
+
|
260
|
+
def initialize(tag: TAG_DEFAULT, **system_arguments)
|
261
|
+
@system_arguments = system_arguments
|
262
|
+
|
263
|
+
@system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, TAG_DEFAULT)
|
264
|
+
@system_arguments[:classes] = class_names(
|
265
|
+
"Layout-sidebar",
|
266
|
+
@system_arguments[:classes]
|
267
|
+
)
|
268
|
+
end
|
269
|
+
|
270
|
+
def call
|
271
|
+
render(Primer::BaseComponent.new(**@system_arguments)) { content }
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
@@ -28,11 +28,7 @@ module Primer
|
|
28
28
|
@system_arguments = system_arguments
|
29
29
|
@system_arguments[:tag] = fetch_or_fallback(TAG_OPTIONS, tag, DEFAULT_TAG)
|
30
30
|
|
31
|
-
if @system_arguments[:tag] == :button
|
32
|
-
@system_arguments[:type] = fetch_or_fallback(TYPE_OPTIONS, type, DEFAULT_TYPE)
|
33
|
-
else
|
34
|
-
@system_arguments[:role] = :button
|
35
|
-
end
|
31
|
+
@system_arguments[:type] = fetch_or_fallback(TYPE_OPTIONS, type, DEFAULT_TYPE) if @system_arguments[:tag] == :button
|
36
32
|
|
37
33
|
@system_arguments[:classes] = class_names(
|
38
34
|
system_arguments[:classes],
|
@@ -27,6 +27,7 @@ module Primer
|
|
27
27
|
class BaseComponent < Primer::Component
|
28
28
|
status :beta
|
29
29
|
|
30
|
+
SELF_CLOSING_TAGS = [:area, :base, :br, :col, :embed, :hr, :img, :input, :link, :meta, :param, :source, :track, :wbr].freeze
|
30
31
|
# ## HTML attributes
|
31
32
|
#
|
32
33
|
# System arguments include most HTML attributes. For example:
|
@@ -155,7 +156,7 @@ module Primer
|
|
155
156
|
raise ArgumentError, "`class` is an invalid argument. Use `classes` instead." if system_arguments.key?(:class) && !Rails.env.production?
|
156
157
|
|
157
158
|
if (denylist = system_arguments[:system_arguments_denylist])
|
158
|
-
if
|
159
|
+
if raise_on_invalid_options? && !ENV["PRIMER_WARNINGS_DISABLED"]
|
159
160
|
# Convert denylist from:
|
160
161
|
# { [:p, :pt] => "message" } to:
|
161
162
|
# { p: "message", pt: "message" }
|
@@ -189,7 +190,11 @@ module Primer
|
|
189
190
|
end
|
190
191
|
|
191
192
|
def call
|
192
|
-
|
193
|
+
if SELF_CLOSING_TAGS.include?(@tag)
|
194
|
+
tag(@tag, @content_tag_args.merge(@result))
|
195
|
+
else
|
196
|
+
content_tag(@tag, content, @content_tag_args.merge(@result))
|
197
|
+
end
|
193
198
|
end
|
194
199
|
end
|
195
200
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<%= wrapper do %>
|
2
|
+
<%= render Primer::BaseComponent.new(**@system_arguments) do %>
|
3
|
+
<%= visual %>
|
4
|
+
|
5
|
+
<%= heading %>
|
6
|
+
<%= description %>
|
7
|
+
|
8
|
+
<%= primary_action %>
|
9
|
+
<% if secondary_action.present? %>
|
10
|
+
<p>
|
11
|
+
<%= secondary_action %>
|
12
|
+
</p>
|
13
|
+
<% end %>
|
14
|
+
<% end %>
|
15
|
+
<% end %>
|
@@ -0,0 +1,240 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
module Beta
|
5
|
+
# Use `Blankslate` when there is a lack of content within a page or section. Use as placeholder to tell users why something isn't there.
|
6
|
+
#
|
7
|
+
# @accessibility
|
8
|
+
# - Set the `heading` level based on what is appropriate for your page hierarchy. <%= link_to_heading_practices %>
|
9
|
+
# - `secondary_action` can be set to provide more information that is relevant in the context of the `Blankslate`.
|
10
|
+
# - `secondary_action` text should be meaningful out of context and clearly describe the destination. Avoid using vague text like, "Learn more" or "Click here".
|
11
|
+
class Blankslate < Primer::Component
|
12
|
+
include ViewComponent::PolymorphicSlots
|
13
|
+
|
14
|
+
status :beta
|
15
|
+
|
16
|
+
VISUAL_OPTIONS = %i[icon spinner image].freeze
|
17
|
+
|
18
|
+
# Optional visual.
|
19
|
+
#
|
20
|
+
# Use:
|
21
|
+
#
|
22
|
+
# - `visual_icon` for an <%= link_to_component(Primer::OcticonComponent) %>.
|
23
|
+
# - `visual_image` for an <%= link_to_component(Primer::Image) %>.
|
24
|
+
# - `visual_spinner` for a <%= link_to_component(Primer::SpinnerComponent) %>.
|
25
|
+
#
|
26
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
27
|
+
renders_one :visual, types: {
|
28
|
+
icon: lambda { |**system_arguments|
|
29
|
+
system_arguments[:mb] = 3
|
30
|
+
system_arguments[:size] ||= :medium
|
31
|
+
system_arguments[:classes] = class_names("blankslate-icon", system_arguments[:classes])
|
32
|
+
|
33
|
+
Primer::OcticonComponent.new(**system_arguments)
|
34
|
+
},
|
35
|
+
spinner: lambda { |**system_arguments|
|
36
|
+
system_arguments[:mb] = 3
|
37
|
+
|
38
|
+
Primer::SpinnerComponent.new(**system_arguments)
|
39
|
+
},
|
40
|
+
image: lambda { |**system_arguments|
|
41
|
+
system_arguments[:mb] = 3
|
42
|
+
system_arguments[:size] = "56x56"
|
43
|
+
|
44
|
+
Primer::Image.new(**system_arguments)
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
# Required heading.
|
49
|
+
#
|
50
|
+
# @param tag [String] <%= one_of(Primer::HeadingComponent::TAG_OPTIONS) %>
|
51
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
52
|
+
renders_one :heading, lambda { |tag:, **system_arguments|
|
53
|
+
system_arguments[:tag] = tag
|
54
|
+
system_arguments[:mb] = 1
|
55
|
+
system_arguments[:classes] = class_names("h2", system_arguments[:classes])
|
56
|
+
|
57
|
+
Primer::HeadingComponent.new(**system_arguments)
|
58
|
+
}
|
59
|
+
|
60
|
+
# Optional description.
|
61
|
+
#
|
62
|
+
# - The description should always be informative and actionable.
|
63
|
+
# - Don't use phrases like "You can".
|
64
|
+
#
|
65
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
66
|
+
renders_one :description, lambda { |**system_arguments|
|
67
|
+
system_arguments[:tag] = :p
|
68
|
+
|
69
|
+
Primer::BaseComponent.new(**system_arguments)
|
70
|
+
}
|
71
|
+
|
72
|
+
# Optional primary action
|
73
|
+
#
|
74
|
+
# The `primary_action` slot renders an anchor link which is visually styled as a button to provide more emphasis to the
|
75
|
+
# Blankslate's primary action.
|
76
|
+
#
|
77
|
+
# @param href [String] URL to be used for the primary action.
|
78
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
79
|
+
renders_one :primary_action, lambda { |href:, **system_arguments|
|
80
|
+
system_arguments[:tag] = :a
|
81
|
+
system_arguments[:href] = href
|
82
|
+
system_arguments[:my] = 3
|
83
|
+
system_arguments[:variant] = :medium
|
84
|
+
system_arguments[:scheme] ||= :primary
|
85
|
+
|
86
|
+
Primer::ButtonComponent.new(**system_arguments)
|
87
|
+
}
|
88
|
+
|
89
|
+
# Optional secondary action
|
90
|
+
#
|
91
|
+
# The `secondary_action` slot renders a normal anchor link, which can be used to redirect the user to additional information
|
92
|
+
# (e.g. Help documentation).
|
93
|
+
#
|
94
|
+
# @param href [String] URL to be used for the secondary action.
|
95
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
96
|
+
renders_one :secondary_action, lambda { |href:, **system_arguments|
|
97
|
+
system_arguments[:href] = href
|
98
|
+
# Padding is needed to increase touch area.
|
99
|
+
system_arguments[:p] = 3
|
100
|
+
|
101
|
+
Primer::LinkComponent.new(**system_arguments)
|
102
|
+
}
|
103
|
+
|
104
|
+
# @example Basic
|
105
|
+
# <%= render Primer::Beta::Blankslate.new do |c| %>
|
106
|
+
# <% c.heading(tag: :h2).with_content("Title") %>
|
107
|
+
# <% c.description { "Description"} %>
|
108
|
+
# <% end %>
|
109
|
+
#
|
110
|
+
# @example Icon
|
111
|
+
# @description
|
112
|
+
# Add an `icon` to give additional context. Refer to the [Octicons](https://primer.style/octicons/) documentation to choose an icon.
|
113
|
+
# @code
|
114
|
+
# <%= render Primer::Beta::Blankslate.new do |c| %>
|
115
|
+
# <% c.visual_icon(icon: :globe) %>
|
116
|
+
# <% c.heading(tag: :h2).with_content("Title") %>
|
117
|
+
# <% c.description { "Description"} %>
|
118
|
+
# <% end %>
|
119
|
+
#
|
120
|
+
# @example Loading
|
121
|
+
# @description
|
122
|
+
# Add a [SpinnerComponent](https://primer.style/view-components/components/spinner) to the blankslate in place of an icon.
|
123
|
+
# @code
|
124
|
+
# <%= render Primer::Beta::Blankslate.new do |c| %>
|
125
|
+
# <% c.visual_spinner(size: :large) %>
|
126
|
+
# <% c.heading(tag: :h2).with_content("Title") %>
|
127
|
+
# <% c.description { "Description"} %>
|
128
|
+
# <% end %>
|
129
|
+
#
|
130
|
+
# @example Using an image
|
131
|
+
# @description
|
132
|
+
# Add an `image` to give context that an Octicon couldn't.
|
133
|
+
# @code
|
134
|
+
# <%= render Primer::Beta::Blankslate.new do |c| %>
|
135
|
+
# <% c.visual_image(src: "https://github.githubassets.com/images/modules/site/features/security-icon.svg", alt: "Security - secure vault") %>
|
136
|
+
# <% c.heading(tag: :h2).with_content("Title") %>
|
137
|
+
# <% c.description { "Description"} %>
|
138
|
+
# <% end %>
|
139
|
+
#
|
140
|
+
# @example Custom content
|
141
|
+
# @description
|
142
|
+
# Pass custom content to `description`.
|
143
|
+
# @code
|
144
|
+
# <%= render Primer::Beta::Blankslate.new do |c| %>
|
145
|
+
# <% c.heading(tag: :h2).with_content("Title") %>
|
146
|
+
# <% c.description do %>
|
147
|
+
# <em>Your custom content here</em>
|
148
|
+
# <% end %>
|
149
|
+
# <% end %>
|
150
|
+
#
|
151
|
+
# @example Primary action
|
152
|
+
# @description
|
153
|
+
# Provide a `primary_action` to guide users to take action from the blankslate. The `primary_action` appears below the description and custom content.
|
154
|
+
# @code
|
155
|
+
# <%= render Primer::Beta::Blankslate.new do |c| %>
|
156
|
+
# <% c.visual_icon(icon: :book) %>
|
157
|
+
# <% c.heading(tag: :h2).with_content("Welcome to the mona wiki!") %>
|
158
|
+
# <% c.description { "Wikis provide a place in your repository to lay out the roadmap of your project, show the current status, and document software better, together."} %>
|
159
|
+
# <% c.primary_action(href: "https://github.com/monalisa/mona/wiki/_new").with_content("Create the first page") %>
|
160
|
+
# <% end %>
|
161
|
+
#
|
162
|
+
# @example Secondary action
|
163
|
+
# @description
|
164
|
+
# Add an additional `secondary_action` to help users learn more about a feature. See <%= link_to_accessibility %>. `secondary_action` will be shown at the very bottom:
|
165
|
+
# @code
|
166
|
+
# <%= render Primer::Beta::Blankslate.new do |c| %>
|
167
|
+
# <% c.visual_icon(icon: :book) %>
|
168
|
+
# <% c.heading(tag: :h2).with_content("Welcome to the mona wiki!") %>
|
169
|
+
# <% c.description { "Wikis provide a place in your repository to lay out the roadmap of your project, show the current status, and document software better, together."} %>
|
170
|
+
# <% c.secondary_action(href: "https://docs.github.com/en/github/building-a-strong-community/about-wikis").with_content("Learn more about wikis") %>
|
171
|
+
# <% end %>
|
172
|
+
#
|
173
|
+
# @example Primary and secondary actions
|
174
|
+
# @description
|
175
|
+
# `primary_action` and `secondary_action` can also be used together. The `primary_action` will always be rendered before the `secondary_action`:
|
176
|
+
# @code
|
177
|
+
# <%= render Primer::Beta::Blankslate.new do |c| %>
|
178
|
+
# <% c.visual_icon(icon: :book) %>
|
179
|
+
# <% c.heading(tag: :h2).with_content("Welcome to the mona wiki!") %>
|
180
|
+
# <% c.description { "Wikis provide a place in your repository to lay out the roadmap of your project, show the current status, and document software better, together."} %>
|
181
|
+
# <% c.primary_action(href: "https://github.com/monalisa/mona/wiki/_new").with_content("Create the first page") %>
|
182
|
+
# <% c.secondary_action(href: "https://docs.github.com/en/github/building-a-strong-community/about-wikis").with_content("Learn more about wikis") %>
|
183
|
+
# <% end %>
|
184
|
+
#
|
185
|
+
# @example Variations
|
186
|
+
# @description
|
187
|
+
# There are a few variations of how the Blankslate appears: `narrow` adds a maximum width of `485px`, and `spacious` increases the padding from `32px` to `80px 40px`.
|
188
|
+
# @code
|
189
|
+
# <%= render Primer::Beta::Blankslate.new(
|
190
|
+
# narrow: true,
|
191
|
+
# spacious: true,
|
192
|
+
# ) do |c| %>
|
193
|
+
# <% c.visual_icon(icon: :book) %>
|
194
|
+
# <% c.heading(tag: :h2).with_content("Welcome to the mona wiki!") %>
|
195
|
+
# <% c.description { "Wikis provide a place in your repository to lay out the roadmap of your project, show the current status, and document software better, together."} %>
|
196
|
+
# <% end %>
|
197
|
+
#
|
198
|
+
# @example With border
|
199
|
+
# @description
|
200
|
+
# It's possible to add a border around the Blankslate. This will wrap the Blankslate in a BorderBox.
|
201
|
+
# @code
|
202
|
+
# <%= render Primer::Beta::Blankslate.new(border: true) do |c| %>
|
203
|
+
# <% c.visual_icon(icon: :book) %>
|
204
|
+
# <% c.heading(tag: :h2).with_content("Welcome to the mona wiki!") %>
|
205
|
+
# <% c.description { "Wikis provide a place in your repository to lay out the roadmap of your project, show the current status, and document software better, together."} %>
|
206
|
+
# <% end %>
|
207
|
+
#
|
208
|
+
# @param narrow [Boolean] Adds a maximum width of `485px` to the Blankslate.
|
209
|
+
# @param spacious [Boolean] Increases the padding from `32px` to `80px 40px`.
|
210
|
+
# @param border [Boolean] Adds a border around the Blankslate.
|
211
|
+
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
212
|
+
def initialize(narrow: false, spacious: false, border: false, **system_arguments)
|
213
|
+
@border = border
|
214
|
+
@system_arguments = system_arguments
|
215
|
+
@system_arguments[:tag] = :div
|
216
|
+
@system_arguments[:classes] = class_names(
|
217
|
+
@system_arguments[:classes],
|
218
|
+
"blankslate",
|
219
|
+
"blankslate-narrow": narrow,
|
220
|
+
"blankslate-spacious": spacious
|
221
|
+
)
|
222
|
+
end
|
223
|
+
|
224
|
+
def render?
|
225
|
+
heading.present?
|
226
|
+
end
|
227
|
+
|
228
|
+
def wrapper
|
229
|
+
unless @border
|
230
|
+
yield
|
231
|
+
return # returning `yield` caused a double render
|
232
|
+
end
|
233
|
+
|
234
|
+
content_tag(:div, class: "Box") do
|
235
|
+
yield if block_given?
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|