primer_view_components 0.0.123 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/app/assets/javascripts/primer_view_components.js +1 -1
  4. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  5. data/app/assets/styles/primer_view_components.css +2 -2
  6. data/app/assets/styles/primer_view_components.css.map +1 -1
  7. data/app/components/primer/alpha/action_list/divider.rb +2 -2
  8. data/app/components/primer/alpha/action_list/heading.html.erb +1 -1
  9. data/app/components/primer/alpha/action_list/heading.rb +10 -4
  10. data/app/components/primer/alpha/action_list/item.rb +7 -3
  11. data/app/components/primer/alpha/action_list.css.json +41 -0
  12. data/app/components/primer/alpha/action_list.html.erb +6 -8
  13. data/app/components/primer/alpha/action_list.rb +5 -10
  14. data/app/components/primer/alpha/auto_complete.css.json +11 -0
  15. data/app/components/primer/alpha/banner.css.json +14 -0
  16. data/app/components/primer/alpha/button_marketing.css.json +10 -0
  17. data/app/components/primer/alpha/dialog.css.json +63 -0
  18. data/app/components/primer/alpha/dropdown.css.json +21 -0
  19. data/app/components/primer/alpha/layout.css.json +27 -0
  20. data/app/components/primer/alpha/menu.css.json +11 -0
  21. data/app/components/primer/alpha/nav_list/{section.rb → group.rb} +9 -9
  22. data/app/components/primer/alpha/nav_list/item.html.erb +1 -1
  23. data/app/components/primer/alpha/nav_list/item.rb +18 -2
  24. data/app/components/primer/alpha/nav_list.d.ts +1 -0
  25. data/app/components/primer/alpha/nav_list.html.erb +8 -8
  26. data/app/components/primer/alpha/nav_list.js +24 -0
  27. data/app/components/primer/alpha/nav_list.rb +28 -32
  28. data/app/components/primer/alpha/nav_list.ts +27 -0
  29. data/app/components/primer/alpha/navigation/tab.rb +168 -0
  30. data/app/components/primer/alpha/overlay/body.rb +26 -0
  31. data/app/components/primer/alpha/overlay/footer.rb +41 -0
  32. data/app/components/primer/alpha/overlay/header.html.erb +15 -0
  33. data/app/components/primer/alpha/overlay/header.rb +47 -0
  34. data/app/components/primer/alpha/overlay.css +1 -0
  35. data/app/components/primer/alpha/overlay.css.json +11 -0
  36. data/app/components/primer/alpha/overlay.css.map +1 -0
  37. data/app/components/primer/alpha/overlay.html.erb +11 -0
  38. data/app/components/primer/alpha/overlay.pcss +14 -0
  39. data/app/components/primer/alpha/overlay.rb +207 -0
  40. data/app/components/primer/alpha/segmented_control.css.json +15 -0
  41. data/app/components/primer/alpha/tab_nav.css.json +10 -0
  42. data/app/components/primer/alpha/tab_nav.rb +10 -3
  43. data/app/components/primer/alpha/tab_panels.rb +2 -2
  44. data/app/components/primer/alpha/text_field.css.json +38 -0
  45. data/app/components/primer/alpha/toggle_switch.css.json +16 -0
  46. data/app/components/primer/alpha/underline_nav.css +1 -1
  47. data/app/components/primer/alpha/underline_nav.css.json +13 -0
  48. data/app/components/primer/alpha/underline_nav.css.map +1 -1
  49. data/app/components/primer/alpha/underline_nav.pcss +1 -0
  50. data/app/components/primer/alpha/underline_nav.rb +2 -2
  51. data/app/components/primer/alpha/underline_panels.rb +2 -2
  52. data/app/components/primer/anchored_position.d.ts +27 -0
  53. data/app/components/primer/anchored_position.js +149 -0
  54. data/app/components/primer/anchored_position.ts +167 -0
  55. data/app/components/primer/beta/avatar.css.json +14 -0
  56. data/app/components/primer/beta/avatar_stack.css.json +9 -0
  57. data/app/components/primer/beta/blankslate.css.json +12 -0
  58. data/app/components/primer/beta/border_box.css.json +32 -0
  59. data/app/components/primer/beta/breadcrumbs.css.json +4 -0
  60. data/app/components/primer/beta/button.css.json +22 -0
  61. data/app/components/primer/beta/button.html.erb +1 -1
  62. data/app/components/primer/beta/button.rb +2 -1
  63. data/app/components/primer/beta/counter.css.json +6 -0
  64. data/app/components/primer/beta/flash.css.json +15 -0
  65. data/app/components/primer/beta/flash.html.erb +1 -2
  66. data/app/components/primer/beta/label.css.json +20 -0
  67. data/app/components/primer/beta/link.css.json +8 -0
  68. data/app/components/primer/beta/popover.css.json +18 -0
  69. data/app/components/primer/beta/progress_bar.css.json +6 -0
  70. data/app/components/primer/beta/state.css.json +10 -0
  71. data/app/components/primer/beta/subhead.css.json +8 -0
  72. data/app/components/primer/beta/timeline_item.css.json +9 -0
  73. data/app/components/primer/beta/truncate.css.json +6 -0
  74. data/app/components/primer/component.rb +34 -0
  75. data/app/components/primer/navigation/tab_component.rb +3 -157
  76. data/app/components/primer/primer.d.ts +2 -0
  77. data/app/components/primer/primer.js +2 -0
  78. data/app/components/primer/primer.pcss +3 -0
  79. data/app/components/primer/primer.ts +2 -0
  80. data/app/components/primer/truncate.css.json +7 -0
  81. data/app/lib/primer/css/layout.css.json +263 -0
  82. data/app/lib/primer/css/utilities.css.json +1636 -0
  83. data/lib/primer/deprecations.yml +4 -0
  84. data/lib/primer/view_components/linters/base_linter.rb +1 -1
  85. data/lib/primer/view_components/linters/disallow_component_css_counter.rb +30 -0
  86. data/lib/primer/view_components/version.rb +2 -2
  87. data/lib/primer/yard/component_manifest.rb +3 -1
  88. data/lib/tasks/docs.rake +1 -1
  89. data/previews/primer/alpha/action_list_preview.rb +6 -14
  90. data/previews/primer/alpha/nav_list_preview/trailing_action.html.erb +19 -0
  91. data/previews/primer/alpha/nav_list_preview.rb +19 -30
  92. data/previews/primer/alpha/overlay_preview/middle_of_page.html.erb +17 -0
  93. data/previews/primer/alpha/overlay_preview.rb +112 -0
  94. data/previews/primer/alpha/tab_nav_preview/with_extra.html.erb +8 -0
  95. data/previews/primer/alpha/tab_nav_preview.rb +5 -0
  96. data/previews/primer/alpha/tab_panels_preview/with_extra.html.erb +17 -0
  97. data/previews/primer/alpha/tab_panels_preview.rb +5 -0
  98. data/static/arguments.json +167 -7
  99. data/static/audited_at.json +6 -1
  100. data/static/classes.json +311 -0
  101. data/static/constants.json +122 -8
  102. data/static/previews.json +31 -0
  103. data/static/statuses.json +7 -2
  104. metadata +25 -6
  105. data/app/components/primer/alpha/nav_list/section.html.erb +0 -3
  106. data/previews/primer/alpha/action_list_preview/heading.html.erb +0 -4
  107. /data/app/components/primer/{navigation/tab_component.html.erb → alpha/navigation/tab.html.erb} +0 -0
@@ -3,13 +3,13 @@
3
3
  module Primer
4
4
  module Alpha
5
5
  # `NavList` provides a simple way to render side navigation, i.e. navigation
6
- # that appears to the left or right side of some main content. Each section in a
6
+ # that appears to the left or right side of some main content. Each group in a
7
7
  # nav list is a list of links.
8
8
  #
9
- # Nav list sections can contain sub items. Rather than navigating to a URL, sections
9
+ # Nav list groups can contain sub items. Rather than navigating to a URL, groups
10
10
  # with sub items expand and collapse on click. To indicate this functionality, the
11
- # section will automatically render with a trailing chevron icon that changes direction
12
- # when the section expands and collapses.
11
+ # group will automatically render with a trailing chevron icon that changes direction
12
+ # when the group expands and collapses.
13
13
  #
14
14
  # Nav list items appear visually active when selected. Each nav item must have one
15
15
  # or more ID values that determine which item will appear selected. Use the
@@ -22,40 +22,40 @@ module Primer
22
22
  "nav-list"
23
23
  end
24
24
 
25
- # Sections. Each section is a list of links and an optional heading.
25
+ # Groups. Each group is a list of links and an optional heading.
26
26
  #
27
- # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::NavList::Section) %>.
28
- renders_many :sections, lambda { |**system_arguments|
29
- Primer::Alpha::NavList::Section.new(selected_item_id: @selected_item_id, **system_arguments)
27
+ # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::NavList::Group) %>.
28
+ renders_many :groups, lambda { |**system_arguments|
29
+ Primer::Alpha::NavList::Group.new(selected_item_id: @selected_item_id, **system_arguments)
30
30
  }
31
31
 
32
32
  # @example Items and headings
33
33
  #
34
34
  # <%= render(Primer::Alpha::NavList.new(selected_item_id: :personal_info)) do |component| %>
35
- # <% component.with_section(aria: { label: "Settings" }) do |section| %>
36
- # <% section.with_item(label: "General", selected_by_ids: :general, href: "/settings/general") %>
35
+ # <% component.with_group(aria: { label: "Settings" }) do |group| %>
36
+ # <% group.with_item(label: "General", selected_by_ids: :general, href: "/settings/general") %>
37
37
  # <% end %>
38
- # <% component.with_section(aria: { label: "Account settings" }) do |section| %>
39
- # <% section.with_heading(title: "Account Settings") %>
40
- # <% section.with_item(label: "Personal Information", selected_by_ids: :personal_info, href: "/account/info") %>
41
- # <% section.with_item(label: "Password", selected_by_ids: :password, href: "/account/password") %>
42
- # <% section.with_item(label: "Billing info", selected_by_ids: :billing, href: "/account/billing") %>
38
+ # <% component.with_group do |group| %>
39
+ # <% group.with_heading(title: "Account Settings") %>
40
+ # <% group.with_item(label: "Personal Information", selected_by_ids: :personal_info, href: "/account/info") %>
41
+ # <% group.with_item(label: "Password", selected_by_ids: :password, href: "/account/password") %>
42
+ # <% group.with_item(label: "Billing info", selected_by_ids: :billing, href: "/account/billing") %>
43
43
  # <% end %>
44
44
  # <% end %>
45
45
  #
46
46
  # @example Leading and trailing visuals
47
47
  #
48
48
  # <%= render(Primer::Alpha::NavList.new(selected_item_id: :personal_info)) do |component| %>
49
- # <% component.with_section(aria: { label: "Account settings" }) do |section| %>
50
- # <% section.with_heading(title: "Account Settings") %>
51
- # <% section.with_item(label: "Personal Information", selected_by_ids: :personal_info, href: "/account/info") do |item| %>
49
+ # <% component.with_group do |group| %>
50
+ # <% group.with_heading(title: "Account Settings") %>
51
+ # <% group.with_item(label: "Personal Information", selected_by_ids: :personal_info, href: "/account/info") do |item| %>
52
52
  # <% item.with_leading_visual_avatar(src: "https://github.com/github.png", alt: "GitHub") %>
53
53
  # <% end %>
54
- # <% section.with_item(label: "Notifications", selected_by_ids: :notifications, href: "/account/notifications") do |item| %>
54
+ # <% group.with_item(label: "Notifications", selected_by_ids: :notifications, href: "/account/notifications") do |item| %>
55
55
  # <% item.with_leading_visual_icon(icon: :bell) %>
56
56
  # <% item.with_trailing_visual_counter(count: 15) %>
57
57
  # <% end %>
58
- # <% section.with_item(label: "Billing info", selected_by_ids: :billing, href: "/account/billing") do |item| %>
58
+ # <% group.with_item(label: "Billing info", selected_by_ids: :billing, href: "/account/billing") do |item| %>
59
59
  # <% item.with_leading_visual_icon(icon: :package) %>
60
60
  # <% item.with_trailing_visual_icon(icon: :"dot-fill", color: :attention) %>
61
61
  # <% end %>
@@ -65,9 +65,9 @@ module Primer
65
65
  # @example Expandable sub items
66
66
  #
67
67
  # <%= render(Primer::Alpha::NavList.new(selected_item_id: :email_notifications)) do |component| %>
68
- # <% component.with_section(aria: { label: "Account settings" }) do |section| %>
69
- # <% section.with_heading(title: "Account Settings") %>
70
- # <% section.with_item(label: "Notification settings", selected_by_ids: :notifications) do |item| %>
68
+ # <% component.with_group do |group| %>
69
+ # <% group.with_heading(title: "Account Settings") %>
70
+ # <% group.with_item(label: "Notification settings") do |item| %>
71
71
  # <% item.with_leading_visual_icon(icon: :bell) %>
72
72
  # <% item.with_item(label: "Email", selected_by_ids: :email_notifications, href: "/account/notifications/email") do |subitem| %>
73
73
  # <% subitem.with_trailing_visual_icon(icon: :mail) %>
@@ -76,7 +76,7 @@ module Primer
76
76
  # <% subitem.with_trailing_visual_icon(icon: :"device-mobile") %>
77
77
  # <% end %>
78
78
  # <% end %>
79
- # <% section.with_item(label: "Messages", selected_by_ids: :messages) do |item| %>
79
+ # <% group.with_item(label: "Messages") do |item| %>
80
80
  # <% item.with_leading_visual_icon(icon: :bookmark) %>
81
81
  # <% item.with_item(label: "Inbox", href: "/account/messages/inbox") do |subitem| %>
82
82
  # <% subitem.with_trailing_visual_counter(count: 10) %>
@@ -91,12 +91,12 @@ module Primer
91
91
  # @example Trailing action
92
92
  #
93
93
  # <%= render(Primer::Alpha::NavList.new) do |component| %>
94
- # <% component.with_section(aria: { label: "Favorite foods" }) do |section| %>
95
- # <% section.with_heading(title: "My Favorite Foods") %>
96
- # <% section.with_item(label: "Popplers", selected_by_ids: :popplers, href: "/foods/popplers") do |item| %>
94
+ # <% component.with_group do |group| %>
95
+ # <% group.with_heading(title: "My Favorite Foods") %>
96
+ # <% group.with_item(label: "Popplers", selected_by_ids: :popplers, href: "/foods/popplers") do |item| %>
97
97
  # <% item.with_trailing_action(show_on_hover: false, icon: "plus", "aria-label": "Add new food", size: :medium) %>
98
98
  # <% end %>
99
- # <% section.with_item(label: "Slurm", selected_by_ids: :slurm, href: "/foods/slurm") do |item| %>
99
+ # <% group.with_item(label: "Slurm", selected_by_ids: :slurm, href: "/foods/slurm") do |item| %>
100
100
  # <% item.with_trailing_action(show_on_hover: true, icon: "plus", "aria-label": "Add new food", size: :medium) %>
101
101
  # <% end %>
102
102
  # <% end %>
@@ -106,10 +106,6 @@ module Primer
106
106
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
107
107
  def initialize(selected_item_id: nil, **system_arguments)
108
108
  @system_arguments = system_arguments
109
- @system_arguments[:classes] = class_names(
110
- @system_arguments[:classes],
111
- "ActionListWrap"
112
- )
113
109
  @selected_item_id = selected_item_id
114
110
  end
115
111
  end
@@ -87,6 +87,7 @@ export class NavListElement extends HTMLElement {
87
87
  collapseItem(item: HTMLElement) {
88
88
  item.nextElementSibling?.setAttribute('data-hidden', '')
89
89
  item.setAttribute('aria-expanded', 'false')
90
+ item.focus()
90
91
  }
91
92
 
92
93
  itemIsExpanded(item: HTMLElement | null) {
@@ -112,6 +113,28 @@ export class NavListElement extends HTMLElement {
112
113
  e.stopPropagation()
113
114
  }
114
115
 
116
+ // collapse item
117
+ handleItemWithSubItemKeydown(e: KeyboardEvent) {
118
+ const el = e.currentTarget
119
+ if (!(el instanceof HTMLElement)) return
120
+
121
+ let button = el.closest<HTMLButtonElement>('button')
122
+ if (!button) {
123
+ const button_id = el.getAttribute('aria-labelledby')
124
+ if (button_id) {
125
+ button = document.getElementById(button_id) as HTMLButtonElement
126
+ } else {
127
+ return
128
+ }
129
+ }
130
+
131
+ if (this.itemIsExpanded(button) && e.key === 'Escape') {
132
+ this.collapseItem(button)
133
+ }
134
+
135
+ e.stopPropagation()
136
+ }
137
+
115
138
  private async showMore(e: Event) {
116
139
  e.preventDefault()
117
140
  if (this.showMoreDisabled) return
@@ -162,6 +185,10 @@ export class NavListElement extends HTMLElement {
162
185
  #findSelectedNavItemById(itemId: string): HTMLElement | null {
163
186
  // First we compare the selected link to data-item-id for each nav item
164
187
  for (const navItem of this.items) {
188
+ if (navItem.classList.contains('ActionListItem--hasSubItem')) {
189
+ continue
190
+ }
191
+
165
192
  const keys = navItem.getAttribute('data-item-id')?.split(' ') || []
166
193
 
167
194
  if (keys.includes(itemId)) {
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Alpha
5
+ module Navigation
6
+ # This component is part of navigation components such as `Primer::Alpha::TabNav`
7
+ # and `Primer::Alpha::UnderlineNav` and should not be used by itself.
8
+ #
9
+ # @accessibility
10
+ # `Tab` renders the selected anchor tab with `aria-current="page"` by default.
11
+ # When the selected tab does not correspond to the current page, such as in a nested inner tab, make sure to use aria-current="true"
12
+ class Tab < Primer::Component
13
+ status :alpha
14
+
15
+ DEFAULT_ARIA_CURRENT_FOR_ANCHOR = :page
16
+ ARIA_CURRENT_OPTIONS_FOR_ANCHOR = [true, DEFAULT_ARIA_CURRENT_FOR_ANCHOR].freeze
17
+ # Panel controlled by the Tab. This will not render anything in the tab itself.
18
+ # It will provide a accessor for the Tab's parent to call and render the panel
19
+ # content in the appropriate place.
20
+ # Refer to `UnderlineNav` and `TabNav` implementations for examples.
21
+ #
22
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
23
+ renders_one :panel, lambda { |**system_arguments|
24
+ return unless @with_panel
25
+
26
+ deny_tag_argument(**system_arguments)
27
+ system_arguments[:id] = @panel_id
28
+ system_arguments[:tag] = :div
29
+ system_arguments[:role] ||= :tabpanel
30
+ system_arguments[:tabindex] = 0
31
+ system_arguments[:hidden] = true unless @selected
32
+
33
+ label_present = aria("label", system_arguments) || aria("labelledby", system_arguments)
34
+ unless label_present
35
+ if @id.present?
36
+ system_arguments[:"aria-labelledby"] = @id
37
+ elsif !Rails.env.production?
38
+ raise ArgumentError, "Panels must be labelled. Either set a unique `id` on the tab, or set an `aria-label` directly on the panel"
39
+ end
40
+ end
41
+
42
+ Primer::BaseComponent.new(**system_arguments)
43
+ }
44
+
45
+ # Icon to be rendered in the Tab left.
46
+ #
47
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::Beta::Octicon) %>.
48
+ renders_one :icon, lambda { |icon = nil, **system_arguments|
49
+ system_arguments[:classes] = class_names(
50
+ @icon_classes,
51
+ system_arguments[:classes]
52
+ )
53
+ Primer::Beta::Octicon.new(icon, **system_arguments)
54
+ }
55
+
56
+ # The Tab's text.
57
+ #
58
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::Beta::Text) %>.
59
+ renders_one :text, Primer::Beta::Text
60
+
61
+ # Counter to be rendered in the Tab right.
62
+ #
63
+ # @param kwargs [Hash] The same arguments as <%= link_to_component(Primer::Beta::Counter) %>.
64
+ renders_one :counter, Primer::Beta::Counter
65
+
66
+ attr_reader :selected
67
+
68
+ # @example Default
69
+ # <%= render(Primer::Alpha::Navigation::Tab.new(selected: true)) do |component| %>
70
+ # <% component.with_text { "Selected" } %>
71
+ # <% end %>
72
+ # <%= render(Primer::Alpha::Navigation::Tab.new) do |component| %>
73
+ # <% component.with_text { "Not selected" } %>
74
+ # <% end %>
75
+ #
76
+ # @example With icons and counters
77
+ # <%= render(Primer::Alpha::Navigation::Tab.new) do |component| %>
78
+ # <% component.with_icon(:star) %>
79
+ # <% component.with_text { "Tab" } %>
80
+ # <% end %>
81
+ # <%= render(Primer::Alpha::Navigation::Tab.new) do |component| %>
82
+ # <% component.with_icon(:star) %>
83
+ # <% component.with_text { "Tab" } %>
84
+ # <% component.with_counter(count: 10) %>
85
+ # <% end %>
86
+ # <%= render(Primer::Alpha::Navigation::Tab.new) do |component| %>
87
+ # <% component.with_text { "Tab" } %>
88
+ # <% component.with_counter(count: 10) %>
89
+ # <% end %>
90
+ #
91
+ # @example Inside a list
92
+ # <%= render(Primer::Alpha::Navigation::Tab.new(list: true)) do |component| %>
93
+ # <% component.with_text { "Tab" } %>
94
+ # <% end %>
95
+ #
96
+ # @example With custom HTML
97
+ # <%= render(Primer::Alpha::Navigation::Tab.new) do %>
98
+ # <div>
99
+ # This is my <strong>custom HTML</strong>
100
+ # </div>
101
+ # <% end %>
102
+ #
103
+ # @param list [Boolean] Whether the Tab is an item in a `<ul>` list.
104
+ # @param selected [Boolean] Whether the Tab is selected or not.
105
+ # @param with_panel [Boolean] Whether the Tab has an associated panel.
106
+ # @param panel_id [String] Only applies if `with_panel` is `true`. Unique id of panel.
107
+ # @param icon_classes [Boolean] Classes that must always be applied to icons.
108
+ # @param wrapper_arguments [Hash] <%= link_to_system_arguments_docs %> to be used in the `<li>` wrapper when the tab is an item in a list.
109
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
110
+ def initialize(list: false, selected: false, with_panel: false, panel_id: "", icon_classes: "", wrapper_arguments: {}, **system_arguments)
111
+ @selected = selected
112
+ @icon_classes = icon_classes
113
+ @list = list
114
+ @with_panel = with_panel
115
+
116
+ @system_arguments = system_arguments
117
+ @id = @system_arguments[:id]
118
+ @wrapper_arguments = wrapper_arguments
119
+
120
+ if with_panel || @system_arguments[:tag] == :button
121
+ @system_arguments[:tag] = :button
122
+ @system_arguments[:type] = :button
123
+ @system_arguments[:role] = :tab
124
+ panel_id(panel_id)
125
+ # https://www.w3.org/TR/wai-aria-practices/#presentation_role
126
+ @wrapper_arguments[:role] = :presentation
127
+ else
128
+ @system_arguments[:tag] = :a
129
+ end
130
+
131
+ @wrapper_arguments[:tag] = :li
132
+ @wrapper_arguments[:display] ||= :inline_flex
133
+
134
+ return unless @selected
135
+
136
+ if @system_arguments[:tag] == :a
137
+ aria_current = aria("current", system_arguments) || DEFAULT_ARIA_CURRENT_FOR_ANCHOR
138
+ @system_arguments[:"aria-current"] = fetch_or_fallback(ARIA_CURRENT_OPTIONS_FOR_ANCHOR, aria_current, DEFAULT_ARIA_CURRENT_FOR_ANCHOR)
139
+ else
140
+ @system_arguments[:"aria-selected"] = true
141
+ end
142
+ end
143
+
144
+ def wrapper
145
+ unless @list
146
+ yield
147
+ return # returning `yield` caused a double render
148
+ end
149
+
150
+ render(Primer::BaseComponent.new(**@wrapper_arguments)) do
151
+ yield if block_given?
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ def panel_id(panel_id)
158
+ if panel_id.blank?
159
+ raise ArgumentError, "`panel_id` is required" unless Rails.env.production?
160
+ else
161
+ @panel_id = panel_id
162
+ @system_arguments[:"aria-controls"] = @panel_id
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Alpha
5
+ class Overlay
6
+ # A `Overlay::Body` is a compositional component, used to render the
7
+ # Body of an overlay. See <%= link_to_component(Primer::Alpha::Overlay) %>.
8
+ class Body < Primer::Component
9
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
10
+ def initialize(padding: DEFAULT_PADDING, **system_arguments)
11
+ @system_arguments = deny_tag_argument(**system_arguments)
12
+ @system_arguments[:tag] = :div
13
+ @system_arguments[:classes] = class_names(
14
+ "Overlay-body",
15
+ PADDING_MAPPINGS[fetch_or_fallback(PADDING_OPTIONS, padding, DEFAULT_PADDING)],
16
+ system_arguments[:classes]
17
+ )
18
+ end
19
+
20
+ def call
21
+ render(Primer::BaseComponent.new(**@system_arguments)) { content }
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Alpha
5
+ class Overlay
6
+ DEFAULT_ALIGN_CONTENT = :end
7
+ ALIGN_CONTENT_MAPPINGS = {
8
+ :start => "Overlay-footer--alignStart",
9
+ :center => "Overlay-footer--alignCenter",
10
+ DEFAULT_ALIGN_CONTENT => "Overlay-footer--alignEnd"
11
+ }.freeze
12
+ ALIGN_CONTENT_OPTIONS = ALIGN_CONTENT_MAPPINGS.keys
13
+
14
+ # A `Overlay::Footer` is a compositional component, used to render the
15
+ # Footer of an overlay. See <%= link_to_component(Primer::Alpha::Overlay) %>.
16
+ class Footer < Primer::Component
17
+ # @param show_divider [Boolean] Show a divider between the footer and body.
18
+ # @param align_content [Symbol] The alginment of contents. <%= one_of(Primer::Alpha::Overlay::ALIGN_CONTENT_OPTIONS) %>
19
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
20
+ def initialize(
21
+ show_divider: false,
22
+ align_content: DEFAULT_ALIGN_CONTENT,
23
+ **system_arguments
24
+ )
25
+ @system_arguments = deny_tag_argument(**system_arguments)
26
+ @system_arguments[:tag] = :div
27
+ @system_arguments[:classes] = class_names(
28
+ "Overlay-footer",
29
+ ALIGN_CONTENT_MAPPINGS[fetch_or_fallback(ALIGN_CONTENT_OPTIONS, align_content, DEFAULT_ALIGN_CONTENT)],
30
+ { "Overlay-footer--divided": show_divider },
31
+ system_arguments[:classes]
32
+ )
33
+ end
34
+
35
+ def call
36
+ render(Primer::BaseComponent.new(**@system_arguments)) { content }
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,15 @@
1
+ <%= render Primer::BaseComponent.new(**@system_arguments) do %>
2
+ <div class="Overlay-headerContentWrap">
3
+ <div class="Overlay-titleWrap">
4
+ <h1 class="Overlay-title <% if @visually_hide_title || content.present? %>sr-only<% end %>"><%= @title %></h1>
5
+ <% if content.present? %>
6
+ <%= content %>
7
+ <% elsif @subtitle.present? %>
8
+ <h2 id="<%= @id %>-description" class="Overlay-description"><%= @subtitle %></h2>
9
+ <% end %>
10
+ </div>
11
+ <div class="Overlay-actionWrap">
12
+ <%= render Primer::Beta::CloseButton.new(classes: "Overlay-closeButton", "popoverhidetarget": @id) %>
13
+ </div>
14
+ </div>
15
+ <% end %>
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Alpha
5
+ class Overlay
6
+ # A `Overlay::Header` is a compositional component, used to render the
7
+ # Header of an overlay. See <%= link_to_component(Primer::Alpha::Overlay) %>.
8
+ class Header < Primer::Component
9
+ DEFAULT_SIZE = :medium
10
+ SIZE_MAPPINGS = {
11
+ DEFAULT_SIZE => nil,
12
+ :large => "Overlay-header--large"
13
+ }.freeze
14
+ SIZE_OPTIONS = SIZE_MAPPINGS.keys
15
+
16
+ # @param title [String] Describes the content of the Overlay.
17
+ # @param subtitle [String] Provides dditional context for the Overlay, also setting the `aria-describedby` attribute.
18
+ # @param size [Symbol] The size of the Header. <%= one_of(Primer::Alpha::Overlay::Header::SIZE_OPTIONS) %>
19
+ # @param divider [Boolean] Show a divider between the header and body.
20
+ # @param visually_hide_title [Boolean] Visually hide the `title` while maintaining a label for assistive technologies.
21
+ # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
22
+ def initialize(
23
+ id:,
24
+ title:,
25
+ subtitle: nil,
26
+ size: DEFAULT_SIZE,
27
+ divider: false,
28
+ visually_hide_title: false,
29
+ **system_arguments
30
+ )
31
+ @id = id
32
+ @title = title
33
+ @subtitle = subtitle
34
+ @visually_hide_title = visually_hide_title
35
+ @system_arguments = deny_tag_argument(**system_arguments)
36
+ @system_arguments[:tag] = :header
37
+ @system_arguments[:classes] = class_names(
38
+ "Overlay-header",
39
+ { "Overlay-header--divided": divider },
40
+ SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)],
41
+ system_arguments[:classes]
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1 @@
1
+ .Overlay[popover]{border-width:0;padding:0;position:absolute}.Overlay[popover]:not(:open){display:none}anchored-position{display:block}
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "alpha/overlay",
3
+ "selectors": [
4
+ ".Overlay[popover]",
5
+ ".Overlay[popover]:not(:open)",
6
+ "anchored-position"
7
+ ],
8
+ "classes": [
9
+ "Overlay"
10
+ ]
11
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["overlay.pcss"],"names":[],"mappings":"AAAA,kBACE,cAAe,CACf,SAAU,CACV,iBACF,CAGA,6BACE,YACF,CAEA,kBACE,aACF","file":"overlay.css","sourcesContent":[".Overlay[popover] {\n border-width: 0;\n padding: 0;\n position: absolute;\n}\n\n/* stylelint-disable-next-line selector-pseudo-class-no-unknown */\n.Overlay[popover]:not(:open) {\n display: none;\n}\n\nanchored-position {\n display: block;\n}\n"]}
@@ -0,0 +1,11 @@
1
+ <%= show_button %>
2
+
3
+ <%= render Primer::BaseComponent.new(**@system_arguments) do %>
4
+ <%= header %>
5
+ <% if content.present? %>
6
+ <%= content %>
7
+ <% else %>
8
+ <%= body %>
9
+ <%= footer %>
10
+ <% end %>
11
+ <% end %>
@@ -0,0 +1,14 @@
1
+ .Overlay[popover] {
2
+ border-width: 0;
3
+ padding: 0;
4
+ position: absolute;
5
+ }
6
+
7
+ /* stylelint-disable-next-line selector-pseudo-class-no-unknown */
8
+ .Overlay[popover]:not(:open) {
9
+ display: none;
10
+ }
11
+
12
+ anchored-position {
13
+ display: block;
14
+ }