primer_view_components 0.13.1 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +41 -0
  3. data/README.md +1 -1
  4. data/app/assets/javascripts/app/components/primer/primer.d.ts +0 -1
  5. data/app/assets/javascripts/primer_view_components.js +1 -1
  6. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  7. data/app/assets/styles/primer_view_components.css +1 -1
  8. data/app/assets/styles/primer_view_components.css.map +1 -1
  9. data/app/components/primer/alpha/action_bar.css +1 -1
  10. data/app/components/primer/alpha/action_bar.css.json +1 -4
  11. data/app/components/primer/alpha/action_bar.css.map +1 -1
  12. data/app/components/primer/alpha/action_bar.pcss +1 -17
  13. data/app/components/primer/alpha/action_bar_element.js +21 -9
  14. data/app/components/primer/alpha/action_list/item.rb +0 -2
  15. data/app/components/primer/alpha/action_list.css +1 -1
  16. data/app/components/primer/alpha/action_list.css.map +1 -1
  17. data/app/components/primer/alpha/action_list.rb +0 -1
  18. data/app/components/primer/alpha/action_menu/action_menu_element.js +52 -26
  19. data/app/components/primer/alpha/action_menu/action_menu_element.ts +9 -6
  20. data/app/components/primer/alpha/action_menu/list.rb +0 -9
  21. data/app/components/primer/alpha/action_menu.html.erb +20 -18
  22. data/app/components/primer/alpha/auto_complete.css +1 -1
  23. data/app/components/primer/alpha/auto_complete.css.map +1 -1
  24. data/app/components/primer/alpha/button_marketing.css +1 -1
  25. data/app/components/primer/alpha/button_marketing.css.map +1 -1
  26. data/app/components/primer/alpha/dialog/header.rb +1 -1
  27. data/app/components/primer/alpha/dropdown.css +1 -1
  28. data/app/components/primer/alpha/dropdown.css.map +1 -1
  29. data/app/components/primer/alpha/layout.css +1 -1
  30. data/app/components/primer/alpha/layout.css.map +1 -1
  31. data/app/components/primer/alpha/menu.css +1 -1
  32. data/app/components/primer/alpha/menu.css.map +1 -1
  33. data/app/components/primer/alpha/nav_list/divider.rb +1 -0
  34. data/app/components/primer/alpha/nav_list/group.rb +1 -0
  35. data/app/components/primer/alpha/nav_list/heading.rb +1 -0
  36. data/app/components/primer/alpha/nav_list/item.rb +1 -0
  37. data/app/components/primer/alpha/nav_list.rb +1 -0
  38. data/app/components/primer/alpha/octicon_symbols.html.erb +1 -1
  39. data/app/components/primer/alpha/overlay.css +1 -1
  40. data/app/components/primer/alpha/overlay.css.json +0 -2
  41. data/app/components/primer/alpha/overlay.css.map +1 -1
  42. data/app/components/primer/alpha/overlay.pcss +0 -12
  43. data/app/components/primer/alpha/segmented_control.css +1 -1
  44. data/app/components/primer/alpha/segmented_control.css.json +0 -1
  45. data/app/components/primer/alpha/segmented_control.css.map +1 -1
  46. data/app/components/primer/alpha/segmented_control.js +2 -1
  47. data/app/components/primer/alpha/segmented_control.pcss +0 -4
  48. data/app/components/primer/alpha/tab_nav.css +1 -1
  49. data/app/components/primer/alpha/tab_nav.css.map +1 -1
  50. data/app/components/primer/alpha/text_field.css +1 -1
  51. data/app/components/primer/alpha/text_field.css.json +11 -5
  52. data/app/components/primer/alpha/text_field.css.map +1 -1
  53. data/app/components/primer/alpha/text_field.pcss +10 -1
  54. data/app/components/primer/alpha/toggle_switch.css +1 -1
  55. data/app/components/primer/alpha/toggle_switch.css.map +1 -1
  56. data/app/components/primer/alpha/underline_nav.css +1 -1
  57. data/app/components/primer/alpha/underline_nav.css.map +1 -1
  58. data/app/components/primer/alpha/x_banner.js +2 -1
  59. data/app/components/primer/anchored_position.js +2 -1
  60. data/app/components/primer/beta/auto_complete/item.rb +1 -1
  61. data/app/components/primer/beta/avatar.rb +1 -1
  62. data/app/components/primer/beta/base_button.rb +1 -1
  63. data/app/components/primer/beta/blankslate.css +1 -1
  64. data/app/components/primer/beta/blankslate.css.json +1 -0
  65. data/app/components/primer/beta/blankslate.css.map +1 -1
  66. data/app/components/primer/beta/blankslate.html.erb +16 -14
  67. data/app/components/primer/beta/blankslate.pcss +52 -2
  68. data/app/components/primer/beta/border_box.css +1 -1
  69. data/app/components/primer/beta/border_box.css.map +1 -1
  70. data/app/components/primer/beta/button.css +1 -1
  71. data/app/components/primer/beta/button.css.json +0 -1
  72. data/app/components/primer/beta/button.css.map +1 -1
  73. data/app/components/primer/beta/button.html.erb +18 -20
  74. data/app/components/primer/beta/button.pcss +0 -5
  75. data/app/components/primer/beta/button.rb +3 -0
  76. data/app/components/primer/beta/button_group.css +1 -1
  77. data/app/components/primer/beta/button_group.css.json +2 -4
  78. data/app/components/primer/beta/button_group.css.map +1 -1
  79. data/app/components/primer/beta/button_group.html.erb +3 -1
  80. data/app/components/primer/beta/button_group.pcss +2 -4
  81. data/app/components/primer/beta/button_group.rb +41 -12
  82. data/app/components/primer/beta/clipboard_copy.rb +4 -0
  83. data/app/components/primer/beta/clipboard_copy_button.rb +25 -0
  84. data/app/components/primer/beta/counter.rb +1 -1
  85. data/app/components/primer/beta/flash.html.erb +1 -1
  86. data/app/components/primer/beta/icon_button.html.erb +4 -6
  87. data/app/components/primer/beta/icon_button.rb +1 -3
  88. data/app/components/primer/beta/label.css +1 -1
  89. data/app/components/primer/beta/label.css.map +1 -1
  90. data/app/components/primer/beta/link.css +1 -1
  91. data/app/components/primer/beta/link.css.map +1 -1
  92. data/app/components/primer/beta/nav_list.js +14 -7
  93. data/app/components/primer/beta/subhead.css +1 -1
  94. data/app/components/primer/beta/subhead.css.json +2 -0
  95. data/app/components/primer/beta/subhead.css.map +1 -1
  96. data/app/components/primer/beta/subhead.pcss +8 -1
  97. data/app/components/primer/beta/subhead.rb +9 -1
  98. data/app/components/primer/focus_group.js +2 -1
  99. data/app/components/primer/primer.d.ts +0 -1
  100. data/app/components/primer/primer.js +0 -1
  101. data/app/components/primer/primer.pcss +0 -2
  102. data/app/components/primer/primer.ts +0 -1
  103. data/app/forms/action_menu_form.rb +20 -0
  104. data/app/forms/immediate_validation_form.rb +2 -2
  105. data/app/lib/primer/fetch_or_fallback_helper.rb +0 -1
  106. data/app/lib/primer/octicon/cache.rb +1 -1
  107. data/lib/primer/classify.rb +0 -2
  108. data/lib/primer/forms/action_menu.html.erb +6 -0
  109. data/lib/primer/forms/action_menu.rb +25 -0
  110. data/lib/primer/forms/acts_as_component.rb +0 -3
  111. data/lib/primer/forms/base.rb +0 -1
  112. data/lib/primer/forms/base_component.rb +0 -2
  113. data/lib/primer/forms/dsl/action_menu_input.rb +36 -0
  114. data/lib/primer/forms/dsl/input.rb +8 -1
  115. data/lib/primer/forms/dsl/input_methods.rb +9 -0
  116. data/lib/primer/forms/dsl/text_field_input.rb +8 -0
  117. data/lib/primer/forms/primer_base_component_wrapper.rb +0 -2
  118. data/lib/primer/forms/primer_text_field.js +40 -5
  119. data/lib/primer/forms/primer_text_field.ts +39 -7
  120. data/lib/primer/forms/validation_message.html.erb +2 -1
  121. data/lib/primer/static/generate_info_arch.rb +7 -3
  122. data/lib/primer/static/generate_previews.rb +0 -2
  123. data/lib/primer/view_components/engine.rb +5 -1
  124. data/lib/primer/view_components/linters/base_linter.rb +3 -2
  125. data/lib/primer/view_components/linters/deprecated_components_counter.rb +1 -1
  126. data/lib/primer/view_components/linters/disallow_action_list.rb +1 -0
  127. data/lib/primer/view_components/linters/severity_schema.rb +1 -0
  128. data/lib/primer/view_components/version.rb +2 -3
  129. data/lib/primer/yard/lookbook_pages_backend.rb +0 -2
  130. data/lib/rubocop/cop/primer/base_cop.rb +1 -1
  131. data/lib/rubocop/cop/primer/deprecated_arguments.rb +0 -2
  132. data/lib/rubocop/cop/primer/deprecated_components.rb +1 -1
  133. data/lib/rubocop/cop/primer/deprecated_label_schemes.rb +1 -1
  134. data/lib/rubocop/cop/primer/deprecated_label_variants.rb +1 -1
  135. data/previews/primer/alpha/octicon_symbols_preview/default.html.erb +6 -0
  136. data/previews/primer/alpha/octicon_symbols_preview/playground.html.erb +13 -0
  137. data/previews/primer/alpha/octicon_symbols_preview.rb +21 -0
  138. data/previews/primer/alpha/text_field_preview.rb +5 -0
  139. data/previews/primer/beta/avatar_preview.rb +6 -0
  140. data/previews/primer/beta/button_group_preview.rb +11 -0
  141. data/previews/primer/beta/clipboard_copy_button_preview.rb +29 -0
  142. data/previews/primer/beta/subhead_preview.rb +32 -4
  143. data/previews/primer/forms_preview/action_menu_form.html.erb +3 -0
  144. data/previews/primer/forms_preview.rb +4 -0
  145. data/static/arguments.json +75 -13
  146. data/static/audited_at.json +2 -0
  147. data/static/classes.json +9 -3
  148. data/static/constants.json +16 -1
  149. data/static/info_arch.json +335 -32
  150. data/static/previews.json +146 -0
  151. data/static/statuses.json +2 -0
  152. metadata +12 -2
@@ -171,12 +171,14 @@ let NavListElement = class NavListElement extends HTMLElement {
171
171
  }
172
172
  }
173
173
  };
174
- _NavListElement_instances = new WeakSet(), _NavListElement_parseHTML = function _NavListElement_parseHTML(document, html) {
174
+ _NavListElement_instances = new WeakSet();
175
+ _NavListElement_parseHTML = function _NavListElement_parseHTML(document, html) {
175
176
  const template = document.createElement('template');
176
177
  // eslint-disable-next-line github/no-inner-html
177
178
  template.innerHTML = html;
178
179
  return document.importNode(template.content, true);
179
- }, _NavListElement_findSelectedNavItemById = function _NavListElement_findSelectedNavItemById(itemId) {
180
+ };
181
+ _NavListElement_findSelectedNavItemById = function _NavListElement_findSelectedNavItemById(itemId) {
180
182
  var _a;
181
183
  // First we compare the selected link to data-item-id for each nav item
182
184
  for (const navItem of this.items) {
@@ -189,16 +191,19 @@ _NavListElement_instances = new WeakSet(), _NavListElement_parseHTML = function
189
191
  }
190
192
  }
191
193
  return null;
192
- }, _NavListElement_findSelectedNavItemByHref = function _NavListElement_findSelectedNavItemByHref(href) {
194
+ };
195
+ _NavListElement_findSelectedNavItemByHref = function _NavListElement_findSelectedNavItemByHref(href) {
193
196
  // If we didn't find a match, we compare the selected link to the href of each nav item
194
197
  const selectedNavItem = this.querySelector(`.ActionListContent[href="${href}"]`);
195
198
  if (selectedNavItem) {
196
199
  return selectedNavItem.closest('.ActionListItem');
197
200
  }
198
201
  return null;
199
- }, _NavListElement_findSelectedNavItemByCurrentLocation = function _NavListElement_findSelectedNavItemByCurrentLocation() {
202
+ };
203
+ _NavListElement_findSelectedNavItemByCurrentLocation = function _NavListElement_findSelectedNavItemByCurrentLocation() {
200
204
  return __classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_findSelectedNavItemByHref).call(this, window.location.pathname);
201
- }, _NavListElement_select = function _NavListElement_select(navItem) {
205
+ };
206
+ _NavListElement_select = function _NavListElement_select(navItem) {
202
207
  const currentlySelectedItem = this.querySelector('.ActionListItem--navActive');
203
208
  if (currentlySelectedItem)
204
209
  __classPrivateFieldGet(this, _NavListElement_instances, "m", _NavListElement_deselect).call(this, currentlySelectedItem);
@@ -211,7 +216,8 @@ _NavListElement_instances = new WeakSet(), _NavListElement_parseHTML = function
211
216
  this.expandItem(parentMenu);
212
217
  parentMenu.classList.add('ActionListContent--hasActiveSubItem');
213
218
  }
214
- }, _NavListElement_deselect = function _NavListElement_deselect(navItem) {
219
+ };
220
+ _NavListElement_deselect = function _NavListElement_deselect(navItem) {
215
221
  navItem.classList.remove('ActionListItem--navActive');
216
222
  if (navItem.children.length > 0) {
217
223
  navItem.children[0].removeAttribute('aria-current');
@@ -221,7 +227,8 @@ _NavListElement_instances = new WeakSet(), _NavListElement_parseHTML = function
221
227
  this.collapseItem(parentMenu);
222
228
  parentMenu.classList.remove('ActionListContent--hasActiveSubItem');
223
229
  }
224
- }, _NavListElement_findParentMenu = function _NavListElement_findParentMenu(navItem) {
230
+ };
231
+ _NavListElement_findParentMenu = function _NavListElement_findParentMenu(navItem) {
225
232
  var _a;
226
233
  if (!navItem.classList.contains('ActionListItem--subItem'))
227
234
  return null;
@@ -1 +1 @@
1
- .Subhead{border-bottom:var(--borderWidth-thin,max(1px,.0625rem)) solid var(--borderColor-muted,var(--color-border-muted));display:flex;flex-flow:row wrap;justify-content:flex-end;margin-bottom:var(--stack-gap-normal,1rem);padding-bottom:var(--stack-padding-condensed,.5rem)}.Subhead--spacious{margin-top:var(--base-size-40,2.5rem)}.Subhead-heading{flex:1 1 auto;font-size:24px;font-weight:var(--base-text-weight-normal,400);order:0}.Subhead-heading--danger{color:var(--fgColor-danger,var(--color-danger-fg));font-weight:var(--base-text-weight-semibold,600)}.Subhead-description{color:var(--fgColor-muted,var(--color-fg-muted));flex:1 100%;font-size:var(--text-body-size-medium,.875rem);order:2}.Subhead-actions{align-self:center;justify-content:flex-end;margin:var(--base-size-4,.25rem) 0 var(--base-size-4,.25rem) var(--base-size-4,.25rem);order:1}.Subhead-actions+.Subhead-description{margin-top:var(--base-size-4,.25rem)}
1
+ .Subhead{border-bottom:var(--borderWidth-thin,max(1px,.0625rem)) solid var(--borderColor-muted,var(--color-border-muted));display:flex;flex-flow:row wrap;justify-content:flex-end;margin-bottom:var(--stack-gap-normal,1rem);padding-bottom:var(--stack-padding-condensed,.5rem)}.Subhead--spacious{margin-top:var(--base-size-40,2.5rem)}.Subhead-heading{flex:1 1 auto;font-weight:var(--base-text-weight-normal,400);order:0}.Subhead-heading--large{font-size:var(--base-size-24,1.5rem)}.Subhead-heading--medium{font-size:var(--text-title-size-medium,1.25rem)}.Subhead-heading--danger{color:var(--fgColor-danger,var(--color-danger-fg));font-weight:var(--base-text-weight-semibold,600)}.Subhead-description{color:var(--fgColor-muted,var(--color-fg-muted));flex:1 100%;font-size:var(--text-body-size-medium,.875rem);order:2}.Subhead-actions{align-self:center;justify-content:flex-end;margin:var(--base-size-4,.25rem) 0 var(--base-size-4,.25rem) var(--base-size-4,.25rem);order:1}.Subhead-actions+.Subhead-description{margin-top:var(--base-size-4,.25rem)}
@@ -4,6 +4,8 @@
4
4
  ".Subhead",
5
5
  ".Subhead--spacious",
6
6
  ".Subhead-heading",
7
+ ".Subhead-heading--large",
8
+ ".Subhead-heading--medium",
7
9
  ".Subhead-heading--danger",
8
10
  ".Subhead-description",
9
11
  ".Subhead-actions",
@@ -1 +1 @@
1
- {"version":3,"sources":["subhead.pcss"],"names":[],"mappings":"AAEA,SAIE,gHAAqE,CAHrE,YAAa,CAIb,kBAAmB,CACnB,wBAAyB,CAHzB,0CAAsC,CADtC,mDAKF,CAGA,mBACE,qCACF,CAGA,iBAGE,aAAc,CAFd,cAAe,CACf,8CAA2C,CAE3C,OACF,CAGA,yBAEE,kDAA4B,CAD5B,gDAEF,CAGA,qBAEE,gDAA2B,CAC3B,WAAY,CAFZ,8CAAuC,CAGvC,OACF,CAGA,iBAEE,iBAAkB,CAClB,wBAAyB,CAFzB,sFAAkE,CAGlE,OAKF,CAHE,sCACE,oCACF","file":"subhead.css","sourcesContent":["/* Subhead */\n\n.Subhead {\n display: flex;\n padding-bottom: var(--stack-padding-condensed);\n margin-bottom: var(--stack-gap-normal);\n border-bottom: var(--borderWidth-thin) solid var(--borderColor-muted);\n flex-flow: row wrap;\n justify-content: flex-end; /* Keep actions right aligned. */\n}\n\n/* Modifier class to give a lot of breathing room between sections of content. */\n.Subhead--spacious {\n margin-top: var(--base-size-40);\n}\n\n/* <h2> sized heading with normal font weight */\n.Subhead-heading {\n font-size: 24px;\n font-weight: var(--base-text-weight-normal);\n flex: 1 1 auto;\n order: 0;\n}\n\n/* Make the text bold and red for dangerous content */\n.Subhead-heading--danger {\n font-weight: var(--base-text-weight-semibold);\n color: var(--fgColor-danger);\n}\n\n/* One-liner of supporting text */\n.Subhead-description {\n font-size: var(--text-body-size-medium);\n color: var(--fgColor-muted);\n flex: 1 100%;\n order: 2;\n}\n\n/* Add 1 or 2 buttons to the right of the heading */\n.Subhead-actions {\n margin: var(--base-size-4) 0 var(--base-size-4) var(--base-size-4);\n align-self: center;\n justify-content: flex-end;\n order: 1;\n\n & + .Subhead-description {\n margin-top: var(--base-size-4);\n }\n}\n"]}
1
+ {"version":3,"sources":["subhead.pcss"],"names":[],"mappings":"AAEA,SAIE,gHAAqE,CAHrE,YAAa,CAIb,kBAAmB,CACnB,wBAAyB,CAHzB,0CAAsC,CADtC,mDAKF,CAGA,mBACE,qCACF,CAGA,iBAEE,aAAc,CADd,8CAA2C,CAE3C,OACF,CAEA,wBACE,oCACF,CAEA,yBACE,+CACF,CAGA,yBAEE,kDAA4B,CAD5B,gDAEF,CAGA,qBAEE,gDAA2B,CAC3B,WAAY,CAFZ,8CAAuC,CAGvC,OACF,CAGA,iBAEE,iBAAkB,CAClB,wBAAyB,CAFzB,sFAAkE,CAGlE,OAKF,CAHE,sCACE,oCACF","file":"subhead.css","sourcesContent":["/* Subhead */\n\n.Subhead {\n display: flex;\n padding-bottom: var(--stack-padding-condensed);\n margin-bottom: var(--stack-gap-normal);\n border-bottom: var(--borderWidth-thin) solid var(--borderColor-muted);\n flex-flow: row wrap;\n justify-content: flex-end; /* Keep actions right aligned. */\n}\n\n/* Modifier class to give a lot of breathing room between sections of content. */\n.Subhead--spacious {\n margin-top: var(--base-size-40);\n}\n\n/* <h2> sized heading with normal font weight */\n.Subhead-heading {\n font-weight: var(--base-text-weight-normal);\n flex: 1 1 auto;\n order: 0;\n}\n\n.Subhead-heading--large {\n font-size: var(--base-size-24);\n}\n\n.Subhead-heading--medium {\n font-size: var(--text-title-size-medium);\n}\n\n/* Make the text bold and red for dangerous content */\n.Subhead-heading--danger {\n font-weight: var(--base-text-weight-semibold);\n color: var(--fgColor-danger);\n}\n\n/* One-liner of supporting text */\n.Subhead-description {\n font-size: var(--text-body-size-medium);\n color: var(--fgColor-muted);\n flex: 1 100%;\n order: 2;\n}\n\n/* Add 1 or 2 buttons to the right of the heading */\n.Subhead-actions {\n margin: var(--base-size-4) 0 var(--base-size-4) var(--base-size-4);\n align-self: center;\n justify-content: flex-end;\n order: 1;\n\n & + .Subhead-description {\n margin-top: var(--base-size-4);\n }\n}\n"]}
@@ -16,12 +16,19 @@
16
16
 
17
17
  /* <h2> sized heading with normal font weight */
18
18
  .Subhead-heading {
19
- font-size: 24px;
20
19
  font-weight: var(--base-text-weight-normal);
21
20
  flex: 1 1 auto;
22
21
  order: 0;
23
22
  }
24
23
 
24
+ .Subhead-heading--large {
25
+ font-size: var(--base-size-24);
26
+ }
27
+
28
+ .Subhead-heading--medium {
29
+ font-size: var(--text-title-size-medium);
30
+ }
31
+
25
32
  /* Make the text bold and red for dangerous content */
26
33
  .Subhead-heading--danger {
27
34
  font-weight: var(--base-text-weight-semibold);
@@ -17,16 +17,24 @@ module Primer
17
17
  DEFAULT_HEADING_TAG = :div
18
18
  HEADING_TAG_OPTIONS = [DEFAULT_HEADING_TAG, :h1, :h2, :h3, :h4, :h5, :h6].freeze
19
19
 
20
+ DEFAULT_HEADING_SIZE = :large
21
+ HEADING_SIZE_MAP = {
22
+ DEFAULT_HEADING_SIZE => "Subhead-heading--large",
23
+ :medium => "Subhead-heading--medium"
24
+ }.freeze
25
+ HEADING_SIZE_OPTIONS = HEADING_SIZE_MAP.keys.freeze
26
+
20
27
  # The heading
21
28
  #
22
29
  # @param tag [Symbol] <%= one_of(Primer::Beta::Subhead::HEADING_TAG_OPTIONS)%>
23
30
  # @param danger [Boolean] Whether to style the heading as dangerous.
24
31
  # @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
25
- renders_one :heading, lambda { |tag: DEFAULT_HEADING_TAG, danger: false, **system_arguments|
32
+ renders_one :heading, lambda { |tag: DEFAULT_HEADING_TAG, danger: false, size: DEFAULT_HEADING_SIZE, **system_arguments|
26
33
  system_arguments[:tag] = fetch_or_fallback(HEADING_TAG_OPTIONS, tag, DEFAULT_HEADING_TAG)
27
34
  system_arguments[:classes] = class_names(
28
35
  system_arguments[:classes],
29
36
  "Subhead-heading",
37
+ HEADING_SIZE_MAP[fetch_or_fallback(HEADING_SIZE_OPTIONS, size, DEFAULT_HEADING_SIZE)],
30
38
  "Subhead-heading--danger": danger
31
39
  )
32
40
 
@@ -15,7 +15,7 @@ const validSelectors = ['[role="menuitem"]', '[role="menuitemcheckbox"]', '[role
15
15
  const menuItemSelector = validSelectors.map(selector => `:not([hidden]) > ${selector}`).join(', ');
16
16
  const getMnemonicFor = (item) => { var _a; return (_a = item.textContent) === null || _a === void 0 ? void 0 : _a.trim()[0].toLowerCase(); };
17
17
  const printable = /^\S$/;
18
- export default class FocusGroupElement extends HTMLElement {
18
+ class FocusGroupElement extends HTMLElement {
19
19
  constructor() {
20
20
  super(...arguments);
21
21
  _FocusGroupElement_instances.add(this);
@@ -159,6 +159,7 @@ export default class FocusGroupElement extends HTMLElement {
159
159
  _FocusGroupElement_retainSignal = new WeakMap(), _FocusGroupElement_abortController = new WeakMap(), _FocusGroupElement_instances = new WeakSet(), _FocusGroupElement_items_get = function _FocusGroupElement_items_get() {
160
160
  return this.querySelectorAll(menuItemSelector);
161
161
  };
162
+ export default FocusGroupElement;
162
163
  if (!customElements.get('focus-group')) {
163
164
  window.FocusGroupElement = FocusGroupElement;
164
165
  customElements.define('focus-group', FocusGroupElement);
@@ -1,5 +1,4 @@
1
1
  import '@github/include-fragment-element';
2
- import '@oddbird/popover-polyfill';
3
2
  import './alpha/action_bar_element';
4
3
  import './alpha/dropdown';
5
4
  import './anchored_position';
@@ -1,5 +1,4 @@
1
1
  import '@github/include-fragment-element';
2
- import '@oddbird/popover-polyfill';
3
2
  import './alpha/action_bar_element';
4
3
  import './alpha/dropdown';
5
4
  import './anchored_position';
@@ -1,5 +1,3 @@
1
- @import "@oddbird/popover-polyfill/dist/popover.css";
2
-
3
1
  /* CSS component styles here */
4
2
 
5
3
  /* alpha */
@@ -1,5 +1,4 @@
1
1
  import '@github/include-fragment-element'
2
- import '@oddbird/popover-polyfill'
3
2
  import './alpha/action_bar_element'
4
3
  import './alpha/dropdown'
5
4
  import './anchored_position'
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nodoc:
4
+ class ActionMenuForm < ApplicationForm
5
+ form do |action_menu_form|
6
+ action_menu_form.action_menu(name: "city", label: "Favorite city", caption: "Select your favorite!") do |city_list|
7
+ city_list.with_item(label: "Lopez Island", data: { value: "lopez_island" }) do |item|
8
+ item.with_leading_visual_icon(icon: :log)
9
+ end
10
+ city_list.with_item(label: "Bellevue", data: { value: "bellevue" }) do |item|
11
+ item.with_leading_visual_icon(icon: :paste)
12
+ end
13
+ city_list.with_item(label: "Seattle", data: { value: "seattle" }) do |item|
14
+ item.with_leading_visual_icon(icon: :"device-camera")
15
+ end
16
+ end
17
+
18
+ action_menu_form.submit(name: :submit, label: "Submit")
19
+ end
20
+ end
@@ -21,8 +21,8 @@ class ImmediateValidationForm < ApplicationForm
21
21
 
22
22
  validation_form.text_field(
23
23
  name: :random_error,
24
- label: "Random error",
25
- caption: "Server checks will randomly respond with errors",
24
+ label: "Random error or success",
25
+ caption: "Server checks will randomly respond with errors or success",
26
26
  auto_check_src: @view_context.example_check_random_path
27
27
  )
28
28
  end
@@ -48,7 +48,6 @@ module Primer
48
48
  end
49
49
  end
50
50
 
51
- # rubocop:disable Style/OptionalBooleanParameter
52
51
  def fetch_or_fallback_boolean(given_value, fallback = false)
53
52
  if [true, false].include?(given_value)
54
53
  given_value
@@ -4,7 +4,7 @@ module Primer
4
4
  module Octicon
5
5
  # :nodoc:
6
6
  class Cache
7
- LOOKUP = {} # rubocop:disable Style/MutableConstant
7
+ LOOKUP = {}
8
8
  # Preload the top 20 used icons.
9
9
  PRELOADED_ICONS = [:alert, :check, :"chevron-down", :paste, :clock, :"dot-fill", :info, :"kebab-horizontal", :link, :lock, :mail, :pencil, :plus, :question, :repo, :search, :"shield-lock", :star, :trash, :x].freeze
10
10
 
@@ -75,7 +75,6 @@ module Primer
75
75
  # are about 30% faster than Hash#dig. It also ensures validate is
76
76
  # only called when necessary, i.e. when the class can't be found
77
77
  # in the lookup table.
78
- # rubocop:disable Style/RescueModifier
79
78
  found = (LOOKUP[key][item][brk] rescue nil) || validate(key, item, brk)
80
79
  # rubocop:enable Style/RescueModifier
81
80
  result << found if found
@@ -93,7 +92,6 @@ module Primer
93
92
  end.join(" ")
94
93
 
95
94
  # This is much faster than Rails' presence method.
96
- # rubocop:disable Rails/Blank
97
95
  {
98
96
  class: !classes || classes.empty? ? nil : classes,
99
97
  style: !style || style.empty? ? nil : style
@@ -0,0 +1,6 @@
1
+ <%= render(FormControl.new(input: @input)) do %>
2
+ <%= render(Primer::Alpha::ActionMenu.new(**@input.input_arguments)) do |menu| %>
3
+ <% menu.with_show_button { "Select..." } %>
4
+ <% @input.block.call(menu) if @input.block %>
5
+ <% end %>
6
+ <% end %>
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Forms
5
+ # :nodoc:
6
+ class ActionMenu < BaseComponent
7
+ delegate :builder, :form, to: :@input
8
+
9
+ def initialize(input:)
10
+ @input = input
11
+
12
+ @input.input_arguments[:form_arguments] = {
13
+ name: @input.name,
14
+ builder: builder
15
+ }
16
+
17
+ @input.input_arguments[:select_variant] ||= :single
18
+
19
+ unless @input.input_arguments.include?(:dynamic_label)
20
+ @input.input_arguments[:dynamic_label] = true
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -36,7 +36,6 @@ module Primer
36
36
  def before_render; end
37
37
 
38
38
  # :nocov:
39
- # rubocop:disable Naming/AccessorMethodName
40
39
  def set_original_view_context(view_context)
41
40
  @view_context = view_context
42
41
  end
@@ -98,8 +97,6 @@ module Primer
98
97
  end
99
98
 
100
99
  def define_template_method(template_path, method_name)
101
- # rubocop:disable Style/DocumentDynamicEvalDefinition
102
- # rubocop:disable Style/EvalWithLocation
103
100
  class_eval <<-RUBY, template_path, 0
104
101
  private def #{method_name}
105
102
  capture { #{compile_template(template_path)} }
@@ -120,7 +120,6 @@ module Primer
120
120
  private
121
121
 
122
122
  def form_object
123
- # rubocop:disable Naming/MemoizedInstanceVariableName
124
123
  @__pf_form_object ||= Primer::Forms::Dsl::FormObject.new(builder: @builder, form: self).tap do |obj|
125
124
  # compile before adding inputs so caption templates are identified
126
125
  self.class.compile!
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "primer/class_name_helper"
4
-
5
3
  module Primer
6
4
  module Forms
7
5
  # :nodoc:
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Primer
4
+ module Forms
5
+ module Dsl
6
+ # :nodoc:
7
+ class ActionMenuInput < Input
8
+ attr_reader :name, :label, :block
9
+
10
+ def initialize(name:, label:, **system_arguments, &block)
11
+ @name = name
12
+ @label = label
13
+ @block = block
14
+
15
+ super(**system_arguments)
16
+ end
17
+
18
+ def to_component
19
+ ActionMenu.new(input: self)
20
+ end
21
+
22
+ # :nocov:
23
+ def type
24
+ :action_menu
25
+ end
26
+ # :nocov:
27
+
28
+ # :nocov:
29
+ def focusable?
30
+ true
31
+ end
32
+ # :nocov:
33
+ end
34
+ end
35
+ end
36
+ end
@@ -81,7 +81,6 @@ module Primer
81
81
  # methods. These methods will use the passed name if provided instead
82
82
  # of generating a scoped one.
83
83
  #
84
- # rubocop:disable Style/IfUnlessModifier
85
84
  unless @input_arguments.delete(:scope_name_to_model) { true }
86
85
  @input_arguments[:name] = name
87
86
  end
@@ -279,6 +278,14 @@ module Primer
279
278
  {}
280
279
  end
281
280
 
281
+ def validation_success_icon_target
282
+ ""
283
+ end
284
+
285
+ def validation_error_icon_target
286
+ ""
287
+ end
288
+
282
289
  private
283
290
 
284
291
  def input_data
@@ -90,6 +90,15 @@ module Primer
90
90
  add_input SelectInput.new(builder: builder, form: form, **options, &block)
91
91
  end
92
92
 
93
+ # Adds an <%= link_to_component(Primer::Alpha::ActionMenu) %> to this form.
94
+ #
95
+ # @param options [Hash] The options accepted by the <%= link_to_component(Primer::Alpha::ActionMenu) %> component.
96
+ # @param block [Proc] The block passed to `#render` when the <%= link_to_component(Primer::Alpha::ActionMenu) %> is rendered. This block is passed an instance of <%= link_to_component(Primer::Alpha::ActionMenu) %>, which can be used to add items, dividers, etc.
97
+ def action_menu(**options, &block)
98
+ options = decorate_options(**options)
99
+ add_input ActionMenuInput.new(builder: builder, form: form, **options, &block)
100
+ end
101
+
93
102
  # END select input methods
94
103
 
95
104
  # START button input methods
@@ -69,6 +69,14 @@ module Primer
69
69
  end
70
70
  end
71
71
 
72
+ def validation_success_icon_target
73
+ "primer-text-field.validationSuccessIcon"
74
+ end
75
+
76
+ def validation_error_icon_target
77
+ "primer-text-field.validationErrorIcon"
78
+ end
79
+
72
80
  def validation_message_arguments
73
81
  if auto_check_src.present?
74
82
  super.merge(
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "primer/class_name_helper"
4
-
5
3
  module Primer
6
4
  module Forms
7
5
  # Wraps Primer::BaseComponent.
@@ -30,8 +30,14 @@ class PrimerTextFieldElement extends HTMLElement {
30
30
  var _a;
31
31
  (_a = __classPrivateFieldGet(this, _PrimerTextFieldElement_abortController, "f")) === null || _a === void 0 ? void 0 : _a.abort();
32
32
  const { signal } = (__classPrivateFieldSet(this, _PrimerTextFieldElement_abortController, new AbortController(), "f"));
33
- this.inputElement.addEventListener('auto-check-success', () => {
34
- this.clearError();
33
+ this.inputElement.addEventListener('auto-check-success', async (event) => {
34
+ const message = await event.detail.response.text();
35
+ if (message && message.length > 0) {
36
+ this.setSuccess(message);
37
+ }
38
+ else {
39
+ this.clearError();
40
+ }
35
41
  }, { signal });
36
42
  this.inputElement.addEventListener('auto-check-error', async (event) => {
37
43
  const errorMessage = await event.detail.response.text();
@@ -49,12 +55,35 @@ class PrimerTextFieldElement extends HTMLElement {
49
55
  clearError() {
50
56
  this.inputElement.removeAttribute('invalid');
51
57
  this.validationElement.hidden = true;
52
- this.validationMessageElement.textContent = '';
58
+ this.validationMessageElement.replaceChildren();
59
+ }
60
+ setValidationMessage(message) {
61
+ const template = document.createElement('template');
62
+ // eslint-disable-next-line github/no-inner-html
63
+ template.innerHTML = message;
64
+ const fragment = document.importNode(template.content, true);
65
+ this.validationMessageElement.replaceChildren(fragment);
66
+ }
67
+ toggleValidationStyling(isError) {
68
+ if (isError) {
69
+ this.validationElement.classList.remove('FormControl-inlineValidation--success');
70
+ }
71
+ else {
72
+ this.validationElement.classList.add('FormControl-inlineValidation--success');
73
+ }
74
+ this.validationSuccessIcon.hidden = isError;
75
+ this.validationErrorIcon.hidden = !isError;
76
+ this.inputElement.setAttribute('invalid', isError ? 'true' : 'false');
77
+ }
78
+ setSuccess(message) {
79
+ this.toggleValidationStyling(false);
80
+ this.setValidationMessage(message);
81
+ this.validationElement.hidden = false;
53
82
  }
54
83
  setError(message) {
55
- this.validationMessageElement.textContent = message;
84
+ this.toggleValidationStyling(true);
85
+ this.setValidationMessage(message);
56
86
  this.validationElement.hidden = false;
57
- this.inputElement.setAttribute('invalid', 'true');
58
87
  }
59
88
  };
60
89
  _PrimerTextFieldElement_abortController = new WeakMap();
@@ -67,6 +96,12 @@ __decorate([
67
96
  __decorate([
68
97
  target
69
98
  ], PrimerTextFieldElement.prototype, "validationMessageElement", void 0);
99
+ __decorate([
100
+ target
101
+ ], PrimerTextFieldElement.prototype, "validationSuccessIcon", void 0);
102
+ __decorate([
103
+ target
104
+ ], PrimerTextFieldElement.prototype, "validationErrorIcon", void 0);
70
105
  PrimerTextFieldElement = __decorate([
71
106
  controller
72
107
  // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars
@@ -8,6 +8,8 @@ class PrimerTextFieldElement extends HTMLElement {
8
8
  @target inputElement: HTMLInputElement
9
9
  @target validationElement: HTMLElement
10
10
  @target validationMessageElement: HTMLElement
11
+ @target validationSuccessIcon: HTMLElement
12
+ @target validationErrorIcon: HTMLElement
11
13
 
12
14
  #abortController: AbortController | null
13
15
 
@@ -17,10 +19,15 @@ class PrimerTextFieldElement extends HTMLElement {
17
19
 
18
20
  this.inputElement.addEventListener(
19
21
  'auto-check-success',
20
- () => {
21
- this.clearError()
22
+ async (event: any) => {
23
+ const message = await event.detail.response.text()
24
+ if (message && message.length > 0) {
25
+ this.setSuccess(message)
26
+ } else {
27
+ this.clearError()
28
+ }
22
29
  },
23
- {signal}
30
+ {signal},
24
31
  )
25
32
 
26
33
  this.inputElement.addEventListener(
@@ -29,7 +36,7 @@ class PrimerTextFieldElement extends HTMLElement {
29
36
  const errorMessage = await event.detail.response.text()
30
37
  this.setError(errorMessage)
31
38
  },
32
- {signal}
39
+ {signal},
33
40
  )
34
41
  }
35
42
 
@@ -45,12 +52,37 @@ class PrimerTextFieldElement extends HTMLElement {
45
52
  clearError(): void {
46
53
  this.inputElement.removeAttribute('invalid')
47
54
  this.validationElement.hidden = true
48
- this.validationMessageElement.textContent = ''
55
+ this.validationMessageElement.replaceChildren()
56
+ }
57
+
58
+ setValidationMessage(message: string): void {
59
+ const template = document.createElement('template')
60
+ // eslint-disable-next-line github/no-inner-html
61
+ template.innerHTML = message
62
+ const fragment = document.importNode(template.content, true)
63
+ this.validationMessageElement.replaceChildren(fragment)
64
+ }
65
+
66
+ toggleValidationStyling(isError: boolean): void {
67
+ if (isError) {
68
+ this.validationElement.classList.remove('FormControl-inlineValidation--success')
69
+ } else {
70
+ this.validationElement.classList.add('FormControl-inlineValidation--success')
71
+ }
72
+ this.validationSuccessIcon.hidden = isError
73
+ this.validationErrorIcon.hidden = !isError
74
+ this.inputElement.setAttribute('invalid', isError ? 'true' : 'false')
75
+ }
76
+
77
+ setSuccess(message: string): void {
78
+ this.toggleValidationStyling(false)
79
+ this.setValidationMessage(message)
80
+ this.validationElement.hidden = false
49
81
  }
50
82
 
51
83
  setError(message: string): void {
52
- this.validationMessageElement.textContent = message
84
+ this.toggleValidationStyling(true)
85
+ this.setValidationMessage(message)
53
86
  this.validationElement.hidden = false
54
- this.inputElement.setAttribute('invalid', 'true')
55
87
  }
56
88
  }
@@ -1,4 +1,5 @@
1
1
  <%= content_tag(:div, **@input.validation_arguments) do %>
2
- <span class="FormControl-inlineValidation--visual"><%= render(Primer::Beta::Octicon.new(icon: :"alert-fill", size: :xsmall, aria: { hidden: true })) %></span>
2
+ <span class="FormControl-inlineValidation--visual" data-target="<%= @input.validation_success_icon_target %>" hidden><%= render(Primer::Beta::Octicon.new(icon: :"check-circle-fill", size: :xsmall, aria: { hidden: true })) %></span>
3
+ <span class=" FormControl-inlineValidation--visual" data-target="<%= @input.validation_error_icon_target %>"><%= render(Primer::Beta::Octicon.new(icon: :"alert-fill", size: :xsmall, aria: { hidden: true })) %></span>
3
4
  <%= content_tag(:span, @input.invalid? ? @input.validation_messages.first : "", **@input.validation_message_arguments) %>
4
5
  <% end %>
@@ -33,7 +33,6 @@ module Primer
33
33
 
34
34
  {
35
35
  "name" => slot_method.name,
36
- # rubocop:disable Style/IfUnlessModifier
37
36
  "description" =>
38
37
  if slot_method.base_docstring.to_s.present?
39
38
  render_erb_ignoring_markdown_code_fences(slot_method.base_docstring)
@@ -70,10 +69,15 @@ module Primer
70
69
  render_erb_ignoring_markdown_code_fences(docs.base_docstring)
71
70
  end
72
71
 
72
+ accessibility_docs =
73
+ if (accessibility_tag_text = docs.tags(:accessibility)&.first&.text)
74
+ render_erb_ignoring_markdown_code_fences(accessibility_tag_text)
75
+ end
76
+
73
77
  memo[component] = {
74
78
  "fully_qualified_name" => component.name,
75
79
  "description" => description,
76
- "accessibility_docs" => docs.tags(:accessibility)&.first&.text,
80
+ "accessibility_docs" => accessibility_docs,
77
81
  "is_form_component" => docs.manifest_entry.form_component?,
78
82
  "is_published" => docs.manifest_entry.published?,
79
83
  "requires_js" => docs.manifest_entry.requires_js?,
@@ -85,7 +89,7 @@ module Primer
85
89
  }
86
90
  end
87
91
 
88
- statuses = Primer::Status::Dsl::STATUSES.keys.map(&:to_s).map(&:capitalize)
92
+ statuses = Primer::Status::Dsl::STATUSES.keys.map { |k| k.to_s.capitalize }
89
93
 
90
94
  Primer::Component.descendants.each do |component|
91
95
  fq_class = component.name.to_s.split("::")
@@ -16,11 +16,9 @@ module Primer
16
16
 
17
17
  component = preview.components.first&.component_class
18
18
 
19
- # rubocop:disable Style/IfUnlessModifier
20
19
  unless component
21
20
  raise "Could not determine which component `#{preview.preview_class}` is designed to preview. Please add a `@component` annotation."
22
21
  end
23
- # rubocop:enable Style/IfUnlessModifier
24
22
 
25
23
  _, _, class_name = Primer::Yard::DocsHelper.status_module_and_short_name(component)
26
24
 
@@ -44,7 +44,11 @@ module Primer
44
44
 
45
45
  initializer "primer.forms.helpers" do
46
46
  ActiveSupport.on_load :action_controller_base do
47
- require "primer/form_helper"
47
+ begin
48
+ require "primer/form_helper"
49
+ rescue LoadError
50
+ end
51
+
48
52
  helper Primer::FormHelper
49
53
 
50
54
  # make primer_form_with available to view components also