openproject-primer_view_components 0.16.0 → 0.17.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/app/assets/javascripts/primer_view_components.js +1 -1
  4. data/app/assets/javascripts/primer_view_components.js.map +1 -1
  5. data/app/assets/styles/primer_view_components.css +1 -1
  6. data/app/assets/styles/primer_view_components.css.map +1 -1
  7. data/app/components/primer/alpha/action_bar.css +1 -1
  8. data/app/components/primer/alpha/action_bar.css.json +1 -4
  9. data/app/components/primer/alpha/action_bar.css.map +1 -1
  10. data/app/components/primer/alpha/action_bar.pcss +1 -17
  11. data/app/components/primer/alpha/action_bar_element.js +21 -9
  12. data/app/components/primer/alpha/action_list/item.rb +0 -2
  13. data/app/components/primer/alpha/action_list.css +1 -1
  14. data/app/components/primer/alpha/action_list.css.map +1 -1
  15. data/app/components/primer/alpha/action_list.rb +0 -1
  16. data/app/components/primer/alpha/action_menu/action_menu_element.js +44 -20
  17. data/app/components/primer/alpha/action_menu/list.rb +0 -9
  18. data/app/components/primer/alpha/action_menu.html.erb +20 -18
  19. data/app/components/primer/alpha/auto_complete.css +1 -1
  20. data/app/components/primer/alpha/auto_complete.css.map +1 -1
  21. data/app/components/primer/alpha/button_marketing.css +1 -1
  22. data/app/components/primer/alpha/button_marketing.css.map +1 -1
  23. data/app/components/primer/alpha/dialog/header.rb +1 -1
  24. data/app/components/primer/alpha/dropdown.css +1 -1
  25. data/app/components/primer/alpha/dropdown.css.map +1 -1
  26. data/app/components/primer/alpha/layout.css +1 -1
  27. data/app/components/primer/alpha/layout.css.map +1 -1
  28. data/app/components/primer/alpha/menu.css +1 -1
  29. data/app/components/primer/alpha/menu.css.map +1 -1
  30. data/app/components/primer/alpha/nav_list/divider.rb +1 -0
  31. data/app/components/primer/alpha/nav_list/group.rb +1 -0
  32. data/app/components/primer/alpha/nav_list/heading.rb +1 -0
  33. data/app/components/primer/alpha/nav_list/item.rb +1 -0
  34. data/app/components/primer/alpha/nav_list.rb +1 -0
  35. data/app/components/primer/alpha/octicon_symbols.html.erb +1 -1
  36. data/app/components/primer/alpha/overlay.css +1 -1
  37. data/app/components/primer/alpha/overlay.css.json +0 -1
  38. data/app/components/primer/alpha/overlay.css.map +1 -1
  39. data/app/components/primer/alpha/segmented_control.css +1 -1
  40. data/app/components/primer/alpha/segmented_control.css.json +0 -1
  41. data/app/components/primer/alpha/segmented_control.css.map +1 -1
  42. data/app/components/primer/alpha/segmented_control.js +2 -1
  43. data/app/components/primer/alpha/segmented_control.pcss +0 -4
  44. data/app/components/primer/alpha/tab_nav.css +1 -1
  45. data/app/components/primer/alpha/tab_nav.css.map +1 -1
  46. data/app/components/primer/alpha/text_field.css +1 -1
  47. data/app/components/primer/alpha/text_field.css.json +11 -5
  48. data/app/components/primer/alpha/text_field.css.map +1 -1
  49. data/app/components/primer/alpha/text_field.pcss +16 -7
  50. data/app/components/primer/alpha/toggle_switch.css +1 -1
  51. data/app/components/primer/alpha/toggle_switch.css.map +1 -1
  52. data/app/components/primer/alpha/underline_nav.css +1 -1
  53. data/app/components/primer/alpha/underline_nav.css.map +1 -1
  54. data/app/components/primer/alpha/x_banner.js +2 -1
  55. data/app/components/primer/anchored_position.js +2 -1
  56. data/app/components/primer/beta/auto_complete/item.rb +1 -1
  57. data/app/components/primer/beta/avatar.rb +1 -1
  58. data/app/components/primer/beta/base_button.rb +1 -1
  59. data/app/components/primer/beta/blankslate.css +1 -1
  60. data/app/components/primer/beta/blankslate.css.json +1 -0
  61. data/app/components/primer/beta/blankslate.css.map +1 -1
  62. data/app/components/primer/beta/blankslate.html.erb +16 -14
  63. data/app/components/primer/beta/blankslate.pcss +52 -2
  64. data/app/components/primer/beta/border_box.css +1 -1
  65. data/app/components/primer/beta/border_box.css.map +1 -1
  66. data/app/components/primer/beta/button.css +1 -1
  67. data/app/components/primer/beta/button.css.json +0 -1
  68. data/app/components/primer/beta/button.css.map +1 -1
  69. data/app/components/primer/beta/button.html.erb +18 -20
  70. data/app/components/primer/beta/button.pcss +0 -5
  71. data/app/components/primer/beta/button.rb +3 -0
  72. data/app/components/primer/beta/button_group.css +1 -1
  73. data/app/components/primer/beta/button_group.css.json +2 -4
  74. data/app/components/primer/beta/button_group.css.map +1 -1
  75. data/app/components/primer/beta/button_group.html.erb +3 -1
  76. data/app/components/primer/beta/button_group.pcss +2 -4
  77. data/app/components/primer/beta/button_group.rb +41 -12
  78. data/app/components/primer/beta/clipboard_copy.rb +4 -0
  79. data/app/components/primer/beta/clipboard_copy_button.rb +25 -0
  80. data/app/components/primer/beta/counter.rb +1 -1
  81. data/app/components/primer/beta/flash.html.erb +1 -1
  82. data/app/components/primer/beta/icon_button.html.erb +4 -6
  83. data/app/components/primer/beta/icon_button.rb +1 -3
  84. data/app/components/primer/beta/label.css +1 -1
  85. data/app/components/primer/beta/label.css.map +1 -1
  86. data/app/components/primer/beta/link.css +1 -1
  87. data/app/components/primer/beta/link.css.map +1 -1
  88. data/app/components/primer/beta/nav_list.js +14 -7
  89. data/app/components/primer/beta/subhead.css +1 -1
  90. data/app/components/primer/beta/subhead.css.json +2 -0
  91. data/app/components/primer/beta/subhead.css.map +1 -1
  92. data/app/components/primer/beta/subhead.pcss +8 -1
  93. data/app/components/primer/beta/subhead.rb +9 -1
  94. data/app/components/primer/focus_group.js +2 -1
  95. data/app/forms/action_menu_form.rb +20 -0
  96. data/app/forms/immediate_validation_form.rb +2 -2
  97. data/app/lib/primer/fetch_or_fallback_helper.rb +0 -1
  98. data/app/lib/primer/octicon/cache.rb +1 -1
  99. data/lib/primer/classify.rb +0 -2
  100. data/lib/primer/forms/action_menu.html.erb +6 -0
  101. data/lib/primer/forms/action_menu.rb +25 -0
  102. data/lib/primer/forms/acts_as_component.rb +0 -3
  103. data/lib/primer/forms/base.rb +0 -1
  104. data/lib/primer/forms/base_component.rb +0 -2
  105. data/lib/primer/forms/dsl/action_menu_input.rb +36 -0
  106. data/lib/primer/forms/dsl/input.rb +8 -1
  107. data/lib/primer/forms/dsl/input_methods.rb +9 -0
  108. data/lib/primer/forms/dsl/text_field_input.rb +8 -0
  109. data/lib/primer/forms/primer_base_component_wrapper.rb +0 -2
  110. data/lib/primer/forms/primer_text_field.js +40 -5
  111. data/lib/primer/forms/primer_text_field.ts +39 -7
  112. data/lib/primer/forms/validation_message.html.erb +2 -1
  113. data/lib/primer/static/generate_info_arch.rb +1 -2
  114. data/lib/primer/static/generate_previews.rb +0 -2
  115. data/lib/primer/view_components/engine.rb +5 -1
  116. data/lib/primer/view_components/linters/base_linter.rb +3 -2
  117. data/lib/primer/view_components/linters/deprecated_components_counter.rb +1 -1
  118. data/lib/primer/view_components/linters/disallow_action_list.rb +1 -0
  119. data/lib/primer/view_components/linters/severity_schema.rb +1 -0
  120. data/lib/primer/view_components/version.rb +2 -3
  121. data/lib/primer/yard/lookbook_pages_backend.rb +0 -2
  122. data/lib/rubocop/cop/primer/base_cop.rb +1 -1
  123. data/lib/rubocop/cop/primer/deprecated_arguments.rb +0 -2
  124. data/lib/rubocop/cop/primer/deprecated_components.rb +1 -1
  125. data/lib/rubocop/cop/primer/deprecated_label_schemes.rb +1 -1
  126. data/lib/rubocop/cop/primer/deprecated_label_variants.rb +1 -1
  127. data/previews/primer/alpha/octicon_symbols_preview/default.html.erb +6 -0
  128. data/previews/primer/alpha/octicon_symbols_preview/playground.html.erb +13 -0
  129. data/previews/primer/alpha/octicon_symbols_preview.rb +21 -0
  130. data/previews/primer/alpha/text_field_preview.rb +5 -0
  131. data/previews/primer/beta/avatar_preview.rb +6 -0
  132. data/previews/primer/beta/button_group_preview.rb +11 -0
  133. data/previews/primer/beta/clipboard_copy_button_preview.rb +29 -0
  134. data/previews/primer/beta/subhead_preview.rb +32 -4
  135. data/previews/primer/forms_preview/action_menu_form.html.erb +3 -0
  136. data/previews/primer/forms_preview.rb +4 -0
  137. data/static/arguments.json +75 -13
  138. data/static/audited_at.json +2 -0
  139. data/static/classes.json +9 -3
  140. data/static/constants.json +16 -1
  141. data/static/info_arch.json +329 -26
  142. data/static/previews.json +146 -0
  143. data/static/statuses.json +2 -0
  144. metadata +14 -4
@@ -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);
@@ -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
@@ -93,7 +93,6 @@ module Primer
93
93
  # methods. These methods will use the passed name if provided instead
94
94
  # of generating a scoped one.
95
95
  #
96
- # rubocop:disable Style/IfUnlessModifier
97
96
  unless @input_arguments.delete(:scope_name_to_model) { true }
98
97
  @input_arguments[:name] = name
99
98
  end
@@ -297,6 +296,14 @@ module Primer
297
296
  {}
298
297
  end
299
298
 
299
+ def validation_success_icon_target
300
+ ""
301
+ end
302
+
303
+ def validation_error_icon_target
304
+ ""
305
+ end
306
+
300
307
  private
301
308
 
302
309
  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)
@@ -90,7 +89,7 @@ module Primer
90
89
  }
91
90
  end
92
91
 
93
- statuses = Primer::Status::Dsl::STATUSES.keys.map(&:to_s).map(&:capitalize)
92
+ statuses = Primer::Status::Dsl::STATUSES.keys.map { |k| k.to_s.capitalize }
94
93
 
95
94
  Primer::Component.descendants.each do |component|
96
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
@@ -24,6 +24,7 @@ module ERBLint
24
24
  CLASSES = [].freeze
25
25
  REQUIRED_ARGUMENTS = [].freeze
26
26
 
27
+ # :nodoc:
27
28
  class ConfigSchema < LinterConfig
28
29
  property :override_ignores_if_correctable, accepts: [true, false], default: false, reader: :override_ignores_if_correctable?
29
30
  end
@@ -154,13 +155,13 @@ module ERBLint
154
155
  # Unless explicitly set, we don't want to mark correctable offenses if the counter is correct.
155
156
  if !@config.override_ignores_if_correctable? && expected_count == @total_offenses
156
157
  clear_offenses
157
- return
158
+ return false
158
159
  end
159
160
 
160
161
  if @offenses_not_corrected.zero?
161
162
  # have to adjust to get `\n` so we delete the whole line
162
163
  add_offense(processed_source.to_source_range(comment_node.loc.adjust(end_pos: 1)), "Unused erblint:count comment for #{rule_name}", "") if comment_node
163
- return
164
+ return false
164
165
  end
165
166
 
166
167
  first_offense = @offenses[0]
@@ -63,7 +63,7 @@ module ERBLint
63
63
  if offenses_count.zero?
64
64
  # have to adjust to get `\n` so we delete the whole line
65
65
  add_offense_with_severity(processed_source.to_source_range(comment_node.loc.adjust(end_pos: 1)), "Unused erblint:counter comment for #{rule_name}", "") if comment_node
66
- return
66
+ return false
67
67
  end
68
68
 
69
69
  first_offense = @offenses[0]
@@ -10,6 +10,7 @@ module ERBLint
10
10
  include ERBLint::LinterRegistry
11
11
  include TagTreeHelpers
12
12
 
13
+ # :nodoc:
13
14
  class ConfigSchema < LinterConfig
14
15
  property :ignore_files, accepts: array_of?(String), default: -> { [] }
15
16
  end
@@ -4,6 +4,7 @@ require "erb_lint/utils/severity_levels"
4
4
 
5
5
  module ERBLint
6
6
  module Linters
7
+ # :nodoc:
7
8
  class SeveritySchema < LinterConfig
8
9
  # SEVERITY_NAMES :info, :refactor, :convention, :warning, :error, :fatal
9
10
  # see https://github.com/Shopify/erb-lint/blob/main/lib/erb_lint/utils/severity_levels.rb
@@ -5,14 +5,13 @@ module Primer
5
5
  module ViewComponents
6
6
  module VERSION
7
7
  MAJOR = 0
8
- MINOR = 16
9
- PATCH = 0
8
+ MINOR = 17
9
+ PATCH = 1
10
10
 
11
11
  STRING = [MAJOR, MINOR, PATCH].join(".")
12
12
  end
13
13
  end
14
14
  end
15
15
 
16
- # rubocop:disable Rails/Output
17
16
  puts Primer::ViewComponents::VERSION::STRING if __FILE__ == $PROGRAM_NAME
18
17
  # rubocop:enable Rails/Output
@@ -42,14 +42,12 @@ module Primer
42
42
  ), __dir__
43
43
  )
44
44
 
45
- # rubocop:disable Lint/UselessAssignment
46
45
  documented_methods = docs.non_slot_methods.select do |mtd|
47
46
  [component.name, "Primer::Forms::Dsl::InputMethods"].include?(mtd.parent.title)
48
47
  end
49
48
 
50
49
  preview_methods = PREVIEW_MAP[component]
51
50
  preview_erbs = preview_methods.map do |preview_method|
52
- # rubocop:disable Style/IfUnlessModifier
53
51
  if Primer::FormsPreview.instance_methods.exclude?(preview_method)
54
52
  raise "Preview '#{preview_method}' does not exist in Primer::FormsPreview"
55
53
  end
@@ -12,7 +12,7 @@ module RuboCop
12
12
  # We only verify SystemArguments if it's a `.new` call on a component or
13
13
  # a ViewHeleper call.
14
14
  def valid_node?(node)
15
- return if node.nil?
15
+ return false if node.nil?
16
16
 
17
17
  view_helpers.include?(node.method_name) || (node.method_name == :new && !node.receiver.nil? && ::Primer::ViewComponents::STATUSES.key?(node.receiver.const_name))
18
18
  end
@@ -298,7 +298,6 @@ module RuboCop
298
298
  def extract_kv_from(pair)
299
299
  key = pair.key.value
300
300
 
301
- # rubocop:disable Lint/BooleanSymbol
302
301
  value = case pair.value.type
303
302
  when :sym, :str
304
303
  pair.value.value.to_sym
@@ -307,7 +306,6 @@ module RuboCop
307
306
  else
308
307
  return []
309
308
  end
310
- # rubocop:enable Lint/BooleanSymbol
311
309
 
312
310
  [key, value]
313
311
  end
@@ -3,7 +3,7 @@
3
3
  require "rubocop"
4
4
  require "json"
5
5
  require "parser/current"
6
- require_relative "./../../../primer/view_components/linters/helpers/deprecated_components_helpers"
6
+ require_relative "../../../primer/view_components/linters/helpers/deprecated_components_helpers"
7
7
 
8
8
  module RuboCop
9
9
  module Cop
@@ -58,7 +58,7 @@ module RuboCop
58
58
  private
59
59
 
60
60
  def label_node?(node)
61
- return if node.nil?
61
+ return false if node.nil?
62
62
 
63
63
  node.method_name == :new && !node.receiver.nil? && node.receiver.const_name == "Primer::Beta::Label"
64
64
  end
@@ -61,7 +61,7 @@ module RuboCop
61
61
  private
62
62
 
63
63
  def label_node?(node)
64
- return if node.nil?
64
+ return false if node.nil?
65
65
 
66
66
  node.method_name == :new && !node.receiver.nil? && node.receiver.const_name == "Primer::Beta::Label"
67
67
  end