primer_view_components 0.0.42 → 0.0.43
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -0
- data/app/components/primer/base_component.rb +5 -1
- data/app/components/primer/blankslate_component.html.erb +1 -0
- data/app/components/primer/blankslate_component.rb +62 -44
- data/app/components/primer/tab_nav_component.html.erb +1 -1
- data/app/components/primer/tab_nav_component.rb +19 -1
- data/app/components/primer/underline_nav_component.html.erb +1 -1
- data/app/components/primer/underline_nav_component.rb +17 -1
- data/app/lib/primer/classify.rb +8 -3
- data/app/lib/primer/classify/cache.rb +15 -0
- data/app/lib/primer/classify/grid.rb +45 -0
- data/app/lib/primer/tabbed_component_helper.rb +2 -2
- data/lib/primer/view_components/linters.rb +3 -0
- data/lib/primer/view_components/linters/button_component_migration_counter.rb +16 -0
- data/lib/primer/view_components/linters/flash_component_migration_counter.rb +16 -0
- data/lib/primer/view_components/linters/helpers.rb +89 -0
- data/lib/primer/view_components/version.rb +1 -1
- data/lib/tasks/docs.rake +54 -32
- data/static/statuses.json +1 -1
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78f4fe987616d5ab746cbd6d6a0bc79ff62ecf33eb2365af3ade87a43461d35b
|
4
|
+
data.tar.gz: 2813cf307508ab9a413d98edbba54e1d7735dc86e853a42587487c7293d42571
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b13c5c05eb27d2a635396d736daa1039024bef7ddb456d20ec037f2cf270e8cb157ebd40619ca8eed4523e9cde5af2d990b428dc2ff663a42faaf01070a48ef
|
7
|
+
data.tar.gz: c5e126fcf17bf04914a2ea054ca181991a8cb0fda5659753c4e03241e6dfcfe8a5e876b915033cfc69b11c10b3b7946403fdb86f1fca3161eb1278eae29d99ed
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,52 @@
|
|
2
2
|
|
3
3
|
## main
|
4
4
|
|
5
|
+
## 0.0.43
|
6
|
+
|
7
|
+
* Upgrade primer/css to 17.2.1
|
8
|
+
|
9
|
+
*Jon Rohan*
|
10
|
+
|
11
|
+
### New
|
12
|
+
|
13
|
+
* Add `clearfix` and `container` system arguments.
|
14
|
+
|
15
|
+
*Manuel Puyol*
|
16
|
+
|
17
|
+
### Updates
|
18
|
+
|
19
|
+
* Promote `TabNav` component to beta.
|
20
|
+
|
21
|
+
*Manuel Puyol*
|
22
|
+
|
23
|
+
* Allow customizing `TabContainer` when using `TabNav` and `UnderlineNav` components.
|
24
|
+
|
25
|
+
*Manuel Puyol*
|
26
|
+
|
27
|
+
### Breaking changes
|
28
|
+
|
29
|
+
* Restrict `col` system arguments to only accept values between 1 and 12.
|
30
|
+
|
31
|
+
*Manuel Puyol*
|
32
|
+
|
33
|
+
### Misc
|
34
|
+
|
35
|
+
* Raise an error if `class` is used as a system argument.
|
36
|
+
|
37
|
+
*Manuel Puyol*
|
38
|
+
|
39
|
+
* Don't commit auto-generated component previews.
|
40
|
+
|
41
|
+
*Kate Higa*
|
42
|
+
|
43
|
+
* Provide linters for component migrations.
|
44
|
+
|
45
|
+
*Manuel Puyol*
|
46
|
+
|
47
|
+
* Update docs to accept multiline descriptions.
|
48
|
+
|
49
|
+
*Manuel Puyol*
|
50
|
+
|
5
51
|
## 0.0.42
|
6
52
|
|
7
53
|
### New
|
@@ -83,7 +83,9 @@ module Primer
|
|
83
83
|
#
|
84
84
|
# | Name | Type | Description |
|
85
85
|
# | :- | :- | :- |
|
86
|
-
# | `
|
86
|
+
# | `clearfix` | Boolean | Wether to assign the `clearfix` class. |
|
87
|
+
# | `col` | Integer | Number of columns. <%= one_of(Primer::Classify::Grid::COL_VALUES) %> |
|
88
|
+
# | `container` | Symbol | Size of the container. <%= one_of(Primer::Classify::Grid::CONTAINER_VALUES) %> |
|
87
89
|
#
|
88
90
|
# ## Layout
|
89
91
|
#
|
@@ -148,6 +150,8 @@ module Primer
|
|
148
150
|
@tag = tag
|
149
151
|
@system_arguments = system_arguments
|
150
152
|
|
153
|
+
raise ArgumentError, "`class` is an invalid argument. Use `classes` instead." if system_arguments.key?(:class) && !Rails.env.production?
|
154
|
+
|
151
155
|
@result = Primer::Classify.call(**@system_arguments.merge(classes: classes))
|
152
156
|
|
153
157
|
@system_arguments[:"data-view-component"] = true
|
@@ -23,56 +23,74 @@ module Primer
|
|
23
23
|
# description: "Description",
|
24
24
|
# ) %>
|
25
25
|
#
|
26
|
-
# @example Icon
|
27
|
-
#
|
28
|
-
# icon
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
26
|
+
# @example Icon
|
27
|
+
# @description
|
28
|
+
# Add an `icon` to give additional context. Refer to the [Octicons](https://primer.style/octicons/) documentation to choose an icon.
|
29
|
+
# @code
|
30
|
+
# <%= render Primer::BlankslateComponent.new(
|
31
|
+
# icon: :globe,
|
32
|
+
# title: "Title",
|
33
|
+
# description: "Description",
|
34
|
+
# ) %>
|
32
35
|
#
|
33
|
-
# @example Loading
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
36
|
+
# @example Loading
|
37
|
+
# @description
|
38
|
+
# Add a [SpinnerComponent](https://primer.style/view-components/components/spinner) to the blankslate in place of an icon.
|
39
|
+
# @code
|
40
|
+
# <%= render Primer::BlankslateComponent.new(
|
41
|
+
# title: "Title",
|
42
|
+
# description: "Description",
|
43
|
+
# ) do |component| %>
|
44
|
+
# <% component.spinner(size: :large) %>
|
45
|
+
# <% end %>
|
40
46
|
#
|
41
|
-
# @example Custom content
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
+
# @example Custom content
|
48
|
+
# @description
|
49
|
+
# Pass custom content as a block in place of `description`.
|
50
|
+
# @code
|
51
|
+
# <%= render Primer::BlankslateComponent.new(
|
52
|
+
# title: "Title",
|
53
|
+
# ) do %>
|
54
|
+
# <em>Your custom content here</em>
|
55
|
+
# <% end %>
|
47
56
|
#
|
48
|
-
# @example Action button
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
57
|
+
# @example Action button
|
58
|
+
# @description
|
59
|
+
# Provide a button to guide users to take action from the blankslate. The button appears below the description and custom content.
|
60
|
+
# @code
|
61
|
+
# <%= render Primer::BlankslateComponent.new(
|
62
|
+
# icon: :book,
|
63
|
+
# title: "Welcome to the mona wiki!",
|
64
|
+
# description: "Wikis provide a place in your repository to lay out the roadmap of your project, show the current status, and document software better, together.",
|
53
65
|
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
66
|
+
# button_text: "Create the first page",
|
67
|
+
# button_url: "https://github.com/monalisa/mona/wiki/_new",
|
68
|
+
# ) %>
|
57
69
|
#
|
58
|
-
# @example Link
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
64
|
-
#
|
65
|
-
#
|
70
|
+
# @example Link
|
71
|
+
# @description
|
72
|
+
# Add an additional link to help users learn more about a feature. The link will be shown at the very bottom:
|
73
|
+
# @code
|
74
|
+
# <%= render Primer::BlankslateComponent.new(
|
75
|
+
# icon: :book,
|
76
|
+
# title: "Welcome to the mona wiki!",
|
77
|
+
# description: "Wikis provide a place in your repository to lay out the roadmap of your project, show the current status, and document software better, together.",
|
78
|
+
# link_text: "Learn more about wikis",
|
79
|
+
# link_url: "https://docs.github.com/en/github/building-a-strong-community/about-wikis",
|
80
|
+
# ) %>
|
66
81
|
#
|
67
|
-
# @example Variations
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
82
|
+
# @example Variations
|
83
|
+
# @description
|
84
|
+
# There are a few variations of how the Blankslate appears: `narrow` adds a maximum width, `large` increases the font size, and `spacious` adds extra padding.
|
85
|
+
# @code
|
86
|
+
# <%= render Primer::BlankslateComponent.new(
|
87
|
+
# icon: :book,
|
88
|
+
# title: "Welcome to the mona wiki!",
|
89
|
+
# description: "Wikis provide a place in your repository to lay out the roadmap of your project, show the current status, and document software better, together.",
|
90
|
+
# narrow: true,
|
91
|
+
# large: true,
|
92
|
+
# spacious: true,
|
93
|
+
# ) %>
|
76
94
|
#
|
77
95
|
# @param title [String] Text that appears in a larger bold font.
|
78
96
|
# @param title_tag [Symbol] HTML tag to use for title.
|
@@ -5,6 +5,8 @@ module Primer
|
|
5
5
|
class TabNavComponent < Primer::Component
|
6
6
|
include Primer::TabbedComponentHelper
|
7
7
|
|
8
|
+
status :beta
|
9
|
+
|
8
10
|
DEFAULT_EXTRA_ALIGN = :left
|
9
11
|
EXTRA_ALIGN_OPTIONS = [DEFAULT_EXTRA_ALIGN, :right].freeze
|
10
12
|
|
@@ -102,15 +104,31 @@ module Primer
|
|
102
104
|
# <% end %>
|
103
105
|
# <% end %>
|
104
106
|
#
|
107
|
+
# @example Customizing the body
|
108
|
+
# <%= render(Primer::TabNavComponent.new(label: "Default", body_arguments: { classes: "custom-class", border: true, border_color: :info })) do |c| %>
|
109
|
+
# <% c.tab(selected: true, href: "#") { "Tab 1" }%>
|
110
|
+
# <% c.tab(href: "#") { "Tab 2" } %>
|
111
|
+
# <% c.tab(href: "#") { "Tab 3" } %>
|
112
|
+
# <% end %>
|
113
|
+
#
|
114
|
+
# @example Customizing the wrapper
|
115
|
+
# <%= render(Primer::TabNavComponent.new(label: "Default", wrapper_arguments: { classes: "custom-class", border: true, border_color: :info })) do |c| %>
|
116
|
+
# <% c.tab(selected: true, href: "#") { "Tab 1" }%>
|
117
|
+
# <% c.tab(href: "#") { "Tab 2" } %>
|
118
|
+
# <% c.tab(href: "#") { "Tab 3" } %>
|
119
|
+
# <% end %>
|
120
|
+
#
|
105
121
|
# @param label [String] Used to set the `aria-label` on the top level `<nav>` element.
|
106
122
|
# @param with_panel [Boolean] Whether the TabNav should navigate through pages or panels.
|
107
123
|
# @param body_arguments [Hash] <%= link_to_system_arguments_docs %> for the body wrapper.
|
124
|
+
# @param wrapper_arguments [Hash] <%= link_to_system_arguments_docs %> for the `TabContainer` wrapper. Only applies if `with_panel` is `true`.
|
108
125
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
109
|
-
def initialize(label:, with_panel: false, body_arguments: {}, **system_arguments)
|
126
|
+
def initialize(label:, with_panel: false, body_arguments: {}, wrapper_arguments: {}, **system_arguments)
|
110
127
|
@align = DEFAULT_EXTRA_ALIGN
|
111
128
|
@with_panel = with_panel
|
112
129
|
@system_arguments = system_arguments
|
113
130
|
@body_arguments = body_arguments
|
131
|
+
@wrapper_arguments = wrapper_arguments
|
114
132
|
|
115
133
|
@system_arguments[:tag] ||= :div
|
116
134
|
@system_arguments[:classes] = class_names(
|
@@ -116,14 +116,30 @@ module Primer
|
|
116
116
|
# <% end %>
|
117
117
|
# <% end %>
|
118
118
|
#
|
119
|
+
# @example Customizing the body
|
120
|
+
# <%= render(Primer::UnderlineNavComponent.new(label: "Default", body_arguments: { tag: :ul, classes: "custom-class", border: true, border_color: :info })) do |c| %>
|
121
|
+
# <% c.tab(selected: true, href: "#") { "Tab 1" }%>
|
122
|
+
# <% c.tab(href: "#") { "Tab 2" } %>
|
123
|
+
# <% c.tab(href: "#") { "Tab 3" } %>
|
124
|
+
# <% end %>
|
125
|
+
#
|
126
|
+
# @example Customizing the wrapper
|
127
|
+
# <%= render(Primer::UnderlineNavComponent.new(label: "Default", wrapper_arguments: { classes: "custom-class", border: true, border_color: :info })) do |c| %>
|
128
|
+
# <% c.tab(selected: true, href: "#") { "Tab 1" }%>
|
129
|
+
# <% c.tab(href: "#") { "Tab 2" } %>
|
130
|
+
# <% c.tab(href: "#") { "Tab 3" } %>
|
131
|
+
# <% end %>
|
132
|
+
#
|
119
133
|
# @param label [String] The `aria-label` on top level `<nav>` element.
|
120
134
|
# @param with_panel [Boolean] Whether the TabNav should navigate through pages or panels.
|
121
135
|
# @param align [Symbol] <%= one_of(Primer::UnderlineNavComponent::ALIGN_OPTIONS) %> - Defaults to <%= Primer::UnderlineNavComponent::ALIGN_DEFAULT %>
|
122
136
|
# @param body_arguments [Hash] <%= link_to_system_arguments_docs %> for the body wrapper.
|
137
|
+
# @param wrapper_arguments [Hash] <%= link_to_system_arguments_docs %> for the `TabContainer` wrapper. Only applies if `with_panel` is `true`.
|
123
138
|
# @param system_arguments [Hash] <%= link_to_system_arguments_docs %>
|
124
|
-
def initialize(label:, with_panel: false, align: ALIGN_DEFAULT, body_arguments: { tag: BODY_TAG_DEFAULT }, **system_arguments)
|
139
|
+
def initialize(label:, with_panel: false, align: ALIGN_DEFAULT, body_arguments: { tag: BODY_TAG_DEFAULT }, wrapper_arguments: {}, **system_arguments)
|
125
140
|
@with_panel = with_panel
|
126
141
|
@align = fetch_or_fallback(ALIGN_OPTIONS, align, ALIGN_DEFAULT)
|
142
|
+
@wrapper_arguments = wrapper_arguments
|
127
143
|
|
128
144
|
@system_arguments = system_arguments
|
129
145
|
@system_arguments[:tag] = navigation_tag(with_panel)
|
data/app/lib/primer/classify.rb
CHANGED
@@ -7,7 +7,7 @@ module Primer
|
|
7
7
|
SPACING_KEYS = Primer::Classify::Spacing::KEYS
|
8
8
|
|
9
9
|
# Keys where we can simply translate { key: value } into ".key-value"
|
10
|
-
CONCAT_KEYS = SPACING_KEYS + %i[hide position v float
|
10
|
+
CONCAT_KEYS = SPACING_KEYS + %i[hide position v float text box_shadow].freeze
|
11
11
|
|
12
12
|
INVALID_CLASS_NAME_PREFIXES =
|
13
13
|
(["bg-", "color-", "text-", "d-", "v-align-", "wb-", "box-shadow-"] + CONCAT_KEYS.map { |k| "#{k}-" }).freeze
|
@@ -22,9 +22,10 @@ module Primer
|
|
22
22
|
BOX_SHADOW_KEY = :box_shadow
|
23
23
|
VISIBILITY_KEY = :visibility
|
24
24
|
ANIMATION_KEY = :animation
|
25
|
+
CONTAINER_KEY = :container
|
25
26
|
|
26
27
|
BREAKPOINTS = ["", "-sm", "-md", "-lg", "-xl"].freeze
|
27
|
-
RESPONSIVE_KEYS = ([DISPLAY_KEY, :
|
28
|
+
RESPONSIVE_KEYS = ([DISPLAY_KEY, :float, Primer::Classify::Grid::COL_KEY] + SPACING_KEYS + Primer::Classify::Flex::RESPONSIVE_KEYS).freeze
|
28
29
|
|
29
30
|
BOOLEAN_MAPPINGS = {
|
30
31
|
underline: {
|
@@ -84,6 +85,7 @@ module Primer
|
|
84
85
|
TYPOGRAPHY_KEYS +
|
85
86
|
TEXT_KEYS +
|
86
87
|
Primer::Classify::Flex::KEYS +
|
88
|
+
Primer::Classify::Grid::KEYS +
|
87
89
|
[
|
88
90
|
BORDER_KEY,
|
89
91
|
BORDER_COLOR_KEY,
|
@@ -97,7 +99,8 @@ module Primer
|
|
97
99
|
HEIGHT_KEY,
|
98
100
|
BOX_SHADOW_KEY,
|
99
101
|
VISIBILITY_KEY,
|
100
|
-
ANIMATION_KEY
|
102
|
+
ANIMATION_KEY,
|
103
|
+
CONTAINER_KEY
|
101
104
|
]
|
102
105
|
).freeze
|
103
106
|
|
@@ -206,6 +209,8 @@ module Primer
|
|
206
209
|
memo[:classes] << "rounded-#{val}"
|
207
210
|
elsif Primer::Classify::Flex::KEYS.include?(key)
|
208
211
|
memo[:classes] << Primer::Classify::Flex.classes(key, val, breakpoint)
|
212
|
+
elsif Primer::Classify::Grid::KEYS.include?(key)
|
213
|
+
memo[:classes] << Primer::Classify::Grid.classes(key, val, breakpoint)
|
209
214
|
elsif key == WIDTH_KEY || key == HEIGHT_KEY
|
210
215
|
if val == :fit
|
211
216
|
memo[:classes] << "#{key}-#{val}"
|
@@ -54,6 +54,21 @@ module Primer
|
|
54
54
|
values: Primer::Classify::Flex::ALIGN_ITEMS_VALUES
|
55
55
|
)
|
56
56
|
|
57
|
+
preload(
|
58
|
+
keys: Primer::Classify::Grid::CONTAINER_KEY,
|
59
|
+
values: Primer::Classify::Grid::CONTAINER_VALUES
|
60
|
+
)
|
61
|
+
|
62
|
+
preload(
|
63
|
+
keys: Primer::Classify::Grid::CLEARFIX_KEY,
|
64
|
+
values: [true]
|
65
|
+
)
|
66
|
+
|
67
|
+
preload(
|
68
|
+
keys: Primer::Classify::Grid::COL_KEY,
|
69
|
+
values: Primer::Classify::Grid::COL_VALUES
|
70
|
+
)
|
71
|
+
|
57
72
|
preload(
|
58
73
|
keys: Primer::Classify::DISPLAY_KEY,
|
59
74
|
values: [:flex, :block, :inline_block, :inline_flex, :none, :table, :table_cell]
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Primer
|
4
|
+
class Classify
|
5
|
+
# Handler for PrimerCSS grid classes.
|
6
|
+
class Grid
|
7
|
+
extend Primer::FetchOrFallbackHelper
|
8
|
+
|
9
|
+
CONTAINER_KEY = :container
|
10
|
+
CONTAINER_VALUES = [:xl, :lg, :md, :sm].freeze
|
11
|
+
|
12
|
+
CLEARFIX_KEY = :clearfix
|
13
|
+
CLEARFIX_VALUES = [true, false].freeze
|
14
|
+
|
15
|
+
COL_KEY = :col
|
16
|
+
COL_VALUES = (1..12).to_a.freeze
|
17
|
+
|
18
|
+
KEYS = [CONTAINER_KEY, CLEARFIX_KEY, COL_KEY].freeze
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def classes(key, value, breakpoint)
|
22
|
+
send(key, value, breakpoint)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def container(value, _breakpoint)
|
28
|
+
val = fetch_or_fallback(CONTAINER_VALUES, value)
|
29
|
+
|
30
|
+
"container-#{val}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def clearfix(value, _breakpoint)
|
34
|
+
"clearfix" if fetch_or_fallback(CLEARFIX_VALUES, value)
|
35
|
+
end
|
36
|
+
|
37
|
+
def col(value, breakpoint)
|
38
|
+
val = fetch_or_fallback(COL_VALUES, value.to_i)
|
39
|
+
|
40
|
+
"col#{breakpoint}-#{val}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -20,10 +20,10 @@ module Primer
|
|
20
20
|
with_panel ? :div : :nav
|
21
21
|
end
|
22
22
|
|
23
|
-
def wrapper
|
23
|
+
def wrapper(**system_arguments)
|
24
24
|
return yield unless @with_panel
|
25
25
|
|
26
|
-
render Primer::TabContainerComponent.new do
|
26
|
+
render Primer::TabContainerComponent.new(**system_arguments) do
|
27
27
|
yield
|
28
28
|
end
|
29
29
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "helpers"
|
4
|
+
|
5
|
+
module ERBLint
|
6
|
+
module Linters
|
7
|
+
# Counts the number of times a HTML button is used instead of the component.
|
8
|
+
class ButtonComponentMigrationCounter < Linter
|
9
|
+
include Helpers
|
10
|
+
|
11
|
+
TAGS = %w[button summary a].freeze
|
12
|
+
CLASS = "btn"
|
13
|
+
MESSAGE = "We are migrating buttons to use [Primer::ButtonComponent](https://primer.style/view-components/components/button), please try to use that instead of raw HTML."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "helpers"
|
4
|
+
|
5
|
+
module ERBLint
|
6
|
+
module Linters
|
7
|
+
# Counts the number of times a HTML flash is used instead of the component.
|
8
|
+
class FlashComponentMigrationCounter < Linter
|
9
|
+
include Helpers
|
10
|
+
|
11
|
+
TAGS = %w[div].freeze
|
12
|
+
CLASS = "flash"
|
13
|
+
MESSAGE = "We are migrating flashes to use [Primer::FlashComponent](https://primer.style/view-components/components/flash), please try to use that instead of raw HTML."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "openssl"
|
5
|
+
|
6
|
+
module ERBLint
|
7
|
+
module Linters
|
8
|
+
# Helper methods for linting ERB.
|
9
|
+
module Helpers
|
10
|
+
def self.included(base)
|
11
|
+
base.include(LinterRegistry)
|
12
|
+
|
13
|
+
define_method "run" do |processed_source|
|
14
|
+
tags(processed_source).each do |tag|
|
15
|
+
next if tag.closing?
|
16
|
+
next unless self.class::TAGS&.include?(tag.name)
|
17
|
+
|
18
|
+
classes = tag.attributes["class"]&.value&.split(" ")
|
19
|
+
|
20
|
+
next unless !self.class::CLASS || classes&.include?(self.class::CLASS)
|
21
|
+
|
22
|
+
generate_offense(self.class, processed_source, tag, self.class::MESSAGE)
|
23
|
+
end
|
24
|
+
|
25
|
+
counter_correct?(processed_source)
|
26
|
+
end
|
27
|
+
|
28
|
+
define_method "autocorrect" do |processed_source, offense|
|
29
|
+
return unless offense.context
|
30
|
+
|
31
|
+
lambda do |corrector|
|
32
|
+
if processed_source.file_content.include?("erblint:counter #{self.class.name.demodulize}")
|
33
|
+
# update the counter if exists
|
34
|
+
corrector.replace(offense.source_range, offense.context)
|
35
|
+
else
|
36
|
+
# add comment with counter if none
|
37
|
+
corrector.insert_before(processed_source.source_buffer.source_range, "#{offense.context}\n")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def tags(processed_source)
|
46
|
+
processed_source.parser.nodes_with_type(:tag).map { |tag_node| BetterHtml::Tree::Tag.from_node(tag_node) }
|
47
|
+
end
|
48
|
+
|
49
|
+
def counter_correct?(processed_source)
|
50
|
+
comment_node = nil
|
51
|
+
expected_count = 0
|
52
|
+
rule_name = self.class.name.match(/:?:?(\w+)\Z/)[1]
|
53
|
+
offenses_count = @offenses.length
|
54
|
+
|
55
|
+
processed_source.parser.ast.descendants(:erb).each do |node|
|
56
|
+
indicator_node, _, code_node, = *node
|
57
|
+
indicator = indicator_node&.loc&.source
|
58
|
+
comment = code_node&.loc&.source&.strip
|
59
|
+
|
60
|
+
if indicator == "#" && comment.start_with?("erblint:count") && comment.match(rule_name)
|
61
|
+
comment_node = code_node
|
62
|
+
expected_count = comment.match(/\s(\d+)\s?$/)[1].to_i
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
if offenses_count.zero?
|
67
|
+
add_offense(processed_source.to_source_range(comment_node.loc), "Unused erblint:count comment for #{rule_name}") if comment_node
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
first_offense = @offenses[0]
|
72
|
+
|
73
|
+
if comment_node.nil?
|
74
|
+
add_offense(processed_source.to_source_range(first_offense.source_range), "#{rule_name}: If you must, add <%# erblint:counter #{rule_name} #{offenses_count} %> to bypass this check.", "<%# erblint:counter #{rule_name} #{offenses_count} %>")
|
75
|
+
else
|
76
|
+
clear_offenses
|
77
|
+
add_offense(processed_source.to_source_range(comment_node.loc), "Incorrect erblint:counter number for #{rule_name}. Expected: #{expected_count}, actual: #{offenses_count}.", " erblint:counter #{rule_name} #{offenses_count} ") if expected_count != offenses_count
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def generate_offense(klass, processed_source, tag, message = nil, replacement = nil)
|
82
|
+
message ||= klass::MESSAGE
|
83
|
+
klass_name = klass.name.split("::")[-1]
|
84
|
+
offense = ["#{klass_name}:#{message}", tag.node.loc.source].join("\n")
|
85
|
+
add_offense(processed_source.to_source_range(tag.loc), offense, replacement)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/tasks/docs.rake
CHANGED
@@ -20,32 +20,12 @@ namespace :docs do
|
|
20
20
|
end
|
21
21
|
|
22
22
|
task :build do
|
23
|
-
|
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
|
23
|
+
registry = generate_yard_registry
|
41
24
|
|
42
25
|
puts "Converting YARD documentation to Markdown files."
|
43
26
|
|
44
27
|
# Rails controller for rendering arbitrary ERB
|
45
28
|
view_context = ApplicationController.new.tap { |c| c.request = ActionDispatch::TestRequest.create }.view_context
|
46
|
-
|
47
|
-
registry = YARD::RegistryStore.new
|
48
|
-
registry.load!(".yardoc")
|
49
29
|
components = [
|
50
30
|
Primer::Image,
|
51
31
|
Primer::LocalTime,
|
@@ -172,7 +152,18 @@ namespace :docs do
|
|
172
152
|
end
|
173
153
|
|
174
154
|
initialize_method.tags(:example).each do |tag|
|
175
|
-
|
155
|
+
name = tag.name
|
156
|
+
description = nil
|
157
|
+
code = nil
|
158
|
+
|
159
|
+
if tag.text.include?("@description")
|
160
|
+
splitted = tag.text.split(/@description|@code/)
|
161
|
+
description = splitted.second.gsub(/^[ \t]{2}/, "").strip
|
162
|
+
code = splitted.last.gsub(/^[ \t]{2}/, "").strip
|
163
|
+
else
|
164
|
+
code = tag.text
|
165
|
+
end
|
166
|
+
|
176
167
|
f.puts
|
177
168
|
f.puts("### #{name}")
|
178
169
|
if description
|
@@ -180,14 +171,14 @@ namespace :docs do
|
|
180
171
|
f.puts(description)
|
181
172
|
end
|
182
173
|
f.puts
|
183
|
-
html = view_context.render(inline:
|
174
|
+
html = view_context.render(inline: code)
|
184
175
|
html.scan(/class="([^"]*)"/) do |classnames|
|
185
176
|
classes_found_in_examples.concat(classnames[0].split(" ").reject { |c| c.starts_with?("octicon", "js", "my-") }.map { ".#{_1}"})
|
186
177
|
end
|
187
178
|
f.puts("<Example src=\"#{html.tr('"', "\'").delete("\n")}\" />")
|
188
179
|
f.puts
|
189
180
|
f.puts("```erb")
|
190
|
-
f.puts(
|
181
|
+
f.puts(code.to_s)
|
191
182
|
f.puts("```")
|
192
183
|
end
|
193
184
|
|
@@ -302,6 +293,22 @@ namespace :docs do
|
|
302
293
|
|
303
294
|
puts "Markdown compiled."
|
304
295
|
|
296
|
+
if components_without_examples.any?
|
297
|
+
puts
|
298
|
+
puts "The following components have no examples defined: #{components_without_examples.map(&:name).join(', ')}. Consider adding an example?"
|
299
|
+
end
|
300
|
+
|
301
|
+
if components_needing_docs.any?
|
302
|
+
puts
|
303
|
+
puts "The following components needs docs. Care to contribute them? #{components_needing_docs.map(&:name).join(', ')}"
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
task :preview do
|
308
|
+
registry = generate_yard_registry
|
309
|
+
|
310
|
+
components = Primer::Component.descendants
|
311
|
+
|
305
312
|
# Generate previews from documentation examples
|
306
313
|
components.each do |component|
|
307
314
|
documentation = registry.get(component.name)
|
@@ -336,15 +343,30 @@ namespace :docs do
|
|
336
343
|
f.puts("end")
|
337
344
|
end
|
338
345
|
end
|
346
|
+
end
|
339
347
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
348
|
+
def generate_yard_registry
|
349
|
+
require File.expand_path("./../../demo/config/environment.rb", __dir__)
|
350
|
+
require "primer/view_components"
|
351
|
+
require "yard/docs_helper"
|
352
|
+
require "view_component/test_helpers"
|
353
|
+
include ViewComponent::TestHelpers
|
354
|
+
include Primer::ViewHelper
|
355
|
+
include YARD::DocsHelper
|
344
356
|
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
357
|
+
Dir["./app/components/primer/**/*.rb"].sort.each { |file| require file }
|
358
|
+
|
359
|
+
YARD::Rake::YardocTask.new
|
360
|
+
|
361
|
+
# Custom tags for yard
|
362
|
+
YARD::Tags::Library.define_tag("Accessibility", :accessibility)
|
363
|
+
YARD::Tags::Library.define_tag("Deprecation", :deprecation)
|
364
|
+
|
365
|
+
puts "Building YARD documentation."
|
366
|
+
Rake::Task["yard"].execute
|
367
|
+
|
368
|
+
registry = YARD::RegistryStore.new
|
369
|
+
registry.load!(".yardoc")
|
370
|
+
registry
|
349
371
|
end
|
350
372
|
end
|
data/static/statuses.json
CHANGED
@@ -43,7 +43,7 @@
|
|
43
43
|
"Primer::StateComponent": "beta",
|
44
44
|
"Primer::SubheadComponent": "beta",
|
45
45
|
"Primer::TabContainerComponent": "alpha",
|
46
|
-
"Primer::TabNavComponent": "
|
46
|
+
"Primer::TabNavComponent": "beta",
|
47
47
|
"Primer::TextComponent": "beta",
|
48
48
|
"Primer::TimeAgoComponent": "beta",
|
49
49
|
"Primer::TimelineItemComponent": "beta",
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: primer_view_components
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.43
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionview
|
@@ -142,6 +142,20 @@ dependencies:
|
|
142
142
|
- - '='
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: '0.13'
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: erb_lint
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
type: :development
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
145
159
|
- !ruby/object:Gem::Dependency
|
146
160
|
name: listen
|
147
161
|
requirement: !ruby/object:Gem::Requirement
|
@@ -444,6 +458,7 @@ files:
|
|
444
458
|
- app/lib/primer/classify/functional_border_colors.rb
|
445
459
|
- app/lib/primer/classify/functional_colors.rb
|
446
460
|
- app/lib/primer/classify/functional_text_colors.rb
|
461
|
+
- app/lib/primer/classify/grid.rb
|
447
462
|
- app/lib/primer/classify/spacing.rb
|
448
463
|
- app/lib/primer/fetch_or_fallback_helper.rb
|
449
464
|
- app/lib/primer/join_style_arguments_helper.rb
|
@@ -454,6 +469,10 @@ files:
|
|
454
469
|
- app/lib/primer/view_helper.rb
|
455
470
|
- lib/primer/view_components.rb
|
456
471
|
- lib/primer/view_components/engine.rb
|
472
|
+
- lib/primer/view_components/linters.rb
|
473
|
+
- lib/primer/view_components/linters/button_component_migration_counter.rb
|
474
|
+
- lib/primer/view_components/linters/flash_component_migration_counter.rb
|
475
|
+
- lib/primer/view_components/linters/helpers.rb
|
457
476
|
- lib/primer/view_components/version.rb
|
458
477
|
- lib/tasks/coverage.rake
|
459
478
|
- lib/tasks/docs.rake
|