showcase-rails 0.2.2 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +71 -5
- data/app/assets/builds/showcase.css +9 -1
- data/app/assets/builds/showcase.highlights.css +112 -0
- data/app/models/showcase/path.rb +8 -16
- data/app/models/showcase/sample.rb +6 -6
- data/app/views/layouts/showcase.html.erb +2 -2
- data/app/views/showcase/engine/_preview.html.erb +1 -1
- data/app/views/showcase/engine/_sample.html.erb +2 -2
- data/app/views/showcase/engine/_stylesheets.html.erb +1 -1
- data/app/views/showcase/engine/path/_path.html.erb +1 -1
- data/lib/showcase/engine.rb +2 -2
- data/lib/showcase/options.rb +89 -0
- data/lib/showcase/previews_test.rb +45 -0
- data/lib/showcase/version.rb +1 -1
- data/lib/showcase.rb +52 -3
- data/lib/tasks/showcase_tasks.rake +6 -6
- metadata +5 -4
- data/app/models/showcase/options.rb +0 -51
- data/lib/showcase/integration_test.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aead2040b7c095f85df0c152c6d96aa3bec927e52f49db09f407926536ce36ae
|
4
|
+
data.tar.gz: c5fd847fd56d1ffa39a4d9aae681106685c71aef2e199bbb637158ec80f43cbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ffe05a31b2be808620a735573c1cbaf705cceb75bdf03f750193f0ad5534096cb3bc81a4d98e51b80712408ca7b3b60465e85a23828ae469da9af3fdb348db5
|
7
|
+
data.tar.gz: f6ec194dec60349be7b4effd48a20517c8af04e45604b104687a6479a4bba92087ae11b36d702fac03e8d1faebb22521b6d49e62d816b5a42d187e027ce9ce00
|
data/README.md
CHANGED
@@ -29,11 +29,59 @@ Which will then render the following:
|
|
29
29
|
|
30
30
|
![](/readme/example.png?raw=true "Showcase showing a button component")
|
31
31
|
|
32
|
-
##
|
32
|
+
## Using options contexts
|
33
33
|
|
34
|
-
Showcase
|
34
|
+
Showcase also supports custom options contexts. They're useful for cases where the options have a very specific format and it would be nice to keep them standardized.
|
35
35
|
|
36
|
-
|
36
|
+
By default, Showcase ships Nice Partials and Stimulus contexts out of the box. Here's a sample of the Stimulus one:
|
37
|
+
|
38
|
+
```erb
|
39
|
+
<% showcase.options.stimulus controller: :welcome do |o| %>
|
40
|
+
<% o.optional.targets :greeter, "If the id of the target element must be printed" %>
|
41
|
+
<% end %>
|
42
|
+
```
|
43
|
+
|
44
|
+
In case Showcase didn't ship with a Stimulus context, here's how you could add it:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# config/initializers/showcase.rb
|
48
|
+
if defined?(Showcase)
|
49
|
+
Showcase.options.define :stimulus do
|
50
|
+
def targets(name, ...)
|
51
|
+
option(%(data-#{@controller}-target="#{name}"), ...)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
```
|
56
|
+
|
57
|
+
## Automatic previews testing
|
58
|
+
|
59
|
+
Showcase automatically runs tests for all your Showcases by rendering them. As long as `gem "showcase-rails"` is in the `:test` group you're set.
|
60
|
+
|
61
|
+
If you want to tweak this, run `bin/rails showcase:install:previews_test` and open `test/views/showcase_test.rb`. You can then add your own `setup` and `teardown` hooks, as well as override the provided `assert_showcase_preview` to add custom assertions.
|
62
|
+
|
63
|
+
If you need custom assertions for specific previews and their samples, you can use the `test` helper:
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
# test/views/showcase_test.rb
|
67
|
+
require "test_helper"
|
68
|
+
|
69
|
+
class ShowcaseTest < Showcase::PreviewsTest
|
70
|
+
test showcase: "combobox" do
|
71
|
+
# This test block runs within the #combobox container element.
|
72
|
+
assert_text "This is a combobox, for sure."
|
73
|
+
end
|
74
|
+
|
75
|
+
test showcase: "button", id: "basic" do
|
76
|
+
# This test block runs within the #button container element's #basic sample.
|
77
|
+
assert_button class: ["text-xs"]
|
78
|
+
end
|
79
|
+
|
80
|
+
test "some non-Showcase test" do
|
81
|
+
# You can still use the regular Rails `test` method too.
|
82
|
+
end
|
83
|
+
end
|
84
|
+
```
|
37
85
|
|
38
86
|
## View examples
|
39
87
|
|
@@ -70,7 +118,7 @@ copy the file from our repo `app/views` directory into your `app/views` director
|
|
70
118
|
|
71
119
|
### Loading your own assets
|
72
120
|
|
73
|
-
Showcase bundles its own `showcase.js` and `showcase.css` asset files through
|
121
|
+
Showcase bundles its own `showcase.js`, `showcase.css` and `showcase.highlights.css` asset files through
|
74
122
|
Action View's [javascript_include_tag][] and [stylesheet_link_tag][].
|
75
123
|
|
76
124
|
If your assets require more sophisticated loading techniques, declare your own
|
@@ -78,12 +126,24 @@ versions of the [showcase/engine/_javascripts.html.erb][] and
|
|
78
126
|
[showcase/engine/_stylesheets.html.erb][] partials. When customizing those
|
79
127
|
partials, make sure to include `"showcase"` in your list of assets.
|
80
128
|
|
81
|
-
|
82
129
|
[javascript_include_tag]: https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html#method-i-javascript_include_tag
|
83
130
|
[stylesheet_link_tag]: https://edgeapi.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html#method-i-stylesheet_link_tag
|
84
131
|
[showcase/engine/_javascripts.html.erb]: ./showcase/engine/_javascripts.html.erb
|
85
132
|
[showcase/engine/_stylesheets.html.erb]: ./showcase/engine/_stylesheets.html.erb
|
86
133
|
|
134
|
+
#### Loading your own syntax highlighting theme
|
135
|
+
|
136
|
+
By default, Showcase's syntax highlighting runs on Rouge's "github" theme.
|
137
|
+
|
138
|
+
To use a different theme, override [showcase/engine/_stylesheets.html.erb][] with the following, replacing `:magritte` with a [valid theme](rouge-themes):
|
139
|
+
|
140
|
+
```erb
|
141
|
+
<%= stylesheet_link_tag "application", "showcase" %> # We've removed the default showcase.highlights file here.
|
142
|
+
<%= tag.style Rouge::Theme.find(:magritte).render(scope: ".sc-highlight") %>
|
143
|
+
```
|
144
|
+
|
145
|
+
[rouge-themes]: https://github.com/rouge-ruby/rouge/tree/master/lib/rouge/themes
|
146
|
+
|
87
147
|
## Installation
|
88
148
|
|
89
149
|
Add this line to your application's Gemfile. To get the automatic integration testing make sure the `showcase-rails` gem is available to your test environment:
|
@@ -104,6 +164,12 @@ Or install it yourself as:
|
|
104
164
|
$ gem install showcase-rails
|
105
165
|
```
|
106
166
|
|
167
|
+
Then add the following in your `config/routes.rb` within the block passed to `Rails.application.routes.draw`:
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
mount Showcase::Engine, at: "/docs/showcase" if defined?(Showcase::Engine)
|
171
|
+
```
|
172
|
+
|
107
173
|
## Contributing
|
108
174
|
Contribution directions go here.
|
109
175
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
/*
|
2
|
-
! tailwindcss v3.2.
|
2
|
+
! tailwindcss v3.2.7 | MIT License | https://tailwindcss.com
|
3
3
|
*/
|
4
4
|
|
5
5
|
/*
|
@@ -985,6 +985,14 @@ select {
|
|
985
985
|
background-color: rgb(238 242 255 / var(--tw-bg-opacity));
|
986
986
|
}
|
987
987
|
|
988
|
+
.hover\:sc-text-inherit:hover {
|
989
|
+
color: inherit;
|
990
|
+
}
|
991
|
+
|
992
|
+
.hover\:sc-no-underline:hover {
|
993
|
+
text-decoration-line: none;
|
994
|
+
}
|
995
|
+
|
988
996
|
@media (min-width: 768px) {
|
989
997
|
.md\:sc-text-lg {
|
990
998
|
font-size: 1.125rem;
|
@@ -0,0 +1,112 @@
|
|
1
|
+
.sc-highlight table td { padding: 5px; }
|
2
|
+
.sc-highlight table pre { margin: 0; }
|
3
|
+
.sc-highlight, .sc-highlight .w {
|
4
|
+
color: #24292f;
|
5
|
+
background-color: #f6f8fa;
|
6
|
+
}
|
7
|
+
.sc-highlight .k, .sc-highlight .kd, .sc-highlight .kn, .sc-highlight .kp, .sc-highlight .kr, .sc-highlight .kt, .sc-highlight .kv {
|
8
|
+
color: #cf222e;
|
9
|
+
}
|
10
|
+
.sc-highlight .gr {
|
11
|
+
color: #f6f8fa;
|
12
|
+
}
|
13
|
+
.sc-highlight .gd {
|
14
|
+
color: #82071e;
|
15
|
+
background-color: #ffebe9;
|
16
|
+
}
|
17
|
+
.sc-highlight .nb {
|
18
|
+
color: #953800;
|
19
|
+
}
|
20
|
+
.sc-highlight .nc {
|
21
|
+
color: #953800;
|
22
|
+
}
|
23
|
+
.sc-highlight .no {
|
24
|
+
color: #953800;
|
25
|
+
}
|
26
|
+
.sc-highlight .nn {
|
27
|
+
color: #953800;
|
28
|
+
}
|
29
|
+
.sc-highlight .sr {
|
30
|
+
color: #116329;
|
31
|
+
}
|
32
|
+
.sc-highlight .na {
|
33
|
+
color: #116329;
|
34
|
+
}
|
35
|
+
.sc-highlight .nt {
|
36
|
+
color: #116329;
|
37
|
+
}
|
38
|
+
.sc-highlight .gi {
|
39
|
+
color: #116329;
|
40
|
+
background-color: #dafbe1;
|
41
|
+
}
|
42
|
+
.sc-highlight .kc {
|
43
|
+
color: #0550ae;
|
44
|
+
}
|
45
|
+
.sc-highlight .l, .sc-highlight .ld, .sc-highlight .m, .sc-highlight .mb, .sc-highlight .mf, .sc-highlight .mh, .sc-highlight .mi, .sc-highlight .il, .sc-highlight .mo, .sc-highlight .mx {
|
46
|
+
color: #0550ae;
|
47
|
+
}
|
48
|
+
.sc-highlight .sb {
|
49
|
+
color: #0550ae;
|
50
|
+
}
|
51
|
+
.sc-highlight .bp {
|
52
|
+
color: #0550ae;
|
53
|
+
}
|
54
|
+
.sc-highlight .ne {
|
55
|
+
color: #0550ae;
|
56
|
+
}
|
57
|
+
.sc-highlight .nl {
|
58
|
+
color: #0550ae;
|
59
|
+
}
|
60
|
+
.sc-highlight .py {
|
61
|
+
color: #0550ae;
|
62
|
+
}
|
63
|
+
.sc-highlight .nv, .sc-highlight .vc, .sc-highlight .vg, .sc-highlight .vi, .sc-highlight .vm {
|
64
|
+
color: #0550ae;
|
65
|
+
}
|
66
|
+
.sc-highlight .o, .sc-highlight .ow {
|
67
|
+
color: #0550ae;
|
68
|
+
}
|
69
|
+
.sc-highlight .gh {
|
70
|
+
color: #0550ae;
|
71
|
+
font-weight: bold;
|
72
|
+
}
|
73
|
+
.sc-highlight .gu {
|
74
|
+
color: #0550ae;
|
75
|
+
font-weight: bold;
|
76
|
+
}
|
77
|
+
.sc-highlight .s, .sc-highlight .sa, .sc-highlight .sc, .sc-highlight .dl, .sc-highlight .sd, .sc-highlight .s2, .sc-highlight .se, .sc-highlight .sh, .sc-highlight .sx, .sc-highlight .s1, .sc-highlight .ss {
|
78
|
+
color: #0a3069;
|
79
|
+
}
|
80
|
+
.sc-highlight .nd {
|
81
|
+
color: #8250df;
|
82
|
+
}
|
83
|
+
.sc-highlight .nf, .sc-highlight .fm {
|
84
|
+
color: #8250df;
|
85
|
+
}
|
86
|
+
.sc-highlight .err {
|
87
|
+
color: #f6f8fa;
|
88
|
+
background-color: #82071e;
|
89
|
+
}
|
90
|
+
.sc-highlight .c, .sc-highlight .ch, .sc-highlight .cd, .sc-highlight .cm, .sc-highlight .cp, .sc-highlight .cpf, .sc-highlight .c1, .sc-highlight .cs {
|
91
|
+
color: #6e7781;
|
92
|
+
}
|
93
|
+
.sc-highlight .gl {
|
94
|
+
color: #6e7781;
|
95
|
+
}
|
96
|
+
.sc-highlight .gt {
|
97
|
+
color: #6e7781;
|
98
|
+
}
|
99
|
+
.sc-highlight .ni {
|
100
|
+
color: #24292f;
|
101
|
+
}
|
102
|
+
.sc-highlight .si {
|
103
|
+
color: #24292f;
|
104
|
+
}
|
105
|
+
.sc-highlight .ge {
|
106
|
+
color: #24292f;
|
107
|
+
font-style: italic;
|
108
|
+
}
|
109
|
+
.sc-highlight .gs {
|
110
|
+
color: #24292f;
|
111
|
+
font-weight: bold;
|
112
|
+
}
|
data/app/models/showcase/path.rb
CHANGED
@@ -1,19 +1,16 @@
|
|
1
1
|
class Showcase::Path
|
2
|
-
class Tree < Struct.new(:id, :children)
|
2
|
+
class Tree < Struct.new(:id, :children, :root)
|
3
3
|
def initialize(id, children = [])
|
4
|
-
super
|
4
|
+
super(id, children, false)
|
5
5
|
end
|
6
|
+
alias_method :root?, :root
|
6
7
|
delegate :<<, to: :children
|
7
8
|
|
8
9
|
cached_partial_path = "showcase/engine/path/tree"
|
9
10
|
define_method(:to_partial_path) { cached_partial_path }
|
10
11
|
|
11
12
|
def name
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
def root?
|
16
|
-
id == "."
|
13
|
+
id == "." ? "Previews" : id
|
17
14
|
end
|
18
15
|
|
19
16
|
def ordered_children
|
@@ -24,15 +21,10 @@ class Showcase::Path
|
|
24
21
|
children.flat_map { _1.is_a?(Tree) ? _1.ordered_paths : _1 }
|
25
22
|
end
|
26
23
|
|
27
|
-
def self.index(
|
28
|
-
new(:discardable_root)
|
29
|
-
|
30
|
-
|
31
|
-
def index(paths)
|
32
|
-
paths.each do |path|
|
33
|
-
ids = yield path
|
34
|
-
ids.inject(self, :edge_for) << path
|
35
|
-
end
|
24
|
+
def self.index(paths)
|
25
|
+
paths.each_with_object new(:discardable_root) do |path, root|
|
26
|
+
yield(path).reduce(root, :edge_for) << path
|
27
|
+
end.children.sort_by(&:id).each { _1.root = true }
|
36
28
|
end
|
37
29
|
|
38
30
|
def edge_for(id)
|
@@ -2,9 +2,9 @@ class Showcase::Sample
|
|
2
2
|
attr_reader :name, :id, :events, :details
|
3
3
|
attr_reader :source, :instrumented
|
4
4
|
|
5
|
-
def initialize(view_context, name, description: nil, id: name.parameterize, events: nil, **details)
|
5
|
+
def initialize(view_context, name, description: nil, id: name.parameterize, syntax: :erb, events: nil, **details)
|
6
6
|
@view_context = view_context
|
7
|
-
@name, @id, @details = name, id, details
|
7
|
+
@name, @id, @syntax, @details = name, id, syntax, details
|
8
8
|
@events = Array(events)
|
9
9
|
description description if description
|
10
10
|
end
|
@@ -34,13 +34,13 @@ class Showcase::Sample
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def extract(&block)
|
37
|
-
|
38
|
-
@source = @view_context.instance_exec(
|
37
|
+
source = extract_source_block_via_matched_indentation_from(*block.source_location)
|
38
|
+
@source = @view_context.instance_exec(source, @syntax, &Showcase.sample_renderer)
|
39
39
|
end
|
40
40
|
|
41
41
|
private
|
42
42
|
|
43
|
-
def
|
43
|
+
def extract_source_block_via_matched_indentation_from(file, starting_index)
|
44
44
|
first_line, *lines = File.readlines(file).from(starting_index - 1)
|
45
45
|
|
46
46
|
indentation = first_line.match(/^\s+(?=\b)/).to_s
|
@@ -48,6 +48,6 @@ class Showcase::Sample
|
|
48
48
|
|
49
49
|
index = lines.index { _1.match?(matcher) }
|
50
50
|
lines.slice!(index..) if index
|
51
|
-
lines
|
51
|
+
lines.join.strip_heredoc
|
52
52
|
end
|
53
53
|
end
|
@@ -4,8 +4,8 @@
|
|
4
4
|
<title>Showcase</title>
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
6
|
|
7
|
-
<%= render "stylesheets" %>
|
8
|
-
<%= render "javascripts" %>
|
7
|
+
<%= render "showcase/engine/stylesheets" %>
|
8
|
+
<%= render "showcase/engine/javascripts" %>
|
9
9
|
</head>
|
10
10
|
|
11
11
|
<body>
|
@@ -27,8 +27,8 @@
|
|
27
27
|
<details>
|
28
28
|
<summary class="sc-px-4 sc-py-2 hover:sc-bg-indigo-50 sc-cursor-pointer sc-select-none">View Source</summary>
|
29
29
|
|
30
|
-
<section class="sc-px-4 sc-py-2 sc-relative sc-overflow-y-auto hover:sc-select-all">
|
31
|
-
|
30
|
+
<section class="sc-highlight sc-px-4 sc-py-2 sc-relative sc-overflow-y-auto hover:sc-select-all">
|
31
|
+
<pre><%= sample.source %></pre>
|
32
32
|
</section>
|
33
33
|
</details>
|
34
34
|
<% end %>
|
@@ -1 +1 @@
|
|
1
|
-
<%= stylesheet_link_tag "application", "showcase" %>
|
1
|
+
<%= stylesheet_link_tag "application", "showcase", "showcase.highlights" %>
|
@@ -1,3 +1,3 @@
|
|
1
1
|
<article class="hover:sc-bg-indigo-50 <%= "sc-bg-indigo-50" if path.id == params[:id] %>">
|
2
|
-
<%= link_to path.basename.titleize, preview_path(path.id), class: "sc-inline-block sc-py-2 sc-px-8 sc-w-full" %>
|
2
|
+
<%= link_to path.basename.titleize, preview_path(path.id), class: "sc-inline-block sc-py-2 sc-px-8 sc-w-full hover:sc-text-inherit hover:sc-no-underline" %>
|
3
3
|
</article>
|
data/lib/showcase/engine.rb
CHANGED
@@ -6,8 +6,8 @@ module Showcase
|
|
6
6
|
config.assets.precompile += %w[showcase_manifest]
|
7
7
|
end
|
8
8
|
|
9
|
-
initializer "showcase.
|
10
|
-
Showcase::
|
9
|
+
initializer "showcase.previews_test.autorun" do
|
10
|
+
Showcase::PreviewsTest.autorun if Rails.env.test?
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require "active_support/option_merger"
|
2
|
+
|
3
|
+
class Showcase::Options
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(view_context)
|
7
|
+
@view_context = view_context
|
8
|
+
@options = []
|
9
|
+
@order = [:name, :required, :type, :default, :description]
|
10
|
+
end
|
11
|
+
delegate :empty?, to: :@options
|
12
|
+
|
13
|
+
# Showcase.options.define :stimulus do
|
14
|
+
# def value(name, ...)
|
15
|
+
# option("data-#{@controller}-#{name}-value", ...)
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
singleton_class.attr_reader :contexts
|
19
|
+
@contexts = Hash.new { |h,k| h[k] = Class.new Context }
|
20
|
+
|
21
|
+
def self.define(key, &block)
|
22
|
+
contexts[key].class_eval(&block) # Lets users reopen an already defined context class.
|
23
|
+
end
|
24
|
+
|
25
|
+
# showcase.options.stimulus controller: :welcome do |o|
|
26
|
+
# o.value :greeting, default: "Hello"
|
27
|
+
# end
|
28
|
+
def context(key, **options, &block)
|
29
|
+
context = self.class.contexts.fetch(key)
|
30
|
+
context.new(@view_context, @options, **options).tap { yield _1 if block_given? }
|
31
|
+
end
|
32
|
+
|
33
|
+
def required(*arguments, **keywords, &block)
|
34
|
+
if arguments.none?
|
35
|
+
ActiveSupport::OptionMerger.new(self, required: true)
|
36
|
+
else
|
37
|
+
option(*arguments, **keywords, required: true, &block)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def optional(*arguments, **keywords, &block)
|
42
|
+
if arguments.none?
|
43
|
+
ActiveSupport::OptionMerger.new(self, required: false)
|
44
|
+
else
|
45
|
+
option(*arguments, **keywords, required: false, &block)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
DEFAULT_OMITTED = Object.new
|
50
|
+
|
51
|
+
def option(name, description = nil, required: false, type: nil, default: DEFAULT_OMITTED, **options, &block)
|
52
|
+
description ||= @view_context.capture(&block).remove(/^\s+/).html_safe if block
|
53
|
+
|
54
|
+
type ||= type_from_default(default)
|
55
|
+
default = default == DEFAULT_OMITTED ? nil : default.inspect
|
56
|
+
|
57
|
+
@options << options.with_defaults(name: name, default: default, type: type, description: description, required: required)
|
58
|
+
end
|
59
|
+
|
60
|
+
def headers
|
61
|
+
@headers ||= @order | @options.flat_map(&:keys).uniq.sort
|
62
|
+
end
|
63
|
+
|
64
|
+
def each(&block)
|
65
|
+
@options.each do |option|
|
66
|
+
yield headers.index_with { option[_1] }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
class Context < Showcase::Options
|
73
|
+
def initialize(view_context, options, **kwargs)
|
74
|
+
super(view_context)
|
75
|
+
@options = options
|
76
|
+
kwargs.each { instance_variable_set(:"@#{_1}", _2) }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def type_from_default(default)
|
81
|
+
case default
|
82
|
+
when DEFAULT_OMITTED then String
|
83
|
+
when true, false then "Boolean"
|
84
|
+
when nil then "nil"
|
85
|
+
else
|
86
|
+
default.class
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class Showcase::PreviewsTest < ActionView::TestCase
|
2
|
+
def self.inherited(test_class)
|
3
|
+
super
|
4
|
+
test_class.prepare
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.autorun
|
8
|
+
at_exit { prepare unless subclasses.any? }
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.prepare
|
12
|
+
tests Showcase::EngineController._helpers
|
13
|
+
|
14
|
+
tree = Showcase::Path.tree
|
15
|
+
tree.flat_map(&:ordered_paths).each do |path|
|
16
|
+
test "Showcase: automatically renders showcase/previews/#{path.id}" do
|
17
|
+
render "showcase/engine/preview", preview: path.preview_for(view)
|
18
|
+
assert_showcase_preview(path.id)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
test "Showcase: isn't empty" do
|
23
|
+
assert_not_empty tree, "Showcase couldn't find any samples to generate tests for"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.test(name = nil, showcase: nil, id: nil, &block)
|
28
|
+
case
|
29
|
+
when name then super(name, &block)
|
30
|
+
when id && showcase.nil? then raise ArgumentError, "can't test a sample without a showcase"
|
31
|
+
else
|
32
|
+
super "Showcase: showcase/previews/#{showcase} #{"sample #{id}" if id}".squish do
|
33
|
+
path = Showcase::Path.new(showcase)
|
34
|
+
render "showcase/engine/preview", preview: path.preview_for(view)
|
35
|
+
|
36
|
+
assert_showcase_preview(path.id)
|
37
|
+
assert_element(id: id || path.id) { instance_eval(&block) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Override `assert_showcase_preview` to add custom assertions.
|
43
|
+
def assert_showcase_preview(id)
|
44
|
+
end
|
45
|
+
end
|
data/lib/showcase/version.rb
CHANGED
data/lib/showcase.rb
CHANGED
@@ -1,11 +1,28 @@
|
|
1
1
|
require_relative "showcase/version"
|
2
2
|
|
3
|
+
# Activate the app-bundled Rouge gem to setup default syntax highlighting.
|
4
|
+
begin
|
5
|
+
gem "rouge"
|
6
|
+
require "rouge"
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
|
3
10
|
module Showcase
|
4
|
-
autoload :
|
5
|
-
autoload :RouteHelper,
|
11
|
+
autoload :PreviewsTest, "showcase/previews_test"
|
12
|
+
autoload :RouteHelper, "showcase/route_helper"
|
13
|
+
autoload :Options, "showcase/options"
|
6
14
|
|
7
15
|
singleton_class.attr_accessor :sample_renderer
|
8
|
-
@sample_renderer =
|
16
|
+
@sample_renderer = proc { _1 }
|
17
|
+
|
18
|
+
if defined?(Rouge)
|
19
|
+
Formatter = Rouge::Formatters::HTML.new
|
20
|
+
|
21
|
+
@sample_renderer = ->(source, syntax) do
|
22
|
+
lexed = Rouge::Lexer.find(syntax).lex(source)
|
23
|
+
Showcase::Formatter.format(lexed).html_safe
|
24
|
+
end
|
25
|
+
end
|
9
26
|
|
10
27
|
singleton_class.attr_reader :previews_path
|
11
28
|
@previews_path = "showcase/previews"
|
@@ -15,6 +32,38 @@ module Showcase
|
|
15
32
|
Dir.glob("**/*.*", base: File.join(root, previews_path))
|
16
33
|
end.uniq
|
17
34
|
end
|
35
|
+
|
36
|
+
def self.options
|
37
|
+
Options
|
38
|
+
end
|
39
|
+
|
40
|
+
options.define :stimulus do
|
41
|
+
def targets(name, ...)
|
42
|
+
option(%(data-#{@controller}-target="#{name}"), ...)
|
43
|
+
end
|
44
|
+
|
45
|
+
def values(name, ...)
|
46
|
+
option("data-#{@controller}-#{name}-value", ...)
|
47
|
+
end
|
48
|
+
|
49
|
+
def classes(name, ...)
|
50
|
+
option("data-#{@controller}-#{name}-class", ...)
|
51
|
+
end
|
52
|
+
|
53
|
+
def outlet(name, ...)
|
54
|
+
option("data-#{@controller}-#{name}-outlet", ...)
|
55
|
+
end
|
56
|
+
|
57
|
+
def action(name, ...)
|
58
|
+
option(%(data-action="#{name}"), ...)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
options.define :nice_partials do
|
63
|
+
def content_block(*arguments, **options, &block)
|
64
|
+
option(*arguments, **options, type: "Content Block", &block)
|
65
|
+
end
|
66
|
+
end
|
18
67
|
end
|
19
68
|
|
20
69
|
require "showcase/engine" if defined?(Rails::Engine)
|
@@ -1,14 +1,14 @@
|
|
1
1
|
namespace :showcase do
|
2
2
|
namespace :install do
|
3
|
-
|
3
|
+
PREVIEWS_TEST_PATH = "test/views/showcase_test.rb"
|
4
4
|
|
5
|
-
desc "Install Showcase
|
6
|
-
task :
|
7
|
-
mkdir_p File.dirname(
|
8
|
-
File.write
|
5
|
+
desc "Install Showcase previews testing in #{PREVIEWS_TEST_PATH}"
|
6
|
+
task :previews_test do
|
7
|
+
mkdir_p File.dirname(PREVIEWS_TEST_PATH)
|
8
|
+
File.write PREVIEWS_TEST_PATH, <<~RUBY
|
9
9
|
require "test_helper"
|
10
10
|
|
11
|
-
class ShowcaseTest < Showcase::
|
11
|
+
class ShowcaseTest < Showcase::PreviewsTest
|
12
12
|
def assert_showcase_preview(id)
|
13
13
|
# Add any custom preview response body assertions here.
|
14
14
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: showcase-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Pence
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-03-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -50,11 +50,11 @@ files:
|
|
50
50
|
- README.md
|
51
51
|
- Rakefile
|
52
52
|
- app/assets/builds/showcase.css
|
53
|
+
- app/assets/builds/showcase.highlights.css
|
53
54
|
- app/assets/config/showcase_manifest.js
|
54
55
|
- app/assets/javascripts/showcase.js
|
55
56
|
- app/controllers/showcase/engine_controller.rb
|
56
57
|
- app/controllers/showcase/previews_controller.rb
|
57
|
-
- app/models/showcase/options.rb
|
58
58
|
- app/models/showcase/path.rb
|
59
59
|
- app/models/showcase/preview.rb
|
60
60
|
- app/models/showcase/sample.rb
|
@@ -74,7 +74,8 @@ files:
|
|
74
74
|
- lib/showcase-rails.rb
|
75
75
|
- lib/showcase.rb
|
76
76
|
- lib/showcase/engine.rb
|
77
|
-
- lib/showcase/
|
77
|
+
- lib/showcase/options.rb
|
78
|
+
- lib/showcase/previews_test.rb
|
78
79
|
- lib/showcase/route_helper.rb
|
79
80
|
- lib/showcase/version.rb
|
80
81
|
- lib/tasks/showcase_tasks.rake
|
@@ -1,51 +0,0 @@
|
|
1
|
-
class Showcase::Options
|
2
|
-
include Enumerable
|
3
|
-
|
4
|
-
def initialize(view_context)
|
5
|
-
@view_context = view_context
|
6
|
-
@options = []
|
7
|
-
@order = [:name, :required, :type, :default, :description]
|
8
|
-
end
|
9
|
-
delegate :empty?, to: :@options
|
10
|
-
|
11
|
-
def required(*arguments, **keywords, &block)
|
12
|
-
option(*arguments, **keywords, required: true, &block)
|
13
|
-
end
|
14
|
-
|
15
|
-
def optional(*arguments, **keywords, &block)
|
16
|
-
option(*arguments, **keywords, required: false, &block)
|
17
|
-
end
|
18
|
-
|
19
|
-
DEFAULT_OMITTED = Object.new
|
20
|
-
|
21
|
-
def option(name, description = nil, required: false, type: nil, default: DEFAULT_OMITTED, **options, &block)
|
22
|
-
description ||= @view_context.capture(&block).remove(/^\s+/).html_safe if block
|
23
|
-
|
24
|
-
type ||= type_from_default(default)
|
25
|
-
default = default == DEFAULT_OMITTED ? nil : default.inspect
|
26
|
-
|
27
|
-
@options << options.with_defaults(name: name, default: default, type: type, description: description, required: required)
|
28
|
-
end
|
29
|
-
|
30
|
-
def headers
|
31
|
-
@headers ||= @order | @options.flat_map(&:keys).uniq.sort
|
32
|
-
end
|
33
|
-
|
34
|
-
def each(&block)
|
35
|
-
@options.each do |option|
|
36
|
-
yield headers.index_with { option[_1] }
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def type_from_default(default)
|
43
|
-
case default
|
44
|
-
when DEFAULT_OMITTED then String
|
45
|
-
when true, false then "Boolean"
|
46
|
-
when nil then "nil"
|
47
|
-
else
|
48
|
-
default.class
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
class Showcase::IntegrationTest < ActionDispatch::IntegrationTest
|
2
|
-
def self.inherited(test_class)
|
3
|
-
super
|
4
|
-
test_class.prepare
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.autorun
|
8
|
-
at_exit { prepare unless subclasses.any? }
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.prepare
|
12
|
-
tree = Showcase::Path.tree
|
13
|
-
tree.flat_map(&:ordered_paths).each do |path|
|
14
|
-
test "Showcase: GET showcase/previews/#{path.id} renders successfully" do
|
15
|
-
get showcase.preview_path(path.id)
|
16
|
-
|
17
|
-
assert_response :ok
|
18
|
-
assert_showcase_preview(path.id)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
test "Showcase: isn't empty" do
|
23
|
-
assert_not_empty tree, "Showcase couldn't find any samples to generate tests for"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Override `assert_showcase_preview` to add custom assertions.
|
28
|
-
def assert_showcase_preview(id)
|
29
|
-
end
|
30
|
-
end
|