primer_view_components 0.0.120 → 0.0.122

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 (172) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/app/assets/styles/primer_view_components.css +2 -2
  4. data/app/assets/styles/primer_view_components.css.map +1 -1
  5. data/app/components/primer/alpha/action_list.css.json +123 -1
  6. data/app/components/primer/alpha/auto_complete.css.json +23 -1
  7. data/app/components/primer/alpha/banner.css.json +24 -1
  8. data/app/components/primer/alpha/button_marketing.css.json +33 -1
  9. data/app/components/primer/alpha/check_box.rb +74 -0
  10. data/app/components/primer/alpha/check_box_group.rb +36 -0
  11. data/app/components/primer/alpha/dialog.css.json +82 -1
  12. data/app/components/primer/alpha/dialog.rb +1 -1
  13. data/app/components/primer/alpha/dropdown.css.json +40 -1
  14. data/app/components/primer/alpha/form_button.rb +32 -0
  15. data/app/components/primer/alpha/form_control.html.erb +26 -0
  16. data/app/components/primer/alpha/form_control.rb +105 -0
  17. data/app/components/primer/alpha/layout.css.json +80 -1
  18. data/app/components/primer/alpha/menu.css.json +28 -1
  19. data/app/components/primer/alpha/multi_input.rb +81 -0
  20. data/app/components/primer/alpha/radio_button.rb +25 -0
  21. data/app/components/primer/alpha/radio_button_group.rb +36 -0
  22. data/app/components/primer/alpha/segmented_control/item.rb +1 -0
  23. data/app/components/primer/alpha/segmented_control.css +1 -1
  24. data/app/components/primer/alpha/segmented_control.css.json +31 -1
  25. data/app/components/primer/alpha/segmented_control.css.map +1 -1
  26. data/app/components/primer/alpha/segmented_control.pcss +43 -12
  27. data/app/components/primer/alpha/segmented_control.rb +30 -0
  28. data/app/components/primer/alpha/select.rb +37 -0
  29. data/app/components/primer/alpha/submit_button.rb +32 -0
  30. data/app/components/primer/alpha/tab_nav.css.json +24 -1
  31. data/app/components/primer/alpha/tab_panels.rb +7 -0
  32. data/app/components/primer/alpha/text_area.rb +24 -0
  33. data/app/components/primer/alpha/text_field.css +2 -2
  34. data/app/components/primer/alpha/text_field.css.json +134 -1
  35. data/app/components/primer/alpha/text_field.css.map +1 -1
  36. data/app/components/primer/alpha/text_field.pcss +27 -0
  37. data/app/components/primer/alpha/text_field.rb +16 -20
  38. data/app/components/primer/alpha/toggle_switch.css +1 -1
  39. data/app/components/primer/alpha/toggle_switch.css.json +40 -1
  40. data/app/components/primer/alpha/toggle_switch.css.map +1 -1
  41. data/app/components/primer/alpha/toggle_switch.pcss +31 -61
  42. data/app/components/primer/alpha/underline_nav.css.json +28 -1
  43. data/app/components/primer/base_component.rb +3 -3
  44. data/app/components/primer/beta/avatar.css.json +17 -1
  45. data/app/components/primer/beta/avatar_stack.css.json +28 -1
  46. data/app/components/primer/beta/blankslate.css.json +22 -1
  47. data/app/components/primer/beta/border_box.css.json +54 -1
  48. data/app/components/primer/beta/breadcrumbs.css.json +11 -1
  49. data/app/components/primer/beta/button.css.json +71 -1
  50. data/app/components/primer/beta/close_button.rb +1 -1
  51. data/app/components/primer/beta/counter.css.json +10 -1
  52. data/app/components/primer/beta/flash.css.json +27 -1
  53. data/app/components/primer/beta/label.css.json +25 -1
  54. data/app/components/primer/beta/link.css.json +19 -1
  55. data/app/components/primer/beta/popover.css.json +39 -1
  56. data/app/components/primer/beta/progress_bar.css.json +10 -1
  57. data/app/components/primer/{local_time.d.ts → beta/relative_time.d.ts} +0 -0
  58. data/app/components/primer/{local_time.js → beta/relative_time.js} +0 -0
  59. data/app/components/primer/{local_time.ts → beta/relative_time.ts} +0 -0
  60. data/app/components/primer/beta/state.css.json +13 -1
  61. data/app/components/primer/beta/subhead.css.json +12 -1
  62. data/app/components/primer/beta/timeline_item.css.json +16 -1
  63. data/app/components/primer/beta/truncate.css.json +12 -1
  64. data/app/components/primer/component.rb +12 -3
  65. data/app/components/primer/primer.d.ts +1 -2
  66. data/app/components/primer/primer.js +1 -2
  67. data/app/components/primer/primer.ts +1 -2
  68. data/app/components/primer/truncate.css.json +13 -1
  69. data/app/forms/example_toggle_switch_form.rb +1 -1
  70. data/app/forms/{select_list_form.rb → select_form.rb} +1 -1
  71. data/app/lib/primer/css/layout.css.json +316 -1
  72. data/app/lib/primer/css/utilities.css.json +1659 -1
  73. data/app/lib/primer/view_helper.rb +0 -1
  74. data/lib/primer/deprecations.yml +0 -78
  75. data/lib/primer/form_components.rb +26 -6
  76. data/lib/primer/forms/acts_as_component.rb +12 -1
  77. data/lib/primer/forms/builder.rb +1 -17
  78. data/lib/primer/forms/button.rb +4 -1
  79. data/lib/primer/forms/check_box_group.html.erb +14 -9
  80. data/lib/primer/forms/check_box_group.rb +5 -0
  81. data/lib/primer/forms/dsl/check_box_group_input.rb +3 -4
  82. data/lib/primer/forms/dsl/input.rb +33 -2
  83. data/lib/primer/forms/dsl/input_methods.rb +49 -1
  84. data/lib/primer/forms/dsl/radio_button_group_input.rb +2 -3
  85. data/lib/primer/forms/dsl/{select_list_input.rb → select_input.rb} +2 -2
  86. data/lib/primer/forms/dsl/text_field_input.rb +7 -5
  87. data/lib/primer/forms/form_control.rb +0 -1
  88. data/lib/primer/forms/group.html.erb +1 -1
  89. data/lib/primer/forms/multi.html.erb +8 -6
  90. data/lib/primer/forms/multi.rb +2 -0
  91. data/lib/primer/forms/radio_button_group.html.erb +14 -9
  92. data/lib/primer/forms/radio_button_group.rb +5 -0
  93. data/lib/primer/forms/{select_list.html.erb → select.html.erb} +0 -0
  94. data/lib/primer/forms/{select_list.rb → select.rb} +2 -2
  95. data/lib/primer/forms/spacing_wrapper.html.erb +1 -1
  96. data/lib/primer/forms/text_area.rb +1 -1
  97. data/lib/primer/forms/text_field.rb +5 -1
  98. data/lib/primer/forms/toggle_switch_form.rb +10 -3
  99. data/lib/primer/forms/utils.rb +20 -0
  100. data/lib/primer/view_components/engine.rb +1 -1
  101. data/lib/primer/view_components/version.rb +1 -1
  102. data/lib/primer/yard/backend.rb +1 -15
  103. data/lib/primer/yard/component_manifest.rb +44 -27
  104. data/lib/primer/yard/component_ref.rb +40 -0
  105. data/lib/primer/yard/docs_helper.rb +16 -2
  106. data/lib/primer/yard/legacy_gatsby_backend.rb +9 -15
  107. data/lib/primer/yard/lookbook_docs_helper.rb +32 -0
  108. data/lib/primer/yard/lookbook_pages_backend.rb +194 -0
  109. data/lib/primer/yard/registry.rb +6 -21
  110. data/lib/primer/yard/renders_many_handler.rb +1 -1
  111. data/lib/primer/yard/renders_one_handler.rb +1 -1
  112. data/lib/primer/yard.rb +14 -0
  113. data/lib/tasks/docs.rake +26 -13
  114. data/lib/tasks/static.rake +22 -0
  115. data/previews/pages/forms/01_introduction.md.erb +44 -0
  116. data/previews/pages/forms/02_getting_started.md.erb +125 -0
  117. data/previews/pages/forms/03_caption_templates.md.erb +30 -0
  118. data/previews/pages/forms/04_after_content.md.erb +39 -0
  119. data/previews/pages/forms/05_groups_layouts.md.erb +22 -0
  120. data/previews/pages/forms/06_miscellaneous_inputs.md.erb +43 -0
  121. data/previews/pages/forms/07_toggle_switch_forms.md.erb +58 -0
  122. data/previews/pages/forms/08_validations.md.erb +28 -0
  123. data/previews/pages/forms/09_compound_forms.md.erb +97 -0
  124. data/previews/primer/alpha/auto_complete_preview.rb +6 -6
  125. data/previews/primer/alpha/check_box_group_preview.rb +89 -0
  126. data/previews/primer/alpha/check_box_preview.rb +62 -0
  127. data/previews/primer/alpha/form_control_preview/playground.html.erb +9 -0
  128. data/previews/primer/alpha/form_control_preview.rb +106 -0
  129. data/previews/primer/alpha/multi_input_preview/playground.html.erb +41 -0
  130. data/previews/primer/alpha/multi_input_preview.rb +80 -0
  131. data/previews/primer/alpha/radio_button_group_preview.rb +83 -0
  132. data/previews/primer/alpha/radio_button_preview.rb +62 -0
  133. data/previews/primer/alpha/segmented_control_preview/with_label_and_caption.html.erb +9 -0
  134. data/previews/primer/alpha/segmented_control_preview/with_subhead_actions.html.erb +11 -0
  135. data/previews/primer/alpha/segmented_control_preview.rb +7 -1
  136. data/previews/primer/alpha/select_preview.rb +130 -0
  137. data/previews/primer/alpha/text_area_preview.rb +87 -0
  138. data/previews/primer/alpha/text_field_preview.rb +24 -1
  139. data/previews/primer/alpha/toggle_switch_preview.rb +9 -9
  140. data/previews/primer/beta/auto_complete_preview.rb +17 -17
  141. data/previews/primer/forms/forms_preview/example_toggle_switch_form.html.erb +2 -2
  142. data/previews/primer/forms/forms_preview/{select_list_form.html.erb → select_form.html.erb} +1 -1
  143. data/previews/primer/forms/forms_preview.rb +3 -1
  144. data/previews/primer/url_helpers.rb +1 -1
  145. data/static/arguments.json +1348 -1412
  146. data/static/audited_at.json +12 -21
  147. data/static/constants.json +20 -51
  148. data/static/previews.json +1812 -0
  149. data/static/statuses.json +10 -19
  150. metadata +47 -32
  151. data/app/components/primer/dropdown/menu.rb +0 -14
  152. data/app/components/primer/dropdown.rb +0 -7
  153. data/app/components/primer/hellip_button.rb +0 -7
  154. data/app/components/primer/label_component.rb +0 -7
  155. data/app/components/primer/link_component.rb +0 -7
  156. data/app/components/primer/local_time.rb +0 -63
  157. data/app/components/primer/markdown.rb +0 -7
  158. data/app/components/primer/menu_component.rb +0 -7
  159. data/app/components/primer/octicon_component.rb +0 -7
  160. data/app/components/primer/octicon_symbols_component.rb +0 -7
  161. data/app/components/primer/popover_component.rb +0 -8
  162. data/app/components/primer/spinner_component.rb +0 -7
  163. data/app/components/primer/state_component.rb +0 -7
  164. data/app/components/primer/subhead_component.rb +0 -7
  165. data/app/components/primer/tab_container_component.rb +0 -7
  166. data/app/components/primer/time_ago_component.d.ts +0 -1
  167. data/app/components/primer/time_ago_component.js +0 -1
  168. data/app/components/primer/time_ago_component.rb +0 -51
  169. data/app/components/primer/time_ago_component.ts +0 -1
  170. data/app/components/primer/timeline_item_component.rb +0 -13
  171. data/previews/primer/local_time_component_preview.rb +0 -57
  172. data/previews/primer/time_ago_component_preview.rb +0 -27
@@ -9,10 +9,14 @@ module Primer
9
9
  def initialize(input:)
10
10
  @input = input
11
11
 
12
+ @input.add_input_classes(
13
+ "FormControl-input",
14
+ Primer::Forms::Dsl::Input::SIZE_MAPPINGS[@input.size]
15
+ )
16
+
12
17
  @field_wrap_arguments = {
13
18
  class: class_names(
14
19
  "FormControl-input-wrap",
15
- Primer::Forms::Dsl::Input::SIZE_MAPPINGS[@input.size],
16
20
  "FormControl-input-wrap--trailingAction": @input.show_clear_button?,
17
21
  "FormControl-input-wrap--leadingVisual": @input.leading_visual?
18
22
  ),
@@ -32,8 +32,10 @@ module Primer
32
32
  #
33
33
  class ToggleSwitchForm < Primer::Forms::Base
34
34
  # Define the form on subclasses so render(Subclass.new) works as expected.
35
- def self.inherited(base)
36
- base.form do |toggle_switch_form|
35
+ # (this is called directly on this class, but also on classes
36
+ # that inherit from this class)
37
+ def self.define_form_on(klass)
38
+ klass.form do |toggle_switch_form|
37
39
  input = Dsl::ToggleSwitchInput.new(
38
40
  builder: toggle_switch_form.builder, form: self, **@system_arguments
39
41
  )
@@ -42,8 +44,13 @@ module Primer
42
44
  end
43
45
  end
44
46
 
47
+ def self.inherited(base)
48
+ super
49
+ define_form_on(base)
50
+ end
51
+
45
52
  # Define the form on self so render(ToggleSwitchForm.new) works as expected.
46
- inherited(self)
53
+ define_form_on(self)
47
54
 
48
55
  # Override to avoid accepting a builder argument. We create our own builder
49
56
  # on render. See the implementation of render_in below.
@@ -5,6 +5,10 @@ module Primer
5
5
  module Forms
6
6
  # :nodoc:
7
7
  module Utils
8
+ include Primer::ClassNameHelper
9
+
10
+ PRIMER_UTILITY_KEYS = Primer::Classify::Utilities::UTILITIES.keys.freeze
11
+
8
12
  # Unfortunately this bug (https://github.com/ruby/ruby/pull/5646) prevents us from using
9
13
  # Ruby's native Module.const_source_location. Instead we have to fudge it by searching
10
14
  # for the file in the configured autoload paths. Doing so relies on Rails' autoloading
@@ -21,6 +25,22 @@ module Primer
21
25
 
22
26
  nil
23
27
  end
28
+
29
+ # This method does the following:
30
+ #
31
+ # 1. Runs Primer's classify routine to convert entries like mb: 1 to mb-1.
32
+ # 2. Runs classify on both options[:class] and options[:classes]. The first
33
+ # is expected by Rails/HTML while the second is specific to Primer.
34
+ # 3. Combines options[:class] and options[:classes] into options[:class]
35
+ # so the options hash can be easily passed to Rails form builder methods.
36
+ #
37
+ def classify(options)
38
+ options[:classes] = class_names(options.delete(:class), options[:classes])
39
+ options.merge!(Primer::Classify.call(options))
40
+ options.except!(*PRIMER_UTILITY_KEYS)
41
+ options[:class] = class_names(options[:class], options.delete(:classes))
42
+ options
43
+ end
24
44
  end
25
45
 
26
46
  Utils.extend(Utils)
@@ -43,7 +43,7 @@ module Primer
43
43
  end
44
44
 
45
45
  initializer "primer.forms.helpers" do
46
- ActiveSupport.on_load :action_controller do
46
+ ActiveSupport.on_load :action_controller_base do
47
47
  require "primer/form_helper"
48
48
  helper Primer::FormHelper
49
49
 
@@ -6,7 +6,7 @@ module Primer
6
6
  module VERSION
7
7
  MAJOR = 0
8
8
  MINOR = 0
9
- PATCH = 120
9
+ PATCH = 122
10
10
 
11
11
  STRING = [MAJOR, MINOR, PATCH].join(".")
12
12
  end
@@ -2,27 +2,13 @@
2
2
 
3
3
  # :nocov:
4
4
  module Primer
5
- module YARD
5
+ module Yard
6
6
  # Shared functionality for generating documentation from YARD comments.
7
7
  class Backend
8
8
  include DocsHelper
9
9
 
10
10
  private
11
11
 
12
- def pretty_default_value(tag, component)
13
- params = tag.object.parameters.find { |param| [tag.name.to_s, "#{tag.name}:"].include?(param[0]) }
14
- default = tag.defaults&.first || params&.second
15
-
16
- return "N/A" unless default
17
-
18
- constant_name = "#{component.name}::#{default}"
19
- constant_value = default.safe_constantize || constant_name.safe_constantize
20
-
21
- return pretty_value(default) if constant_value.nil?
22
-
23
- pretty_value(constant_value)
24
- end
25
-
26
12
  def view_context
27
13
  @view_context ||= begin
28
14
  # Rails controller for rendering arbitrary ERB
@@ -2,7 +2,7 @@
2
2
 
3
3
  # :nocov:
4
4
  module Primer
5
- module YARD
5
+ module Yard
6
6
  # The set of documented components (and associated metadata).
7
7
  class ComponentManifest
8
8
  COMPONENTS = {
@@ -13,7 +13,6 @@ module Primer
13
13
  Primer::Alpha::Layout => {},
14
14
  Primer::Alpha::HellipButton => {},
15
15
  Primer::Alpha::Image => {},
16
- Primer::LocalTime => { js: true },
17
16
  Primer::Alpha::OcticonSymbols => {},
18
17
  Primer::Alpha::ImageCrop => { js: true },
19
18
  Primer::IconButton => { js: true },
@@ -54,7 +53,6 @@ module Primer
54
53
  Primer::Beta::Subhead => {},
55
54
  Primer::Alpha::TabContainer => { js: true },
56
55
  Primer::Beta::Text => {},
57
- Primer::TimeAgoComponent => { js: true },
58
56
  Primer::Beta::TimelineItem => {},
59
57
  Primer::Tooltip => {},
60
58
  Primer::Truncate => {},
@@ -79,42 +77,61 @@ module Primer
79
77
  Primer::Alpha::ActionList::Item => { examples: false },
80
78
 
81
79
  # Forms
82
- Primer::Alpha::TextField => { form_component: true }
80
+ Primer::Alpha::TextField => { form_component: true },
81
+ Primer::Alpha::TextArea => { form_component: true, published: false },
82
+ Primer::Alpha::Select => { form_component: true, published: false },
83
+ Primer::Alpha::MultiInput => { form_component: true, js: true, published: false },
84
+ Primer::Alpha::RadioButton => { form_component: true, published: false },
85
+ Primer::Alpha::RadioButtonGroup => { form_component: true, published: false },
86
+ Primer::Alpha::CheckBox => { form_component: true, published: false },
87
+ Primer::Alpha::CheckBoxGroup => { form_component: true, published: false },
88
+ Primer::Alpha::SubmitButton => { form_component: true, published: false },
89
+ Primer::Alpha::FormButton => { form_component: true, published: false }
83
90
  }.freeze
84
91
 
85
- class << self
86
- def each(&block)
87
- COMPONENTS.keys.each(&block)
88
- end
92
+ include Enumerable
89
93
 
90
- def components_with_docs
91
- @components_with_docs ||= COMPONENTS.keys
92
- end
94
+ def initialize(components)
95
+ @components = components
96
+ end
97
+
98
+ def each
99
+ return to_enum(__method__) unless block_given?
93
100
 
94
- def all_components
95
- @all_components ||= Primer::Component.descendants - [Primer::BaseComponent]
101
+ @components.each do |klass|
102
+ yield self.class.ref_for(klass)
96
103
  end
104
+ end
97
105
 
98
- def components_without_docs
99
- @components_without_docs ||= all_components - components_with_docs
106
+ def where(**attrs)
107
+ self.class.where(@components, **attrs)
108
+ end
109
+
110
+ class << self
111
+ def where(components = COMPONENTS, **desired_attrs)
112
+ new(
113
+ components.each_with_object([]) do |(klass, component_attrs), memo|
114
+ matches = desired_attrs.all? do |name, desired_value|
115
+ component_attrs.fetch(name, ComponentRef::ATTR_DEFAULTS[name]) == desired_value
116
+ end
117
+
118
+ memo << klass if matches
119
+ end
120
+ )
100
121
  end
101
122
 
102
- def components_with_examples
103
- @components_with_examples ||= COMPONENTS.keys.select do |c|
104
- COMPONENTS[c].fetch(:examples, true)
105
- end
123
+ def all
124
+ new(COMPONENTS.keys)
106
125
  end
107
126
 
108
- def components_requiring_js
109
- @components_requiring_js ||= COMPONENTS.keys.select do |c|
110
- COMPONENTS[c].fetch(:js, false)
111
- end
127
+ def ref_for(klass)
128
+ ref_cache[klass] ||= ComponentRef.new(klass, COMPONENTS[klass])
112
129
  end
113
130
 
114
- def form_components
115
- @form_components ||= COMPONENTS.keys.select do |c|
116
- COMPONENTS[c].fetch(:form_component, false)
117
- end
131
+ private
132
+
133
+ def ref_cache
134
+ @ref_cache ||= {}
118
135
  end
119
136
  end
120
137
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nocov:
4
+ module Primer
5
+ module Yard
6
+ # :nodoc:
7
+ class ComponentRef
8
+ ATTR_DEFAULTS = {
9
+ js: false,
10
+ examples: true,
11
+ published: true,
12
+ form_component: false
13
+ }.freeze
14
+
15
+ attr_reader :klass, :attrs
16
+
17
+ def initialize(klass, attrs)
18
+ @klass = klass
19
+ @attrs = attrs
20
+ end
21
+
22
+ def requires_js?
23
+ @attrs.fetch(:js, ATTR_DEFAULTS[:js])
24
+ end
25
+
26
+ def should_have_examples?
27
+ @attrs.fetch(:examples, ATTR_DEFAULTS[:examples])
28
+ end
29
+
30
+ def published?
31
+ @attrs.fetch(:published, ATTR_DEFAULTS[:published])
32
+ end
33
+
34
+ def form_component?
35
+ @attrs.fetch(:form_component, ATTR_DEFAULTS[:form_component])
36
+ end
37
+ end
38
+ end
39
+ end
40
+ # :nocov:
@@ -1,9 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # :nocov:
4
-
5
4
  module Primer
6
- module YARD
5
+ module Yard
7
6
  # Helper methods to use for yard documentation
8
7
  module DocsHelper
9
8
  def one_of(enumerable, lower: false, sort: true)
@@ -66,6 +65,20 @@ module Primer
66
65
  [m[:status]&.downcase, m[:name].gsub("::", ""), m[:name]]
67
66
  end
68
67
 
68
+ def pretty_default_value(tag, component)
69
+ params = tag.object.parameters.find { |param| [tag.name.to_s, "#{tag.name}:"].include?(param[0]) }
70
+ default = tag.defaults&.first || params&.second
71
+
72
+ return "N/A" unless default
73
+
74
+ constant_name = "#{component.name}::#{default}"
75
+ constant_value = default.safe_constantize || constant_name.safe_constantize
76
+
77
+ return pretty_value(default) if constant_value.nil?
78
+
79
+ pretty_value(constant_value)
80
+ end
81
+
69
82
  def pretty_value(val)
70
83
  case val
71
84
  when nil
@@ -79,3 +92,4 @@ module Primer
79
92
  end
80
93
  end
81
94
  end
95
+ # :nocov:
@@ -3,12 +3,8 @@
3
3
  # rubocop:disable Naming/MethodParameterName
4
4
 
5
5
  # :nocov:
6
-
7
- require "primer/yard/component_manifest"
8
- require "primer/yard/backend"
9
-
10
6
  module Primer
11
- module YARD
7
+ module Yard
12
8
  # Backend that generates documentation for the legacy, Gatsby-powered PVC docsite.
13
9
  class LegacyGatsbyBackend < Backend
14
10
  class << self
@@ -29,17 +25,19 @@ module Primer
29
25
  end
30
26
  end
31
27
 
32
- attr_reader :registry
28
+ attr_reader :registry, :manifest
33
29
 
34
- def initialize(registry)
30
+ def initialize(registry, manifest)
35
31
  @registry = registry
32
+ @manifest = manifest
36
33
  end
37
34
 
38
35
  def generate
39
36
  args_for_components = []
40
37
  errors = []
41
38
 
42
- each_component do |component|
39
+ each_component do |component_ref|
40
+ component = component_ref.klass
43
41
  docs = registry.find(component)
44
42
  status_path = docs.status_module.nil? ? "" : "#{docs.status_module}/"
45
43
 
@@ -66,7 +64,7 @@ module Primer
66
64
  f.puts
67
65
  f.puts("import Example from '#{metadata[:example_path]}'")
68
66
 
69
- if docs.requires_js?
67
+ if component_ref.requires_js?
70
68
  f.puts("import RequiresJSFlash from '#{metadata[:require_js_path]}'")
71
69
  f.puts
72
70
  f.puts("<RequiresJSFlash />")
@@ -170,7 +168,7 @@ module Primer
170
168
  f.puts(code.to_s)
171
169
  f.puts("```")
172
170
  end
173
- elsif manifest.components_with_examples.include?(component)
171
+ elsif component_ref.should_have_examples?
174
172
  errors << { component.name => { example: "No examples found" } }
175
173
  end
176
174
  end
@@ -219,11 +217,7 @@ module Primer
219
217
  end
220
218
 
221
219
  def each_component(&block)
222
- manifest.components_with_docs.sort_by(&:name).each(&block)
223
- end
224
-
225
- def manifest
226
- Primer::YARD::ComponentManifest
220
+ manifest.each(&block)
227
221
  end
228
222
 
229
223
  def source_url(component)
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nocov:
4
+ module Primer
5
+ module Yard
6
+ # Helper methods for documentation generated in Lookbook pages.
7
+ module LookbookDocsHelper
8
+ # Adheres to the same signature as Primer::Yard::DocsHelper#link_to_component so link_to_component
9
+ # may be used in a Gatsby or Lookbook context and produce the correct link for each platform.
10
+ #
11
+ # @param component [Class] The component class to link to.
12
+ # @return [String] The link, either in HTML or markdown format.
13
+ def link_to_component(component)
14
+ backend = Primer::Yard::LookbookPagesBackend.new(Primer::Yard::Registry.make, nil)
15
+ component_ref = Primer::Yard::ComponentManifest.ref_for(component)
16
+ page = backend.page_for(component_ref)
17
+
18
+ # If the page_path method is available, we're being rendered into HTML by Lookbook
19
+ # and should emit an HTML <a> tag. No page_path means we're being rendered into
20
+ # markdown by LookbookPagesBackend and should emit a markdown + ERB link that
21
+ # Lookbook will eventually render on page load.
22
+ if respond_to?(:page_path)
23
+ link_to(page.docs.short_name, page_path(page.page_id.to_sym.inspect))
24
+ else
25
+ # rubocop:disable Rails/OutputSafety
26
+ "[#{page.docs.short_name}](<%= page_path(#{page.page_id.to_sym.inspect}) %>)".html_safe
27
+ # rubocop:enable Rails/OutputSafety
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nocov:
4
+ module Primer
5
+ module Yard
6
+ # A single Lookbook page.
7
+ class LookbookPage
8
+ include DocsHelper
9
+
10
+ PREVIEW_MAP = {
11
+ Primer::Alpha::TextField => [:single_text_field_form, :multi_text_field_form],
12
+ Primer::Alpha::TextArea => [],
13
+ Primer::Alpha::Select => [:select_list_form],
14
+ Primer::Alpha::MultiInput => [:multi_input_form],
15
+ Primer::Alpha::RadioButton => [:radio_button_with_nested_form],
16
+ Primer::Alpha::RadioButtonGroup => [:radio_button_group_form],
17
+ Primer::Alpha::CheckBox => [:check_box_with_nested_form],
18
+ Primer::Alpha::CheckBoxGroup => [:check_box_group_form],
19
+ Primer::Alpha::SubmitButton => [:submit_button_form],
20
+ Primer::Alpha::FormButton => [:submit_button_form]
21
+ }.freeze
22
+
23
+ attr_reader :component_ref, :backend, :docs
24
+
25
+ def initialize(component_ref, backend, docs)
26
+ @component_ref = component_ref
27
+ @backend = backend
28
+ @docs = docs
29
+ end
30
+
31
+ def page_id
32
+ @page_id ||= docs.short_name.dasherize.underscore.tap do |page_id|
33
+ page_id << "_input" unless page_id.end_with?("_input")
34
+ end
35
+ end
36
+
37
+ def generate
38
+ path = File.expand_path(
39
+ File.join(
40
+ *%w[.. .. .. previews pages forms inputs],
41
+ "#{docs.short_name.dasherize.underscore}.md.erb"
42
+ ), __dir__
43
+ )
44
+
45
+ # rubocop:disable Lint/UselessAssignment
46
+ documented_methods = docs.non_slot_methods.select do |mtd|
47
+ [component.name, "Primer::Forms::Dsl::InputMethods"].include?(mtd.parent.title)
48
+ end
49
+
50
+ preview_methods = PREVIEW_MAP[component]
51
+ preview_erbs = preview_methods.map do |preview_method|
52
+ "<%= embed Primer::Forms::FormsPreview, #{preview_method.inspect} %>"
53
+ end
54
+ # rubocop:enable Lint/UselessAssignment
55
+
56
+ # rubocop:disable Security/Eval
57
+ File.open(path, "w") do |f|
58
+ f.write(eval(Erubi::Engine.new(<<~ERB, trim: true).src))
59
+ ---
60
+ title: <%= docs.title.underscore.titleize %>
61
+ id: <%= page_id %>
62
+ ---
63
+
64
+ <%= docs.base_docstring %>
65
+
66
+ ## Usage
67
+
68
+ ```ruby
69
+ <%= docs.tags(:form_usage).first.text %>
70
+ ```
71
+
72
+ <% specific_args = specific_args_from(docs.params) %>
73
+ <% unless specific_args.empty? %>
74
+ ## Arguments
75
+
76
+ <%= generate_args_table(specific_args) %>
77
+ <% end %>
78
+
79
+ ## Common arguments
80
+
81
+ <%= generate_args_table(common_args_from(docs.params)) %>
82
+
83
+ <% unless documented_methods.empty? %>
84
+ ## Methods
85
+
86
+ <% documented_methods.each do |method_docs| %>
87
+ ### `#<%= method_docs.signature.sub(/def /, "") %>`
88
+
89
+ <%= method_docs.base_docstring %>
90
+
91
+ <% param_tags = method_docs.tags(:param) %>
92
+
93
+ <% if param_tags.any? %>
94
+
95
+ <%= generate_args_table(param_tags) %>
96
+ <% end %>
97
+ <% end %>
98
+ <% end %>
99
+
100
+ <% unless preview_methods.empty? %>
101
+ ## Examples
102
+
103
+ <%= preview_erbs.join("\n") %>
104
+ <% end %>
105
+ ERB
106
+ end
107
+ # rubocop:enable Security/Eval
108
+ end
109
+
110
+ private
111
+
112
+ def registry
113
+ backend.registry
114
+ end
115
+
116
+ def generate_args_table(params)
117
+ rows = params.map do |tag|
118
+ description = backend.view_context.render(inline: tag.text.squish)
119
+ parts = [
120
+ "`#{tag.name}`",
121
+ tag.types.join(", "),
122
+ description
123
+ ]
124
+
125
+ "| #{parts.join(' | ')} |"
126
+ end
127
+
128
+ <<~MARKDOWN
129
+ | Name | Type | Description |
130
+ | :- | :- | :- |
131
+ #{rows.join("\n")}
132
+ MARKDOWN
133
+ end
134
+
135
+ def common_args_from(params)
136
+ params.select { |param| common_form_input_argument_names.include?(param.name) }
137
+ end
138
+
139
+ def specific_args_from(params)
140
+ params.reject { |param| common_form_input_argument_names.include?(param.name) }
141
+ end
142
+
143
+ def common_form_input_argument_names
144
+ @common_form_input_argument_names ||= begin
145
+ macro = registry.yard_registry[".macro.form_input_arguments"]
146
+ parser = ::YARD::Docstring.parser
147
+ parser.parse(macro.macro_data)
148
+ parser
149
+ .tags
150
+ .select { |tag| tag.tag_name == "param" }
151
+ .map(&:name)
152
+ end
153
+ end
154
+
155
+ def component
156
+ component_ref.klass
157
+ end
158
+ end
159
+
160
+ # Backend that generates Lookbook pages.
161
+ class LookbookPagesBackend < Backend
162
+ attr_reader :registry, :manifest
163
+
164
+ def initialize(registry, manifest)
165
+ @registry = registry
166
+ @manifest = manifest
167
+ end
168
+
169
+ def generate
170
+ each_component do |component_ref|
171
+ page_for(component_ref).generate
172
+ end
173
+ end
174
+
175
+ def page_for(component_ref)
176
+ docs = registry.find(component_ref.klass)
177
+ LookbookPage.new(component_ref, self, docs)
178
+ end
179
+
180
+ def view_context
181
+ @view_context ||= super.tap do |vc|
182
+ vc.singleton_class.include(LookbookDocsHelper)
183
+ end
184
+ end
185
+
186
+ private
187
+
188
+ def each_component(&block)
189
+ manifest.each(&block)
190
+ end
191
+ end
192
+ end
193
+ end
194
+ # :nocov:
@@ -2,12 +2,10 @@
2
2
 
3
3
  # :nocov:
4
4
 
5
- require "primer/view_components"
6
- require "primer/yard/docs_helper"
7
5
  require "view_component/test_helpers"
8
6
 
9
7
  module Primer
10
- module YARD
8
+ module Yard
11
9
  # A wrapper around a YARD class reference that provides convenience methods
12
10
  # for extracting component parameters, accessibility status, etc.
13
11
  class RegistryEntry
@@ -62,8 +60,9 @@ module Primer
62
60
  def public_methods
63
61
  # Returns: only public methods that belong to this class (i.e. no inherited methods)
64
62
  # excluding the constructor
65
- @public_methods ||=
66
- docs.meths.reject { |mtd| mtd.tag(:private) || mtd.name == :initialize }
63
+ @public_methods ||= docs.meths.reject do |mtd|
64
+ mtd.tag(:private) || mtd.name == :initialize
65
+ end
67
66
  end
68
67
 
69
68
  def title
@@ -89,20 +88,6 @@ module Primer
89
88
  def a11y_reviewed?
90
89
  metadata[:a11y_reviewed]
91
90
  end
92
-
93
- def requires_js?
94
- manifest.components_requiring_js.include?(component)
95
- end
96
-
97
- def includes_examples?
98
- manifest.components_with_examples.include?(component)
99
- end
100
-
101
- private
102
-
103
- def manifest
104
- Primer::YARD::ComponentManifest
105
- end
106
91
  end
107
92
 
108
93
  # Wrapper around an instance of YARD::Registry that provides easy access to component
@@ -111,11 +96,11 @@ module Primer
111
96
  class << self
112
97
  include ViewComponent::TestHelpers
113
98
  include Primer::ViewHelper
114
- include Primer::YARD::DocsHelper
99
+ include Primer::Yard::DocsHelper
115
100
 
116
101
  def make
117
102
  registry = ::YARD::RegistryStore.new
118
- registry.load!(".yardoc")
103
+ registry.load!(File.expand_path(File.join("..", "..", "..", ".yardoc"), __dir__))
119
104
 
120
105
  new(registry)
121
106
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  # :nocov:
4
4
  module Primer
5
- module YARD
5
+ module Yard
6
6
  # YARD Handler to parse `renders_many` calls.
7
7
  class RendersManyHandler < ::YARD::Handlers::Ruby::Base
8
8
  handles method_call(:renders_many)
@@ -2,7 +2,7 @@
2
2
 
3
3
  # :nocov:
4
4
  module Primer
5
- module YARD
5
+ module Yard
6
6
  # YARD Handler to parse `renders_one` calls.
7
7
  class RendersOneHandler < ::YARD::Handlers::Ruby::Base
8
8
  handles method_call(:renders_one)