openproject-primer_view_components 0.86.2 → 0.88.0

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -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/components/primer/alpha/action_menu/action_menu_element.js +14 -5
  6. data/app/components/primer/alpha/action_menu/action_menu_element.ts +14 -4
  7. data/app/components/primer/alpha/select_panel_element.js +11 -1
  8. data/app/components/primer/alpha/select_panel_element.ts +11 -2
  9. data/app/components/primer/alpha/tree_view/tree_view.js +27 -11
  10. data/app/components/primer/alpha/tree_view/tree_view.ts +24 -9
  11. data/app/components/primer/open_project/avatar_fallback.js +1 -1
  12. data/app/components/primer/open_project/avatar_fallback.ts +1 -1
  13. data/app/components/primer/open_project/border_box/collapsible_header.js +1 -1
  14. data/app/components/primer/open_project/border_box/collapsible_header.ts +1 -1
  15. data/app/components/primer/open_project/collapsible_section.js +1 -1
  16. data/app/components/primer/open_project/collapsible_section.ts +1 -1
  17. data/app/components/primer/open_project/danger_dialog_form_helper.js +1 -1
  18. data/app/components/primer/open_project/danger_dialog_form_helper.ts +1 -1
  19. data/app/components/primer/open_project/fieldset.rb +2 -2
  20. data/app/components/primer/open_project/filterable_tree_view.js +2 -1
  21. data/app/components/primer/open_project/filterable_tree_view.rb +6 -5
  22. data/app/components/primer/open_project/filterable_tree_view.ts +3 -2
  23. data/app/components/primer/open_project/flex_layout.rb +2 -2
  24. data/app/components/primer/open_project/inline_message.rb +1 -1
  25. data/app/components/primer/open_project/input_group.html.erb +9 -0
  26. data/app/components/primer/open_project/input_group.rb +13 -0
  27. data/app/components/primer/open_project/page_header.rb +1 -1
  28. data/app/components/primer/open_project/page_header_element.js +1 -1
  29. data/app/components/primer/open_project/page_header_element.ts +1 -1
  30. data/app/components/primer/open_project/pagination.rb +1 -3
  31. data/app/components/primer/open_project/sub_header/{quick_filter.rb → quick_action_component.rb} +7 -1
  32. data/app/components/primer/open_project/sub_header.html.erb +2 -0
  33. data/app/components/primer/open_project/sub_header.rb +38 -24
  34. data/app/components/primer/open_project/sub_header_element.js +4 -3
  35. data/app/components/primer/open_project/sub_header_element.ts +4 -3
  36. data/app/components/primer/open_project/zen_mode_button.js +1 -1
  37. data/app/components/primer/open_project/zen_mode_button.ts +1 -1
  38. data/lib/primer/view_components/version.rb +2 -2
  39. data/previews/primer/alpha/select_panel_preview/with_dynamic_label.html.erb +1 -1
  40. data/previews/primer/alpha/select_panel_preview/with_dynamic_label_and_aria_prefix.html.erb +1 -1
  41. data/previews/primer/alpha/select_panel_preview.rb +14 -4
  42. data/previews/primer/alpha/tree_view_preview/doubled_path.html.erb +12 -0
  43. data/previews/primer/alpha/tree_view_preview/form_input.html.erb +7 -7
  44. data/previews/primer/alpha/tree_view_preview.rb +6 -0
  45. data/previews/primer/open_project/filterable_tree_view_preview/custom_no_results_text.html.erb +1 -1
  46. data/previews/primer/open_project/input_group_preview.rb +9 -0
  47. data/previews/primer/open_project/sub_header_preview/quick_filters.html.erb +8 -0
  48. data/static/arguments.json +8 -8
  49. data/static/audited_at.json +1 -1
  50. data/static/constants.json +3 -3
  51. data/static/info_arch.json +33 -10
  52. data/static/previews.json +23 -10
  53. data/static/statuses.json +1 -1
  54. metadata +3 -2
@@ -469,6 +469,7 @@ _ActionMenuElement_handleItemActivated = function _ActionMenuElement_handleItemA
469
469
  else {
470
470
  // multi-select mode allows unchecking a checked item
471
471
  item.setAttribute('aria-checked', `${checked}`);
472
+ __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_setDynamicLabel).call(this);
472
473
  }
473
474
  __classPrivateFieldGet(this, _ActionMenuElement_instances, "m", _ActionMenuElement_updateInput).call(this);
474
475
  this.dispatchEvent(new CustomEvent('itemActivated', {
@@ -498,21 +499,29 @@ _ActionMenuElement_isOpen = function _ActionMenuElement_isOpen() {
498
499
  return this.popoverElement?.matches(':popover-open');
499
500
  };
500
501
  _ActionMenuElement_setDynamicLabel = function _ActionMenuElement_setDynamicLabel() {
501
- if (this.selectVariant !== 'single')
502
- return;
503
502
  if (!this.dynamicLabel)
504
503
  return;
505
504
  const invokerLabel = this.invokerLabel;
506
505
  if (!invokerLabel)
507
506
  return;
508
507
  __classPrivateFieldSet(this, _ActionMenuElement_originalLabel, __classPrivateFieldGet(this, _ActionMenuElement_originalLabel, "f") || (invokerLabel.textContent || ''), "f");
509
- const itemLabel = this.querySelector('[aria-checked=true] .ActionListItem-label');
508
+ let itemLabel;
509
+ if (this.selectVariant === 'single') {
510
+ itemLabel = this.querySelector('[aria-checked=true] .ActionListItem-label')?.textContent?.trim();
511
+ }
512
+ else if (this.selectVariant === 'multiple') {
513
+ itemLabel = Array.from(this.querySelectorAll(`[aria-checked=true] .ActionListItem-label`))
514
+ .map(label => (label.textContent || '').trim())
515
+ .filter(Boolean)
516
+ .join(', ');
517
+ }
518
+ itemLabel || (itemLabel = __classPrivateFieldGet(this, _ActionMenuElement_originalLabel, "f"));
510
519
  if (itemLabel && this.dynamicLabel) {
511
520
  const prefixSpan = document.createElement('span');
512
521
  prefixSpan.classList.add('color-fg-muted');
513
522
  const contentSpan = document.createElement('span');
514
- prefixSpan.textContent = this.dynamicLabelPrefix;
515
- contentSpan.textContent = itemLabel.textContent || '';
523
+ prefixSpan.textContent = this.dynamicLabelPrefix ? `${this.dynamicLabelPrefix} ` : '';
524
+ contentSpan.textContent = itemLabel;
516
525
  invokerLabel.replaceChildren(prefixSpan, contentSpan);
517
526
  }
518
527
  else {
@@ -455,6 +455,8 @@ export class ActionMenuElement extends HTMLElement {
455
455
  } else {
456
456
  // multi-select mode allows unchecking a checked item
457
457
  item.setAttribute('aria-checked', `${checked}`)
458
+
459
+ this.#setDynamicLabel()
458
460
  }
459
461
 
460
462
  this.#updateInput()
@@ -497,18 +499,26 @@ export class ActionMenuElement extends HTMLElement {
497
499
  }
498
500
 
499
501
  #setDynamicLabel() {
500
- if (this.selectVariant !== 'single') return
501
502
  if (!this.dynamicLabel) return
502
503
  const invokerLabel = this.invokerLabel
503
504
  if (!invokerLabel) return
504
505
  this.#originalLabel ||= invokerLabel.textContent || ''
505
- const itemLabel = this.querySelector('[aria-checked=true] .ActionListItem-label')
506
+ let itemLabel: string | undefined
507
+ if (this.selectVariant === 'single') {
508
+ itemLabel = this.querySelector('[aria-checked=true] .ActionListItem-label')?.textContent?.trim()
509
+ } else if (this.selectVariant === 'multiple') {
510
+ itemLabel = Array.from(this.querySelectorAll(`[aria-checked=true] .ActionListItem-label`))
511
+ .map(label => (label.textContent || '').trim())
512
+ .filter(Boolean)
513
+ .join(', ')
514
+ }
515
+ itemLabel ||= this.#originalLabel
506
516
  if (itemLabel && this.dynamicLabel) {
507
517
  const prefixSpan = document.createElement('span')
508
518
  prefixSpan.classList.add('color-fg-muted')
509
519
  const contentSpan = document.createElement('span')
510
- prefixSpan.textContent = this.dynamicLabelPrefix
511
- contentSpan.textContent = itemLabel.textContent || ''
520
+ prefixSpan.textContent = this.dynamicLabelPrefix ? `${this.dynamicLabelPrefix} ` : ''
521
+ contentSpan.textContent = itemLabel
512
522
  invokerLabel.replaceChildren(prefixSpan, contentSpan)
513
523
  } else {
514
524
  invokerLabel.textContent = this.#originalLabel
@@ -866,6 +866,7 @@ _SelectPanelElement_handleItemActivated = function _SelectPanelElement_handleIte
866
866
  else {
867
867
  __classPrivateFieldGet(this, _SelectPanelElement_instances, "m", _SelectPanelElement_removeSelectedItem).call(this, item);
868
868
  }
869
+ __classPrivateFieldGet(this, _SelectPanelElement_instances, "m", _SelectPanelElement_setDynamicLabel).call(this);
869
870
  }
870
871
  __classPrivateFieldGet(this, _SelectPanelElement_instances, "m", _SelectPanelElement_updateInput).call(this);
871
872
  __classPrivateFieldGet(this, _SelectPanelElement_instances, "m", _SelectPanelElement_updateTabIndices).call(this);
@@ -885,7 +886,16 @@ _SelectPanelElement_setDynamicLabel = function _SelectPanelElement_setDynamicLab
885
886
  if (!invokerLabel)
886
887
  return;
887
888
  __classPrivateFieldSet(this, _SelectPanelElement_originalLabel, __classPrivateFieldGet(this, _SelectPanelElement_originalLabel, "f") || (invokerLabel.textContent || ''), "f");
888
- const itemLabel = this.querySelector(`[${this.ariaSelectionType}=true] .ActionListItem-label`)?.textContent || __classPrivateFieldGet(this, _SelectPanelElement_originalLabel, "f");
889
+ let itemLabel;
890
+ if (this.selectVariant === 'single') {
891
+ itemLabel = this.querySelector(`[${this.ariaSelectionType}=true] .ActionListItem-label`)?.textContent;
892
+ }
893
+ else if (this.selectVariant === 'multiple') {
894
+ itemLabel = Array.from(this.querySelectorAll(`[${this.ariaSelectionType}=true] .ActionListItem-label`))
895
+ .map(label => label.textContent?.trim() ?? '')
896
+ .join(', ');
897
+ }
898
+ itemLabel || (itemLabel = __classPrivateFieldGet(this, _SelectPanelElement_originalLabel, "f"));
889
899
  if (itemLabel) {
890
900
  const prefixSpan = document.createElement('span');
891
901
  prefixSpan.classList.add('color-fg-muted');
@@ -916,6 +916,8 @@ export class SelectPanelElement extends HTMLElement {
916
916
  } else {
917
917
  this.#removeSelectedItem(item)
918
918
  }
919
+
920
+ this.#setDynamicLabel()
919
921
  }
920
922
 
921
923
  this.#updateInput()
@@ -952,8 +954,15 @@ export class SelectPanelElement extends HTMLElement {
952
954
  const invokerLabel = this.invokerLabel
953
955
  if (!invokerLabel) return
954
956
  this.#originalLabel ||= invokerLabel.textContent || ''
955
- const itemLabel =
956
- this.querySelector(`[${this.ariaSelectionType}=true] .ActionListItem-label`)?.textContent || this.#originalLabel
957
+ let itemLabel: string | undefined
958
+ if (this.selectVariant === 'single') {
959
+ itemLabel = this.querySelector(`[${this.ariaSelectionType}=true] .ActionListItem-label`)?.textContent
960
+ } else if (this.selectVariant === 'multiple') {
961
+ itemLabel = Array.from(this.querySelectorAll(`[${this.ariaSelectionType}=true] .ActionListItem-label`))
962
+ .map(label => label.textContent?.trim() ?? '')
963
+ .join(', ')
964
+ }
965
+ itemLabel ||= this.#originalLabel
957
966
  if (itemLabel) {
958
967
  const prefixSpan = document.createElement('span')
959
968
  prefixSpan.classList.add('color-fg-muted')
@@ -15,7 +15,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
15
15
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
16
16
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
17
17
  };
18
- var _TreeViewElement_instances, _TreeViewElement_abortController, _TreeViewElement_autoExpandFrom, _TreeViewElement_eventIsActivation, _TreeViewElement_nodeForEvent, _TreeViewElement_handleNodeEvent, _TreeViewElement_eventIsCheckboxToggle, _TreeViewElement_handleCheckboxToggle, _TreeViewElement_eventIsSingleSelection, _TreeViewElement_handleNodeActivated, _TreeViewElement_handleNodeFocused, _TreeViewElement_handleNodeKeyboardEvent;
18
+ var _TreeViewElement_instances, _TreeViewElement_abortController, _TreeViewElement_autoExpandFrom, _TreeViewElement_eventIsActivation, _TreeViewElement_nodeForEvent, _TreeViewElement_handleNodeEvent, _TreeViewElement_eventIsCheckboxToggle, _TreeViewElement_handleCheckboxToggle, _TreeViewElement_eventIsSingleSelection, _TreeViewElement_handleNodeActivated, _TreeViewElement_handleNodeFocused, _TreeViewElement_handleNodeKeyboardEvent, _TreeViewElement_checkNodeOnly;
19
19
  import { controller, target } from '@github/catalyst';
20
20
  import { useRovingTabIndex } from './tree_view_roving_tab_index';
21
21
  let TreeViewElement = class TreeViewElement extends HTMLElement {
@@ -97,8 +97,8 @@ let TreeViewElement = class TreeViewElement extends HTMLElement {
97
97
  // behavior for these element types is user- or browser-defined
98
98
  if (!(node instanceof HTMLDivElement))
99
99
  return;
100
- const path = this.getNodePath(node);
101
- const nodeInfo = this.infoFromNode(node, 'true');
100
+ const newCheckedValue = this.getNodeCheckedValue(node) === 'true' ? 'false' : 'true';
101
+ const nodeInfo = this.infoFromNode(node, newCheckedValue);
102
102
  const checkSuccess = this.dispatchEvent(new CustomEvent('treeViewBeforeNodeChecked', {
103
103
  bubbles: true,
104
104
  cancelable: true,
@@ -106,11 +106,11 @@ let TreeViewElement = class TreeViewElement extends HTMLElement {
106
106
  }));
107
107
  if (!checkSuccess)
108
108
  return;
109
- if (this.getNodeCheckedValue(node) === 'true') {
109
+ if (newCheckedValue === 'false') {
110
110
  this.setNodeCheckedValue(node, 'false');
111
111
  }
112
112
  else {
113
- this.checkOnlyAtPath(path);
113
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_checkNodeOnly).call(this, node);
114
114
  }
115
115
  this.dispatchEvent(new CustomEvent('treeViewNodeChecked', {
116
116
  bubbles: true,
@@ -142,7 +142,7 @@ let TreeViewElement = class TreeViewElement extends HTMLElement {
142
142
  return this.querySelector('[aria-current=true]');
143
143
  }
144
144
  get activeNodes() {
145
- return document.querySelectorAll('[aria-checked="true"]');
145
+ return this.querySelectorAll('[aria-checked="true"]');
146
146
  }
147
147
  expandAtPath(path) {
148
148
  const node = this.subTreeAtPath(path);
@@ -175,10 +175,10 @@ let TreeViewElement = class TreeViewElement extends HTMLElement {
175
175
  this.setNodeCheckedValue(node, 'false');
176
176
  }
177
177
  checkOnlyAtPath(path) {
178
- for (const el of this.activeNodes) {
179
- this.uncheckAtPath(this.getNodePath(el));
180
- }
181
- this.checkAtPath(path);
178
+ const node = this.nodeAtPath(path);
179
+ if (!node)
180
+ return;
181
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_checkNodeOnly).call(this, node);
182
182
  }
183
183
  toggleCheckedAtPath(path) {
184
184
  const node = this.nodeAtPath(path);
@@ -286,6 +286,9 @@ let TreeViewElement = class TreeViewElement extends HTMLElement {
286
286
  const payload = {
287
287
  path: this.getNodePath(node),
288
288
  };
289
+ const nodeId = node.getAttribute('data-node-id');
290
+ if (nodeId)
291
+ payload.nodeId = nodeId;
289
292
  const inputValue = this.getFormInputValueForNode(node);
290
293
  if (inputValue)
291
294
  payload.value = inputValue;
@@ -424,7 +427,12 @@ _TreeViewElement_handleNodeKeyboardEvent = function _TreeViewElement_handleNodeK
424
427
  }
425
428
  else if (this.selectVariant(node) === 'single') {
426
429
  event.preventDefault();
427
- this.checkOnlyAtPath(this.getNodePath(node));
430
+ if (this.getNodeCheckedValue(node) === 'true') {
431
+ this.setNodeCheckedValue(node, 'false');
432
+ }
433
+ else {
434
+ __classPrivateFieldGet(this, _TreeViewElement_instances, "m", _TreeViewElement_checkNodeOnly).call(this, node);
435
+ }
428
436
  }
429
437
  else if (node instanceof HTMLAnchorElement) {
430
438
  // simulate click on space
@@ -433,6 +441,14 @@ _TreeViewElement_handleNodeKeyboardEvent = function _TreeViewElement_handleNodeK
433
441
  break;
434
442
  }
435
443
  };
444
+ _TreeViewElement_checkNodeOnly = function _TreeViewElement_checkNodeOnly(node) {
445
+ for (const el of this.activeNodes) {
446
+ if (el === node)
447
+ continue;
448
+ this.setNodeCheckedValue(el, 'false');
449
+ }
450
+ this.setNodeCheckedValue(node, 'true');
451
+ };
436
452
  __decorate([
437
453
  target
438
454
  ], TreeViewElement.prototype, "formInputContainer", void 0);
@@ -179,8 +179,8 @@ export class TreeViewElement extends HTMLElement {
179
179
  // behavior for these element types is user- or browser-defined
180
180
  if (!(node instanceof HTMLDivElement)) return
181
181
 
182
- const path = this.getNodePath(node)
183
- const nodeInfo = this.infoFromNode(node, 'true')
182
+ const newCheckedValue = this.getNodeCheckedValue(node) === 'true' ? 'false' : 'true'
183
+ const nodeInfo = this.infoFromNode(node, newCheckedValue)
184
184
 
185
185
  const checkSuccess = this.dispatchEvent(
186
186
  new CustomEvent('treeViewBeforeNodeChecked', {
@@ -192,10 +192,10 @@ export class TreeViewElement extends HTMLElement {
192
192
 
193
193
  if (!checkSuccess) return
194
194
 
195
- if (this.getNodeCheckedValue(node) === 'true') {
195
+ if (newCheckedValue === 'false') {
196
196
  this.setNodeCheckedValue(node, 'false')
197
197
  } else {
198
- this.checkOnlyAtPath(path)
198
+ this.#checkNodeOnly(node)
199
199
  }
200
200
 
201
201
  this.dispatchEvent(
@@ -271,7 +271,11 @@ export class TreeViewElement extends HTMLElement {
271
271
  } else if (this.selectVariant(node) === 'single') {
272
272
  event.preventDefault()
273
273
 
274
- this.checkOnlyAtPath(this.getNodePath(node))
274
+ if (this.getNodeCheckedValue(node) === 'true') {
275
+ this.setNodeCheckedValue(node, 'false')
276
+ } else {
277
+ this.#checkNodeOnly(node)
278
+ }
275
279
  } else if (node instanceof HTMLAnchorElement) {
276
280
  // simulate click on space
277
281
  node.click()
@@ -313,7 +317,7 @@ export class TreeViewElement extends HTMLElement {
313
317
  }
314
318
 
315
319
  get activeNodes() {
316
- return document.querySelectorAll('[aria-checked="true"]')
320
+ return this.querySelectorAll('[aria-checked="true"]')
317
321
  }
318
322
 
319
323
  expandAtPath(path: string[]) {
@@ -352,11 +356,19 @@ export class TreeViewElement extends HTMLElement {
352
356
  }
353
357
 
354
358
  checkOnlyAtPath(path: string[]) {
359
+ const node = this.nodeAtPath(path)
360
+ if (!node) return
361
+
362
+ this.#checkNodeOnly(node)
363
+ }
364
+
365
+ #checkNodeOnly(node: Element) {
355
366
  for (const el of this.activeNodes) {
356
- this.uncheckAtPath(this.getNodePath(el))
367
+ if (el === node) continue
368
+ this.setNodeCheckedValue(el, 'false')
357
369
  }
358
370
 
359
- this.checkAtPath(path)
371
+ this.setNodeCheckedValue(node, 'true')
360
372
  }
361
373
 
362
374
  toggleCheckedAtPath(path: string[]) {
@@ -479,10 +491,13 @@ export class TreeViewElement extends HTMLElement {
479
491
  newInput.removeAttribute('data-target')
480
492
  newInput.removeAttribute('form')
481
493
 
482
- const payload: {path: string[]; value?: string} = {
494
+ const payload: {path: string[]; nodeId?: string; value?: string} = {
483
495
  path: this.getNodePath(node),
484
496
  }
485
497
 
498
+ const nodeId = node.getAttribute('data-node-id')
499
+ if (nodeId) payload.nodeId = nodeId
500
+
486
501
  const inputValue = this.getFormInputValueForNode(node)
487
502
  if (inputValue) payload.value = inputValue
488
503
 
@@ -100,6 +100,6 @@ let AvatarFallbackElement = class AvatarFallbackElement extends HTMLElement {
100
100
  }
101
101
  };
102
102
  AvatarFallbackElement = __decorate([
103
- controller
103
+ controller('avatar-fallback')
104
104
  ], AvatarFallbackElement);
105
105
  export { AvatarFallbackElement };
@@ -13,7 +13,7 @@ import {controller} from '@github/catalyst'
13
13
  * due to a Catalyst bug where @attr accessors aren't properly initialized
14
14
  * when elements have pre-existing attribute values.
15
15
  */
16
- @controller
16
+ @controller('avatar-fallback')
17
17
  export class AvatarFallbackElement extends HTMLElement {
18
18
  private img: HTMLImageElement | null = null
19
19
  private testImage: HTMLImageElement | null = null
@@ -17,5 +17,5 @@ let CollapsibleHeaderElement = class CollapsibleHeaderElement extends Collapsibl
17
17
  }
18
18
  };
19
19
  CollapsibleHeaderElement = __decorate([
20
- controller
20
+ controller('collapsible-header')
21
21
  ], CollapsibleHeaderElement);
@@ -1,7 +1,7 @@
1
1
  import {controller} from '@github/catalyst'
2
2
  import {CollapsibleElement} from '../collapsible'
3
3
 
4
- @controller
4
+ @controller('collapsible-header')
5
5
  class CollapsibleHeaderElement extends CollapsibleElement {
6
6
  connectedCallback() {
7
7
  if (!this.closest('.Box')) {
@@ -12,5 +12,5 @@ let CollapsibleSectionElement = class CollapsibleSectionElement extends Collapsi
12
12
  }
13
13
  };
14
14
  CollapsibleSectionElement = __decorate([
15
- controller
15
+ controller('collapsible-section')
16
16
  ], CollapsibleSectionElement);
@@ -1,7 +1,7 @@
1
1
  import {controller} from '@github/catalyst'
2
2
  import {CollapsibleElement} from './collapsible'
3
3
 
4
- @controller
4
+ @controller('collapsible-section')
5
5
  class CollapsibleSectionElement extends CollapsibleElement {
6
6
  get baseClass(): string {
7
7
  return 'CollapsibleSection'
@@ -62,7 +62,7 @@ __decorate([
62
62
  attr
63
63
  ], DangerDialogFormHelperElement.prototype, "confirmationLiveMessageUnchecked", void 0);
64
64
  DangerDialogFormHelperElement = __decorate([
65
- controller
65
+ controller('danger-dialog-form-helper')
66
66
  ], DangerDialogFormHelperElement);
67
67
  if (!window.customElements.get('danger-dialog-form-helper')) {
68
68
  window.DangerDialogFormHelperElement = DangerDialogFormHelperElement;
@@ -3,7 +3,7 @@ import type {LiveRegionElement} from '@primer/live-region-element'
3
3
 
4
4
  const SUBMIT_BUTTON_SELECTOR = 'input[type=submit],button[type=submit],button[data-submit-dialog-id]'
5
5
 
6
- @controller
6
+ @controller('danger-dialog-form-helper')
7
7
  class DangerDialogFormHelperElement extends HTMLElement {
8
8
  @target checkbox: HTMLInputElement | undefined
9
9
  @target liveRegion: LiveRegionElement
@@ -18,7 +18,7 @@ module Primer
18
18
  # @param legend_text [String] A legend should be short and concise. The String will also be read by assistive technology.
19
19
  # @param visually_hide_legend [Boolean] Controls if the legend is visible. If `true`, screen reader only text will be added.
20
20
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
21
- def initialize(legend_text: nil, visually_hide_legend: false, **system_arguments) # rubocop:disable Lint/MissingSuper
21
+ def initialize(legend_text: nil, visually_hide_legend: false, **system_arguments)
22
22
  @legend_text = legend_text
23
23
  @visually_hide_legend = visually_hide_legend
24
24
  @system_arguments = deny_tag_argument(**system_arguments)
@@ -40,7 +40,7 @@ module Primer
40
40
 
41
41
  attr_reader :text
42
42
 
43
- def initialize(text: nil, visually_hide_legend: false, **system_arguments) # rubocop:disable Lint/MissingSuper
43
+ def initialize(text: nil, visually_hide_legend: false, **system_arguments)
44
44
  @text = text
45
45
 
46
46
  @system_arguments = deny_tag_argument(**system_arguments)
@@ -224,6 +224,7 @@ _FilterableTreeViewElement_updateCheckedNodeIds = function _FilterableTreeViewEl
224
224
  }
225
225
  __classPrivateFieldGet(this, _FilterableTreeViewElement_checkedNodeIds, "f").set(nodeId, nodeInfo.checkedValue);
226
226
  const payload = { path: nodeInfo.path };
227
+ payload.nodeId = nodeId;
227
228
  const dataValue = node.getAttribute('data-value');
228
229
  if (dataValue)
229
230
  payload.value = dataValue;
@@ -689,7 +690,7 @@ __decorate([
689
690
  target
690
691
  ], FilterableTreeViewElement.prototype, "includeSubItemsCheckBox", void 0);
691
692
  FilterableTreeViewElement = __decorate([
692
- controller
693
+ controller('filterable-tree-view')
693
694
  ], FilterableTreeViewElement);
694
695
  export { FilterableTreeViewElement };
695
696
  if (!window.customElements.get('filterable-tree-view')) {
@@ -199,10 +199,10 @@ module Primer
199
199
  src: nil,
200
200
  tree_view_arguments: {},
201
201
  form_arguments: {},
202
- filter_input_arguments: DEFAULT_FILTER_INPUT_ARGUMENTS.dup,
203
- filter_mode_control_arguments: DEFAULT_FILTER_MODE_CONTROL_ARGUMENTS.dup,
204
- include_sub_items_check_box_arguments: DEFAULT_INCLUDE_SUB_ITEMS_CHECK_BOX_ARGUMENTS.dup,
205
- no_results_node_arguments: DEFAULT_NO_RESULTS_NODE_ARGUMENTS.dup,
202
+ filter_input_arguments: {},
203
+ filter_mode_control_arguments: {},
204
+ include_sub_items_check_box_arguments: {},
205
+ no_results_node_arguments: {},
206
206
  **system_arguments
207
207
  )
208
208
  @tree_view_arguments = tree_view_arguments.dup
@@ -218,6 +218,7 @@ module Primer
218
218
  **tree_view_arguments
219
219
  )
220
220
 
221
+ filter_input_arguments = filter_input_arguments.reverse_merge(DEFAULT_FILTER_INPUT_ARGUMENTS)
221
222
  filter_input_arguments[:data] = merge_data(
222
223
  filter_input_arguments, {
223
224
  data: { target: "filterable-tree-view.filterInput" }
@@ -250,7 +251,7 @@ module Primer
250
251
  @system_arguments[:tag] = :"filterable-tree-view"
251
252
  @system_arguments[:src] = src if src
252
253
 
253
- @no_results_node_arguments = no_results_node_arguments
254
+ @no_results_node_arguments = no_results_node_arguments.reverse_merge(DEFAULT_NO_RESULTS_NODE_ARGUMENTS)
254
255
  end
255
256
 
256
257
  def with_default_filter_modes
@@ -18,7 +18,7 @@ type NodeState = {
18
18
 
19
19
  const ASYNC_DEBOUNCE_MS = 300
20
20
 
21
- @controller
21
+ @controller('filterable-tree-view')
22
22
  export class FilterableTreeViewElement extends HTMLElement {
23
23
  @target filterInput: HTMLInputElement
24
24
  @target filterModeControlList: HTMLElement
@@ -119,7 +119,8 @@ export class FilterableTreeViewElement extends HTMLElement {
119
119
  }
120
120
 
121
121
  this.#checkedNodeIds.set(nodeId, nodeInfo.checkedValue)
122
- const payload: {path: string[]; value?: string} = {path: nodeInfo.path}
122
+ const payload: {path: string[]; nodeId?: string; value?: string} = {path: nodeInfo.path}
123
+ payload.nodeId = nodeId
123
124
  const dataValue = node.getAttribute('data-value')
124
125
  if (dataValue) payload.value = dataValue
125
126
  this.#checkedNodeFormPayloads.set(nodeId, payload)
@@ -29,10 +29,10 @@ module Primer
29
29
  # no slot provided
30
30
  return false if rows.empty? && columns.empty? && boxes.empty?
31
31
 
32
- if [rows, columns, boxes].count { |arr| !arr.empty? } == 1
32
+ if [rows, columns, boxes].one? { |arr| !arr.empty? }
33
33
  # only rows or columns or boxes are used
34
34
  true
35
- elsif [rows, columns, boxes].count { |arr| !arr.empty? } > 1
35
+ elsif [rows, columns, boxes].many? { |arr| !arr.empty? }
36
36
  # rows, columns and boxes are used together, which is not allowed
37
37
  raise ArgumentError, "You can't mix row, column and box slots"
38
38
  end
@@ -33,7 +33,7 @@ module Primer
33
33
  # @param scheme [Symbol] <%= one_of(Primer::OpenProject::InlineMessage::SCHEME_OPTIONS) %>
34
34
  # @param size [Symbol] <%= one_of(Primer::OpenProject::InlineMessage::SIZE_OPTIONS) %>
35
35
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
36
- def initialize(scheme:, size: DEFAULT_SIZE, **system_arguments) # rubocop:disable Lint/MissingSuper
36
+ def initialize(scheme:, size: DEFAULT_SIZE, **system_arguments)
37
37
  resolved_scheme = fetch_or_fallback(SCHEME_OPTIONS, scheme)
38
38
  resolved_size = fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)
39
39
 
@@ -4,6 +4,15 @@
4
4
  <%= trailing_action %>
5
5
  <% end %>
6
6
 
7
+ <% if @text_input_validation_message %>
8
+ <div class="FormControl-inlineValidation mt-1" id="<%= @text_input_validation_message_id %>">
9
+ <span class="FormControl-inlineValidation--visual">
10
+ <%= render(Primer::Beta::Octicon.new(icon: :"alert-fill", size: :xsmall, aria: { hidden: true })) %>
11
+ </span>
12
+ <span><%= @text_input_validation_message %></span>
13
+ </div>
14
+ <% end %>
15
+
7
16
  <% if caption %>
8
17
  <%= caption %>
9
18
  <% end %>
@@ -49,6 +49,19 @@ module Primer
49
49
  renders_one :text_input, lambda { |readonly: true, **system_arguments|
50
50
  deny_single_argument(:input_width, "Set the `input_width` on the `InputGroup`", **system_arguments)
51
51
  deny_single_argument(:caption, "Set the `caption` on the `InputGroup`", **system_arguments)
52
+
53
+ # Extract validation_message so it can be rendered outside the flex row.
54
+ # This prevents the trailing action from misaligning when validation is present.
55
+ @text_input_validation_message = system_arguments.delete(:validation_message)
56
+ if @text_input_validation_message
57
+ @text_input_validation_message_id = self.class.generate_id(base_name: "input-group-validation")
58
+ system_arguments[:invalid] = true
59
+ system_arguments[:aria] = merge_aria(
60
+ system_arguments,
61
+ { aria: { describedby: @text_input_validation_message_id } }
62
+ )
63
+ end
64
+
52
65
  system_arguments[:input_width] = @system_arguments[:input_width]
53
66
 
54
67
  system_arguments[:classes] = class_names(
@@ -288,7 +288,7 @@ module Primer
288
288
  end
289
289
 
290
290
  def render_mobile_menu?
291
- actions.count > 1
291
+ actions.many?
292
292
  end
293
293
 
294
294
  def show_state?
@@ -15,7 +15,7 @@ let PageHeaderElement = class PageHeaderElement extends HTMLElement {
15
15
  }
16
16
  };
17
17
  PageHeaderElement = __decorate([
18
- controller
18
+ controller('page-header')
19
19
  ], PageHeaderElement);
20
20
  if (!window.customElements.get('page-header')) {
21
21
  window.PageHeaderElement = PageHeaderElement;
@@ -1,6 +1,6 @@
1
1
  import {controller} from '@github/catalyst'
2
2
 
3
- @controller
3
+ @controller('page-header')
4
4
  class PageHeaderElement extends HTMLElement {
5
5
  menuItemClick(event: Event) {
6
6
  const currentTarget = event.currentTarget as HTMLButtonElement
@@ -269,9 +269,7 @@ module Primer
269
269
  key = :"page-#{page[:num]}-break"
270
270
  content = "…"
271
271
 
272
- props.merge!(
273
- role: "presentation"
274
- )
272
+ props[:role] = "presentation"
275
273
  end
276
274
 
277
275
  props[:class] = class_names("Page", props[:class])
@@ -5,11 +5,17 @@ module Primer
5
5
  # Thin wrapper for quick filter slots that defers BaseComponent construction to render time,
6
6
  # allowing system arguments (e.g. display) to be mutated in before_render.
7
7
  # Do not use standalone
8
- class SubHeader::QuickFilter < Primer::Component
8
+ class SubHeader::QuickActionComponent < Primer::Component
9
9
  status :open_project
10
10
 
11
11
  def initialize(**system_arguments)
12
12
  @system_arguments = system_arguments
13
+ system_arguments[:tag] = :div
14
+ system_arguments[:mr] ||= 2
15
+ system_arguments[:classes] = class_names(
16
+ "SubHeader-hiddenOnExpand",
17
+ system_arguments[:classes]
18
+ )
13
19
  end
14
20
 
15
21
  def merge_system_arguments!(**other_arguments)
@@ -7,6 +7,8 @@
7
7
 
8
8
  <%= render @collapsed_filter_trigger if @collapsed_filter_trigger.present? %>
9
9
 
10
+ <%= quick_sort %>
11
+ <%= quick_group %>
10
12
  <% quick_filters.each do |quick_filter| %>
11
13
  <%= quick_filter %>
12
14
  <% end %>