rspec-documentation 0.0.1 → 0.0.2
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/.rspec +1 -0
- data/.rubocop.yml +2 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +25 -2
- data/README.md +1 -1
- data/Rakefile +1 -0
- data/exe/rspec-documentation +5 -0
- data/lib/rspec/documentation/version.rb +2 -2
- data/lib/rspec/documentation.rb +65 -2
- data/lib/rspec_documentation/ansi_html.rb +28 -0
- data/lib/rspec_documentation/configuration.rb +15 -0
- data/lib/rspec_documentation/context.rb +44 -0
- data/lib/rspec_documentation/document.rb +61 -0
- data/lib/rspec_documentation/html_element.rb +55 -0
- data/lib/rspec_documentation/markdown_renderer.rb +7 -0
- data/lib/rspec_documentation/page_collection.rb +49 -0
- data/lib/rspec_documentation/page_tree.rb +73 -0
- data/lib/rspec_documentation/page_tree_element.rb +56 -0
- data/lib/rspec_documentation/parsed_document.rb +70 -0
- data/lib/rspec_documentation/rspec/example_group.rb +26 -0
- data/lib/rspec_documentation/rspec/failure.rb +37 -0
- data/lib/rspec_documentation/rspec/reporter.rb +45 -0
- data/lib/rspec_documentation/rspec.rb +14 -0
- data/lib/rspec_documentation/spec.rb +62 -0
- data/lib/rspec_documentation/util.rb +46 -0
- data/lib/rspec_documentation.rb +58 -0
- data/lib/tasks/rspec/documentation/generate.rake +10 -0
- data/lib/templates/footer.html.erb +1 -0
- data/lib/templates/header.html.erb +7 -0
- data/lib/templates/layout.css.erb +39 -0
- data/lib/templates/layout.html.erb +100 -0
- data/lib/templates/tabbed_spec.html.erb +119 -0
- data/lib/templates/themes/cerulean.css +12 -0
- data/lib/templates/themes/cosmo.css +12 -0
- data/lib/templates/themes/cyborg.css +12 -0
- data/lib/templates/themes/darkly.css +12 -0
- data/lib/templates/themes/flatly.css +12 -0
- data/lib/templates/themes/journal.css +12 -0
- data/lib/templates/themes/litera.css +12 -0
- data/lib/templates/themes/lumen.css +12 -0
- data/lib/templates/themes/lux.css +12 -0
- data/lib/templates/themes/materia.css +12 -0
- data/lib/templates/themes/minty.css +12 -0
- data/lib/templates/themes/morph.css +12 -0
- data/lib/templates/themes/pulse.css +12 -0
- data/lib/templates/themes/quartz.css +12 -0
- data/lib/templates/themes/sandstone.css +12 -0
- data/lib/templates/themes/simplex.css +12 -0
- data/lib/templates/themes/sketchy.css +12 -0
- data/lib/templates/themes/slate.css +12 -0
- data/lib/templates/themes/solar.css +12 -0
- data/lib/templates/themes/spacelab.css +12 -0
- data/lib/templates/themes/superhero.css +12 -0
- data/lib/templates/themes/united.css +12 -0
- data/lib/templates/themes/vapor.css +12 -0
- data/lib/templates/themes/yeti.css +12 -0
- data/lib/templates/themes/zephyr.css +12 -0
- data/rspec-documentation/pages/000-Introduction.md +39 -0
- data/rspec-documentation/pages/010-File System/000-Ordering.md +14 -0
- data/rspec-documentation/pages/010-File System/010-Standalone Directories.md +17 -0
- data/rspec-documentation/pages/010-File System/020-Standalone Directory/Example Page.md +3 -0
- data/rspec-documentation/pages/010-File System.md +26 -0
- data/rspec-documentation/pages/020-Running Specs.md +11 -0
- data/rspec-documentation.gemspec +9 -1
- data/sig/rspec/documentation.rbs +1 -1
- metadata +158 -4
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecDocumentation
|
4
|
+
module RSpec
|
5
|
+
# Replicates behaviour of a regular RSpec example group but also defines `it_documents` as an
|
6
|
+
# alias for `it`.
|
7
|
+
class ExampleGroup < ::RSpec::Core::ExampleGroup
|
8
|
+
def self.it_documents(described_object, &block)
|
9
|
+
raise Error, 'Must pass an example object to `it_documents`' if described_object.nil?
|
10
|
+
|
11
|
+
metadata = { described_object: described_object }
|
12
|
+
::RSpec::Core::Example.new(self, 'documentation', metadata, block)
|
13
|
+
end
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def method_missing(...)
|
17
|
+
RSpecDocumentation.context.public_send(...)
|
18
|
+
end
|
19
|
+
|
20
|
+
def respond_to_missing?(*args)
|
21
|
+
RSpecDocumentation.context.respond_to_missing?(*args)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecDocumentation
|
4
|
+
module RSpec
|
5
|
+
# Stores information about a failed RSpec example. Thin wrapper around `RSpec::Failure`.
|
6
|
+
class Failure
|
7
|
+
def initialize(cause:, spec:)
|
8
|
+
@cause = cause
|
9
|
+
@spec = spec
|
10
|
+
end
|
11
|
+
|
12
|
+
def message
|
13
|
+
"\n#{formatted_header}\n\n#{formatted_source}\n#{formatted_cause}\n\n"
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :cause, :spec
|
19
|
+
|
20
|
+
def formatted_header
|
21
|
+
"\e[1;37m#{indented('Failure in example')}:\e[0m"
|
22
|
+
end
|
23
|
+
|
24
|
+
def formatted_source
|
25
|
+
"\e[1;34m#{indented(spec.source)}\e[0m"
|
26
|
+
end
|
27
|
+
|
28
|
+
def formatted_cause
|
29
|
+
"\e[31m#{indented(cause.message)}\e[0m"
|
30
|
+
end
|
31
|
+
|
32
|
+
def indented(text)
|
33
|
+
text.split("\n").map { |line| " #{line}" }.join("\n")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecDocumentation
|
4
|
+
module RSpec
|
5
|
+
# RSpec reporter, compliant with RSpec::Core::Reporter. Stores outcomes of specs.
|
6
|
+
class Reporter
|
7
|
+
attr_reader :passed_examples, :failed_examples
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@passed_examples = []
|
11
|
+
@failed_examples = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def described_object
|
15
|
+
raise Error, 'Code block did not contain an example.' if passed_examples.empty? && failed_examples.empty?
|
16
|
+
|
17
|
+
passed_examples&.first&.metadata&.fetch(:described_object)
|
18
|
+
end
|
19
|
+
|
20
|
+
def report(*_); end
|
21
|
+
|
22
|
+
def finish(*_); end
|
23
|
+
|
24
|
+
def example_passed(example)
|
25
|
+
raise Error, 'Cannot define more than one example per code block.' if passed_examples.size >= 1
|
26
|
+
|
27
|
+
passed_examples << example
|
28
|
+
end
|
29
|
+
|
30
|
+
def example_group_started(*_); end
|
31
|
+
|
32
|
+
def example_group_finished(*_); end
|
33
|
+
|
34
|
+
def example_started(*_); end
|
35
|
+
|
36
|
+
def example_finished(*_); end
|
37
|
+
|
38
|
+
def example_failed(example)
|
39
|
+
failed_examples << example
|
40
|
+
end
|
41
|
+
|
42
|
+
def fail_fast_limit_met?(*_); end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'rspec/example_group'
|
4
|
+
require_relative 'rspec/reporter'
|
5
|
+
require_relative 'rspec/failure'
|
6
|
+
|
7
|
+
module RSpecDocumentation
|
8
|
+
# Provides various classes that wrap or interface RSpec internals.
|
9
|
+
module RSpec
|
10
|
+
def self.with_failure_notifier(callable, &block)
|
11
|
+
::RSpec::Support.with_failure_notifier(callable, &block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecDocumentation
|
4
|
+
# Executes specs from a Markdown code block and provides the outcome in the appropriate format.
|
5
|
+
class Spec
|
6
|
+
attr_reader :parent, :location, :index, :format
|
7
|
+
|
8
|
+
def initialize(spec:, format:, parent:, location:, index:)
|
9
|
+
@spec = spec
|
10
|
+
@format = format.empty? ? :plaintext : format.to_sym
|
11
|
+
@parent = parent
|
12
|
+
@location = location
|
13
|
+
@index = index
|
14
|
+
@failures = []
|
15
|
+
@reporter = RSpec::Reporter.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def described_object
|
19
|
+
@described_object ||= reporter.described_object
|
20
|
+
end
|
21
|
+
|
22
|
+
def source
|
23
|
+
spec
|
24
|
+
end
|
25
|
+
|
26
|
+
def failure
|
27
|
+
failures.first
|
28
|
+
end
|
29
|
+
|
30
|
+
def run
|
31
|
+
RSpec.with_failure_notifier(failure_notifier) do
|
32
|
+
example_group.run(reporter)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :spec, :failures, :reporter
|
39
|
+
|
40
|
+
def examples
|
41
|
+
@examples ||= example_group.examples
|
42
|
+
end
|
43
|
+
|
44
|
+
def example_group
|
45
|
+
# rubocop:disable Style/DocumentDynamicEvalDefinition, Security/Eval
|
46
|
+
@example_group ||= binding.eval(
|
47
|
+
<<-SPEC, __FILE__, __LINE__.to_i
|
48
|
+
RSpec::ExampleGroup.describe do
|
49
|
+
#{spec}
|
50
|
+
end
|
51
|
+
SPEC
|
52
|
+
)
|
53
|
+
# rubocop:enable Style/DocumentDynamicEvalDefinition, Security/Eval
|
54
|
+
end
|
55
|
+
|
56
|
+
def failure_notifier
|
57
|
+
proc do |failure|
|
58
|
+
failures << RSpec::Failure.new(cause: failure, spec: self)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecDocumentation
|
4
|
+
# Misc utility classes for functionality shared between components, helpers for path normalization.
|
5
|
+
class Util
|
6
|
+
ORDERING_PREFIX_REGEXP = /^[0-9]+-/.freeze
|
7
|
+
|
8
|
+
def self.bundle_path(path)
|
9
|
+
relative_path = Pathname.new(path).relative_path_from(base_dir)
|
10
|
+
bundle_dir.join(*relative_path.split.map { |segment| normalized_filename(segment) }).sub_ext('.html')
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.base_dir
|
14
|
+
root_dir.join('rspec-documentation', 'pages')
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.bundle_dir
|
18
|
+
return root_dir.join('rspec-documentation', 'bundle') unless ENV.key?('RSPEC_DOCUMENTATION_BUNDLE_PATH')
|
19
|
+
|
20
|
+
Pathname.new(ENV.fetch('RSPEC_DOCUMENTATION_BUNDLE_PATH'))
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.root_dir
|
24
|
+
Pathname.new(Dir.pwd)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.label(path)
|
28
|
+
Pathname.new(path).basename.sub_ext('').sub(ORDERING_PREFIX_REGEXP, '')
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.href(path)
|
32
|
+
relative_path = Pathname.new(path).relative_path_from(base_dir)
|
33
|
+
url_root.join(*relative_path.split.map { |segment| normalized_filename(segment) }).sub_ext('.html')
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.url_root
|
37
|
+
return bundle_dir unless ENV.key?('RSPEC_DOCUMENTATION_URL_ROOT')
|
38
|
+
|
39
|
+
Pathname.new(ENV.fetch('RSPEC_DOCUMENTATION_URL_ROOT'))
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.normalized_filename(path)
|
43
|
+
path.to_s.gsub(' ', '-').downcase.sub(ORDERING_PREFIX_REGEXP, '')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'htmlbeautifier'
|
4
|
+
require 'kramdown'
|
5
|
+
require 'kramdown-parser-gfm'
|
6
|
+
require 'paintbrush'
|
7
|
+
require 'redcarpet'
|
8
|
+
require 'rouge'
|
9
|
+
require 'rouge/plugins/redcarpet'
|
10
|
+
require 'rspec'
|
11
|
+
require 'rake'
|
12
|
+
|
13
|
+
require 'pathname'
|
14
|
+
require 'securerandom'
|
15
|
+
|
16
|
+
require_relative 'rspec_documentation/rspec'
|
17
|
+
require_relative 'rspec_documentation/util'
|
18
|
+
require_relative 'rspec_documentation/context'
|
19
|
+
require_relative 'rspec_documentation/configuration'
|
20
|
+
require_relative 'rspec_documentation/document'
|
21
|
+
require_relative 'rspec_documentation/parsed_document'
|
22
|
+
require_relative 'rspec_documentation/html_element'
|
23
|
+
require_relative 'rspec_documentation/ansi_html'
|
24
|
+
require_relative 'rspec_documentation/spec'
|
25
|
+
require_relative 'rspec_documentation/markdown_renderer'
|
26
|
+
require_relative 'rspec_documentation/page_collection'
|
27
|
+
require_relative 'rspec_documentation/page_tree'
|
28
|
+
require_relative 'rspec_documentation/page_tree_element'
|
29
|
+
|
30
|
+
# Internal module used by RSpec::Documentation to run examples and write output into an HTML document.
|
31
|
+
module RSpecDocumentation
|
32
|
+
class Error < StandardError; end
|
33
|
+
|
34
|
+
def self.root
|
35
|
+
Pathname.new(File.dirname(__dir__))
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.template(name, format = :html)
|
39
|
+
ERB.new(root.join('lib/templates', "#{name}.#{format}.erb").read)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.theme(name)
|
43
|
+
ERB.new(root.join('lib/templates/themes', "#{name}.css").read).result(binding)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.context
|
47
|
+
yield configuration.context if block_given?
|
48
|
+
configuration.context
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.configure
|
52
|
+
yield configuration
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.configuration
|
56
|
+
@configuration ||= RSpecDocumentation::Configuration.new
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<hr/>
|
@@ -0,0 +1,39 @@
|
|
1
|
+
.code {
|
2
|
+
font-family: monospace;
|
3
|
+
max-height: 30rem;
|
4
|
+
overflow-y: auto;
|
5
|
+
}
|
6
|
+
|
7
|
+
h1.title {
|
8
|
+
display: inline;
|
9
|
+
}
|
10
|
+
|
11
|
+
.header .separator {
|
12
|
+
display: inline-block;
|
13
|
+
height: 2.5rem;
|
14
|
+
}
|
15
|
+
|
16
|
+
.version {
|
17
|
+
color: #bbb;
|
18
|
+
font-size: 2rem;
|
19
|
+
}
|
20
|
+
|
21
|
+
.ansi-html {
|
22
|
+
display: inline-block;
|
23
|
+
background-color: #2e2e2e;
|
24
|
+
border: 5px solid #2e2e2e;
|
25
|
+
border-radius: 10px;
|
26
|
+
}
|
27
|
+
|
28
|
+
.ansi-html .ansi-color-0 { color: #555555; }
|
29
|
+
.ansi-html .ansi-color-1 { color: #ff0000; }
|
30
|
+
.ansi-html .ansi-color-2 { color: #47ff47; }
|
31
|
+
.ansi-html .ansi-color-3 { color: #e9e947; }
|
32
|
+
.ansi-html .ansi-color-4 { color: #49a0dd; }
|
33
|
+
.ansi-html .ansi-color-5 { color: #8d7eeb; }
|
34
|
+
.ansi-html .ansi-color-6 { color: #2eecff; }
|
35
|
+
.ansi-html .ansi-color-7 { color: #ffffff; }
|
36
|
+
.ansi-html .ansi-color-9 { color: #606060; }
|
37
|
+
.ansi-html .ansi-color-reset { color: #dddddd; }
|
38
|
+
|
39
|
+
<%= Rouge::Themes::Github.render %>
|
@@ -0,0 +1,100 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<style>
|
4
|
+
|
5
|
+
/* cerulean, cosmo, cyborg, darkly, flatly, journal, litera, lumen, lux, materia, minty, morph,
|
6
|
+
pulse, quartz, sandstone, simplex, sketchy, slate, solar, spacelab, superhero, united, vapor,
|
7
|
+
yeti, zephyr */
|
8
|
+
<%= RSpecDocumentation.theme(:materia) %>
|
9
|
+
|
10
|
+
/* Base16, BlackWhiteTheme, Colorful, Github, Gruvbox, IgorPro, Magritte, Molokai, Monokai,
|
11
|
+
MonokaiSublime, Pastie, ThankfulEyes, Tulip */
|
12
|
+
<%= Rouge::Themes::Molokai.render %>
|
13
|
+
|
14
|
+
/* https://stackoverflow.com/a/61587938 */
|
15
|
+
.consistent-height .tab-content {
|
16
|
+
display: flex;
|
17
|
+
}
|
18
|
+
|
19
|
+
.consistent-height .tab-content > .tab-pane {
|
20
|
+
display: block; /* undo "display: none;" */
|
21
|
+
visibility: hidden;
|
22
|
+
margin-right: -100%;
|
23
|
+
width: 100%;
|
24
|
+
}
|
25
|
+
|
26
|
+
.consistent-height .tab-content > .active {
|
27
|
+
visibility: visible;
|
28
|
+
}
|
29
|
+
|
30
|
+
|
31
|
+
.code {
|
32
|
+
font-family: monospace;
|
33
|
+
max-height: 30rem;
|
34
|
+
overflow-y: auto;
|
35
|
+
}
|
36
|
+
|
37
|
+
h1.title {
|
38
|
+
display: inline;
|
39
|
+
}
|
40
|
+
|
41
|
+
.header .separator {
|
42
|
+
display: inline-block;
|
43
|
+
height: 2.5rem;
|
44
|
+
}
|
45
|
+
|
46
|
+
.version {
|
47
|
+
font-size: 2rem;
|
48
|
+
}
|
49
|
+
|
50
|
+
.highlight {
|
51
|
+
padding: 1rem;
|
52
|
+
}
|
53
|
+
|
54
|
+
.ansi-html {
|
55
|
+
display: inline-block;
|
56
|
+
background-color: #2e2e2e;
|
57
|
+
border: 5px solid #2e2e2e;
|
58
|
+
border-radius: 10px;
|
59
|
+
}
|
60
|
+
|
61
|
+
.ansi-html .ansi-color-0 { color: var(--bs-dark); }
|
62
|
+
.ansi-html .ansi-color-1 { color: var(--bs-red); }
|
63
|
+
.ansi-html .ansi-color-2 { color: var(--bs-green); }
|
64
|
+
.ansi-html .ansi-color-3 { color: var(--bs-yellow) }
|
65
|
+
.ansi-html .ansi-color-4 { color: var(--bs-blue); }
|
66
|
+
.ansi-html .ansi-color-5 { color: var(--bs-purple); }
|
67
|
+
.ansi-html .ansi-color-6 { color: var(--bs-cyan); }
|
68
|
+
.ansi-html .ansi-color-7 { color: var(--bs-white); }
|
69
|
+
.ansi-html .ansi-color-9 { color: var(--bs-light); }
|
70
|
+
.ansi-html .ansi-color-reset { color: var(--bs-light);; }
|
71
|
+
|
72
|
+
</style>
|
73
|
+
</head>
|
74
|
+
|
75
|
+
<body>
|
76
|
+
<div class="container m-3 p-3">
|
77
|
+
<div class="bg-light sticky-top">
|
78
|
+
<%= header %>
|
79
|
+
</div>
|
80
|
+
|
81
|
+
<div class="row">
|
82
|
+
<div class="col-auto page-tree fs-6 mt-1 ms-3 me-3">
|
83
|
+
<% page_tree.elements.each do |element| %>
|
84
|
+
<%= element %>
|
85
|
+
<% end %>
|
86
|
+
</div>
|
87
|
+
|
88
|
+
<div class="col">
|
89
|
+
<%= html %>
|
90
|
+
</div>
|
91
|
+
</div>
|
92
|
+
<%= footer %>
|
93
|
+
</div>
|
94
|
+
|
95
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/js/bootstrap.bundle.min.js"
|
96
|
+
integrity="sha512-i9cEfJwUwViEPFKdC1enz4ZRGBj8YQo6QByFTF92YXHi7waCqyexvRD75S5NVTsSiTv7rKWqG9Y5eFxmRsOn0A=="
|
97
|
+
crossorigin="anonymous"
|
98
|
+
referrerpolicy="no-referrer"></script>
|
99
|
+
</body>
|
100
|
+
</html>
|
@@ -0,0 +1,119 @@
|
|
1
|
+
<div class="modal fade" id="modal-<%= element_id %>-side-by-side" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
|
2
|
+
<div class="modal-dialog modal-xl">
|
3
|
+
<div class="modal-content">
|
4
|
+
<div class="modal-header">
|
5
|
+
<h5 class="modal-title" id="modal-<%= element_id %>-side-by-side-label">Side-by-side view</h5>
|
6
|
+
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
7
|
+
</div>
|
8
|
+
<div class="modal-body">
|
9
|
+
<div class="container p-3">
|
10
|
+
<div class="row border">
|
11
|
+
<div class="col border">
|
12
|
+
<div class="p-3 mb-5 code highlight">
|
13
|
+
<%= code_source.gsub("\n", '<br/>').gsub(' ', ' ') %>
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<% unless html_source.nil? %>
|
18
|
+
<div class="col border">
|
19
|
+
<div class="p-3 mb-5 code highlight">
|
20
|
+
<%= html_source.gsub("\n", '<br/>').gsub(' ', ' ') %>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
<% end %>
|
24
|
+
|
25
|
+
<div class="col border">
|
26
|
+
<div class="p-3 mb-5 <%= html_source.nil? ? 'code highlight' : nil %>">
|
27
|
+
<%= rendered_result %>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
<div class="modal-footer">
|
34
|
+
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
35
|
+
</div>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
</div>
|
39
|
+
|
40
|
+
<div class="consistent-height">
|
41
|
+
|
42
|
+
<ul class="nav nav-tabs" id="html-tabs-<%= element_id %>" role="tablist">
|
43
|
+
|
44
|
+
<li class="nav-item" role="presentation">
|
45
|
+
<button class="nav-link active"
|
46
|
+
id="code-source-<%= element_id %>-tab"
|
47
|
+
data-bs-toggle="tab"
|
48
|
+
data-bs-target="#code-source-<%= element_id %>"
|
49
|
+
type="button"
|
50
|
+
role="tab"
|
51
|
+
aria-controls="code-source"
|
52
|
+
aria-selected="true">Code</button>
|
53
|
+
</li>
|
54
|
+
|
55
|
+
<% unless html_source.nil? %>
|
56
|
+
<li class="nav-item" role="presentation">
|
57
|
+
<button class="nav-link"
|
58
|
+
id="html-source-<%= element_id %>-tab"
|
59
|
+
data-bs-toggle="tab"
|
60
|
+
data-bs-target="#html-source-<%= element_id %>"
|
61
|
+
type="button"
|
62
|
+
role="tab"
|
63
|
+
aria-controls="html-source"
|
64
|
+
aria-selected="true">HTML</button>
|
65
|
+
</li>
|
66
|
+
<% end %>
|
67
|
+
|
68
|
+
<li class="nav-item" role="presentation">
|
69
|
+
<button class="nav-link"
|
70
|
+
id="rendered-<%= element_id %>-tab"
|
71
|
+
data-bs-toggle="tab"
|
72
|
+
data-bs-target="#rendered-<%= element_id %>"
|
73
|
+
type="button"
|
74
|
+
role="tab"
|
75
|
+
aria-controls="rendered"
|
76
|
+
aria-selected="false"><%= html_source.nil? ? 'Output' : 'Rendered HTML' %></button>
|
77
|
+
</li>
|
78
|
+
|
79
|
+
<li class="nav-item bs-auto ms-auto mb-2">
|
80
|
+
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#modal-<%= element_id %>-side-by-side">
|
81
|
+
View Side-by-side
|
82
|
+
</button>
|
83
|
+
</li>
|
84
|
+
|
85
|
+
</ul>
|
86
|
+
|
87
|
+
<div class="tab-content" id="html-tab-content-<%= element_id %>">
|
88
|
+
|
89
|
+
<div class="tab-pane fade show active"
|
90
|
+
id="code-source-<%= element_id %>"
|
91
|
+
role="tabpanel"
|
92
|
+
aria-labelledby="code-source-<%= element_id %>-tab">
|
93
|
+
<div class="p-3 mb-5 code highlight">
|
94
|
+
<%= code_source.gsub("\n", '<br/>').gsub(' ', ' ') %>
|
95
|
+
</div>
|
96
|
+
</div>
|
97
|
+
|
98
|
+
<% unless html_source.nil? %>
|
99
|
+
<div class="tab-pane fade"
|
100
|
+
id="html-source-<%= element_id %>"
|
101
|
+
role="tabpanel"
|
102
|
+
aria-labelledby="html-source-<%= element_id %>-tab">
|
103
|
+
<div class="p-3 mb-5 code highlight">
|
104
|
+
<%= html_source.gsub("\n", '<br/>').gsub(' ', ' ') %>
|
105
|
+
</div>
|
106
|
+
</div>
|
107
|
+
<% end %>
|
108
|
+
|
109
|
+
<div class="tab-pane fade"
|
110
|
+
id="rendered-<%= element_id %>"
|
111
|
+
role="tabpanel"
|
112
|
+
aria-labelledby="rendered-<%= element_id %>-tab">
|
113
|
+
<div class="p-3 mb-5 <%= html_source.nil? ? 'code highlight' : nil %>">
|
114
|
+
<%= rendered_result %>
|
115
|
+
</div>
|
116
|
+
</div>
|
117
|
+
</div>
|
118
|
+
</div>
|
119
|
+
|