primer_view_components 0.0.39 → 0.0.40
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 +66 -2
- 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/avatar_stack_component.rb +5 -2
- data/app/components/primer/base_component.rb +2 -2
- data/app/components/primer/blankslate_component.rb +3 -3
- data/app/components/primer/button_group.rb +1 -1
- data/app/components/primer/component.rb +5 -0
- data/app/components/primer/counter_component.rb +6 -1
- data/app/components/primer/flex_component.rb +27 -0
- data/app/components/primer/flex_item_component.rb +1 -1
- data/app/components/primer/heading_component.rb +11 -18
- data/app/components/primer/hidden_text_expander.rb +1 -1
- data/app/components/primer/icon_button.rb +20 -3
- data/app/components/primer/image_crop.d.ts +1 -0
- data/app/components/primer/image_crop.html.erb +12 -0
- data/app/components/primer/image_crop.js +1 -0
- data/app/components/primer/image_crop.rb +36 -0
- data/app/components/primer/image_crop.ts +1 -0
- data/app/components/primer/{markdown_component.rb → markdown.rb} +5 -4
- data/app/components/primer/octicon_component.html.erb +7 -0
- data/app/components/primer/octicon_component.rb +15 -8
- data/app/components/primer/octicon_symbols_component.html.erb +3 -0
- data/app/components/primer/octicon_symbols_component.rb +61 -0
- data/app/components/primer/primer.d.ts +1 -0
- data/app/components/primer/primer.js +1 -0
- data/app/components/primer/primer.ts +1 -0
- data/app/components/primer/spinner_component.rb +2 -2
- data/app/components/primer/subhead_component.rb +34 -4
- data/app/components/primer/text_component.rb +5 -2
- data/app/lib/primer/classify.rb +1 -1
- data/app/lib/primer/classify/cache.rb +1 -1
- data/app/lib/primer/octicon/cache.rb +4 -0
- data/lib/primer/view_components.rb +1 -1
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/tasks/coverage.rake +14 -0
- data/lib/tasks/docs.rake +312 -0
- data/lib/tasks/statuses.rake +12 -0
- data/lib/yard/docs_helper.rb +57 -0
- data/static/statuses.json +52 -1
- metadata +15 -3
@@ -4,14 +4,18 @@ require "octicons"
|
|
4
4
|
|
5
5
|
module Primer
|
6
6
|
# `Octicon` renders an <%= link_to_octicons %> with <%= link_to_system_arguments_docs %>.
|
7
|
+
# `Octicon` can also be rendered with the `primer_octicon` helper, which accepts the same arguments.
|
7
8
|
class OcticonComponent < Primer::Component
|
8
9
|
status :beta
|
9
10
|
|
10
11
|
SIZE_DEFAULT = :small
|
12
|
+
SIZE_MEDIUM = :medium
|
13
|
+
SIZE_LARGE = :large
|
14
|
+
|
11
15
|
SIZE_MAPPINGS = {
|
12
16
|
SIZE_DEFAULT => 16,
|
13
|
-
|
14
|
-
|
17
|
+
SIZE_MEDIUM => 32,
|
18
|
+
SIZE_LARGE => 64
|
15
19
|
}.freeze
|
16
20
|
SIZE_OPTIONS = SIZE_MAPPINGS.keys
|
17
21
|
|
@@ -25,16 +29,21 @@ module Primer
|
|
25
29
|
# @example Large
|
26
30
|
# <%= render(Primer::OcticonComponent.new("x", size: :large)) %>
|
27
31
|
#
|
32
|
+
# @example Helper
|
33
|
+
# <%= primer_octicon("check") %>
|
34
|
+
#
|
28
35
|
# @param icon [String] Name of <%= link_to_octicons %> to use.
|
29
36
|
# @param size [Symbol] <%= one_of(Primer::OcticonComponent::SIZE_MAPPINGS) %>
|
37
|
+
# @param use_symbol [Boolean] EXPERIMENTAL (May change or be removed) - Set to true when using with <%= link_to_component(Primer::OcticonSymbolsComponent) %>.
|
30
38
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
31
|
-
def initialize(icon_name = nil, icon: nil, size: SIZE_DEFAULT, **system_arguments)
|
39
|
+
def initialize(icon_name = nil, icon: nil, size: SIZE_DEFAULT, use_symbol: false, **system_arguments)
|
32
40
|
icon_key = icon_name || icon
|
33
|
-
cache_key =
|
41
|
+
cache_key = Primer::Octicon::Cache.get_key(symbol: icon_key, size: size, **system_arguments.slice(:height, :width))
|
34
42
|
|
35
43
|
@system_arguments = system_arguments
|
36
44
|
@system_arguments[:tag] = :svg
|
37
45
|
@system_arguments[:aria] ||= {}
|
46
|
+
@use_symbol = use_symbol
|
38
47
|
|
39
48
|
if @system_arguments[:aria][:label] || @system_arguments[:"aria-label"]
|
40
49
|
@system_arguments[:role] = "img"
|
@@ -62,10 +71,8 @@ module Primer
|
|
62
71
|
@system_arguments.merge!(@icon.options.except(:class, :'aria-hidden'))
|
63
72
|
end
|
64
73
|
|
65
|
-
def
|
66
|
-
|
74
|
+
def self._after_compile
|
75
|
+
Primer::Octicon::Cache.preload!
|
67
76
|
end
|
68
|
-
|
69
|
-
Primer::Octicon::Cache.preload!
|
70
77
|
end
|
71
78
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "octicons"
|
4
|
+
|
5
|
+
module Primer
|
6
|
+
# OcticonSymbols renders a symbol dictionary using a list of <%= link_to_octicons %>.
|
7
|
+
class OcticonSymbolsComponent < Primer::Component
|
8
|
+
# @example Symbol dictionary
|
9
|
+
# <%= render(Primer::OcticonComponent.new(icon: :check, use_symbol: true, color: :icon_success)) %>
|
10
|
+
# <%= render(Primer::OcticonComponent.new(icon: :check, use_symbol: true, color: :text_danger)) %>
|
11
|
+
# <%= render(Primer::OcticonComponent.new(icon: :check, use_symbol: true, size: :medium)) %>
|
12
|
+
# <%= render(Primer::OcticonSymbolsComponent.new(icons: [{ symbol: :check }, { symbol: :check, size: :medium }])) %>
|
13
|
+
#
|
14
|
+
# @param icons [Array<Hash>] List of icons to render, in the format { symbol: :icon_name, size: :small }
|
15
|
+
def initialize(icons: [])
|
16
|
+
@icons = {}
|
17
|
+
icons.each do |icon|
|
18
|
+
symbol = icon[:symbol]
|
19
|
+
size = Primer::OcticonComponent::SIZE_MAPPINGS[
|
20
|
+
fetch_or_fallback(Primer::OcticonComponent::SIZE_OPTIONS, icon[:size] || Primer::OcticonComponent::SIZE_DEFAULT, Primer::OcticonComponent::SIZE_DEFAULT)
|
21
|
+
]
|
22
|
+
|
23
|
+
cache_key = Primer::Octicon::Cache.get_key(symbol: symbol, size: size)
|
24
|
+
|
25
|
+
if (cache_icon = Primer::Octicon::Cache.read(cache_key))
|
26
|
+
icon_instance = cache_icon
|
27
|
+
else
|
28
|
+
icon_instance = Octicons::Octicon.new(symbol, height: size)
|
29
|
+
|
30
|
+
Primer::Octicon::Cache.set(cache_key, icon_instance)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Don't put the same icon twice
|
34
|
+
@icons[[symbol, icon_instance.height]] = icon_instance if @icons[[symbol, icon_instance.height]].nil?
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def render?
|
39
|
+
@icons.any?
|
40
|
+
end
|
41
|
+
|
42
|
+
def self._after_compile
|
43
|
+
Primer::Octicon::Cache.preload!
|
44
|
+
end
|
45
|
+
|
46
|
+
def symbol_tags
|
47
|
+
safe_join(
|
48
|
+
@icons.values.map do |icon|
|
49
|
+
content_tag(
|
50
|
+
:symbol,
|
51
|
+
icon.path.html_safe, # rubocop:disable Rails/OutputSafety
|
52
|
+
id: "octicon_#{icon.symbol}_#{icon.height}",
|
53
|
+
viewBox: icon.options[:viewBox],
|
54
|
+
width: icon.width,
|
55
|
+
height: icon.height
|
56
|
+
)
|
57
|
+
end
|
58
|
+
)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -27,10 +27,10 @@ module Primer
|
|
27
27
|
# <%= render(Primer::SpinnerComponent.new(size: :large)) %>
|
28
28
|
#
|
29
29
|
# @param size [Symbol] <%= one_of(Primer::SpinnerComponent::SIZE_MAPPINGS) %>
|
30
|
-
def initialize(size: DEFAULT_SIZE, **system_arguments)
|
30
|
+
def initialize(size: DEFAULT_SIZE, style: DEFAULT_STYLE, **system_arguments)
|
31
31
|
@system_arguments = system_arguments
|
32
32
|
@system_arguments[:tag] = :svg
|
33
|
-
@system_arguments[:style] ||=
|
33
|
+
@system_arguments[:style] ||= style
|
34
34
|
@system_arguments[:animation] = :rotate
|
35
35
|
@system_arguments[:width] = SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)]
|
36
36
|
@system_arguments[:height] = SIZE_MAPPINGS[fetch_or_fallback(SIZE_OPTIONS, size, DEFAULT_SIZE)]
|
@@ -1,16 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Primer
|
4
|
-
# Use `Subhead`
|
4
|
+
# Use `Subhead` as the start of a section. The `:heading` slot will render an `<h2>` font-sized text.
|
5
|
+
#
|
6
|
+
# - Optionally set the `:description` slot to render a short description and the `:actions` slot for a related action.
|
7
|
+
# - Use a succint, one-line description for the `:description` slot. For longer descriptions, omit the description slot and render a paragraph below the `Subhead`.
|
8
|
+
# - Use the actions slot to render a related action to the right of the heading. Use <%= link_to_component(Primer::ButtonComponent) %> or <%= link_to_component(Primer::LinkComponent) %>.
|
9
|
+
#
|
10
|
+
# @accessibility
|
11
|
+
# The `:heading` slot defaults to rendering a `<div>`. Update the tag to a heading element with the appropriate level to improve page navigation for assistive technologies.
|
12
|
+
# <%= link_to_heading_practices %>
|
5
13
|
class SubheadComponent < Primer::Component
|
6
14
|
status :beta
|
7
15
|
|
16
|
+
DEFAULT_HEADING_TAG = :div
|
17
|
+
HEADING_TAG_OPTIONS = [DEFAULT_HEADING_TAG, :h1, :h2, :h3, :h4, :h5, :h6].freeze
|
18
|
+
|
8
19
|
# The heading
|
9
20
|
#
|
21
|
+
# @param tag [Symbol] <%= one_of(Primer::SubheadComponent::HEADING_TAG_OPTIONS)%>
|
10
22
|
# @param danger [Boolean] Whether to style the heading as dangerous.
|
11
23
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
12
|
-
renders_one :heading, lambda { |danger: false, **system_arguments|
|
13
|
-
system_arguments[:tag]
|
24
|
+
renders_one :heading, lambda { |tag: DEFAULT_HEADING_TAG, danger: false, **system_arguments|
|
25
|
+
system_arguments[:tag] = fetch_or_fallback(HEADING_TAG_OPTIONS, tag, DEFAULT_HEADING_TAG)
|
14
26
|
system_arguments[:classes] = class_names(
|
15
27
|
system_arguments[:classes],
|
16
28
|
"Subhead-heading",
|
@@ -42,7 +54,7 @@ module Primer
|
|
42
54
|
|
43
55
|
# @example Default
|
44
56
|
# <%= render(Primer::SubheadComponent.new) do |component| %>
|
45
|
-
# <% component.heading do %>
|
57
|
+
# <% component.heading(tag: :h3) do %>
|
46
58
|
# My Heading
|
47
59
|
# <% end %>
|
48
60
|
# <% component.description do %>
|
@@ -50,6 +62,24 @@ module Primer
|
|
50
62
|
# <% end %>
|
51
63
|
# <% end %>
|
52
64
|
#
|
65
|
+
# @example With dangerous heading
|
66
|
+
# <%= render(Primer::SubheadComponent.new) do |component| %>
|
67
|
+
# <% component.heading(tag: :h3, danger: true) do %>
|
68
|
+
# My Heading
|
69
|
+
# <% end %>
|
70
|
+
# <% component.description do %>
|
71
|
+
# My Description
|
72
|
+
# <% end %>
|
73
|
+
# <% end %>
|
74
|
+
#
|
75
|
+
# @example With long description
|
76
|
+
# <%= render(Primer::SubheadComponent.new) do |component| %>
|
77
|
+
# <% component.heading(tag: :h3) do %>
|
78
|
+
# My Heading
|
79
|
+
# <% end %>
|
80
|
+
# <% end %>
|
81
|
+
# <p> This is a longer description that is sitting below the Subhead. It's much longer than a description that could sit comfortably in the Subhead. </p>
|
82
|
+
#
|
53
83
|
# @example Without border
|
54
84
|
# <%= render(Primer::SubheadComponent.new(hide_border: true)) do |component| %>
|
55
85
|
# <% component.heading do %>
|
@@ -5,14 +5,17 @@ module Primer
|
|
5
5
|
class TextComponent < Primer::Component
|
6
6
|
status :beta
|
7
7
|
|
8
|
+
DEFAULT_TAG = :span
|
9
|
+
|
8
10
|
# @example Default
|
9
11
|
# <%= render(Primer::TextComponent.new(tag: :p, font_weight: :bold)) { "Bold Text" } %>
|
10
12
|
# <%= render(Primer::TextComponent.new(tag: :p, color: :text_danger)) { "Danger Text" } %>
|
11
13
|
#
|
14
|
+
# @param tag [Symbol]
|
12
15
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
13
|
-
def initialize(**system_arguments)
|
16
|
+
def initialize(tag: DEFAULT_TAG, **system_arguments)
|
14
17
|
@system_arguments = system_arguments
|
15
|
-
@system_arguments[:tag]
|
18
|
+
@system_arguments[:tag] = tag
|
16
19
|
end
|
17
20
|
|
18
21
|
def call
|
data/app/lib/primer/classify.rb
CHANGED
@@ -207,7 +207,7 @@ module Primer
|
|
207
207
|
elsif Primer::Classify::Flex::KEYS.include?(key)
|
208
208
|
memo[:classes] << Primer::Classify::Flex.classes(key, val, breakpoint)
|
209
209
|
elsif key == WIDTH_KEY || key == HEIGHT_KEY
|
210
|
-
if val == :fit
|
210
|
+
if val == :fit
|
211
211
|
memo[:classes] << "#{key}-#{val}"
|
212
212
|
else
|
213
213
|
memo[key] = val
|
@@ -9,6 +9,10 @@ module Primer
|
|
9
9
|
PRELOADED_ICONS = [:alert, :check, :"chevron-down", :clippy, :clock, :"dot-fill", :info, :"kebab-horizontal", :link, :lock, :mail, :pencil, :plus, :question, :repo, :search, :"shield-lock", :star, :trash, :x].freeze
|
10
10
|
|
11
11
|
class << self
|
12
|
+
def get_key(symbol:, size:, width: nil, height: nil)
|
13
|
+
[symbol, size, width, height].join("_")
|
14
|
+
end
|
15
|
+
|
12
16
|
def read(key)
|
13
17
|
LOOKUP[key]
|
14
18
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :coverage do
|
4
|
+
task :report do
|
5
|
+
require "simplecov"
|
6
|
+
require "simplecov-console"
|
7
|
+
|
8
|
+
SimpleCov.minimum_coverage 100
|
9
|
+
|
10
|
+
SimpleCov.collate Dir["simplecov-resultset-*/.resultset.json"], "rails" do
|
11
|
+
formatter SimpleCov::Formatter::Console
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/tasks/docs.rake
ADDED
@@ -0,0 +1,312 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :docs do
|
4
|
+
task :livereload do
|
5
|
+
require "listen"
|
6
|
+
|
7
|
+
Rake::Task["docs:build"].execute
|
8
|
+
|
9
|
+
puts "Listening for changes to documentation..."
|
10
|
+
|
11
|
+
listener = Listen.to("app") do |modified, added, removed|
|
12
|
+
puts "modified absolute path: #{modified}"
|
13
|
+
puts "added absolute path: #{added}"
|
14
|
+
puts "removed absolute path: #{removed}"
|
15
|
+
|
16
|
+
Rake::Task["docs:build"].execute
|
17
|
+
end
|
18
|
+
listener.start # not blocking
|
19
|
+
sleep
|
20
|
+
end
|
21
|
+
|
22
|
+
task :build do
|
23
|
+
require File.expand_path("./../../demo/config/environment.rb", __dir__)
|
24
|
+
require "primer/view_components"
|
25
|
+
require "yard/docs_helper"
|
26
|
+
require "view_component/test_helpers"
|
27
|
+
include ViewComponent::TestHelpers
|
28
|
+
include Primer::ViewHelper
|
29
|
+
include YARD::DocsHelper
|
30
|
+
|
31
|
+
Dir["./app/components/primer/**/*.rb"].sort.each { |file| require file }
|
32
|
+
|
33
|
+
YARD::Rake::YardocTask.new
|
34
|
+
|
35
|
+
# Custom tags for yard
|
36
|
+
YARD::Tags::Library.define_tag("Accessibility", :accessibility)
|
37
|
+
YARD::Tags::Library.define_tag("Deprecation", :deprecation)
|
38
|
+
|
39
|
+
puts "Building YARD documentation."
|
40
|
+
Rake::Task["yard"].execute
|
41
|
+
|
42
|
+
puts "Converting YARD documentation to Markdown files."
|
43
|
+
|
44
|
+
# Rails controller for rendering arbitrary ERB
|
45
|
+
view_context = ApplicationController.new.tap { |c| c.request = ActionDispatch::TestRequest.create }.view_context
|
46
|
+
|
47
|
+
registry = YARD::RegistryStore.new
|
48
|
+
registry.load!(".yardoc")
|
49
|
+
components = [
|
50
|
+
Primer::OcticonSymbolsComponent,
|
51
|
+
Primer::ImageCrop,
|
52
|
+
Primer::IconButton,
|
53
|
+
Primer::AutoComplete,
|
54
|
+
Primer::AutoComplete::Item,
|
55
|
+
Primer::AvatarComponent,
|
56
|
+
Primer::AvatarStackComponent,
|
57
|
+
Primer::BaseButton,
|
58
|
+
Primer::BlankslateComponent,
|
59
|
+
Primer::BorderBoxComponent,
|
60
|
+
Primer::BoxComponent,
|
61
|
+
Primer::BreadcrumbComponent,
|
62
|
+
Primer::ButtonComponent,
|
63
|
+
Primer::ButtonGroup,
|
64
|
+
Primer::ButtonMarketingComponent,
|
65
|
+
Primer::ClipboardCopy,
|
66
|
+
Primer::CloseButton,
|
67
|
+
Primer::CounterComponent,
|
68
|
+
Primer::DetailsComponent,
|
69
|
+
Primer::DropdownComponent,
|
70
|
+
Primer::DropdownMenuComponent,
|
71
|
+
Primer::FlashComponent,
|
72
|
+
Primer::FlexComponent,
|
73
|
+
Primer::FlexItemComponent,
|
74
|
+
Primer::HeadingComponent,
|
75
|
+
Primer::HiddenTextExpander,
|
76
|
+
Primer::LabelComponent,
|
77
|
+
Primer::LayoutComponent,
|
78
|
+
Primer::LinkComponent,
|
79
|
+
Primer::Markdown,
|
80
|
+
Primer::MenuComponent,
|
81
|
+
Primer::Navigation::TabComponent,
|
82
|
+
Primer::OcticonComponent,
|
83
|
+
Primer::PopoverComponent,
|
84
|
+
Primer::ProgressBarComponent,
|
85
|
+
Primer::StateComponent,
|
86
|
+
Primer::SpinnerComponent,
|
87
|
+
Primer::SubheadComponent,
|
88
|
+
Primer::TabContainerComponent,
|
89
|
+
Primer::TabNavComponent,
|
90
|
+
Primer::TextComponent,
|
91
|
+
Primer::TimeAgoComponent,
|
92
|
+
Primer::TimelineItemComponent,
|
93
|
+
Primer::TooltipComponent,
|
94
|
+
Primer::Truncate,
|
95
|
+
Primer::UnderlineNavComponent
|
96
|
+
]
|
97
|
+
|
98
|
+
js_components = [
|
99
|
+
Primer::ImageCrop,
|
100
|
+
Primer::AutoComplete,
|
101
|
+
Primer::ClipboardCopy,
|
102
|
+
Primer::TabContainerComponent,
|
103
|
+
Primer::TabNavComponent,
|
104
|
+
Primer::TimeAgoComponent,
|
105
|
+
Primer::UnderlineNavComponent
|
106
|
+
]
|
107
|
+
|
108
|
+
all_components = Primer::Component.descendants - [Primer::BaseComponent]
|
109
|
+
components_needing_docs = all_components - components
|
110
|
+
|
111
|
+
components_without_examples = []
|
112
|
+
args_for_components = []
|
113
|
+
classes_found_in_examples = []
|
114
|
+
|
115
|
+
components.each do |component|
|
116
|
+
documentation = registry.get(component.name)
|
117
|
+
|
118
|
+
# Primer::AvatarComponent => Avatar
|
119
|
+
short_name = component.name.gsub(/Primer|::|Component/, "")
|
120
|
+
|
121
|
+
path = Pathname.new("docs/content/components/#{short_name.downcase}.md")
|
122
|
+
path.dirname.mkdir unless path.dirname.exist?
|
123
|
+
File.open(path, "w") do |f|
|
124
|
+
f.puts("---")
|
125
|
+
f.puts("title: #{short_name}")
|
126
|
+
f.puts("status: #{component.status.to_s.capitalize}")
|
127
|
+
f.puts("source: https://github.com/primer/view_components/tree/main/app/components/primer/#{component.to_s.demodulize.underscore}.rb")
|
128
|
+
f.puts("storybook: https://primer.style/view-components/stories/?path=/story/primer-#{short_name.underscore.dasherize}-component")
|
129
|
+
f.puts("---")
|
130
|
+
f.puts
|
131
|
+
f.puts("import Example from '../../src/@primer/gatsby-theme-doctocat/components/example'")
|
132
|
+
|
133
|
+
if js_components.include?(component)
|
134
|
+
f.puts("import RequiresJSFlash from '../../src/@primer/gatsby-theme-doctocat/components/requires-js-flash'")
|
135
|
+
f.puts
|
136
|
+
f.puts("<RequiresJSFlash />")
|
137
|
+
end
|
138
|
+
|
139
|
+
f.puts
|
140
|
+
f.puts("<!-- Warning: AUTO-GENERATED file, do not edit. Add code comments to your Ruby instead <3 -->")
|
141
|
+
f.puts
|
142
|
+
f.puts(view_context.render(inline: documentation.base_docstring))
|
143
|
+
|
144
|
+
if documentation.tags(:accessibility).any?
|
145
|
+
f.puts
|
146
|
+
f.puts("## Accessibility")
|
147
|
+
documentation.tags(:accessibility).each do |tag|
|
148
|
+
f.puts
|
149
|
+
f.puts view_context.render(inline: tag.text)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
if documentation.tags(:deprecated).any?
|
154
|
+
f.puts
|
155
|
+
f.puts("## Deprecation")
|
156
|
+
documentation.tags(:deprecated).each do |tag|
|
157
|
+
f.puts
|
158
|
+
f.puts view_context.render(inline: tag.text)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
initialize_method = documentation.meths.find(&:constructor?)
|
163
|
+
|
164
|
+
if initialize_method.tags(:example).any?
|
165
|
+
f.puts
|
166
|
+
f.puts("## Examples")
|
167
|
+
else
|
168
|
+
components_without_examples << component
|
169
|
+
end
|
170
|
+
|
171
|
+
initialize_method.tags(:example).each do |tag|
|
172
|
+
(name, description) = tag.name.split("|")
|
173
|
+
f.puts
|
174
|
+
f.puts("### #{name}")
|
175
|
+
if description
|
176
|
+
f.puts
|
177
|
+
f.puts(description)
|
178
|
+
end
|
179
|
+
f.puts
|
180
|
+
html = view_context.render(inline: tag.text)
|
181
|
+
html.scan(/class="([^"]*)"/) do |classnames|
|
182
|
+
classes_found_in_examples.concat(classnames[0].split(" ").reject { |c| c.starts_with?("octicon", "js", "my-") }.map { ".#{_1}"})
|
183
|
+
end
|
184
|
+
f.puts("<Example src=\"#{html.tr('"', "\'").delete("\n")}\" />")
|
185
|
+
f.puts
|
186
|
+
f.puts("```erb")
|
187
|
+
f.puts(tag.text.to_s)
|
188
|
+
f.puts("```")
|
189
|
+
end
|
190
|
+
|
191
|
+
params = initialize_method.tags(:param)
|
192
|
+
if params.any?
|
193
|
+
f.puts
|
194
|
+
f.puts("## Arguments")
|
195
|
+
f.puts
|
196
|
+
f.puts("| Name | Type | Default | Description |")
|
197
|
+
f.puts("| :- | :- | :- | :- |")
|
198
|
+
|
199
|
+
args = []
|
200
|
+
params.each do |tag|
|
201
|
+
params = tag.object.parameters.find { |param| [tag.name.to_s, tag.name.to_s + ":"].include?(param[0]) }
|
202
|
+
|
203
|
+
default =
|
204
|
+
if params && params[1]
|
205
|
+
constant_name = "#{component.name}::#{params[1]}"
|
206
|
+
constant_value = constant_name.safe_constantize
|
207
|
+
if constant_value.nil?
|
208
|
+
pretty_value(params[1])
|
209
|
+
else
|
210
|
+
pretty_value(constant_value)
|
211
|
+
end
|
212
|
+
else
|
213
|
+
"N/A"
|
214
|
+
end
|
215
|
+
|
216
|
+
args << {
|
217
|
+
"name" => tag.name,
|
218
|
+
"type" => tag.types.join(", "),
|
219
|
+
"default" => default,
|
220
|
+
"description" => view_context.render(inline: tag.text)
|
221
|
+
}
|
222
|
+
|
223
|
+
f.puts("| `#{tag.name}` | `#{tag.types.join(', ')}` | #{default} | #{view_context.render(inline: tag.text)} |")
|
224
|
+
end
|
225
|
+
|
226
|
+
component_args = {
|
227
|
+
"component" => short_name,
|
228
|
+
"source" => "https://github.com/primer/view_components/tree/main/app/components/primer/#{component.to_s.demodulize.underscore}.rb",
|
229
|
+
"parameters" => args
|
230
|
+
}
|
231
|
+
|
232
|
+
args_for_components << component_args
|
233
|
+
end
|
234
|
+
|
235
|
+
# Slots V2 docs
|
236
|
+
slot_v2_methods = documentation.meths.select { |x| x[:renders_one] || x[:renders_many] }
|
237
|
+
|
238
|
+
if slot_v2_methods.any?
|
239
|
+
f.puts
|
240
|
+
f.puts("## Slots")
|
241
|
+
|
242
|
+
slot_v2_methods.each do |slot_documentation|
|
243
|
+
f.puts
|
244
|
+
f.puts("### `#{slot_documentation.name.to_s.capitalize}`")
|
245
|
+
|
246
|
+
if slot_documentation.base_docstring.present?
|
247
|
+
f.puts
|
248
|
+
f.puts(view_context.render(inline: slot_documentation.base_docstring))
|
249
|
+
end
|
250
|
+
|
251
|
+
param_tags = slot_documentation.tags(:param)
|
252
|
+
if param_tags.any?
|
253
|
+
f.puts
|
254
|
+
f.puts("| Name | Type | Default | Description |")
|
255
|
+
f.puts("| :- | :- | :- | :- |")
|
256
|
+
end
|
257
|
+
|
258
|
+
param_tags.each do |tag|
|
259
|
+
params = tag.object.parameters.find { |param| [tag.name.to_s, tag.name.to_s + ":"].include?(param[0]) }
|
260
|
+
|
261
|
+
default =
|
262
|
+
if params && params[1]
|
263
|
+
"`#{params[1]}`"
|
264
|
+
else
|
265
|
+
"N/A"
|
266
|
+
end
|
267
|
+
|
268
|
+
f.puts("| `#{tag.name}` | `#{tag.types.join(', ')}` | #{default} | #{view_context.render(inline: tag.text)} |")
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
File.open("static/classes.yml", "w") do |f|
|
276
|
+
f.puts YAML.dump(classes_found_in_examples.sort.uniq)
|
277
|
+
end
|
278
|
+
|
279
|
+
File.open("static/arguments.yml", "w") do |f|
|
280
|
+
f.puts YAML.dump(args_for_components)
|
281
|
+
end
|
282
|
+
|
283
|
+
# Build system arguments docs from BaseComponent
|
284
|
+
documentation = registry.get(Primer::BaseComponent.name)
|
285
|
+
File.open("docs/content/system-arguments.md", "w") do |f|
|
286
|
+
f.puts("---")
|
287
|
+
f.puts("title: System arguments")
|
288
|
+
f.puts("---")
|
289
|
+
f.puts
|
290
|
+
f.puts("<!-- Warning: AUTO-GENERATED file, do not edit. Add code comments to your Ruby instead <3 -->")
|
291
|
+
f.puts
|
292
|
+
f.puts(documentation.base_docstring)
|
293
|
+
f.puts
|
294
|
+
|
295
|
+
initialize_method = documentation.meths.find(&:constructor?)
|
296
|
+
|
297
|
+
f.puts(view_context.render(inline: initialize_method.base_docstring))
|
298
|
+
end
|
299
|
+
|
300
|
+
puts "Markdown compiled."
|
301
|
+
|
302
|
+
if components_without_examples.any?
|
303
|
+
puts
|
304
|
+
puts "The following components have no examples defined: #{components_without_examples.map(&:name).join(', ')}. Consider adding an example?"
|
305
|
+
end
|
306
|
+
|
307
|
+
if components_needing_docs.any?
|
308
|
+
puts
|
309
|
+
puts "The following components needs docs. Care to contribute them? #{components_needing_docs.map(&:name).join(', ')}"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|