primer_view_components 0.0.120 → 0.0.122

Sign up to get free protection for your applications and to get access to all the features.
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)