openproject-primer_view_components 0.73.1 → 0.74.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/CHANGELOG.md +8 -0
- data/app/assets/javascripts/components/primer/alpha/tree_view/tree_view.d.ts +6 -2
- data/app/assets/javascripts/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.d.ts +2 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/assets/styles/primer_view_components.css +1 -1
- data/app/assets/styles/primer_view_components.css.map +1 -1
- data/app/components/primer/alpha/tree_view/node.html.erb +5 -1
- data/app/components/primer/alpha/tree_view/node.rb +7 -1
- data/app/components/primer/alpha/tree_view/sub_tree_node.rb +1 -1
- data/app/components/primer/alpha/tree_view/tree_view.d.ts +6 -2
- data/app/components/primer/alpha/tree_view/tree_view.js +52 -2
- data/app/components/primer/alpha/tree_view/tree_view.ts +67 -3
- data/app/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.d.ts +2 -0
- data/app/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.js +11 -0
- data/app/components/primer/alpha/tree_view/tree_view_sub_tree_node_element.ts +12 -0
- data/app/components/primer/alpha/tree_view.css +1 -1
- data/app/components/primer/alpha/tree_view.css.json +1 -0
- data/app/components/primer/alpha/tree_view.css.map +1 -1
- data/app/components/primer/alpha/tree_view.pcss +7 -0
- data/app/components/primer/alpha/tree_view.rb +15 -1
- data/app/components/primer/open_project/filterable_tree_view/sub_tree.rb +14 -4
- data/app/components/primer/open_project/filterable_tree_view.rb +26 -2
- data/lib/primer/view_components/version.rb +2 -2
- data/previews/primer/alpha/tree_view_preview/default.html.erb +1 -1
- data/previews/primer/alpha/tree_view_preview/form_input.html.erb +4 -4
- data/previews/primer/alpha/tree_view_preview/multi_select.html.erb +10 -0
- data/previews/primer/alpha/tree_view_preview/single_select.html.erb +10 -0
- data/previews/primer/alpha/tree_view_preview.rb +38 -4
- data/previews/primer/open_project/filterable_tree_view_preview/form_input.html.erb +15 -15
- data/previews/primer/open_project/filterable_tree_view_preview/playground.html.erb +15 -15
- data/previews/primer/open_project/filterable_tree_view_preview.rb +6 -2
- data/static/arguments.json +1 -1
- data/static/constants.json +1 -0
- data/static/info_arch.json +28 -2
- data/static/previews.json +26 -0
- metadata +4 -2
@@ -230,19 +230,43 @@ module Primer
|
|
230
230
|
end
|
231
231
|
|
232
232
|
def with_sub_tree(**system_arguments, &block)
|
233
|
+
system_arguments[:select_variant] ||= :multiple
|
234
|
+
|
235
|
+
if system_arguments[:select_variant] != :multiple && system_arguments[:select_variant] != :single
|
236
|
+
raise ArgumentError, "FilterableTreeView only supports `:multiple` or `:single` as select_variant"
|
237
|
+
end
|
238
|
+
|
239
|
+
if system_arguments[:select_variant] == :single
|
240
|
+
# In single selection, the include sub-items checkbox and the SegmentedControl make no sense
|
241
|
+
@include_sub_items_check_box_arguments[:hidden] = true
|
242
|
+
@include_sub_items_check_box_arguments[:checked] = false
|
243
|
+
@filter_mode_control_arguments[:hidden] = true
|
244
|
+
end
|
245
|
+
|
233
246
|
@tree_view.with_sub_tree(
|
234
247
|
sub_tree_component_klass: SubTree,
|
235
248
|
**system_arguments,
|
236
|
-
select_variant: :multiple,
|
237
249
|
select_strategy: :self,
|
238
250
|
&block
|
239
251
|
)
|
240
252
|
end
|
241
253
|
|
242
254
|
def with_leaf(**system_arguments, &block)
|
255
|
+
system_arguments[:select_variant] ||= :multiple
|
256
|
+
|
257
|
+
if system_arguments[:select_variant] != :multiple && system_arguments[:select_variant] != :single
|
258
|
+
raise ArgumentError, "FilterableTreeView only supports `:multiple` or `:single` as select_variant"
|
259
|
+
end
|
260
|
+
|
261
|
+
if system_arguments[:select_variant] == :single
|
262
|
+
# In single selection, the include sub-items checkbox and the SegmentedControl make no sense
|
263
|
+
@include_sub_items_check_box_arguments[:hidden] = true
|
264
|
+
@include_sub_items_check_box_arguments[:checked] = false
|
265
|
+
@filter_mode_control_arguments[:hidden] = true
|
266
|
+
end
|
267
|
+
|
243
268
|
@tree_view.with_leaf(
|
244
269
|
**system_arguments,
|
245
|
-
select_variant: :multiple,
|
246
270
|
&block
|
247
271
|
)
|
248
272
|
end
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<% icons.with_collapsed_icon(icon: :"file-directory-fill", color: :accent) %>
|
7
7
|
<% end %>
|
8
8
|
|
9
|
-
<% sub_tree.with_trailing_visual_icon(icon: :"diff-modified") %>
|
9
|
+
<% sub_tree.with_trailing_visual_icon(icon: :"diff-modified") unless select_variant == :single %>
|
10
10
|
|
11
11
|
<% sub_tree.with_leaf(label: "button.rb", disabled: disabled, select_variant: select_variant) do |item| %>
|
12
12
|
<% item.with_leading_visual_icon(icon: :file) %>
|
@@ -1,12 +1,12 @@
|
|
1
1
|
<%= form_with(url: primer_view_components.generic_form_submission_path(format: :json)) do |f| %>
|
2
2
|
<%= render(Primer::Alpha::Stack.new) do %>
|
3
3
|
<%= render(Primer::Alpha::TreeView.new(form_arguments: { builder: f, name: "folder_structure" })) do |tree| %>
|
4
|
-
<% tree.with_sub_tree(label: "src", expanded: expanded, select_variant:
|
5
|
-
<% sub_tree.with_leaf(label: "button.rb", select_variant:
|
6
|
-
<% sub_tree.with_leaf(label: "icon_button.rb", current: true, select_variant:
|
4
|
+
<% tree.with_sub_tree(label: "src", expanded: expanded, select_variant: select_variant, value: 0) do |sub_tree| %>
|
5
|
+
<% sub_tree.with_leaf(label: "button.rb", select_variant: select_variant, value: 1) %>
|
6
|
+
<% sub_tree.with_leaf(label: "icon_button.rb", current: true, select_variant: select_variant, value: 2) %>
|
7
7
|
<% end %>
|
8
8
|
|
9
|
-
<% tree.with_leaf(label: "action_menu.rb", select_variant:
|
9
|
+
<% tree.with_leaf(label: "action_menu.rb", select_variant: select_variant, value: 3) %>
|
10
10
|
<% end %>
|
11
11
|
|
12
12
|
<%= render(Primer::Alpha::SubmitButton.new(name: :submit, label: "Submit")) %>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<div style="max-width: 400px">
|
2
|
+
<%= render(Primer::Alpha::TreeView.new) do |tree_view| %>
|
3
|
+
<% tree_view.with_sub_tree(label: "src", expanded: expanded, disabled: disabled, select_variant: select_variant) do |sub_tree| %>
|
4
|
+
<% sub_tree.with_leaf(label: "button.rb", disabled: disabled, select_variant: select_variant) %>
|
5
|
+
<% sub_tree.with_leaf(label: "icon_button.rb", current: true, disabled: disabled, select_variant: select_variant) %>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<% tree_view.with_leaf(label: "action_menu.rb", disabled: disabled, select_variant: select_variant) %>
|
9
|
+
<% end %>
|
10
|
+
</div>
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<div style="max-width: 400px">
|
2
|
+
<%= render(Primer::Alpha::TreeView.new) do |tree_view| %>
|
3
|
+
<% tree_view.with_sub_tree(label: "src", expanded: expanded, disabled: disabled, select_variant: select_variant) do |sub_tree| %>
|
4
|
+
<% sub_tree.with_leaf(label: "button.rb", disabled: disabled, select_variant: select_variant) %>
|
5
|
+
<% sub_tree.with_leaf(label: "icon_button.rb", current: true, disabled: disabled, select_variant: select_variant) %>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<% tree_view.with_leaf(label: "action_menu.rb", disabled: disabled, select_variant: select_variant) %>
|
9
|
+
<% end %>
|
10
|
+
</div>
|
@@ -9,7 +9,7 @@ module Primer
|
|
9
9
|
# @snapshot interactive
|
10
10
|
# @param expanded [Boolean] toggle
|
11
11
|
# @param disabled [Boolean] toggle
|
12
|
-
# @param select_variant [Symbol] select [multiple, none]
|
12
|
+
# @param select_variant [Symbol] select [multiple, single, none]
|
13
13
|
# @param select_strategy [Symbol] select [self, descendants, mixed_descendants]
|
14
14
|
def default(
|
15
15
|
expanded: false,
|
@@ -28,7 +28,7 @@ module Primer
|
|
28
28
|
# @label Playground
|
29
29
|
#
|
30
30
|
# @param expanded [Boolean] toggle
|
31
|
-
# @param select_variant [Symbol] select [multiple, none]
|
31
|
+
# @param select_variant [Symbol] select [multiple, single, none]
|
32
32
|
# @param select_strategy [Symbol] select [self, descendants, mixed_descendants]
|
33
33
|
def playground(
|
34
34
|
expanded: false,
|
@@ -43,6 +43,38 @@ module Primer
|
|
43
43
|
})
|
44
44
|
end
|
45
45
|
|
46
|
+
# @label Single select
|
47
|
+
#
|
48
|
+
# @snapshot interactive
|
49
|
+
# @param expanded [Boolean] toggle
|
50
|
+
# @param disabled [Boolean] toggle
|
51
|
+
def single_select(
|
52
|
+
expanded: false,
|
53
|
+
disabled: false
|
54
|
+
)
|
55
|
+
render_with_template(locals: {
|
56
|
+
expanded: coerce_bool(expanded),
|
57
|
+
disabled: coerce_bool(disabled),
|
58
|
+
select_variant: :single,
|
59
|
+
})
|
60
|
+
end
|
61
|
+
|
62
|
+
# @label Multi select
|
63
|
+
#
|
64
|
+
# @snapshot interactive
|
65
|
+
# @param expanded [Boolean] toggle
|
66
|
+
# @param disabled [Boolean] toggle
|
67
|
+
def multi_select(
|
68
|
+
expanded: false,
|
69
|
+
disabled: false
|
70
|
+
)
|
71
|
+
render_with_template(locals: {
|
72
|
+
expanded: coerce_bool(expanded),
|
73
|
+
disabled: coerce_bool(disabled),
|
74
|
+
select_variant: :multiple,
|
75
|
+
})
|
76
|
+
end
|
77
|
+
|
46
78
|
# @label Empty
|
47
79
|
#
|
48
80
|
# @snapshot interactive
|
@@ -94,7 +126,7 @@ module Primer
|
|
94
126
|
# @param leading_visual_icon [Symbol] octicon
|
95
127
|
# @param leading_action_icon [Symbol] octicon
|
96
128
|
# @param trailing_visual_icon [Symbol] octicon
|
97
|
-
# @param select_variant [Symbol] select [multiple, none]
|
129
|
+
# @param select_variant [Symbol] select [multiple, single, none]
|
98
130
|
# @param disabled [Boolean] toggle
|
99
131
|
def leaf_node_playground(
|
100
132
|
label: "Leaf node",
|
@@ -153,9 +185,11 @@ module Primer
|
|
153
185
|
|
154
186
|
# @label Form input
|
155
187
|
#
|
188
|
+
# @param select_variant [Symbol] select [multiple, single]
|
156
189
|
# @param expanded [Boolean] toggle
|
157
|
-
def form_input(expanded: true)
|
190
|
+
def form_input(select_variant: :multiple, expanded: true)
|
158
191
|
render_with_template(locals: {
|
192
|
+
select_variant: select_variant.to_sym,
|
159
193
|
expanded: coerce_bool(expanded)
|
160
194
|
})
|
161
195
|
end
|
@@ -1,30 +1,30 @@
|
|
1
1
|
<%= form_with(url: primer_view_components.generic_form_submission_path(format: :json)) do |f| %>
|
2
2
|
<%= render(Primer::Alpha::Stack.new) do %>
|
3
3
|
<%= render(Primer::OpenProject::FilterableTreeView.new(form_arguments: { builder: f, name: "characters" })) do |tree| %>
|
4
|
-
<% tree.with_sub_tree(label: "Students", expanded: expanded) do |hogwarts| %>
|
5
|
-
<% hogwarts.with_sub_tree(label: "Ravenclaw", expanded: expanded) do |ravenclaw| %>
|
6
|
-
<% ravenclaw.with_leaf(label: "Luna Lovegood") %>
|
4
|
+
<% tree.with_sub_tree(label: "Students", select_variant: select_variant, expanded: expanded) do |hogwarts| %>
|
5
|
+
<% hogwarts.with_sub_tree(label: "Ravenclaw", select_variant: select_variant, expanded: expanded) do |ravenclaw| %>
|
6
|
+
<% ravenclaw.with_leaf(label: "Luna Lovegood", select_variant: select_variant) %>
|
7
7
|
<% end %>
|
8
8
|
|
9
|
-
<% hogwarts.with_sub_tree(label: "Slytherin", expanded: expanded) do |hufflepuff| %>
|
10
|
-
<% hufflepuff.with_leaf(label: "Draco Malfoy") %>
|
9
|
+
<% hogwarts.with_sub_tree(label: "Slytherin", select_variant: select_variant, expanded: expanded) do |hufflepuff| %>
|
10
|
+
<% hufflepuff.with_leaf(label: "Draco Malfoy", select_variant: select_variant) %>
|
11
11
|
<% end %>
|
12
12
|
|
13
|
-
<% hogwarts.with_sub_tree(label: "Hufflepuff", expanded: expanded) do |hufflepuff| %>
|
14
|
-
<% hufflepuff.with_leaf(label: "Susan Bones") %>
|
13
|
+
<% hogwarts.with_sub_tree(label: "Hufflepuff", select_variant: select_variant, expanded: expanded) do |hufflepuff| %>
|
14
|
+
<% hufflepuff.with_leaf(label: "Susan Bones", select_variant: select_variant) %>
|
15
15
|
<% end %>
|
16
16
|
|
17
|
-
<% hogwarts.with_sub_tree(label: "Gryffindor", expanded: expanded) do |hufflepuff| %>
|
18
|
-
<% hufflepuff.with_leaf(label: "Harry Potter") %>
|
19
|
-
<% hufflepuff.with_leaf(label: "Ronald Weasley") %>
|
20
|
-
<% hufflepuff.with_leaf(label: "Hermione Granger") %>
|
17
|
+
<% hogwarts.with_sub_tree(label: "Gryffindor", select_variant: select_variant, expanded: expanded) do |hufflepuff| %>
|
18
|
+
<% hufflepuff.with_leaf(label: "Harry Potter", select_variant: select_variant) %>
|
19
|
+
<% hufflepuff.with_leaf(label: "Ronald Weasley", select_variant: select_variant) %>
|
20
|
+
<% hufflepuff.with_leaf(label: "Hermione Granger", select_variant: select_variant) %>
|
21
21
|
<% end %>
|
22
22
|
<% end %>
|
23
23
|
|
24
|
-
<% tree.with_leaf(label: "Albus Dumbledore") %>
|
25
|
-
<% tree.with_leaf(label: "Minerva McGonagall") %>
|
26
|
-
<% tree.with_leaf(label: "Severus Snape") %>
|
27
|
-
<% tree.with_leaf(label: "Rubeus Hagrid") %>
|
24
|
+
<% tree.with_leaf(label: "Albus Dumbledore", select_variant: select_variant) %>
|
25
|
+
<% tree.with_leaf(label: "Minerva McGonagall", select_variant: select_variant) %>
|
26
|
+
<% tree.with_leaf(label: "Severus Snape", select_variant: select_variant) %>
|
27
|
+
<% tree.with_leaf(label: "Rubeus Hagrid", select_variant: select_variant) %>
|
28
28
|
<% end %>
|
29
29
|
|
30
30
|
<%= render(Primer::Alpha::SubmitButton.new(name: :submit, label: "Submit")) %>
|
@@ -2,28 +2,28 @@
|
|
2
2
|
include_sub_items_check_box_arguments: { hidden: !show_checkbox },
|
3
3
|
filter_mode_control_arguments: { hidden: !show_segmented_control }
|
4
4
|
)) do |tree| %>
|
5
|
-
<% tree.with_sub_tree(label: "Students", expanded: expanded) do |hogwarts| %>
|
6
|
-
<% hogwarts.with_sub_tree(label: "Ravenclaw", expanded: expanded) do |ravenclaw| %>
|
7
|
-
<% ravenclaw.with_leaf(label: "Luna Lovegood") %>
|
5
|
+
<% tree.with_sub_tree(label: "Students", select_variant: select_variant, expanded: expanded) do |hogwarts| %>
|
6
|
+
<% hogwarts.with_sub_tree(label: "Ravenclaw", select_variant: select_variant, expanded: expanded) do |ravenclaw| %>
|
7
|
+
<% ravenclaw.with_leaf(label: "Luna Lovegood", select_variant: select_variant) %>
|
8
8
|
<% end %>
|
9
9
|
|
10
|
-
<% hogwarts.with_sub_tree(label: "Slytherin", expanded: expanded) do |hufflepuff| %>
|
11
|
-
<% hufflepuff.with_leaf(label: "Draco Malfoy") %>
|
10
|
+
<% hogwarts.with_sub_tree(label: "Slytherin", select_variant: select_variant, expanded: expanded) do |hufflepuff| %>
|
11
|
+
<% hufflepuff.with_leaf(label: "Draco Malfoy", select_variant: select_variant) %>
|
12
12
|
<% end %>
|
13
13
|
|
14
|
-
<% hogwarts.with_sub_tree(label: "Hufflepuff", expanded: expanded) do |hufflepuff| %>
|
15
|
-
<% hufflepuff.with_leaf(label: "Susan Bones") %>
|
14
|
+
<% hogwarts.with_sub_tree(label: "Hufflepuff", select_variant: select_variant, expanded: expanded) do |hufflepuff| %>
|
15
|
+
<% hufflepuff.with_leaf(label: "Susan Bones", select_variant: select_variant) %>
|
16
16
|
<% end %>
|
17
17
|
|
18
|
-
<% hogwarts.with_sub_tree(label: "Gryffindor", expanded: expanded) do |hufflepuff| %>
|
19
|
-
<% hufflepuff.with_leaf(label: "Harry Potter") %>
|
20
|
-
<% hufflepuff.with_leaf(label: "Ronald Weasley") %>
|
21
|
-
<% hufflepuff.with_leaf(label: "Hermione Granger") %>
|
18
|
+
<% hogwarts.with_sub_tree(label: "Gryffindor", select_variant: select_variant, expanded: expanded) do |hufflepuff| %>
|
19
|
+
<% hufflepuff.with_leaf(label: "Harry Potter", select_variant: select_variant) %>
|
20
|
+
<% hufflepuff.with_leaf(label: "Ronald Weasley", select_variant: select_variant) %>
|
21
|
+
<% hufflepuff.with_leaf(label: "Hermione Granger", select_variant: select_variant) %>
|
22
22
|
<% end %>
|
23
23
|
<% end %>
|
24
24
|
|
25
|
-
<% tree.with_leaf(label: "Albus Dumbledore") %>
|
26
|
-
<% tree.with_leaf(label: "Minerva McGonagall") %>
|
27
|
-
<% tree.with_leaf(label: "Severus Snape") %>
|
28
|
-
<% tree.with_leaf(label: "Rubeus Hagrid") %>
|
25
|
+
<% tree.with_leaf(label: "Albus Dumbledore", select_variant: select_variant) %>
|
26
|
+
<% tree.with_leaf(label: "Minerva McGonagall", select_variant: select_variant) %>
|
27
|
+
<% tree.with_leaf(label: "Severus Snape", select_variant: select_variant) %>
|
28
|
+
<% tree.with_leaf(label: "Rubeus Hagrid", select_variant: select_variant) %>
|
29
29
|
<% end %>
|
@@ -7,11 +7,13 @@ module Primer
|
|
7
7
|
# @label Playground
|
8
8
|
#
|
9
9
|
# @param expanded [Boolean] toggle
|
10
|
+
# @param select_variant [Symbol] select [multiple, single]
|
10
11
|
# @param show_checkbox [Boolean] toggle
|
11
12
|
# @param show_segmented_control [Boolean] toggle
|
12
|
-
def playground(expanded: true, show_checkbox: true, show_segmented_control: true)
|
13
|
+
def playground(expanded: true, select_variant: :multiple, show_checkbox: true, show_segmented_control: true)
|
13
14
|
render_with_template(locals: {
|
14
15
|
expanded: coerce_bool(expanded),
|
16
|
+
select_variant: select_variant.to_sym,
|
15
17
|
show_checkbox: coerce_bool(show_checkbox),
|
16
18
|
show_segmented_control: coerce_bool(show_segmented_control)
|
17
19
|
})
|
@@ -29,9 +31,11 @@ module Primer
|
|
29
31
|
|
30
32
|
# @label Form input
|
31
33
|
#
|
34
|
+
# @param select_variant [Symbol] select [multiple, single]
|
32
35
|
# @param expanded [Boolean] toggle
|
33
|
-
def form_input(expanded: true)
|
36
|
+
def form_input(select_variant: :multiple, expanded: true)
|
34
37
|
render_with_template(locals: {
|
38
|
+
select_variant: select_variant.to_sym,
|
35
39
|
expanded: coerce_bool(expanded)
|
36
40
|
})
|
37
41
|
end
|
data/static/arguments.json
CHANGED
@@ -3529,7 +3529,7 @@
|
|
3529
3529
|
"name": "select_variant",
|
3530
3530
|
"type": "Symbol",
|
3531
3531
|
"default": "`:none`",
|
3532
|
-
"description": "Controls the type of checkbox that appears. One of `:multiple
|
3532
|
+
"description": "Controls the type of checkbox that appears. One of `:multiple`, `:none`, or `:single`."
|
3533
3533
|
},
|
3534
3534
|
{
|
3535
3535
|
"name": "checked",
|
data/static/constants.json
CHANGED
data/static/info_arch.json
CHANGED
@@ -10428,7 +10428,7 @@
|
|
10428
10428
|
},
|
10429
10429
|
{
|
10430
10430
|
"fully_qualified_name": "Primer::Alpha::TreeView",
|
10431
|
-
"description": "TreeView is a hierarchical list of items that may have a parent-child relationship where children\ncan be toggled into view by expanding or collapsing their parent item.\n\n## Terminology\n\nConsider the following tree structure:\n\nsrc\n├ button.rb\n└ action_list\n ├ item.rb\n └ header.rb\n\n1. **Node**. A node is an item in the tree. Nodes can either be \"leaf\" nodes (i.e. have no children), or \"sub-tree\"\nnodes, which do have children. In the example above, button.rb, item.rb, and header.rb are all leaf nodes, while\naction_list is a sub-tree node.\n2. **Path**. A node's path is like its ID. It's an array of strings containing the current node's label and all the\nlabels of its ancestors, in order. In the example above, header.rb's path is [\"src\", \"action_list\", \"header.rb\"].\n\n## Static nodes\n\nThe `TreeView` component allows items to be provided statically or loaded dynamically from the server.\nProviding items statically is done using the `leaf` and `sub_tree` slots:\n\n```erb\n<%= render(Primer::Alpha::TreeView.new) do |tree| %>\n <% tree.with_sub_tree(label: \"Directory\") do |sub_tree| %>\n <% sub_tree.with_leaf(label: \"File 1\")\n <% end %>\n <% tree.with_leaf(label: \"File 2\") %>\n<% end %>\n```\n\n## Dynamic nodes\n\nTree nodes can also be fetched dynamically from the server and will require creating a Rails controller action\nto respond with the list of nodes. Unlike other Primer components, `TreeView` allows the programmer to specify\nloading behavior on a per-sub-tree basis, i.e. each sub-tree must specify how its nodes are loaded. To load nodes\ndynamically for a given sub-tree, configure it with either a loading spinner or a loading skeleton, and provide\nthe URL to fetch nodes from:\n\n```erb\n<%= render(Primer::Alpha::TreeView.new) do |tree| %>\n <% tree.with_sub_tree(label: \"Directory\") do |sub_tree| %>\n <% sub_tree.with_loading_spinner(src: primer_view_components.tree_view_items_path) %>\n <% end %>\n<% end %>\n```\n\nDefine a controller action to serve the list of nodes. The `TreeView` component automatically includes the\nsub-tree's path as a GET parameter, encoded as a JSON array.\n\n```ruby\nclass TreeViewItemsController < ApplicationController\n def show\n @path = JSON.parse(params[:path])\n @results = get_tree_items(starting_at: path)\n end\nend\n```\n\nResponses must be HTML fragments, eg. have a content type of `text/html+fragment`. This content type isn't\navailable by default in Rails, so you may have to register it eg. in an initializer:\n\n```ruby\nMime::Type.register(\"text/fragment+html\", :html_fragment)\n```\n\nRender a `Primer::Alpha::TreeView::SubTree` in the action's template, tree_view_items/show.html_fragment.erb:\n\n```erb\n<%= render(Primer::Alpha::TreeView::SubTree.new(path: @path, node_variant: :div)) do |tree| %>\n <% tree.with_leaf(...) %>\n <% tree.with_sub_tree(...) do |sub_tree| %>\n ...\n <% end %>\n<% end %>\n```\n\n## Multi-select mode\n\nPassing `select_variant: :multiple` to both sub-tree and leaf nodes will add a check box to the left of the node's\nlabel. These check boxes behave according to the value of a second argument, `select_strategy:`.\n\nThe default select strategy, `:descendants`, will cause all child nodes to be checked when the node is checked.\nThis includes both sub-tree and leaf nodes. When the node is unchecked, all child nodes will also be unchecked.\nUnchecking a child node of a checked parent will cause the parent to enter a mixed or indeterminate state, which\nis represented by a horizontal line icon instead of a check mark. This icon indicates that some children are\nchecked, but not all.\n\nA secondary select strategy, `:self`, is provided to allow disabling the automatic checking of child nodes. When\n`select_strategy: :self` is specified, checking sub-tree nodes does not check child nodes, and sub-tree nodes\ncannot enter a mixed or indeterminate state.\n\nNodes can be checked via the keyboard by pressing the space key.\n\n## Node tags\n\n`TreeView`s support three different node variants, `:anchor`, `:button`, and `:div` (the default), which controls\nwhich HTML tag is used to construct the nodes. The `:anchor` and `:button` variants correspond to `<a>` and\n`<button>` tags respectively, which are browser-native elements. Anchors and buttons can be activated (i.e.\n\"clicked\") using the mouse or keyboard via the enter or space keys. The node variant must be the same for all\nnodes in the tree, and is therefore specified at the root level, eg. `TreeView.new(node_variant: :anchor)`.\n\nTrees with node variants other than `:div` cannot have check boxes, i.e. cannot be put into multi-select mode.\n\nTrees with node variants other than `:div` do not emit the `treeViewNodeActivated` or `treeViewBeforeNodeActivated`\nevents, since it is assumed any behavior associated with these variants is user- or browser-defined.\n\n## Interaction behavior matrix\n\n|Interaction |Select variant|Tag |Result |\n|:---------------|:-------------|:------------|:--------------------------|\n|Enter/space |none |div |Expands/collapses |\n|Enter/space |none |anchor/button|Activates anchor/button |\n|Enter/space |multiple |div |Checks or unchecks |\n|Enter/space |multiple |anchor/button|N/A (not allowed) |\n|Left/right arrow|none |div |Expands/collapses |\n|Left/right arrow|none |anchor/button|Expands/collapses |\n|Left/right arrow|multiple |div |Expands/collapses |\n|Left/right arrow|multiple |anchor/button|N/A (not allowed) |\n|Click |none |div |Expands/collapses |\n|Click |multiple |div |Checks or unchecks |\n|Click |multiple |anchor/button|N/A (not allowed) |\n\n## JavaScript API\n\n`TreeView`s render a `<tree-view>` custom element that exposes behavior to the client.\n\n|Name |Notes |\n|:-----------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------|\n|`getNodePath(node: Element): string[]` |Returns the path to the given node. |\n|`getNodeType(node: Element): TreeViewNodeType | null` |Returns either `\"leaf\"` or `\"sub-tree\"`. |\n|`markCurrentAtPath(path: string[])` |Marks the node as the \"current\" node, which appears visually distinct from other nodes. |\n|`get currentNode(): HTMLLIElement | null` |Returns the current node. |\n|`expandAtPath(path: string[])` |Expands the sub-tree at `path`. |\n|`collapseAtPath(path: string[])` |Collapses the sub-tree at `path`. |\n|`toggleAtPath(path: string[])` |If the sub-tree at `path` is collapsed, this function expands it, and vice-versa. |\n|`checkAtPath(path: string[])` |If the node at `path` has a checkbox, this function checks it. |\n|`uncheckAtPath(path: string[])` |If the node at `path` has a checkbox, this function unchecks it. |\n|`toggleCheckedAtPath(path: string[])` |If the sub-tree at `path` is checked, this function unchecks it, and vice-versa. |\n|`checkedValueAtPath(path: string[]): TreeViewCheckedValue` |Returns `\"true\"` (all child nodes are checked), `\"false\"` (no child nodes are checked), or `\"mixed\"` (some child nodes are checked, some are not).|\n|`nodeAtPath(path: string[], selector?: string): Element | null` |Returns the node for the given `path`, either a leaf node or sub-tree node. |\n|`subTreeAtPath(path: string[]): TreeViewSubTreeNodeElement | null`|Returns the sub-tree at the given `path`, if it exists. |\n|`leafAtPath(path: string[]): HTMLLIElement | null` |Returns the leaf node at the given `path`, if it exists. |\n|`getNodeCheckedValue(node: Element): TreeViewCheckedValue` |The same as `checkedValueAtPath`, but accepts a node instead of a path. |\n\n### Events\n\nThe events enumerated below include node information by way of the `TreeViewNodeInfo` object, which has the\nfollowing signature:\n\n```typescript\ntype TreeViewNodeType = 'leaf' | 'sub-tree'\ntype TreeViewCheckedValue = 'true' | 'false' | 'mixed'\n\ntype TreeViewNodeInfo = {\n node: Element\n type: TreeViewNodeType\n path: string[]\n checkedValue: TreeViewCheckedValue\n previousCheckedValue: TreeViewCheckedValue\n}\n```\n\n|Name |Type |Bubbles |Cancelable |\n|:----------------------------|:------------------------------------------|:-------|:----------|\n|`treeViewNodeActivated` |`CustomEvent<TreeViewNodeInfo>` |Yes |No |\n|`treeViewBeforeNodeActivated`|`CustomEvent<TreeViewNodeInfo>` |Yes |Yes |\n|`treeViewNodeExpanded` |`CustomEvent<TreeViewNodeInfo>>` |Yes |No |\n|`treeViewNodeCollapsed` |`CustomEvent<TreeViewNodeInfo>>` |Yes |No |\n|`treeViewNodeChecked` |`CustomEvent<TreeViewNodeInfo[]>` |Yes |Yes |\n|`treeViewBeforeNodeChecked` |`CustomEvent<TreeViewNodeInfo[]>` |Yes |No |\n\n_Item activation_\n\nThe `<tree-view>` element fires an `treeViewNodeActivated` event whenever a node is activated (eg. clicked)\nvia the mouse or keyboard.\n\nThe `treeViewBeforeNodeActivated` event fires before a node is activated. Canceling this event will prevent the\nnode from being activated.\n\n```typescript\ndocument.querySelector(\"select-panel\").addEventListener(\n \"treeViewBeforeNodeActivated\",\n (event: CustomEvent<TreeViewNodeInfo>) => {\n event.preventDefault() // Cancel the event to prevent activation (eg. expanding/collapsing)\n }\n)\n```\n\n_Item checking/unchecking_\n\nThe `tree-view` element fires a `treeViewNodeChecked` event whenever a node is checked or unchecked.\n\nThe `treeViewBeforeNodeChecked` event fires before a node is checked or unchecked. Canceling this event will\nprevent the check/uncheck operation.\n\n```typescript\ndocument.querySelector(\"select-panel\").addEventListener(\n \"treeViewBeforeNodeChecked\",\n (event: CustomEvent<TreeViewNodeInfo[]>) => {\n event.preventDefault() // Cancel the event to prevent activation (eg. expanding/collapsing)\n }\n)\n```\n\nBecause checking or unchecking a sub-tree results in the checking or unchecking of all its children recursively,\nboth the `treeViewNodeChecked` and `treeViewBeforeNodeChecked` events provide an array of `TreeViewNodeInfo`\nobjects, which contain entries for every modified node in the tree.",
|
10431
|
+
"description": "TreeView is a hierarchical list of items that may have a parent-child relationship where children\ncan be toggled into view by expanding or collapsing their parent item.\n\n## Terminology\n\nConsider the following tree structure:\n\nsrc\n├ button.rb\n└ action_list\n ├ item.rb\n └ header.rb\n\n1. **Node**. A node is an item in the tree. Nodes can either be \"leaf\" nodes (i.e. have no children), or \"sub-tree\"\nnodes, which do have children. In the example above, button.rb, item.rb, and header.rb are all leaf nodes, while\naction_list is a sub-tree node.\n2. **Path**. A node's path is like its ID. It's an array of strings containing the current node's label and all the\nlabels of its ancestors, in order. In the example above, header.rb's path is [\"src\", \"action_list\", \"header.rb\"].\n\n## Static nodes\n\nThe `TreeView` component allows items to be provided statically or loaded dynamically from the server.\nProviding items statically is done using the `leaf` and `sub_tree` slots:\n\n```erb\n<%= render(Primer::Alpha::TreeView.new) do |tree| %>\n <% tree.with_sub_tree(label: \"Directory\") do |sub_tree| %>\n <% sub_tree.with_leaf(label: \"File 1\")\n <% end %>\n <% tree.with_leaf(label: \"File 2\") %>\n<% end %>\n```\n\n## Dynamic nodes\n\nTree nodes can also be fetched dynamically from the server and will require creating a Rails controller action\nto respond with the list of nodes. Unlike other Primer components, `TreeView` allows the programmer to specify\nloading behavior on a per-sub-tree basis, i.e. each sub-tree must specify how its nodes are loaded. To load nodes\ndynamically for a given sub-tree, configure it with either a loading spinner or a loading skeleton, and provide\nthe URL to fetch nodes from:\n\n```erb\n<%= render(Primer::Alpha::TreeView.new) do |tree| %>\n <% tree.with_sub_tree(label: \"Directory\") do |sub_tree| %>\n <% sub_tree.with_loading_spinner(src: primer_view_components.tree_view_items_path) %>\n <% end %>\n<% end %>\n```\n\nDefine a controller action to serve the list of nodes. The `TreeView` component automatically includes the\nsub-tree's path as a GET parameter, encoded as a JSON array.\n\n```ruby\nclass TreeViewItemsController < ApplicationController\n def show\n @path = JSON.parse(params[:path])\n @results = get_tree_items(starting_at: path)\n end\nend\n```\n\nResponses must be HTML fragments, eg. have a content type of `text/html+fragment`. This content type isn't\navailable by default in Rails, so you may have to register it eg. in an initializer:\n\n```ruby\nMime::Type.register(\"text/fragment+html\", :html_fragment)\n```\n\nRender a `Primer::Alpha::TreeView::SubTree` in the action's template, tree_view_items/show.html_fragment.erb:\n\n```erb\n<%= render(Primer::Alpha::TreeView::SubTree.new(path: @path, node_variant: :div)) do |tree| %>\n <% tree.with_leaf(...) %>\n <% tree.with_sub_tree(...) do |sub_tree| %>\n ...\n <% end %>\n<% end %>\n```\n\n## Multi-select mode\n\nPassing `select_variant: :multiple` to both sub-tree and leaf nodes will add a check box to the left of the node's\nlabel. These check boxes behave according to the value of a second argument, `select_strategy:`.\n\nThe default select strategy, `:descendants`, will cause all child nodes to be checked when the node is checked.\nThis includes both sub-tree and leaf nodes. When the node is unchecked, all child nodes will also be unchecked.\nUnchecking a child node of a checked parent will cause the parent to enter a mixed or indeterminate state, which\nis represented by a horizontal line icon instead of a check mark. This icon indicates that some children are\nchecked, but not all.\n\nA secondary select strategy, `:self`, is provided to allow disabling the automatic checking of child nodes. When\n`select_strategy: :self` is specified, checking sub-tree nodes does not check child nodes, and sub-tree nodes\ncannot enter a mixed or indeterminate state.\n\nNodes can be checked via the keyboard by pressing the space key.\n\n## Single-select mode\n\nBy passing `select_variant: :single` to both sub-tree and leaf nodes:\n- Nodes become selectable and can be toggled via keyboard (space key).\n- A selected node displays a checkmark at the end of the line.\nNote: This checkmark conflicts with the `trailing_visual_icon` slot,\nso both cannot be used simultaneously.\n\n## Node tags\n\n`TreeView`s support three different node variants, `:anchor`, `:button`, and `:div` (the default), which controls\nwhich HTML tag is used to construct the nodes. The `:anchor` and `:button` variants correspond to `<a>` and\n`<button>` tags respectively, which are browser-native elements. Anchors and buttons can be activated (i.e.\n\"clicked\") using the mouse or keyboard via the enter or space keys. The node variant must be the same for all\nnodes in the tree, and is therefore specified at the root level, eg. `TreeView.new(node_variant: :anchor)`.\n\nTrees with node variants other than `:div` cannot have check boxes, i.e. cannot be put into multi-select mode.\n\nTrees with node variants other than `:div` do not emit the `treeViewNodeActivated` or `treeViewBeforeNodeActivated`\nevents, since it is assumed any behavior associated with these variants is user- or browser-defined.\n\n## Interaction behavior matrix\n\n|Interaction |Select variant|Tag |Result |\n|:---------------|:-------------|:------------|:--------------------------|\n|Enter/space |none |div |Expands/collapses |\n|Enter/space |none |anchor/button|Activates anchor/button |\n|Enter/space |single |div |Selects |\n|Enter/space |single |anchor/button|N/A (not allowed) |\n|Enter/space |multiple |div |Checks or unchecks |\n|Enter/space |multiple |anchor/button|N/A (not allowed) |\n|Left/right arrow|none |div |Expands/collapses |\n|Left/right arrow|none |anchor/button|Expands/collapses |\n|Left/right arrow|single |div |Expands/collapses |\n|Left/right arrow|single |anchor/button|N/A (not allowed) |\n|Left/right arrow|multiple |div |Expands/collapses |\n|Left/right arrow|multiple |anchor/button|N/A (not allowed) |\n|Click |none |div |Expands/collapses |\n|Click |single |div |Selects |\n|Click |single |anchor/button|N/A (not allowed) |\n|Click |multiple |div |Checks or unchecks |\n|Click |multiple |anchor/button|N/A (not allowed) |\n\n## JavaScript API\n\n`TreeView`s render a `<tree-view>` custom element that exposes behavior to the client.\n\n|Name |Notes |\n|:-----------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------|\n|`getNodePath(node: Element): string[]` |Returns the path to the given node. |\n|`getNodeType(node: Element): TreeViewNodeType | null` |Returns either `\"leaf\"` or `\"sub-tree\"`. |\n|`markCurrentAtPath(path: string[])` |Marks the node as the \"current\" node, which appears visually distinct from other nodes. |\n|`get currentNode(): HTMLLIElement | null` |Returns the current node. |\n|`expandAtPath(path: string[])` |Expands the sub-tree at `path`. |\n|`collapseAtPath(path: string[])` |Collapses the sub-tree at `path`. |\n|`toggleAtPath(path: string[])` |If the sub-tree at `path` is collapsed, this function expands it, and vice-versa. |\n|`checkAtPath(path: string[])` |If the node at `path` has a checkbox, this function checks it. |\n|`uncheckAtPath(path: string[])` |If the node at `path` has a checkbox, this function unchecks it. |\n|`toggleCheckedAtPath(path: string[])` |If the sub-tree at `path` is checked, this function unchecks it, and vice-versa. |\n|`checkedValueAtPath(path: string[]): TreeViewCheckedValue` |Returns `\"true\"` (all child nodes are checked), `\"false\"` (no child nodes are checked), or `\"mixed\"` (some child nodes are checked, some are not).|\n|`nodeAtPath(path: string[], selector?: string): Element | null` |Returns the node for the given `path`, either a leaf node or sub-tree node. |\n|`subTreeAtPath(path: string[]): TreeViewSubTreeNodeElement | null`|Returns the sub-tree at the given `path`, if it exists. |\n|`leafAtPath(path: string[]): HTMLLIElement | null` |Returns the leaf node at the given `path`, if it exists. |\n|`getNodeCheckedValue(node: Element): TreeViewCheckedValue` |The same as `checkedValueAtPath`, but accepts a node instead of a path. |\n\n### Events\n\nThe events enumerated below include node information by way of the `TreeViewNodeInfo` object, which has the\nfollowing signature:\n\n```typescript\ntype TreeViewNodeType = 'leaf' | 'sub-tree'\ntype TreeViewCheckedValue = 'true' | 'false' | 'mixed'\n\ntype TreeViewNodeInfo = {\n node: Element\n type: TreeViewNodeType\n path: string[]\n checkedValue: TreeViewCheckedValue\n previousCheckedValue: TreeViewCheckedValue\n}\n```\n\n|Name |Type |Bubbles |Cancelable |\n|:----------------------------|:------------------------------------------|:-------|:----------|\n|`treeViewNodeActivated` |`CustomEvent<TreeViewNodeInfo>` |Yes |No |\n|`treeViewBeforeNodeActivated`|`CustomEvent<TreeViewNodeInfo>` |Yes |Yes |\n|`treeViewNodeExpanded` |`CustomEvent<TreeViewNodeInfo>>` |Yes |No |\n|`treeViewNodeCollapsed` |`CustomEvent<TreeViewNodeInfo>>` |Yes |No |\n|`treeViewNodeChecked` |`CustomEvent<TreeViewNodeInfo[]>` |Yes |Yes |\n|`treeViewBeforeNodeChecked` |`CustomEvent<TreeViewNodeInfo[]>` |Yes |No |\n\n_Item activation_\n\nThe `<tree-view>` element fires an `treeViewNodeActivated` event whenever a node is activated (eg. clicked)\nvia the mouse or keyboard.\n\nThe `treeViewBeforeNodeActivated` event fires before a node is activated. Canceling this event will prevent the\nnode from being activated.\n\n```typescript\ndocument.querySelector(\"select-panel\").addEventListener(\n \"treeViewBeforeNodeActivated\",\n (event: CustomEvent<TreeViewNodeInfo>) => {\n event.preventDefault() // Cancel the event to prevent activation (eg. expanding/collapsing)\n }\n)\n```\n\n_Item checking/unchecking_\n\nThe `tree-view` element fires a `treeViewNodeChecked` event whenever a node is checked or unchecked.\n\nThe `treeViewBeforeNodeChecked` event fires before a node is checked or unchecked. Canceling this event will\nprevent the check/uncheck operation.\n\n```typescript\ndocument.querySelector(\"select-panel\").addEventListener(\n \"treeViewBeforeNodeChecked\",\n (event: CustomEvent<TreeViewNodeInfo[]>) => {\n event.preventDefault() // Cancel the event to prevent activation (eg. expanding/collapsing)\n }\n)\n```\n\nBecause checking or unchecking a sub-tree may result in the checking or unchecking of all its children recursively,\nboth the `treeViewNodeChecked` and `treeViewBeforeNodeChecked` events provide an array of `TreeViewNodeInfo`\nobjects, which contain entries for every modified node in the tree.",
|
10432
10432
|
"accessibility_docs": null,
|
10433
10433
|
"is_form_component": false,
|
10434
10434
|
"is_published": true,
|
@@ -10539,6 +10539,32 @@
|
|
10539
10539
|
]
|
10540
10540
|
}
|
10541
10541
|
},
|
10542
|
+
{
|
10543
|
+
"preview_path": "primer/alpha/tree_view/single_select",
|
10544
|
+
"name": "single_select",
|
10545
|
+
"snapshot": "interactive",
|
10546
|
+
"skip_rules": {
|
10547
|
+
"wont_fix": [
|
10548
|
+
"region"
|
10549
|
+
],
|
10550
|
+
"will_fix": [
|
10551
|
+
"color-contrast"
|
10552
|
+
]
|
10553
|
+
}
|
10554
|
+
},
|
10555
|
+
{
|
10556
|
+
"preview_path": "primer/alpha/tree_view/multi_select",
|
10557
|
+
"name": "multi_select",
|
10558
|
+
"snapshot": "interactive",
|
10559
|
+
"skip_rules": {
|
10560
|
+
"wont_fix": [
|
10561
|
+
"region"
|
10562
|
+
],
|
10563
|
+
"will_fix": [
|
10564
|
+
"color-contrast"
|
10565
|
+
]
|
10566
|
+
}
|
10567
|
+
},
|
10542
10568
|
{
|
10543
10569
|
"preview_path": "primer/alpha/tree_view/empty",
|
10544
10570
|
"name": "empty",
|
@@ -11016,7 +11042,7 @@
|
|
11016
11042
|
"name": "select_variant",
|
11017
11043
|
"type": "Symbol",
|
11018
11044
|
"default": "`:none`",
|
11019
|
-
"description": "Controls the type of checkbox that appears. One of `:multiple
|
11045
|
+
"description": "Controls the type of checkbox that appears. One of `:multiple`, `:none`, or `:single`."
|
11020
11046
|
},
|
11021
11047
|
{
|
11022
11048
|
"name": "checked",
|
data/static/previews.json
CHANGED
@@ -9113,6 +9113,32 @@
|
|
9113
9113
|
]
|
9114
9114
|
}
|
9115
9115
|
},
|
9116
|
+
{
|
9117
|
+
"preview_path": "primer/alpha/tree_view/single_select",
|
9118
|
+
"name": "single_select",
|
9119
|
+
"snapshot": "interactive",
|
9120
|
+
"skip_rules": {
|
9121
|
+
"wont_fix": [
|
9122
|
+
"region"
|
9123
|
+
],
|
9124
|
+
"will_fix": [
|
9125
|
+
"color-contrast"
|
9126
|
+
]
|
9127
|
+
}
|
9128
|
+
},
|
9129
|
+
{
|
9130
|
+
"preview_path": "primer/alpha/tree_view/multi_select",
|
9131
|
+
"name": "multi_select",
|
9132
|
+
"snapshot": "interactive",
|
9133
|
+
"skip_rules": {
|
9134
|
+
"wont_fix": [
|
9135
|
+
"region"
|
9136
|
+
],
|
9137
|
+
"will_fix": [
|
9138
|
+
"color-contrast"
|
9139
|
+
]
|
9140
|
+
}
|
9141
|
+
},
|
9116
9142
|
{
|
9117
9143
|
"preview_path": "primer/alpha/tree_view/empty",
|
9118
9144
|
"name": "empty",
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openproject-primer_view_components
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.74.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2025-
|
12
|
+
date: 2025-10-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionview
|
@@ -1104,7 +1104,9 @@ files:
|
|
1104
1104
|
- previews/primer/alpha/tree_view_preview/loading_failure.html.erb
|
1105
1105
|
- previews/primer/alpha/tree_view_preview/loading_skeleton.html.erb
|
1106
1106
|
- previews/primer/alpha/tree_view_preview/loading_spinner.html.erb
|
1107
|
+
- previews/primer/alpha/tree_view_preview/multi_select.html.erb
|
1107
1108
|
- previews/primer/alpha/tree_view_preview/playground.html.erb
|
1109
|
+
- previews/primer/alpha/tree_view_preview/single_select.html.erb
|
1108
1110
|
- previews/primer/alpha/underline_nav_preview.rb
|
1109
1111
|
- previews/primer/alpha/underline_nav_preview/default.html.erb
|
1110
1112
|
- previews/primer/alpha/underline_nav_preview/playground.html.erb
|