primer_view_components 0.0.21 → 0.0.22

Sign up to get free protection for your applications and to get access to all the features.
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