primer_view_components 0.0.119 → 0.0.120
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/app/assets/javascripts/primer_view_components.js +1 -1
- data/app/assets/javascripts/primer_view_components.js.map +1 -1
- data/app/components/primer/alpha/modal_dialog.js +2 -0
- data/app/components/primer/alpha/modal_dialog.ts +2 -0
- data/lib/primer/deprecations.yml +0 -13
- data/lib/primer/view_components/linters/button_component_migration_counter.rb +2 -2
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/primer/view_components.rb +5 -0
- data/lib/primer/yard/backend.rb +38 -0
- data/lib/primer/yard/component_manifest.rb +123 -0
- data/lib/primer/yard/docs_helper.rb +81 -0
- data/lib/primer/yard/legacy_gatsby_backend.rb +271 -0
- data/lib/primer/yard/registry.rb +146 -0
- data/lib/primer/yard/renders_many_handler.rb +23 -0
- data/lib/primer/yard/renders_one_handler.rb +23 -0
- data/lib/rubocop/config/default.yml +3 -0
- data/lib/rubocop/cop/primer/test_selector.rb +48 -0
- data/lib/tasks/docs.rake +37 -405
- data/previews/primer/alpha/dialog_preview/body_has_scrollbar_overflow.html.erb +9 -0
- data/previews/primer/alpha/dialog_preview.rb +15 -0
- data/static/arguments.json +0 -32
- data/static/audited_at.json +0 -3
- data/static/constants.json +0 -20
- data/static/statuses.json +0 -3
- metadata +17 -15
- data/app/components/primer/box_component.rb +0 -7
- data/app/components/primer/clipboard_copy.rb +0 -7
- data/app/components/primer/dropdown_menu_component.html.erb +0 -8
- data/app/components/primer/dropdown_menu_component.rb +0 -58
- data/lib/yard/docs_helper.rb +0 -79
- data/lib/yard/renders_many_handler.rb +0 -19
- data/lib/yard/renders_one_handler.rb +0 -19
@@ -86,6 +86,7 @@ export class ModalDialogElement extends HTMLElement {
|
|
86
86
|
return;
|
87
87
|
this.setAttribute('open', '');
|
88
88
|
(_a = __classPrivateFieldGet(this, _ModalDialogElement_instances, "a", _ModalDialogElement_overlayBackdrop_get)) === null || _a === void 0 ? void 0 : _a.classList.remove('Overlay--hidden');
|
89
|
+
document.body.style.paddingRight = `${window.innerWidth - document.body.clientWidth}px`;
|
89
90
|
document.body.style.overflow = 'hidden';
|
90
91
|
if (__classPrivateFieldGet(this, _ModalDialogElement_focusAbortController, "f").signal.aborted) {
|
91
92
|
__classPrivateFieldSet(this, _ModalDialogElement_focusAbortController, new AbortController(), "f");
|
@@ -98,6 +99,7 @@ export class ModalDialogElement extends HTMLElement {
|
|
98
99
|
return;
|
99
100
|
this.removeAttribute('open');
|
100
101
|
(_b = __classPrivateFieldGet(this, _ModalDialogElement_instances, "a", _ModalDialogElement_overlayBackdrop_get)) === null || _b === void 0 ? void 0 : _b.classList.add('Overlay--hidden');
|
102
|
+
document.body.style.paddingRight = '0';
|
101
103
|
document.body.style.overflow = 'initial';
|
102
104
|
__classPrivateFieldGet(this, _ModalDialogElement_focusAbortController, "f").abort();
|
103
105
|
// if #openButton is a child of a menu, we need to focus a suitable child of the menu
|
@@ -81,6 +81,7 @@ export class ModalDialogElement extends HTMLElement {
|
|
81
81
|
if (this.open) return
|
82
82
|
this.setAttribute('open', '')
|
83
83
|
this.#overlayBackdrop?.classList.remove('Overlay--hidden')
|
84
|
+
document.body.style.paddingRight = `${window.innerWidth - document.body.clientWidth}px`
|
84
85
|
document.body.style.overflow = 'hidden'
|
85
86
|
if (this.#focusAbortController.signal.aborted) {
|
86
87
|
this.#focusAbortController = new AbortController()
|
@@ -91,6 +92,7 @@ export class ModalDialogElement extends HTMLElement {
|
|
91
92
|
if (!this.open) return
|
92
93
|
this.removeAttribute('open')
|
93
94
|
this.#overlayBackdrop?.classList.add('Overlay--hidden')
|
95
|
+
document.body.style.paddingRight = '0'
|
94
96
|
document.body.style.overflow = 'initial'
|
95
97
|
this.#focusAbortController.abort()
|
96
98
|
// if #openButton is a child of a menu, we need to focus a suitable child of the menu
|
data/lib/primer/deprecations.yml
CHANGED
@@ -17,24 +17,11 @@ deprecations:
|
|
17
17
|
autocorrect: true
|
18
18
|
replacement: "Primer::Beta::Blankslate"
|
19
19
|
|
20
|
-
- component: "Primer::BoxComponent"
|
21
|
-
autocorrect: true
|
22
|
-
replacement: "Primer::Box"
|
23
|
-
|
24
20
|
- component: "Primer::ButtonComponent"
|
25
21
|
autocorrect: false
|
26
22
|
replacement: "Primer::Beta::Button"
|
27
23
|
guide: "https://primer.style/view-components/guides/primer_button_component"
|
28
24
|
|
29
|
-
- component: "Primer::ClipboardCopy"
|
30
|
-
autocorrect: true
|
31
|
-
replacement: "Primer::Beta::ClipboardCopy"
|
32
|
-
|
33
|
-
- component: "Primer::DropdownMenuComponent"
|
34
|
-
autocorrect: false
|
35
|
-
replacement: "Primer::Beta::Dropdown"
|
36
|
-
guide: "https://primer.style/view-components/guides/primer_dropdown_menu_component"
|
37
|
-
|
38
25
|
- component: "Primer::Dropdown"
|
39
26
|
autocorrect: true
|
40
27
|
replacement: "Primer::Alpha::Dropdown"
|
@@ -18,9 +18,9 @@ module ERBLint
|
|
18
18
|
# CloseButton component has preference when this class is seen in conjunction with `btn`.
|
19
19
|
DISALLOWED_CLASSES = %w[close-button].freeze
|
20
20
|
CLASSES = %w[btn btn-link].freeze
|
21
|
-
MESSAGE = "We are migrating buttons to use [Primer::
|
21
|
+
MESSAGE = "We are migrating buttons to use [Primer::Beta::Button](https://primer.style/view-components/components/beta/button), please try to use that instead of raw HTML."
|
22
22
|
ARGUMENT_MAPPER = ArgumentMappers::Button
|
23
|
-
COMPONENT = "Primer::
|
23
|
+
COMPONENT = "Primer::Beta::Button"
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :nocov:
|
4
|
+
module Primer
|
5
|
+
module YARD
|
6
|
+
# Shared functionality for generating documentation from YARD comments.
|
7
|
+
class Backend
|
8
|
+
include DocsHelper
|
9
|
+
|
10
|
+
private
|
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
|
+
def view_context
|
27
|
+
@view_context ||= begin
|
28
|
+
# Rails controller for rendering arbitrary ERB
|
29
|
+
vc = ApplicationController.new.tap { |c| c.request = ActionDispatch::TestRequest.create }.view_context
|
30
|
+
vc.singleton_class.include(DocsHelper)
|
31
|
+
vc.singleton_class.include(Primer::ViewHelper)
|
32
|
+
vc
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
# :nocov:
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :nocov:
|
4
|
+
module Primer
|
5
|
+
module YARD
|
6
|
+
# The set of documented components (and associated metadata).
|
7
|
+
class ComponentManifest
|
8
|
+
COMPONENTS = {
|
9
|
+
Primer::Beta::RelativeTime => {},
|
10
|
+
Primer::Beta::IconButton => {},
|
11
|
+
Primer::Beta::Button => {},
|
12
|
+
Primer::Alpha::SegmentedControl => {},
|
13
|
+
Primer::Alpha::Layout => {},
|
14
|
+
Primer::Alpha::HellipButton => {},
|
15
|
+
Primer::Alpha::Image => {},
|
16
|
+
Primer::LocalTime => { js: true },
|
17
|
+
Primer::Alpha::OcticonSymbols => {},
|
18
|
+
Primer::Alpha::ImageCrop => { js: true },
|
19
|
+
Primer::IconButton => { js: true },
|
20
|
+
Primer::Beta::AutoComplete => { js: true },
|
21
|
+
Primer::Beta::AutoComplete::Item => {},
|
22
|
+
Primer::Beta::Avatar => {},
|
23
|
+
Primer::Beta::AvatarStack => {},
|
24
|
+
Primer::Beta::BaseButton => {},
|
25
|
+
Primer::Alpha::Banner => { js: true },
|
26
|
+
Primer::Beta::Blankslate => {},
|
27
|
+
Primer::Beta::BorderBox => {},
|
28
|
+
Primer::Beta::BorderBox::Header => {},
|
29
|
+
Primer::Box => {},
|
30
|
+
Primer::Beta::Breadcrumbs => {},
|
31
|
+
Primer::ButtonComponent => { js: true },
|
32
|
+
Primer::Beta::ButtonGroup => {},
|
33
|
+
Primer::Alpha::ButtonMarketing => {},
|
34
|
+
Primer::Beta::ClipboardCopy => { js: true },
|
35
|
+
Primer::Beta::CloseButton => {},
|
36
|
+
Primer::Beta::Counter => {},
|
37
|
+
Primer::Beta::Details => {},
|
38
|
+
Primer::Alpha::Dialog => {},
|
39
|
+
Primer::Alpha::Dropdown => { js: true },
|
40
|
+
Primer::Beta::Flash => {},
|
41
|
+
Primer::Beta::Heading => {},
|
42
|
+
Primer::Alpha::HiddenTextExpander => {},
|
43
|
+
Primer::Beta::Label => {},
|
44
|
+
Primer::LayoutComponent => {},
|
45
|
+
Primer::Beta::Link => { js: true },
|
46
|
+
Primer::Beta::Markdown => {},
|
47
|
+
Primer::Alpha::Menu => {},
|
48
|
+
Primer::Navigation::TabComponent => {},
|
49
|
+
Primer::Beta::Octicon => {},
|
50
|
+
Primer::Beta::Popover => {},
|
51
|
+
Primer::Beta::ProgressBar => {},
|
52
|
+
Primer::Beta::State => {},
|
53
|
+
Primer::Beta::Spinner => {},
|
54
|
+
Primer::Beta::Subhead => {},
|
55
|
+
Primer::Alpha::TabContainer => { js: true },
|
56
|
+
Primer::Beta::Text => {},
|
57
|
+
Primer::TimeAgoComponent => { js: true },
|
58
|
+
Primer::Beta::TimelineItem => {},
|
59
|
+
Primer::Tooltip => {},
|
60
|
+
Primer::Truncate => {},
|
61
|
+
Primer::Beta::Truncate => {},
|
62
|
+
Primer::Alpha::UnderlineNav => {},
|
63
|
+
Primer::Alpha::UnderlinePanels => { js: true },
|
64
|
+
Primer::Alpha::TabNav => {},
|
65
|
+
Primer::Alpha::TabPanels => { js: true },
|
66
|
+
Primer::Alpha::Tooltip => { js: true },
|
67
|
+
Primer::Alpha::ToggleSwitch => { js: true },
|
68
|
+
|
69
|
+
# Examples can be seen in the NavList docs
|
70
|
+
Primer::Alpha::NavList => { js: true },
|
71
|
+
Primer::Alpha::NavList::Item => { js: true, examples: false },
|
72
|
+
Primer::Alpha::NavList::Section => { js: true, examples: false },
|
73
|
+
|
74
|
+
# ActionList is a base component that should not be used by itself, and thus
|
75
|
+
# does not have examples of its own
|
76
|
+
Primer::Alpha::ActionList => { js: true, examples: false },
|
77
|
+
Primer::Alpha::ActionList::Divider => { examples: false },
|
78
|
+
Primer::Alpha::ActionList::Heading => { examples: false },
|
79
|
+
Primer::Alpha::ActionList::Item => { examples: false },
|
80
|
+
|
81
|
+
# Forms
|
82
|
+
Primer::Alpha::TextField => { form_component: true }
|
83
|
+
}.freeze
|
84
|
+
|
85
|
+
class << self
|
86
|
+
def each(&block)
|
87
|
+
COMPONENTS.keys.each(&block)
|
88
|
+
end
|
89
|
+
|
90
|
+
def components_with_docs
|
91
|
+
@components_with_docs ||= COMPONENTS.keys
|
92
|
+
end
|
93
|
+
|
94
|
+
def all_components
|
95
|
+
@all_components ||= Primer::Component.descendants - [Primer::BaseComponent]
|
96
|
+
end
|
97
|
+
|
98
|
+
def components_without_docs
|
99
|
+
@components_without_docs ||= all_components - components_with_docs
|
100
|
+
end
|
101
|
+
|
102
|
+
def components_with_examples
|
103
|
+
@components_with_examples ||= COMPONENTS.keys.select do |c|
|
104
|
+
COMPONENTS[c].fetch(:examples, true)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def components_requiring_js
|
109
|
+
@components_requiring_js ||= COMPONENTS.keys.select do |c|
|
110
|
+
COMPONENTS[c].fetch(:js, false)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def form_components
|
115
|
+
@form_components ||= COMPONENTS.keys.select do |c|
|
116
|
+
COMPONENTS[c].fetch(:form_component, false)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
# :nocov:
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# :nocov:
|
4
|
+
|
5
|
+
module Primer
|
6
|
+
module YARD
|
7
|
+
# Helper methods to use for yard documentation
|
8
|
+
module DocsHelper
|
9
|
+
def one_of(enumerable, lower: false, sort: true)
|
10
|
+
# Sort the array if requested
|
11
|
+
if sort
|
12
|
+
enumerable = enumerable.sort do |a, b|
|
13
|
+
a.instance_of?(b.class) ? a <=> b : a.class.to_s <=> b.class.to_s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
values =
|
18
|
+
case enumerable
|
19
|
+
when Hash
|
20
|
+
enumerable.map do |key, value|
|
21
|
+
"#{pretty_value(key)} (#{pretty_value(value)})"
|
22
|
+
end
|
23
|
+
else
|
24
|
+
enumerable.map do |key|
|
25
|
+
pretty_value(key)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
prefix = "One of"
|
30
|
+
prefix = prefix.downcase if lower
|
31
|
+
|
32
|
+
"#{prefix} #{values.to_sentence(two_words_connector: ' or ', last_word_connector: ', or ')}."
|
33
|
+
end
|
34
|
+
|
35
|
+
def link_to_accessibility
|
36
|
+
"[Accessibility](#accessibility)"
|
37
|
+
end
|
38
|
+
|
39
|
+
def link_to_system_arguments_docs
|
40
|
+
"[System arguments](/system-arguments)"
|
41
|
+
end
|
42
|
+
|
43
|
+
def link_to_typography_docs
|
44
|
+
"[Typography](/system-arguments#typography)"
|
45
|
+
end
|
46
|
+
|
47
|
+
def link_to_component(component)
|
48
|
+
status_module, short_name, class_name = status_module_and_short_name(component)
|
49
|
+
status_path = status_module.nil? ? "" : "#{status_module}/"
|
50
|
+
|
51
|
+
"[#{class_name}](/components/#{status_path}#{short_name.downcase})"
|
52
|
+
end
|
53
|
+
|
54
|
+
def link_to_octicons
|
55
|
+
"[Octicon](https://primer.style/octicons/)"
|
56
|
+
end
|
57
|
+
|
58
|
+
def link_to_heading_practices
|
59
|
+
"[Learn more about best heading practices (WAI Headings)](https://www.w3.org/WAI/tutorials/page-structure/headings/)"
|
60
|
+
end
|
61
|
+
|
62
|
+
def status_module_and_short_name(component)
|
63
|
+
name_with_status = component.name.gsub(/Primer::|Component/, "")
|
64
|
+
|
65
|
+
m = name_with_status.match(/(?<status>Beta|Alpha|Deprecated)?(?<_colons>::)?(?<name>.*)/)
|
66
|
+
[m[:status]&.downcase, m[:name].gsub("::", ""), m[:name]]
|
67
|
+
end
|
68
|
+
|
69
|
+
def pretty_value(val)
|
70
|
+
case val
|
71
|
+
when nil
|
72
|
+
"`nil`"
|
73
|
+
when Symbol
|
74
|
+
"`:#{val}`"
|
75
|
+
else
|
76
|
+
"`#{val}`"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,271 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Naming/MethodParameterName
|
4
|
+
|
5
|
+
# :nocov:
|
6
|
+
|
7
|
+
require "primer/yard/component_manifest"
|
8
|
+
require "primer/yard/backend"
|
9
|
+
|
10
|
+
module Primer
|
11
|
+
module YARD
|
12
|
+
# Backend that generates documentation for the legacy, Gatsby-powered PVC docsite.
|
13
|
+
class LegacyGatsbyBackend < Backend
|
14
|
+
class << self
|
15
|
+
def parse_example_tag(tag)
|
16
|
+
name = tag.name
|
17
|
+
description = nil
|
18
|
+
code = nil
|
19
|
+
|
20
|
+
if tag.text.include?("@description")
|
21
|
+
splitted = tag.text.split(/@description|@code/)
|
22
|
+
description = splitted.second.gsub(/^[ \t]{2}/, "").strip
|
23
|
+
code = splitted.last.gsub(/^[ \t]{2}/, "").strip
|
24
|
+
else
|
25
|
+
code = tag.text
|
26
|
+
end
|
27
|
+
|
28
|
+
[name, description, code]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :registry
|
33
|
+
|
34
|
+
def initialize(registry)
|
35
|
+
@registry = registry
|
36
|
+
end
|
37
|
+
|
38
|
+
def generate
|
39
|
+
args_for_components = []
|
40
|
+
errors = []
|
41
|
+
|
42
|
+
each_component do |component|
|
43
|
+
docs = registry.find(component)
|
44
|
+
status_path = docs.status_module.nil? ? "" : "#{docs.status_module}/"
|
45
|
+
|
46
|
+
metadata = docs.metadata.merge(
|
47
|
+
source: source_url(component),
|
48
|
+
lookbook: lookbook_url(component),
|
49
|
+
path: "docs/content/components/#{status_path}#{docs.short_name.downcase}.md",
|
50
|
+
example_path: example_path(component),
|
51
|
+
require_js_path: require_js_path(component)
|
52
|
+
)
|
53
|
+
|
54
|
+
path = Pathname.new(metadata[:path])
|
55
|
+
path.dirname.mkpath unless path.dirname.exist?
|
56
|
+
|
57
|
+
File.open(path, "w") do |f|
|
58
|
+
f.puts("---")
|
59
|
+
f.puts("title: #{metadata[:title]}")
|
60
|
+
f.puts("componentId: #{metadata[:component_id]}")
|
61
|
+
f.puts("status: #{metadata[:status]}")
|
62
|
+
f.puts("source: #{metadata[:source]}")
|
63
|
+
f.puts("a11yReviewed: #{metadata[:a11y_reviewed]}")
|
64
|
+
f.puts("lookbook: #{metadata[:lookbook]}") if preview_exists?(component)
|
65
|
+
f.puts("---")
|
66
|
+
f.puts
|
67
|
+
f.puts("import Example from '#{metadata[:example_path]}'")
|
68
|
+
|
69
|
+
if docs.requires_js?
|
70
|
+
f.puts("import RequiresJSFlash from '#{metadata[:require_js_path]}'")
|
71
|
+
f.puts
|
72
|
+
f.puts("<RequiresJSFlash />")
|
73
|
+
end
|
74
|
+
|
75
|
+
f.puts
|
76
|
+
f.puts("<!-- Warning: AUTO-GENERATED file, do not edit. Add code comments to your Ruby instead <3 -->")
|
77
|
+
f.puts
|
78
|
+
f.puts(view_context.render(inline: docs.base_docstring))
|
79
|
+
|
80
|
+
if docs.tags(:deprecated).any?
|
81
|
+
f.puts
|
82
|
+
f.puts("## Deprecation")
|
83
|
+
docs.tags(:deprecated).each do |tag|
|
84
|
+
f.puts
|
85
|
+
f.puts view_context.render(inline: tag.text)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
if docs.tags(:accessibility).any?
|
90
|
+
f.puts
|
91
|
+
f.puts("## Accessibility")
|
92
|
+
docs.tags(:accessibility).each do |tag|
|
93
|
+
f.puts
|
94
|
+
f.puts view_context.render(inline: tag.text)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
errors << { component.name => { arguments: "No argument documentation found" } } unless docs.params.any?
|
99
|
+
|
100
|
+
f.puts
|
101
|
+
f.puts("## Arguments")
|
102
|
+
f.puts
|
103
|
+
f.puts("| Name | Type | Default | Description |")
|
104
|
+
f.puts("| :- | :- | :- | :- |")
|
105
|
+
|
106
|
+
documented_params = docs.params.map(&:name)
|
107
|
+
component_params = component.instance_method(:initialize).parameters.map { |p| p.last.to_s }
|
108
|
+
|
109
|
+
if (documented_params & component_params).size != component_params.size
|
110
|
+
err = { arguments: {} }
|
111
|
+
(component_params - documented_params).each do |arg|
|
112
|
+
err[:arguments][arg] = "Not documented"
|
113
|
+
end
|
114
|
+
|
115
|
+
errors << { component.name => err }
|
116
|
+
end
|
117
|
+
|
118
|
+
args = []
|
119
|
+
docs.params.each do |tag|
|
120
|
+
default_value = pretty_default_value(tag, component)
|
121
|
+
|
122
|
+
args << {
|
123
|
+
"name" => tag.name,
|
124
|
+
"type" => tag.types.join(", "),
|
125
|
+
"default" => default_value,
|
126
|
+
"description" => view_context.render(inline: tag.text.squish)
|
127
|
+
}
|
128
|
+
|
129
|
+
f.puts("| `#{tag.name}` | `#{tag.types.join(', ')}` | #{default_value} | #{view_context.render(inline: tag.text.squish)} |")
|
130
|
+
end
|
131
|
+
|
132
|
+
component_args = {
|
133
|
+
"component" => metadata[:title],
|
134
|
+
"status" => component.status.to_s,
|
135
|
+
"source" => metadata[:source],
|
136
|
+
"lookbook" => metadata[:lookbook],
|
137
|
+
"parameters" => args
|
138
|
+
}
|
139
|
+
|
140
|
+
args_for_components << component_args
|
141
|
+
|
142
|
+
if docs.slot_methods.any?
|
143
|
+
f.puts
|
144
|
+
f.puts("## Slots")
|
145
|
+
|
146
|
+
docs.slot_methods.each do |slot_docs|
|
147
|
+
emit_method(slot_docs, component, f)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
example_tags = docs.constructor.tags(:example)
|
152
|
+
|
153
|
+
if example_tags.any?
|
154
|
+
f.puts
|
155
|
+
f.puts("## Examples")
|
156
|
+
|
157
|
+
example_tags.each do |tag|
|
158
|
+
name, description, code = parse_example_tag(tag)
|
159
|
+
f.puts
|
160
|
+
f.puts("### #{name}")
|
161
|
+
if description
|
162
|
+
f.puts
|
163
|
+
f.puts(view_context.render(inline: description.squish))
|
164
|
+
end
|
165
|
+
f.puts
|
166
|
+
html = view_context.render(inline: code)
|
167
|
+
f.puts("<Example src=\"#{html.tr('"', "\'").delete("\n")}\" />")
|
168
|
+
f.puts
|
169
|
+
f.puts("```erb")
|
170
|
+
f.puts(code.to_s)
|
171
|
+
f.puts("```")
|
172
|
+
end
|
173
|
+
elsif manifest.components_with_examples.include?(component)
|
174
|
+
errors << { component.name => { example: "No examples found" } }
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Build system arguments docs from BaseComponent
|
180
|
+
system_args_docs = registry.find(Primer::BaseComponent)
|
181
|
+
|
182
|
+
File.open("docs/content/system-arguments.md", "w") do |f|
|
183
|
+
f.puts("---")
|
184
|
+
f.puts("title: System arguments")
|
185
|
+
f.puts("---")
|
186
|
+
f.puts
|
187
|
+
f.puts("<!-- Warning: AUTO-GENERATED file, do not edit. Add code comments to your Ruby instead <3 -->")
|
188
|
+
f.puts
|
189
|
+
f.puts(system_args_docs.base_docstring)
|
190
|
+
f.puts
|
191
|
+
|
192
|
+
f.puts(view_context.render(inline: system_args_docs.constructor.base_docstring))
|
193
|
+
end
|
194
|
+
|
195
|
+
[args_for_components, errors]
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def emit_method(method_docs, component, f)
|
201
|
+
f.puts
|
202
|
+
f.puts("### `#{method_docs.name}`")
|
203
|
+
|
204
|
+
if method_docs.base_docstring.to_s.present?
|
205
|
+
f.puts
|
206
|
+
f.puts(view_context.render(inline: method_docs.base_docstring))
|
207
|
+
end
|
208
|
+
|
209
|
+
param_tags = method_docs.tags(:param)
|
210
|
+
if param_tags.any?
|
211
|
+
f.puts
|
212
|
+
f.puts("| Name | Type | Default | Description |")
|
213
|
+
f.puts("| :- | :- | :- | :- |")
|
214
|
+
end
|
215
|
+
|
216
|
+
param_tags.each do |tag|
|
217
|
+
f.puts("| `#{tag.name}` | `#{tag.types.join(', ')}` | #{pretty_default_value(tag, component)} | #{view_context.render(inline: tag.text)} |")
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
def each_component(&block)
|
222
|
+
manifest.components_with_docs.sort_by(&:name).each(&block)
|
223
|
+
end
|
224
|
+
|
225
|
+
def manifest
|
226
|
+
Primer::YARD::ComponentManifest
|
227
|
+
end
|
228
|
+
|
229
|
+
def source_url(component)
|
230
|
+
path = component.name.split("::").map(&:underscore).join("/")
|
231
|
+
|
232
|
+
"https://github.com/primer/view_components/tree/main/app/components/#{path}.rb"
|
233
|
+
end
|
234
|
+
|
235
|
+
def lookbook_url(component)
|
236
|
+
path = component.name.underscore.gsub("_component", "")
|
237
|
+
|
238
|
+
"https://primer.style/view-components/lookbook/inspect/#{path}/default/"
|
239
|
+
end
|
240
|
+
|
241
|
+
def example_path(component)
|
242
|
+
example_path = "../../src/@primer/gatsby-theme-doctocat/components/example"
|
243
|
+
example_path = "../#{example_path}" if status_module?(component)
|
244
|
+
example_path
|
245
|
+
end
|
246
|
+
|
247
|
+
def require_js_path(component)
|
248
|
+
require_js_path = "../../src/@primer/gatsby-theme-doctocat/components/requires-js-flash"
|
249
|
+
require_js_path = "../#{require_js_path}" if status_module?(component)
|
250
|
+
require_js_path
|
251
|
+
end
|
252
|
+
|
253
|
+
def preview_exists?(component)
|
254
|
+
path = component.name.underscore
|
255
|
+
|
256
|
+
File.exist?("previews/#{path}_preview.rb")
|
257
|
+
end
|
258
|
+
|
259
|
+
def status_module?(component)
|
260
|
+
(%w[Alpha Beta] & component.name.split("::")).any?
|
261
|
+
end
|
262
|
+
|
263
|
+
def parse_example_tag(tag)
|
264
|
+
self.class.parse_example_tag(tag)
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
# :nocov:
|
270
|
+
|
271
|
+
# rubocop:enable Naming/MethodParameterName
|