primer_view_components 0.0.19 → 0.0.24

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +78 -1
  3. data/app/assets/javascripts/primer_view_components.js +2 -0
  4. data/app/assets/javascripts/primer_view_components.js.map +1 -0
  5. data/app/components/primer/avatar_component.rb +7 -7
  6. data/app/components/primer/avatar_stack_component.rb +3 -3
  7. data/app/components/primer/base_component.rb +1 -1
  8. data/app/components/primer/blankslate_component.html.erb +3 -3
  9. data/app/components/primer/blankslate_component.rb +16 -24
  10. data/app/components/primer/border_box_component.html.erb +4 -18
  11. data/app/components/primer/border_box_component.rb +58 -67
  12. data/app/components/primer/box_component.rb +2 -2
  13. data/app/components/primer/breadcrumb_component.html.erb +1 -2
  14. data/app/components/primer/breadcrumb_component.rb +24 -13
  15. data/app/components/primer/button_component.rb +2 -2
  16. data/app/components/primer/button_group_component.rb +1 -1
  17. data/app/components/primer/button_marketing_component.rb +2 -2
  18. data/app/components/primer/component.rb +4 -0
  19. data/app/components/primer/counter_component.rb +1 -1
  20. data/app/components/primer/details_component.html.erb +2 -6
  21. data/app/components/primer/details_component.rb +22 -35
  22. data/app/components/primer/dropdown_component.html.erb +2 -2
  23. data/app/components/primer/dropdown_component.rb +4 -6
  24. data/app/components/primer/dropdown_menu_component.rb +4 -4
  25. data/app/components/primer/flash_component.html.erb +4 -7
  26. data/app/components/primer/flash_component.rb +16 -20
  27. data/app/components/primer/flex_component.rb +4 -4
  28. data/app/components/primer/flex_item_component.rb +1 -1
  29. data/app/components/primer/heading_component.rb +3 -1
  30. data/app/components/primer/label_component.rb +15 -25
  31. data/app/components/primer/layout_component.rb +2 -2
  32. data/app/components/primer/link_component.rb +2 -2
  33. data/app/components/primer/markdown_component.rb +8 -8
  34. data/app/components/primer/menu_component.rb +1 -1
  35. data/app/components/primer/octicon_component.rb +5 -3
  36. data/app/components/primer/popover_component.rb +3 -3
  37. data/app/components/primer/primer.js +1 -0
  38. data/app/components/primer/primer.ts +1 -0
  39. data/app/components/primer/progress_bar_component.html.erb +1 -1
  40. data/app/components/primer/progress_bar_component.rb +27 -31
  41. data/app/components/primer/spinner_component.rb +3 -3
  42. data/app/components/primer/state_component.rb +21 -10
  43. data/app/components/primer/subhead_component.html.erb +3 -15
  44. data/app/components/primer/subhead_component.rb +45 -61
  45. data/app/components/primer/tab_container_component.js +1 -0
  46. data/app/components/primer/tab_container_component.rb +41 -0
  47. data/app/components/primer/tab_container_component.ts +1 -0
  48. data/app/components/primer/tab_nav_component.html.erb +17 -0
  49. data/app/components/primer/tab_nav_component.rb +108 -0
  50. data/app/components/primer/text_component.rb +1 -1
  51. data/app/components/primer/timeline_item_component.html.erb +4 -16
  52. data/app/components/primer/timeline_item_component.rb +41 -52
  53. data/app/components/primer/tooltip_component.rb +5 -5
  54. data/app/components/primer/truncate_component.rb +4 -4
  55. data/app/components/primer/underline_nav_component.rb +2 -2
  56. data/{lib → app/lib}/primer/class_name_helper.rb +0 -0
  57. data/{lib → app/lib}/primer/classify.rb +1 -3
  58. data/{lib → app/lib}/primer/classify/cache.rb +0 -0
  59. data/{lib → app/lib}/primer/fetch_or_fallback_helper.rb +0 -0
  60. data/{lib → app/lib}/primer/join_style_arguments_helper.rb +0 -0
  61. data/app/lib/primer/view_helper.rb +22 -0
  62. data/app/lib/primer/view_helper/dsl.rb +34 -0
  63. data/lib/primer/view_components/engine.rb +10 -2
  64. data/lib/primer/view_components/version.rb +5 -1
  65. data/static/statuses.json +1 -1
  66. metadata +18 -8
  67. data/app/components/primer/view_components.rb +0 -60
@@ -0,0 +1 @@
1
+ import '@github/tab-container-element';
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use TabContainer to create tabbed content with keyboard support. This component does not add any styles.
5
+ # It only provides the tab functionality. If you want styled Tabs you can look at <%= link_to_component(Primer::TabNavComponent) %>.
6
+ #
7
+ # This component requires javascript.
8
+ class TabContainerComponent < Primer::Component
9
+ # @example Default
10
+ # <%= render(Primer::TabContainerComponent.new) do %>
11
+ # <div role="tablist">
12
+ # <button type="button" role="tab" aria-selected="true">Tab one</button>
13
+ # <button type="button" role="tab" tabindex="-1">Tab two</button>
14
+ # <button type="button" role="tab" tabindex="-1">Tab three</button>
15
+ # </div>
16
+ # <div role="tabpanel">
17
+ # Panel 1
18
+ # </div>
19
+ # <div role="tabpanel" hidden>
20
+ # Panel 2
21
+ # </div>
22
+ # <div role="tabpanel" hidden>
23
+ # Panel 3
24
+ # </div>
25
+ # <% end %>
26
+ #
27
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
28
+ def initialize(**system_arguments)
29
+ @system_arguments = system_arguments
30
+ @system_arguments[:tag] = "tab-container"
31
+ end
32
+
33
+ def call
34
+ render(Primer::BaseComponent.new(**@system_arguments)) { content }
35
+ end
36
+
37
+ def render?
38
+ content.present?
39
+ end
40
+ end
41
+ end
@@ -0,0 +1 @@
1
+ import '@github/tab-container-element'
@@ -0,0 +1,17 @@
1
+ <%= render wrapper.new(**@system_arguments) do %>
2
+ <nav role="tablist" aria-label="<%= @aria_label %>" class="tabnav-tabs">
3
+ <% tabs.each do |tab| %>
4
+ <%= tab %>
5
+ <% end %>
6
+ </nav >
7
+
8
+ <% if @with_panel %>
9
+ <% tabs.each do |tab| %>
10
+ <% if tab.panel.present? %>
11
+ <div role="tabpanel" <%= "hidden" if tab.hidden? %>>
12
+ <%= tab.panel %>
13
+ </div>
14
+ <% end %>
15
+ <% end %>
16
+ <% end %>
17
+ <% end %>
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Use TabNav to style navigation with a tab-based selected state, typically used for navigation placed at the top of the page.
5
+ class TabNavComponent < Primer::Component
6
+ include ViewComponent::SlotableV2
7
+
8
+ class MultipleSelectedTabsError < StandardError; end
9
+ class NoSelectedTabsError < StandardError; end
10
+
11
+ # Tabs to be rendered.
12
+ #
13
+ # @param title [String] Text to be rendered by the tab.
14
+ # @param selected [Boolean] Whether the tab is selected.
15
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
16
+ renders_many :tabs, lambda { |**system_arguments|
17
+ return TabComponent.new(**system_arguments) unless @with_panel
18
+
19
+ TabComponent.new(tag: :button, type: :button, **system_arguments)
20
+ }
21
+
22
+ # @example Default
23
+ # <%= render(Primer::TabNavComponent.new) do |c| %>
24
+ # <% c.tab(selected: true, title: "Tab 1", href: "#") %>
25
+ # <% c.tab(title: "Tab 2", href: "#") %>
26
+ # <% c.tab(title: "Tab 3", href: "#") %>
27
+ # <% end %>
28
+ #
29
+ # @example With panels
30
+ # <%= render(Primer::TabNavComponent.new(with_panel: true)) do |c| %>
31
+ # <% c.tab(selected: true, title: "Tab 1") { "Panel 1" } %>
32
+ # <% c.tab(title: "Tab 2") { "Panel 1" } %>
33
+ # <% c.tab(title: "Tab 3") { "Panel 1" } %>
34
+ # <% end %>
35
+ #
36
+ # @param aria_label [String] Used to set the `aria-label` on the top level `<nav>` element.
37
+ # @param with_panel [Boolean] Whether the TabNav should navigate through pages or panels.
38
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
39
+ def initialize(aria_label: nil, with_panel: false, **system_arguments)
40
+ @aria_label = aria_label
41
+ @with_panel = with_panel
42
+ @system_arguments = system_arguments
43
+ @system_arguments[:tag] ||= :div
44
+
45
+ @system_arguments[:classes] = class_names(
46
+ "tabnav",
47
+ system_arguments[:classes]
48
+ )
49
+ end
50
+
51
+ def before_render
52
+ validate_single_selected_tab
53
+ end
54
+
55
+ private
56
+
57
+ def wrapper
58
+ @with_panel ? Primer::TabContainerComponent : Primer::BaseComponent
59
+ end
60
+
61
+ def validate_single_selected_tab
62
+ raise MultipleSelectedTabsError, "only one tab can be selected" if selected_tabs_count > 1
63
+ raise NoSelectedTabsError, "a tab must be selected" if selected_tabs_count != 1
64
+ end
65
+
66
+ def selected_tabs_count
67
+ @selected_tabs_count ||= tabs.count(&:selected)
68
+ end
69
+
70
+ # Tabs to be rendered.
71
+ class TabComponent < Primer::Component
72
+ attr_reader :selected
73
+
74
+ def initialize(title:, selected: false, **system_arguments)
75
+ @title = title
76
+ @selected = selected
77
+ @system_arguments = system_arguments
78
+ @system_arguments[:tag] ||= :a
79
+ @system_arguments[:role] = :tab
80
+
81
+ if selected
82
+ if @system_arguments[:tag] == :a
83
+ @system_arguments[:"aria-current"] = :page
84
+ else
85
+ @system_arguments[:"aria-selected"] = true
86
+ end
87
+ end
88
+
89
+ @system_arguments[:classes] = class_names(
90
+ "tabnav-tab",
91
+ system_arguments[:classes]
92
+ )
93
+ end
94
+
95
+ def call
96
+ render(Primer::BaseComponent.new(**@system_arguments)) { @title }
97
+ end
98
+
99
+ def panel
100
+ content
101
+ end
102
+
103
+ def hidden?
104
+ !@selected
105
+ end
106
+ end
107
+ end
108
+ end
@@ -3,7 +3,7 @@
3
3
  module Primer
4
4
  # The Text component is a wrapper component that will apply typography styles to the text inside.
5
5
  class TextComponent < Primer::Component
6
- # @example auto|Default
6
+ # @example Default
7
7
  # <%= render(Primer::TextComponent.new(tag: :p, font_weight: :bold)) { "Bold Text" } %>
8
8
  # <%= render(Primer::TextComponent.new(tag: :p, color: :red_5)) { "Red Text" } %>
9
9
  #
@@ -1,17 +1,5 @@
1
- <%= render Primer::BaseComponent.new(**system_arguments) do %>
2
- <% if avatar %>
3
- <%= render Primer::AvatarComponent.new(alt: avatar.alt, src: avatar.src, size: avatar.size, square: avatar.square, **avatar.system_arguments) %>
4
- <% end %>
5
-
6
- <% if badge %>
7
- <%= render Primer::BaseComponent.new(**badge.system_arguments) do %>
8
- <%= octicon badge.icon %>
9
- <% end %>
10
- <% end %>
11
-
12
- <% if body %>
13
- <%= render Primer::BaseComponent.new(**body.system_arguments) do %>
14
- <%= body.content %>
15
- <% end %>
16
- <% end %>
1
+ <%= render Primer::BaseComponent.new(**@system_arguments) do %>
2
+ <%= avatar %>
3
+ <%= badge %>
4
+ <%= body %>
17
5
  <% end %>
@@ -3,20 +3,45 @@
3
3
  module Primer
4
4
  # Use `TimelineItem` to display items on a vertical timeline, connected by badge elements.
5
5
  class TimelineItemComponent < Primer::Component
6
- include ViewComponent::Slotable
6
+ include ViewComponent::SlotableV2
7
7
 
8
- with_slot :avatar, class_name: "Avatar"
9
- with_slot :badge, class_name: "Badge"
10
- with_slot :body, class_name: "Body"
8
+ # Avatar to be rendered to the left of the Badge.
9
+ #
10
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::AvatarComponent) %>.
11
+ renders_one :avatar, lambda { |src:, size: 40, square: true, **system_arguments|
12
+ system_arguments[:classes] = class_names(
13
+ "TimelineItem-avatar",
14
+ system_arguments[:classes]
15
+ )
16
+
17
+ Primer::AvatarComponent.new(src: src, size: size, square: square, **system_arguments)
18
+ }
11
19
 
12
- attr_reader :system_arguments
20
+ # Badge that will be connected to other TimelineItems.
21
+ #
22
+ # @param icon [String] Name of [Octicon](https://primer.style/octicons/) to use.
23
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
24
+ renders_one :badge, "BadgeComponent"
13
25
 
14
- # @example auto|Default
26
+ # Body to be rendered to the left of the Badge.
27
+ #
28
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
29
+ renders_one :body, lambda { |**system_arguments|
30
+ system_arguments[:tag] = :div
31
+ system_arguments[:classes] = class_names(
32
+ "TimelineItem-body",
33
+ system_arguments[:classes]
34
+ )
35
+
36
+ Primer::BaseComponent.new(**system_arguments)
37
+ }
38
+
39
+ # @example Default
15
40
  # <div style="padding-left: 60px">
16
41
  # <%= render(Primer::TimelineItemComponent.new) do |component| %>
17
- # <% component.slot(:avatar, src: "https://github.com/github.png", alt: "github") %>
18
- # <% component.slot(:badge, bg: :green, color: :white, icon: :check) %>
19
- # <% component.slot(:body) { "Success!" } %>
42
+ # <% component.avatar(src: "https://github.com/github.png", alt: "github") %>
43
+ # <% component.badge(bg: :green, color: :white, icon: :check) %>
44
+ # <% component.body { "Success!" } %>
20
45
  # <% end %>
21
46
  # </div>
22
47
  #
@@ -36,36 +61,9 @@ module Primer
36
61
  avatar.present? || badge.present? || body.present?
37
62
  end
38
63
 
39
- # :nodoc:
40
- class Avatar < Primer::Slot
41
- attr_reader :system_arguments, :alt, :src, :size, :square
42
-
43
- # @param alt [String] Alt text for avatar image.
44
- # @param src [String] Src attribute for avatar image.
45
- # @param size [Integer] Image size.
46
- # @param square [Boolean] Whether to round the edges of the image.
47
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
48
- def initialize(alt: nil, src: nil, size: 40, square: true, **system_arguments)
49
- @alt = alt
50
- @src = src
51
- @size = size
52
- @square = square
53
-
54
- @system_arguments = system_arguments
55
- @system_arguments[:tag] = :div
56
- @system_arguments[:classes] = class_names(
57
- "TimelineItem-avatar",
58
- system_arguments[:classes]
59
- )
60
- end
61
- end
62
-
63
- # :nodoc:
64
- class Badge < Primer::Slot
65
- attr_reader :system_arguments, :icon
66
-
67
- # @param icon [String] Name of [Octicon](https://primer.style/octicons/) to use.
68
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
64
+ # This component is part of `Primer::TimelineItemComponent` and should not be
65
+ # used as a standalone component.
66
+ class BadgeComponent < Primer::Component
69
67
  def initialize(icon: nil, **system_arguments)
70
68
  @icon = icon
71
69
 
@@ -76,20 +74,11 @@ module Primer
76
74
  system_arguments[:classes]
77
75
  )
78
76
  end
79
- end
80
-
81
- # :nodoc:
82
- class Body < Primer::Slot
83
- attr_reader :system_arguments
84
77
 
85
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
86
- def initialize(**system_arguments)
87
- @system_arguments = system_arguments
88
- @system_arguments[:tag] = :div
89
- @system_arguments[:classes] = class_names(
90
- "TimelineItem-body",
91
- system_arguments[:classes]
92
- )
78
+ def call
79
+ render(Primer::BaseComponent.new(**@system_arguments)) do
80
+ render(Primer::OcticonComponent.new(icon: @icon))
81
+ end
93
82
  end
94
83
  end
95
84
  end
@@ -26,29 +26,29 @@ module Primer
26
26
  se
27
27
  ]
28
28
 
29
- # @example 55|Default
29
+ # @example Default
30
30
  # <div class="pt-5">
31
31
  # <%= render(Primer::TooltipComponent.new(label: "Even bolder")) { "Default Bold Text" } %>
32
32
  # </div>
33
33
  #
34
- # @example 65|Wrapping another component
34
+ # @example Wrapping another component
35
35
  # <div class="pt-5">
36
36
  # <%= render(Primer::TooltipComponent.new(label: "Even bolder")) do %>
37
37
  # <%= render(Primer::ButtonComponent.new) { "Bold Button" } %>
38
38
  # <% end %>
39
39
  # </div>
40
40
  #
41
- # @example 65|With a direction
41
+ # @example With a direction
42
42
  # <div class="pt-5">
43
43
  # <%= render(Primer::TooltipComponent.new(label: "Even bolder", direction: :s)) { "Bold Text With a Direction" } %>
44
44
  # </div>
45
45
  #
46
- # @example 65|With an alignment
46
+ # @example With an alignment
47
47
  # <div class="pt-5">
48
48
  # <%= render(Primer::TooltipComponent.new(label: "Even bolder", direction: :s, alignment: :right_1)) { "Bold Text With an Alignment" } %>
49
49
  # </div>
50
50
  #
51
- # @example 65|Without a delay
51
+ # @example Without a delay
52
52
  # <div class="pt-5">
53
53
  # <%= render(Primer::TooltipComponent.new(label: "Even bolder", direction: :s, no_delay: true)) { "Bold Text without a delay" } %>
54
54
  # </div>
@@ -3,18 +3,18 @@
3
3
  module Primer
4
4
  # Use TruncateComponent to shorten overflowing text with an ellipsis.
5
5
  class TruncateComponent < Primer::Component
6
- # @example auto|Default
6
+ # @example Default
7
7
  # <div class="col-2">
8
8
  # <%= render(Primer::TruncateComponent.new(tag: :p)) { "branch-name-that-is-really-long" } %>
9
9
  # </div>
10
10
  #
11
- # @example auto|Inline
11
+ # @example Inline
12
12
  # <%= render(Primer::TruncateComponent.new(tag: :span, inline: true)) { "branch-name-that-is-really-long" } %>
13
13
  #
14
- # @example auto|Expandable
14
+ # @example Expandable
15
15
  # <%= render(Primer::TruncateComponent.new(tag: :span, inline: true, expandable: true)) { "branch-name-that-is-really-long" } %>
16
16
  #
17
- # @example auto|Custom size
17
+ # @example Custom size
18
18
  # <%= render(Primer::TruncateComponent.new(tag: :span, inline: true, expandable: true, max_width: 100)) { "branch-name-that-is-really-long" } %>
19
19
  #
20
20
  # @param inline [Boolean] Whether the element is inline (or inline-block).
@@ -10,7 +10,7 @@ module Primer
10
10
 
11
11
  with_content_areas :body, :actions
12
12
 
13
- # @example auto|Default
13
+ # @example Default
14
14
  # <%= render(Primer::UnderlineNavComponent.new) do |component| %>
15
15
  # <% component.with(:body) do %>
16
16
  # <%= render(Primer::LinkComponent.new(href: "#url")) { "Item 1" } %>
@@ -20,7 +20,7 @@ module Primer
20
20
  # <% end %>
21
21
  # <% end %>
22
22
  #
23
- # @example auto|Align right
23
+ # @example Align right
24
24
  # <%= render(Primer::UnderlineNavComponent.new(align: :right)) do |component| %>
25
25
  # <% component.with(:body) do %>
26
26
  # <%= render(Primer::LinkComponent.new(href: "#url")) { "Item 1" } %>
File without changes
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "classify/cache"
4
-
5
3
  module Primer
6
4
  # :nodoc:
7
5
  class Classify
@@ -182,7 +180,7 @@ module Primer
182
180
  end
183
181
 
184
182
  def extract_value(memo, key, val, breakpoint)
185
- return if val.nil?
183
+ return if val.nil? || val == ""
186
184
 
187
185
  if SPACING_KEYS.include?(key)
188
186
  if MARGIN_DIRECTION_KEYS.include?(key)
File without changes
File without changes
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ # Module to allow shorthand calls for registered Primer components
5
+ #
6
+ # Registered components can be called with
7
+ # `primer(:name, **kwargs) { block }` instead of
8
+ # `render Primer::NameComponent.new(**kwargs) { block }`
9
+ module ViewHelper
10
+ extend ActiveSupport::Concern
11
+
12
+ class ViewHelperNotFound < StandardError; end
13
+
14
+ def primer(name, **component_args, &block)
15
+ component = Primer::Component.primer_helpers[name]
16
+
17
+ raise ViewHelperNotFound, "no component defined for helper #{name}" if component.blank?
18
+
19
+ render component.new(**component_args), &block
20
+ end
21
+ end
22
+ end