primer_view_components 0.9.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +78 -0
  3. data/app/assets/javascripts/app/components/primer/primer.d.ts +1 -1
  4. data/app/assets/javascripts/primer_view_components.js +1 -1
  5. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  6. data/app/assets/styles/primer_view_components.css +1 -1
  7. data/app/assets/styles/primer_view_components.css.map +1 -1
  8. data/app/components/primer/alpha/action_bar/item.rb +7 -4
  9. data/app/components/primer/alpha/action_bar.rb +2 -2
  10. data/app/components/primer/alpha/action_bar_element.js +2 -2
  11. data/app/components/primer/alpha/action_bar_element.ts +2 -2
  12. data/app/components/primer/alpha/action_menu/action_menu_element.js +170 -85
  13. data/app/components/primer/alpha/action_menu/action_menu_element.ts +206 -80
  14. data/app/components/primer/alpha/action_menu/list.rb +0 -2
  15. data/app/components/primer/alpha/check_box_group.rb +2 -0
  16. data/app/components/primer/alpha/dialog/header.rb +12 -0
  17. data/app/components/primer/alpha/dialog.rb +1 -1
  18. data/app/components/primer/alpha/nav_list/divider.rb +2 -5
  19. data/app/components/primer/alpha/nav_list/group.rb +2 -98
  20. data/app/components/primer/alpha/nav_list/heading.rb +2 -27
  21. data/app/components/primer/alpha/nav_list/item.rb +2 -147
  22. data/app/components/primer/alpha/nav_list.rb +2 -205
  23. data/app/components/primer/alpha/overlay.css +1 -1
  24. data/app/components/primer/alpha/overlay.css.map +1 -1
  25. data/app/components/primer/alpha/overlay.pcss +1 -7
  26. data/app/components/primer/alpha/overlay.rb +6 -4
  27. data/app/components/primer/alpha/radio_button_group.rb +2 -0
  28. data/app/components/primer/alpha/text_field.css +1 -1
  29. data/app/components/primer/alpha/text_field.css.json +4 -1
  30. data/app/components/primer/alpha/text_field.css.map +1 -1
  31. data/app/components/primer/alpha/text_field.pcss +18 -3
  32. data/app/components/primer/alpha/tooltip.rb +3 -1
  33. data/app/components/primer/beta/button.css +1 -1
  34. data/app/components/primer/beta/button.css.json +2 -0
  35. data/app/components/primer/beta/button.css.map +1 -1
  36. data/app/components/primer/beta/button.pcss +11 -3
  37. data/app/components/primer/beta/icon_button.html.erb +1 -1
  38. data/app/components/primer/beta/icon_button.rb +8 -1
  39. data/app/components/primer/beta/link.css +1 -1
  40. data/app/components/primer/beta/link.css.json +1 -0
  41. data/app/components/primer/beta/link.css.map +1 -1
  42. data/app/components/primer/beta/link.pcss +5 -0
  43. data/app/components/primer/beta/link.rb +2 -2
  44. data/app/components/primer/beta/nav_list/divider.rb +14 -0
  45. data/app/components/primer/beta/nav_list/group.rb +107 -0
  46. data/app/components/primer/beta/nav_list/heading.rb +36 -0
  47. data/app/components/primer/beta/nav_list/item.rb +156 -0
  48. data/app/components/primer/beta/nav_list.rb +212 -0
  49. data/app/components/primer/focus_group.js +30 -4
  50. data/app/components/primer/focus_group.ts +29 -2
  51. data/app/components/primer/primer.d.ts +1 -1
  52. data/app/components/primer/primer.js +1 -1
  53. data/app/components/primer/primer.ts +1 -1
  54. data/app/helpers/primer/form_helper.rb +10 -0
  55. data/lib/primer/deprecations.yml +20 -0
  56. data/lib/primer/forms/check_box_group.html.erb +3 -0
  57. data/lib/primer/forms/dsl/check_box_group_input.rb +1 -5
  58. data/lib/primer/forms/dsl/check_box_input.rb +5 -0
  59. data/lib/primer/forms/dsl/radio_button_input.rb +5 -0
  60. data/lib/primer/forms/form_control.html.erb +1 -4
  61. data/lib/primer/forms/radio_button_group.html.erb +3 -0
  62. data/lib/primer/forms/utils.rb +2 -0
  63. data/lib/primer/forms/validation_message.html.erb +4 -0
  64. data/lib/primer/forms/validation_message.rb +14 -0
  65. data/lib/primer/forms.rb +16 -0
  66. data/lib/primer/view_components/version.rb +1 -1
  67. data/lib/primer/yard/component_manifest.rb +4 -0
  68. data/previews/primer/alpha/action_menu_preview/with_actions.html.erb +5 -4
  69. data/previews/primer/alpha/action_menu_preview.rb +4 -1
  70. data/previews/primer/alpha/check_box_group_preview.rb +13 -0
  71. data/previews/primer/alpha/dialog_preview/with_header.html.erb +5 -0
  72. data/previews/primer/alpha/dialog_preview.rb +17 -0
  73. data/previews/primer/alpha/overlay_preview.rb +1 -1
  74. data/previews/primer/alpha/radio_button_group_preview.rb +13 -0
  75. data/previews/primer/alpha/radio_button_preview.rb +1 -1
  76. data/previews/primer/alpha/text_field_preview/input_group_leading_action_menu.html.erb +21 -0
  77. data/previews/primer/alpha/text_field_preview/input_group_leading_button.html.erb +18 -0
  78. data/previews/primer/alpha/text_field_preview/input_group_trailing_button.html.erb +18 -0
  79. data/previews/primer/alpha/text_field_preview.rb +21 -0
  80. data/previews/primer/beta/button_preview.rb +1 -1
  81. data/previews/primer/{alpha → beta}/nav_list_preview/trailing_action.html.erb +1 -1
  82. data/previews/primer/{alpha → beta}/nav_list_preview.rb +5 -5
  83. data/static/arguments.json +194 -7
  84. data/static/audited_at.json +5 -0
  85. data/static/classes.json +3 -0
  86. data/static/constants.json +23 -0
  87. data/static/info_arch.json +1015 -527
  88. data/static/previews.json +85 -7
  89. data/static/statuses.json +10 -5
  90. metadata +23 -11
  91. /data/app/assets/javascripts/app/components/primer/{alpha → beta}/nav_list.d.ts +0 -0
  92. /data/app/components/primer/{alpha → beta}/nav_list/group.html.erb +0 -0
  93. /data/app/components/primer/{alpha → beta}/nav_list/item.html.erb +0 -0
  94. /data/app/components/primer/{alpha → beta}/nav_list.d.ts +0 -0
  95. /data/app/components/primer/{alpha → beta}/nav_list.html.erb +0 -0
  96. /data/app/components/primer/{alpha → beta}/nav_list.js +0 -0
  97. /data/app/components/primer/{alpha → beta}/nav_list.ts +0 -0
@@ -9,7 +9,8 @@ type SelectedItem = {
9
9
  element: Element
10
10
  }
11
11
 
12
- const menuItemSelectors = ['[role="menuitem"]', '[role="menuitemcheckbox"]', '[role="menuitemradio"]']
12
+ const validSelectors = ['[role="menuitem"]', '[role="menuitemcheckbox"]', '[role="menuitemradio"]']
13
+ const menuItemSelectors = validSelectors.map(selector => `:not([hidden]) > ${selector}`)
13
14
 
14
15
  @controller
15
16
  export class ActionMenuElement extends HTMLElement {
@@ -19,6 +20,7 @@ export class ActionMenuElement extends HTMLElement {
19
20
  #abortController: AbortController
20
21
  #originalLabel = ''
21
22
  #inputName = ''
23
+ #invokerBeingClicked = false
22
24
 
23
25
  get selectVariant(): SelectVariant {
24
26
  return this.getAttribute('data-select-variant') as SelectVariant
@@ -51,7 +53,7 @@ export class ActionMenuElement extends HTMLElement {
51
53
  }
52
54
 
53
55
  get popoverElement(): HTMLElement | null {
54
- return this.invokerElement?.popoverTargetElement || null
56
+ return (this.invokerElement?.popoverTargetElement as HTMLElement) || null
55
57
  }
56
58
 
57
59
  get invokerElement(): HTMLButtonElement | null {
@@ -93,8 +95,10 @@ export class ActionMenuElement extends HTMLElement {
93
95
  this.addEventListener('click', this, {signal})
94
96
  this.addEventListener('mouseover', this, {signal})
95
97
  this.addEventListener('focusout', this, {signal})
98
+ this.addEventListener('mousedown', this, {signal})
96
99
  this.#setDynamicLabel()
97
100
  this.#updateInput()
101
+ this.#softDisableItems()
98
102
 
99
103
  if (this.includeFragment) {
100
104
  this.includeFragment.addEventListener('include-fragment-replaced', this, {
@@ -103,19 +107,69 @@ export class ActionMenuElement extends HTMLElement {
103
107
  }
104
108
  }
105
109
 
110
+ #softDisableItems() {
111
+ const {signal} = this.#abortController
112
+
113
+ for (const item of this.#items) {
114
+ item.addEventListener('click', this.#potentiallyDisallowActivation.bind(this), {signal})
115
+ item.addEventListener('keydown', this.#potentiallyDisallowActivation.bind(this), {signal})
116
+ }
117
+ }
118
+
119
+ #potentiallyDisallowActivation(event: Event) {
120
+ if (!this.#isActivation(event)) return
121
+
122
+ const item = (event.target as HTMLElement).closest(menuItemSelectors.join(','))
123
+ if (!item) return
124
+
125
+ if (item.getAttribute('aria-disabled')) {
126
+ event.preventDefault()
127
+ event.stopPropagation()
128
+ event.stopImmediatePropagation()
129
+ }
130
+ }
131
+
106
132
  disconnectedCallback() {
107
133
  this.#abortController.abort()
108
134
  }
109
135
 
136
+ #isKeyboardActivation(event: Event): boolean {
137
+ return (
138
+ event instanceof KeyboardEvent &&
139
+ event.type === 'keydown' &&
140
+ !(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
141
+ (event.key === 'Enter' || event.key === ' ')
142
+ )
143
+ }
144
+
145
+ #isMouseActivation(event: Event): boolean {
146
+ return event instanceof MouseEvent && event.type === 'click'
147
+ }
148
+
149
+ #isActivation(event: Event): boolean {
150
+ return this.#isMouseActivation(event) || this.#isKeyboardActivation(event)
151
+ }
152
+
110
153
  handleEvent(event: Event) {
111
- const activation = this.#isActivationKeydown(event)
112
- if (event.target === this.invokerElement && activation) {
113
- if (this.#firstItem) {
114
- event.preventDefault()
115
- this.popoverElement?.showPopover()
116
- this.#firstItem.focus()
117
- return
118
- }
154
+ const targetIsInvoker = this.invokerElement?.contains(event.target as HTMLElement)
155
+ const eventIsActivation = this.#isActivation(event)
156
+
157
+ if (targetIsInvoker && event.type === 'mousedown') {
158
+ this.#invokerBeingClicked = true
159
+ return
160
+ }
161
+
162
+ // Prevent safari bug that dismisses menu on mousedown instead of allowing
163
+ // the click event to propagate to the button
164
+ if (event.type === 'mousedown') {
165
+ event.preventDefault()
166
+ return
167
+ }
168
+
169
+ if (targetIsInvoker && eventIsActivation) {
170
+ this.#handleInvokerActivated(event)
171
+ this.#invokerBeingClicked = false
172
+ return
119
173
  }
120
174
 
121
175
  // Ignore events within dialogs within menus
@@ -123,84 +177,161 @@ export class ActionMenuElement extends HTMLElement {
123
177
  return
124
178
  }
125
179
 
126
- // If a dialog has been rendered within the menu, we do not want to hide
127
- // the entire menu, as that will also hide the Dialog. Instead we want to
128
- // show the Dialog while hiding just the visible part of the menu.
129
- if ((activation || event.type === 'click') && (event.target as HTMLElement)?.closest('[data-show-dialog-id]')) {
130
- const dialogInvoker = (event.target as HTMLElement)!.closest('[data-show-dialog-id]')
131
- const dialog = this.ownerDocument.getElementById(dialogInvoker?.getAttribute('data-show-dialog-id') || '')
132
- if (dialogInvoker && dialog && this.contains(dialogInvoker) && this.contains(dialog)) {
133
- this.querySelector<HTMLElement>('.ActionListWrap')!.style.display = 'none'
134
- const dialog_controller = new AbortController()
135
- const {signal} = dialog_controller
136
- const handleDialogClose = () => {
137
- dialog_controller.abort()
138
- this.querySelector<HTMLElement>('.ActionListWrap')!.style.display = ''
139
- if (this.popoverElement?.matches(':popover-open')) {
140
- this.popoverElement?.hidePopover()
141
- }
180
+ if (event.type === 'focusout') {
181
+ if (this.#invokerBeingClicked) return
182
+
183
+ // Give the browser time to focus the next element
184
+ requestAnimationFrame(() => {
185
+ if (!this.contains(document.activeElement) || document.activeElement === this.invokerElement) {
186
+ this.#handleFocusOut()
142
187
  }
143
- dialog.addEventListener('close', handleDialogClose, {signal})
144
- dialog.addEventListener('cancel', handleDialogClose, {signal})
145
- return
146
- }
188
+ })
189
+
190
+ return
147
191
  }
148
192
 
149
- if (!this.popoverElement?.matches(':popover-open')) return
193
+ const item = (event.target as Element).closest(menuItemSelectors.join(','))
194
+ const targetIsItem = item !== null
150
195
 
151
- if (event.type === 'include-fragment-replaced') {
152
- if (this.#firstItem) this.#firstItem.focus()
153
- } else if (activation || (event instanceof MouseEvent && event.type === 'click')) {
154
- // Hide popover after current event loop to prevent changes in focus from
155
- // altering the target of the event. Not doing this specifically affects
156
- // <a> tags. It causes the event to be sent to the currently focused element
157
- // instead of the anchor, which effectively prevents navigation, i.e. it
158
- // appears as if hitting enter does nothing. Curiously, clicking instead
159
- // works fine.
160
- if (this.selectVariant !== 'multiple') {
161
- setTimeout(() => {
162
- if (this.popoverElement?.matches(':popover-open')) {
163
- this.popoverElement?.hidePopover()
164
- }
165
- })
196
+ if (targetIsItem && eventIsActivation) {
197
+ const dialogInvoker = item.closest('[data-show-dialog-id]')
198
+
199
+ if (dialogInvoker) {
200
+ const dialog = this.ownerDocument.getElementById(dialogInvoker.getAttribute('data-show-dialog-id') || '')
201
+
202
+ if (dialog && this.contains(dialogInvoker) && this.contains(dialog)) {
203
+ this.#handleDialogItemActivated(event, dialog)
204
+ return
205
+ }
166
206
  }
167
207
 
168
- // The rest of the code below deals with single/multiple selection behavior, and should not
169
- // interfere with events fired by menu items whose behavior is specified outside the library.
170
- if (this.selectVariant !== 'multiple' && this.selectVariant !== 'single') return
208
+ this.#activateItem(event, item)
209
+ this.#handleItemActivated(event, item)
210
+ return
211
+ }
171
212
 
172
- const item = (event.target as Element).closest(menuItemSelectors.join(','))
173
- if (!item) return
174
- const ariaChecked = item.getAttribute('aria-checked')
175
- const checked = ariaChecked !== 'true'
213
+ if (event.type === 'include-fragment-replaced') {
214
+ this.#handleIncludeFragmentReplaced()
215
+ }
216
+ }
176
217
 
177
- if (this.selectVariant === 'single') {
178
- // Only check, never uncheck here. Single-select mode does not allow unchecking a checked item.
179
- if (checked) {
180
- item.setAttribute('aria-checked', 'true')
181
- }
218
+ #handleInvokerActivated(event: Event) {
219
+ event.preventDefault()
220
+ event.stopPropagation()
182
221
 
183
- for (const checkedItem of this.querySelectorAll('[aria-checked]')) {
184
- if (checkedItem !== item) {
185
- checkedItem.setAttribute('aria-checked', 'false')
186
- }
187
- }
222
+ if (this.#isOpen()) {
223
+ this.#hide()
224
+ } else {
225
+ this.#show()
226
+ this.#firstItem?.focus()
227
+ }
228
+ }
188
229
 
189
- this.#setDynamicLabel()
190
- } else {
191
- // multi-select mode allows unchecking a checked item
192
- item.setAttribute('aria-checked', `${checked}`)
230
+ #handleDialogItemActivated(event: Event, dialog: HTMLElement) {
231
+ this.querySelector<HTMLElement>('.ActionListWrap')!.style.display = 'none'
232
+ const dialog_controller = new AbortController()
233
+ const {signal} = dialog_controller
234
+ const handleDialogClose = () => {
235
+ dialog_controller.abort()
236
+ this.querySelector<HTMLElement>('.ActionListWrap')!.style.display = ''
237
+ if (this.#isOpen()) {
238
+ this.#hide()
193
239
  }
240
+ }
241
+ dialog.addEventListener('close', handleDialogClose, {signal})
242
+ dialog.addEventListener('cancel', handleDialogClose, {signal})
243
+ }
244
+
245
+ #handleItemActivated(event: Event, item: Element) {
246
+ // Hide popover after current event loop to prevent changes in focus from
247
+ // altering the target of the event. Not doing this specifically affects
248
+ // <a> tags. It causes the event to be sent to the currently focused element
249
+ // instead of the anchor, which effectively prevents navigation, i.e. it
250
+ // appears as if hitting enter does nothing. Curiously, clicking instead
251
+ // works fine.
252
+ if (this.selectVariant !== 'multiple') {
253
+ setTimeout(() => {
254
+ if (this.#isOpen()) {
255
+ this.#hide()
256
+ }
257
+ })
258
+ }
259
+
260
+ // The rest of the code below deals with single/multiple selection behavior, and should not
261
+ // interfere with events fired by menu items whose behavior is specified outside the library.
262
+ if (this.selectVariant !== 'multiple' && this.selectVariant !== 'single') return
194
263
 
195
- this.#updateInput()
264
+ const ariaChecked = item.getAttribute('aria-checked')
265
+ const checked = ariaChecked !== 'true'
196
266
 
197
- if (event instanceof KeyboardEvent && event.target instanceof HTMLButtonElement) {
198
- // prevent buttons from being clicked twice
199
- event.preventDefault()
267
+ if (this.selectVariant === 'single') {
268
+ // Only check, never uncheck here. Single-select mode does not allow unchecking a checked item.
269
+ if (checked) {
270
+ item.setAttribute('aria-checked', 'true')
271
+ }
272
+
273
+ for (const checkedItem of this.querySelectorAll('[aria-checked]')) {
274
+ if (checkedItem !== item) {
275
+ checkedItem.setAttribute('aria-checked', 'false')
276
+ }
200
277
  }
278
+
279
+ this.#setDynamicLabel()
280
+ } else {
281
+ // multi-select mode allows unchecking a checked item
282
+ item.setAttribute('aria-checked', `${checked}`)
283
+ }
284
+
285
+ this.#updateInput()
286
+
287
+ if (event instanceof KeyboardEvent && event.target instanceof HTMLButtonElement) {
288
+ // prevent buttons from being clicked twice
289
+ event.preventDefault()
290
+ return
201
291
  }
202
292
  }
203
293
 
294
+ #activateItem(event: Event, item: Element) {
295
+ const eventWillActivateByDefault =
296
+ (event instanceof MouseEvent && event.type === 'click') ||
297
+ (event instanceof KeyboardEvent &&
298
+ event.type === 'keydown' &&
299
+ !(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
300
+ event.key === 'Enter')
301
+
302
+ // if the event will result in activating the current item by default, i.e. is a
303
+ // mouse click or keyboard enter, bail out
304
+ if (eventWillActivateByDefault) return
305
+
306
+ // otherwise, event will not result in activation by default, so we stop it and
307
+ // simulate a click
308
+ event.stopPropagation()
309
+ const elem = item as HTMLElement
310
+ elem.click()
311
+ }
312
+
313
+ #handleIncludeFragmentReplaced() {
314
+ if (this.#firstItem) this.#firstItem.focus()
315
+ this.#softDisableItems()
316
+ }
317
+
318
+ // Close when focus leaves menu
319
+ #handleFocusOut() {
320
+ this.#hide()
321
+ }
322
+
323
+ #show() {
324
+ this.popoverElement?.showPopover()
325
+ }
326
+
327
+ #hide() {
328
+ this.popoverElement?.hidePopover()
329
+ }
330
+
331
+ #isOpen() {
332
+ return this.popoverElement?.matches(':popover-open')
333
+ }
334
+
204
335
  #setDynamicLabel() {
205
336
  if (!this.dynamicLabel) return
206
337
  const invokerLabel = this.invokerLabel
@@ -260,18 +391,13 @@ export class ActionMenuElement extends HTMLElement {
260
391
  }
261
392
  }
262
393
 
263
- #isActivationKeydown(event: Event): boolean {
264
- return (
265
- event instanceof KeyboardEvent &&
266
- event.type === 'keydown' &&
267
- !(event.ctrlKey || event.altKey || event.metaKey || event.shiftKey) &&
268
- (event.key === 'Enter' || event.key === ' ')
269
- )
270
- }
271
-
272
394
  get #firstItem(): HTMLElement | null {
273
395
  return this.querySelector(menuItemSelectors.join(','))
274
396
  }
397
+
398
+ get #items(): HTMLElement[] {
399
+ return Array.from(this.querySelectorAll(menuItemSelectors.join(',')))
400
+ }
275
401
  }
276
402
 
277
403
  if (!window.customElements.get('action-menu')) {
@@ -111,8 +111,6 @@ module Primer
111
111
  system_arguments,
112
112
  { aria: { disabled: true } }
113
113
  )
114
-
115
- content_arguments[:disabled] = "" if content_arguments[:tag] == :button
116
114
  end
117
115
 
118
116
  { data: data, **system_arguments, content_arguments: content_arguments }
@@ -23,6 +23,8 @@ module Primer
23
23
  # @param label [String] Label text displayed above the input.
24
24
  # @param hidden [Boolean] When set to `true`, visually hides the group.
25
25
  # @param caption [String] A string describing the field and what sorts of input it expects. Displayed below the group.
26
+ # @param invalid [Boolean] If set to `true`, the input will be marked as invalid. Implied if `validation_message` is truthy. This option is set to `true` automatically if the model object associated with the form reports that the input is invalid via Rails validations. It is provided for cases where the form does not have an associated model. If the input is invalid as determined by Rails validations, setting `invalid` to `false` will have no effect.
27
+ # @param validation_message [String] A string displayed between the caption and the input indicating the input's contents are invalid. This option is, by default, set to the first Rails validation message for the input (assuming the form is associated with a model object). Use `validation_message` to override the default or to provide a validation message in case there is no associated model object.
26
28
  # @param label_arguments [Hash] Attributes that will be passed to Rails' `builder.label` method. These can be HTML attributes or any of the other label options Rails supports. They will appear as HTML attributes on the `<label>` tag.
27
29
 
28
30
  # @!method check_box
@@ -9,10 +9,19 @@ module Primer
9
9
  status :alpha
10
10
  audited_at "2022-10-10"
11
11
 
12
+ DEFAULT_VARIANT = :medium
13
+ VARIANT_MAPPINGS = {
14
+ DEFAULT_VARIANT => "",
15
+ :large => "Overlay-header--large"
16
+ }.freeze
17
+ VARIANT_OPTIONS = VARIANT_MAPPINGS.keys
18
+
19
+ # @param id [String] The HTML element's ID value.
12
20
  # @param title [String] Describes the content of the dialog.
13
21
  # @param subtitle [String] Provides dditional context for the dialog, also setting the `aria-describedby` attribute.
14
22
  # @param show_divider [Boolean] Show a divider between the header and body.
15
23
  # @param visually_hide_title [Boolean] Visually hide the `title` while maintaining a label for assistive technologies.
24
+ # @param variant [Symbol] <%= one_of(Primer::Alpha::Dialog::Header::VARIANT_OPTIONS) %>
16
25
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
17
26
  def initialize(
18
27
  id:,
@@ -20,6 +29,7 @@ module Primer
20
29
  subtitle: nil,
21
30
  show_divider: false,
22
31
  visually_hide_title: false,
32
+ variant: DEFAULT_VARIANT,
23
33
  **system_arguments
24
34
  )
25
35
  @id = id
@@ -28,8 +38,10 @@ module Primer
28
38
  @visually_hide_title = visually_hide_title
29
39
  @system_arguments = deny_tag_argument(**system_arguments)
30
40
  @system_arguments[:tag] = :div
41
+
31
42
  @system_arguments[:classes] = class_names(
32
43
  "Overlay-header",
44
+ VARIANT_MAPPINGS[fetch_or_fallback(VARIANT_OPTIONS, variant, DEFAULT_VARIANT)],
33
45
  { "Overlay-header--divided": show_divider },
34
46
  system_arguments[:classes]
35
47
  )
@@ -74,7 +74,7 @@ module Primer
74
74
  #
75
75
  # @param show_divider [Boolean] Show a divider between the header and body.
76
76
  # @param visually_hide_title [Boolean] Visually hide the `title` while maintaining a label for assistive technologies.
77
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
77
+ # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::Dialog::Header) %>.
78
78
  renders_one :header, lambda { |show_divider: false, visually_hide_title: @visually_hide_title, **system_arguments|
79
79
  Primer::Alpha::Dialog::Header.new(
80
80
  id: @id,
@@ -3,11 +3,8 @@
3
3
  module Primer
4
4
  module Alpha
5
5
  class NavList
6
- # Separator with optional text rendered above groups or between individual items.
7
- class Divider < Primer::Alpha::ActionList::Divider
8
- def kind
9
- :divider
10
- end
6
+ class Divider < Beta::NavList::Divider
7
+ status :deprecated
11
8
  end
12
9
  end
13
10
  end
@@ -3,104 +3,8 @@
3
3
  module Primer
4
4
  module Alpha
5
5
  class NavList
6
- # A logical grouping of navigation links with an optional heading.
7
- #
8
- # See <%= link_to_component(Primer::Alpha::NavList) %> for usage examples.
9
- class Group < ActionList
10
- # A special "show more" list item that appears at the bottom of the group. Clicking
11
- # the item will fetch the next page of results from the URL passed in the `src` argument
12
- # and append the resulting chunk of HTML to the group.
13
- #
14
- # @param src [String] The URL to query for additional pages of list items.
15
- # @param pages [Integer] The total number of pages in the result set.
16
- # @param component_klass [Class] A component class to use instead of the default `Primer::Alpha::NavList::Item` class.
17
- # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::NavList::Item) %>.
18
- renders_one :show_more_item, lambda { |src:, pages:, component_klass: NavList::Item, **system_arguments|
19
- system_arguments[:classes] = class_names(
20
- @item_classes,
21
- system_arguments[:classes]
22
- )
23
- system_arguments[:tag] = :div
24
- system_arguments[:id] ||= self.class.generate_id(base_name: "item")
25
- system_arguments[:hidden] = true
26
- system_arguments[:href] = "#"
27
- system_arguments[:data] ||= {}
28
- system_arguments[:data][:target] = "nav-list.showMoreItem"
29
- system_arguments[:data][:action] = "click:nav-list#showMore"
30
- system_arguments[:data][:current_page] = "1"
31
- system_arguments[:data][:total_pages] = pages.to_s
32
- system_arguments[:label_arguments] = {
33
- **system_arguments[:label_arguments] || {},
34
- color: :accent
35
- }
36
-
37
- system_arguments[:content_arguments] = {
38
- **system_arguments[:content_arguments] || {},
39
- tag: :button
40
- }
41
-
42
- system_arguments[:content_arguments][:data] = merge_data(
43
- system_arguments[:content_arguments],
44
- data: { list_id: id }
45
- )
46
-
47
- component_klass.new(list: self, src: src, **system_arguments)
48
- }
49
-
50
- # @private
51
- def self.custom_element_name
52
- Primer::Alpha::NavList.custom_element_name
53
- end
54
-
55
- # @param selected_item_id [Symbol] The ID of the currently selected item. Used internally.
56
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
57
- def initialize(selected_item_id: nil, **system_arguments)
58
- @system_arguments = system_arguments
59
- @selected_item_id = selected_item_id
60
-
61
- super(**@system_arguments)
62
- end
63
-
64
- # Cause this group to show its list of sub items when rendered.
65
- # :nocov:
66
- def expand!
67
- @expanded = true
68
- end
69
- # :nocov:
70
-
71
- # @!parse
72
- # # Items.
73
- # #
74
- # # @param system_arguments [Hash] The arguments accepted by <%= link_to_component(Primer::Alpha::NavList::Item) %>.
75
- # renders_many :items
76
-
77
- # @private
78
- def build_item(component_klass: NavList::Item, **system_arguments)
79
- super(
80
- component_klass: component_klass,
81
- selected_item_id: @selected_item_id,
82
- **system_arguments
83
- )
84
- end
85
-
86
- # @private
87
- def build_avatar_item(component_klass: NavList::Item, **system_arguments)
88
- super(
89
- component_klass: component_klass,
90
- selected_item_id: @selected_item_id,
91
- **system_arguments
92
- )
93
- end
94
-
95
- def kind
96
- :group
97
- end
98
-
99
- def before_render
100
- super
101
-
102
- raise ArgumentError, "NavList groups are required to have headings" unless heading?
103
- end
6
+ class Group < Beta::NavList::Group
7
+ status :deprecated
104
8
  end
105
9
  end
106
10
  end
@@ -3,33 +3,8 @@
3
3
  module Primer
4
4
  module Alpha
5
5
  class NavList
6
- # The heading placed above a `NavList`'s items.
7
- #
8
- # See <%= link_to_component(Primer::Alpha::NavList) %> for usage examples.
9
- class Heading < Primer::Component
10
- attr_reader :title, :id, :heading_level, :system_arguments
11
-
12
- # @param title [String] The text content of the heading.
13
- # @param id [String] The value of the ID HTML attribute. Auto-generated by default.
14
- # @param heading_level [Integer] The heading level, i.e. 2 for an `<h2>`, 3 for an `<h3>`, etc.
15
- # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
16
- def initialize(title:, id: self.class.generate_id, heading_level: 2, **system_arguments)
17
- @title = title
18
- @id = id
19
- @heading_level = heading_level
20
- @system_arguments = system_arguments
21
- end
22
-
23
- def call
24
- render(
25
- Primer::BaseComponent.new(
26
- tag: :"h#{heading_level}",
27
- id: id,
28
- classes: "ActionListHeader",
29
- **system_arguments
30
- ).with_content(title)
31
- )
32
- end
6
+ class Heading < Beta::NavList::Heading
7
+ status :deprecated
33
8
  end
34
9
  end
35
10
  end