primer_view_components 0.0.21 → 0.0.22

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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  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 +3 -3
  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 +2 -2
  9. data/app/components/primer/blankslate_component.rb +7 -7
  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.rb +1 -1
  14. data/app/components/primer/button_component.rb +2 -2
  15. data/app/components/primer/button_group_component.rb +1 -1
  16. data/app/components/primer/button_marketing_component.rb +2 -2
  17. data/app/components/primer/component.rb +4 -0
  18. data/app/components/primer/counter_component.rb +1 -1
  19. data/app/components/primer/details_component.html.erb +2 -6
  20. data/app/components/primer/details_component.rb +22 -35
  21. data/app/components/primer/dropdown_component.html.erb +2 -2
  22. data/app/components/primer/dropdown_component.rb +4 -6
  23. data/app/components/primer/dropdown_menu_component.rb +4 -4
  24. data/app/components/primer/flash_component.html.erb +2 -2
  25. data/app/components/primer/flash_component.rb +5 -5
  26. data/app/components/primer/flex_component.rb +4 -4
  27. data/app/components/primer/flex_item_component.rb +1 -1
  28. data/app/components/primer/heading_component.rb +3 -1
  29. data/app/components/primer/label_component.rb +2 -2
  30. data/app/components/primer/layout_component.rb +2 -2
  31. data/app/components/primer/link_component.rb +2 -2
  32. data/app/components/primer/markdown_component.rb +8 -8
  33. data/app/components/primer/menu_component.rb +1 -1
  34. data/app/components/primer/octicon_component.rb +5 -3
  35. data/app/components/primer/popover_component.rb +3 -3
  36. data/app/components/primer/primer.js +1 -0
  37. data/app/components/primer/primer.ts +1 -0
  38. data/app/components/primer/progress_bar_component.rb +4 -4
  39. data/app/components/primer/spinner_component.rb +3 -3
  40. data/app/components/primer/state_component.rb +21 -10
  41. data/app/components/primer/subhead_component.rb +3 -3
  42. data/app/components/primer/tab_container_component.js +1 -0
  43. data/app/components/primer/tab_container_component.rb +41 -0
  44. data/app/components/primer/tab_container_component.ts +1 -0
  45. data/app/components/primer/tab_nav_component.html.erb +17 -0
  46. data/app/components/primer/tab_nav_component.rb +108 -0
  47. data/app/components/primer/text_component.rb +1 -1
  48. data/app/components/primer/timeline_item_component.rb +1 -1
  49. data/app/components/primer/tooltip_component.rb +5 -5
  50. data/app/components/primer/truncate_component.rb +4 -4
  51. data/app/components/primer/underline_nav_component.rb +2 -2
  52. data/{lib → app/lib}/primer/class_name_helper.rb +0 -0
  53. data/{lib → app/lib}/primer/classify.rb +0 -2
  54. data/{lib → app/lib}/primer/classify/cache.rb +0 -0
  55. data/{lib → app/lib}/primer/fetch_or_fallback_helper.rb +0 -0
  56. data/{lib → app/lib}/primer/join_style_arguments_helper.rb +0 -0
  57. data/app/lib/primer/view_helper.rb +22 -0
  58. data/app/lib/primer/view_helper/dsl.rb +34 -0
  59. data/lib/primer/view_components/engine.rb +10 -2
  60. data/lib/primer/view_components/version.rb +5 -1
  61. data/static/statuses.json +1 -1
  62. metadata +18 -8
  63. data/app/components/primer/view_components.rb +0 -60
@@ -15,13 +15,13 @@ module Primer
15
15
  DEFAULT_STYLE = "box-sizing: content-box; color: var(--color-icon-primary);"
16
16
 
17
17
  #
18
- # @example auto|Default
18
+ # @example Default
19
19
  # <%= render(Primer::SpinnerComponent.new) %>
20
20
  #
21
- # @example auto|Small
21
+ # @example Small
22
22
  # <%= render(Primer::SpinnerComponent.new(size: :small)) %>
23
23
  #
24
- # @example auto|Large
24
+ # @example Large
25
25
  # <%= render(Primer::SpinnerComponent.new(size: :large)) %>
26
26
  #
27
27
  # @param size [Symbol] <%= one_of(Primer::SpinnerComponent::SIZE_MAPPINGS) %>
@@ -4,12 +4,19 @@ module Primer
4
4
  # Component for rendering the status of an item.
5
5
  class StateComponent < Primer::Component
6
6
  COLOR_DEFAULT = :default
7
- COLOR_MAPPINGS = {
7
+ NEW_COLOR_MAPPINGS = {
8
+ open: "State--open",
9
+ closed: "State--closed",
10
+ merged: "State--merged"
11
+ }.freeze
12
+
13
+ DEPRECATED_COLOR_MAPPINGS = {
8
14
  COLOR_DEFAULT => "",
9
- :green => "State--green",
10
- :red => "State--red",
11
- :purple => "State--purple"
15
+ :green => "State--open",
16
+ :red => "State--closed",
17
+ :purple => "State--merged"
12
18
  }.freeze
19
+ COLOR_MAPPINGS = NEW_COLOR_MAPPINGS.merge(DEPRECATED_COLOR_MAPPINGS)
13
20
  COLOR_OPTIONS = COLOR_MAPPINGS.keys
14
21
 
15
22
  SIZE_DEFAULT = :default
@@ -22,16 +29,16 @@ module Primer
22
29
  TAG_DEFAULT = :span
23
30
  TAG_OPTIONS = [TAG_DEFAULT, :div, :a].freeze
24
31
 
25
- # @example auto|Default
32
+ # @example Default
26
33
  # <%= render(Primer::StateComponent.new(title: "title")) { "State" } %>
27
34
  #
28
- # @example auto|Colors
35
+ # @example Colors
29
36
  # <%= render(Primer::StateComponent.new(title: "title")) { "Default" } %>
30
- # <%= render(Primer::StateComponent.new(title: "title", color: :green)) { "Green" } %>
31
- # <%= render(Primer::StateComponent.new(title: "title", color: :red)) { "Red" } %>
32
- # <%= render(Primer::StateComponent.new(title: "title", color: :purple)) { "Purple" } %>
37
+ # <%= render(Primer::StateComponent.new(title: "title", color: :open)) { "Open" } %>
38
+ # <%= render(Primer::StateComponent.new(title: "title", color: :closed)) { "Closed" } %>
39
+ # <%= render(Primer::StateComponent.new(title: "title", color: :merged)) { "Merged" } %>
33
40
  #
34
- # @example auto|Sizes
41
+ # @example Sizes
35
42
  # <%= render(Primer::StateComponent.new(title: "title")) { "Default" } %>
36
43
  # <%= render(Primer::StateComponent.new(title: "title", size: :small)) { "Small" } %>
37
44
  #
@@ -61,5 +68,9 @@ module Primer
61
68
  def call
62
69
  render(Primer::BaseComponent.new(**@system_arguments)) { content }
63
70
  end
71
+
72
+ def self.status
73
+ Primer::Component::STATUSES[:beta]
74
+ end
64
75
  end
65
76
  end
@@ -9,7 +9,7 @@ module Primer
9
9
  with_slot :actions, class_name: "Actions"
10
10
  with_slot :description, class_name: "Description"
11
11
 
12
- # @example auto|Default
12
+ # @example Default
13
13
  # <%= render(Primer::SubheadComponent.new) do |component| %>
14
14
  # <% component.slot(:heading) do %>
15
15
  # My Heading
@@ -19,7 +19,7 @@ module Primer
19
19
  # <% end %>
20
20
  # <% end %>
21
21
  #
22
- # @example auto|Without border
22
+ # @example Without border
23
23
  # <%= render(Primer::SubheadComponent.new(hide_border: true)) do |component| %>
24
24
  # <% component.slot(:heading) do %>
25
25
  # My Heading
@@ -29,7 +29,7 @@ module Primer
29
29
  # <% end %>
30
30
  # <% end %>
31
31
  #
32
- # @example auto|With actions
32
+ # @example With actions
33
33
  # <%= render(Primer::SubheadComponent.new) do |component| %>
34
34
  # <% component.slot(:heading) do %>
35
35
  # My Heading
@@ -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
  #
@@ -36,7 +36,7 @@ module Primer
36
36
  Primer::BaseComponent.new(**system_arguments)
37
37
  }
38
38
 
39
- # @example auto|Default
39
+ # @example Default
40
40
  # <div style="padding-left: 60px">
41
41
  # <%= render(Primer::TimelineItemComponent.new) do |component| %>
42
42
  # <% component.avatar(src: "https://github.com/github.png", alt: "github") %>
@@ -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
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
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ module Primer
6
+ # :nodoc:
7
+ module ViewHelper
8
+ # DSL to allow components to register a View Helper for shorthand calls.
9
+ #
10
+ # Example:
11
+ #
12
+ # class MyComponent < ViewComponent::Base
13
+ # include Primer::ViewHelper::DSL
14
+ # view_helper :my_component
15
+ # end
16
+ module DSL
17
+ extend ActiveSupport::Concern
18
+
19
+ class ViewHelperAlreadyDefined < StandardError; end
20
+
21
+ included do
22
+ class_attribute :primer_helpers, instance_writer: false, default: {}
23
+ end
24
+
25
+ class_methods do
26
+ def view_helper(name)
27
+ raise ViewHelperAlreadyDefined, "#{name} is already defined" if primer_helpers[name].present?
28
+
29
+ primer_helpers[name] = self
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end