hologram 1.2.0 → 1.3.0

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -1
  3. data/CHANGELOG.md +37 -0
  4. data/README.md +243 -5
  5. data/hologram.gemspec +2 -0
  6. data/lib/hologram.rb +2 -0
  7. data/lib/hologram/block_code_renderer.rb +45 -0
  8. data/lib/hologram/code_example_renderer.rb +73 -0
  9. data/lib/hologram/code_example_renderer/example.rb +27 -0
  10. data/lib/hologram/code_example_renderer/factory.rb +60 -0
  11. data/lib/hologram/code_example_renderer/renderers/haml_renderer.rb +17 -0
  12. data/lib/hologram/code_example_renderer/renderers/html_renderer.rb +6 -0
  13. data/lib/hologram/code_example_renderer/renderers/js_renderer.rb +5 -0
  14. data/lib/hologram/code_example_renderer/renderers/jsx_renderer.rb +4 -0
  15. data/lib/hologram/code_example_renderer/renderers/react_renderer.rb +21 -0
  16. data/lib/hologram/code_example_renderer/renderers/slim_renderer.rb +16 -0
  17. data/lib/hologram/code_example_renderer/template.rb +33 -0
  18. data/lib/hologram/display_message.rb +13 -0
  19. data/lib/hologram/doc_block_collection.rb +5 -1
  20. data/lib/hologram/doc_builder.rb +58 -7
  21. data/lib/hologram/doc_parser.rb +20 -4
  22. data/lib/hologram/document_block.rb +22 -4
  23. data/lib/hologram/link_helper.rb +18 -0
  24. data/lib/hologram/markdown_renderer.rb +56 -35
  25. data/lib/hologram/version.rb +1 -1
  26. data/lib/template/code_example_templates/js_example_template.html.erb +7 -0
  27. data/lib/template/code_example_templates/jsx_example_template.html.erb +7 -0
  28. data/lib/template/code_example_templates/markup_example_template.html.erb +10 -0
  29. data/lib/template/code_example_templates/markup_table_template.html.erb +23 -0
  30. data/lib/template/hologram_config.yml +23 -0
  31. data/spec/block_code_renderer_spec.rb +279 -0
  32. data/spec/code_example_renderer/example_spec.rb +26 -0
  33. data/spec/code_example_renderer/factory_spec.rb +102 -0
  34. data/spec/code_example_renderer/template_spec.rb +50 -0
  35. data/spec/display_message_spec.rb +18 -0
  36. data/spec/doc_block_collection_spec.rb +26 -1
  37. data/spec/doc_builder_spec.rb +28 -5
  38. data/spec/doc_parser_spec.rb +67 -7
  39. data/spec/document_block_spec.rb +33 -2
  40. data/spec/fixtures/source/components/button/buttons.css +8 -6
  41. data/spec/fixtures/source/components/button/skin/buttonSkins.css +17 -0
  42. data/spec/fixtures/styleguide/base_css.html +176 -52
  43. data/spec/fixtures/styleguide/index.html +6 -14
  44. data/spec/link_helper_spec.rb +57 -0
  45. data/spec/markdown_renderer_spec.rb +82 -0
  46. metadata +75 -19
@@ -1,6 +1,6 @@
1
1
  module Hologram
2
2
  class DocumentBlock
3
- COMMENT_REGEX = /^\s*---\s(.*?)\s---$/m
3
+ COMMENT_REGEX = /^\s*---\s(.*?)\s---[\r]?$/m
4
4
 
5
5
  attr_accessor :name, :parent, :children, :title, :categories, :markdown, :config, :heading, :errors
6
6
 
@@ -26,7 +26,7 @@ module Hologram
26
26
  @config = config
27
27
  @name = config['name']
28
28
  @categories = Array(config['category'] || config['categories'])
29
- @title = config['title']
29
+ @title = h(config['title'])
30
30
  @parent = config['parent']
31
31
  @markdown = markdown
32
32
 
@@ -51,8 +51,26 @@ module Hologram
51
51
  end
52
52
 
53
53
  # sets the header tag based on how deep your nesting is
54
- def markdown_with_heading(heading = 1)
55
- "\n\n<h#{heading.to_s} id=\"#{@name}\">#{@title}</h#{heading.to_s}>" + @markdown
54
+
55
+ def markdown_with_heading(heading = 1, opts={})
56
+ include_sub_nav = opts[:include_sub_nav] || false
57
+
58
+ output = "\n\n<h#{heading.to_s} id=\"#{@name}\" class=\"#{css_class_name}\">#{@title}</h#{heading.to_s}>"
59
+ if include_sub_nav && !children.empty?
60
+ output += "\n<ul class=\"section-nav\">"
61
+ children.values.each do |child|
62
+ output += "\n <li><a href=\"\##{child.name}\">#{child.title}</a></li>"
63
+ end
64
+ output += "\n</ul>\n"
65
+ end
66
+ output += @markdown
67
+ output
68
+ end
69
+
70
+ private
71
+
72
+ def css_class_name
73
+ 'styleguide'
56
74
  end
57
75
  end
58
76
  end
@@ -0,0 +1,18 @@
1
+ module Hologram
2
+ class LinkHelper
3
+ def initialize(pages)
4
+ @all_links = {}
5
+ pages.each do |page|
6
+ page[:component_names].each do |component_name|
7
+ @all_links[component_name] ||= "#{page[:name]}\##{component_name}"
8
+ end
9
+ end
10
+ end
11
+
12
+ attr_reader :all_links
13
+
14
+ def link_for(component_name)
15
+ all_links[component_name]
16
+ end
17
+ end
18
+ end
@@ -1,52 +1,73 @@
1
+ require 'hologram/block_code_renderer'
2
+
3
+ include ERB::Util
4
+
1
5
  module Hologram
2
6
  class MarkdownRenderer < Redcarpet::Render::HTML
3
- def block_code(code, language)
4
- formatter = Rouge::Formatters::HTML.new(wrap: false)
5
- if language and language.include?('example')
6
- if language.include?('js')
7
- lexer = Rouge::Lexer.find('js')
8
- # first actually insert the code in the docs so that it will run and make our example work.
9
- '<script>' + code + '</script> <div class="codeBlock jsExample"><div class="highlight"><pre>' + formatter.format(lexer.lex(code)) + '</pre></div></div>'
10
- else
11
- lexer = Rouge::Lexer.find(get_lexer(language))
12
- '<div class="codeExample">' + '<div class="exampleOutput">' + render_html(code, language) + '</div>' + '<div class="codeBlock"><div class="highlight"><pre>' + formatter.format(lexer.lex(code)) + '</pre></div></div>' + '</div>'
13
- end
7
+ def initialize(opts={})
8
+ super(opts)
9
+ @link_helper = opts[:link_helper]
10
+ end
11
+
12
+ def list(contents, list_type)
13
+ case list_type
14
+ when :ordered
15
+ "<ol class=\"#{css_class_name}\">#{contents}</ol>"
14
16
  else
15
- lexer = Rouge::Lexer.find_fancy('guess', code)
16
- '<div class="codeBlock"><div class="highlight"><pre>' + formatter.format(lexer.lex(code)) + '</pre></div></div>'
17
+ "<ul class=\"#{css_class_name}\">#{contents}</ul>"
17
18
  end
18
19
  end
19
20
 
20
- private
21
- def render_html(code, language)
22
- case language
23
- when 'haml_example'
24
- safe_require('haml', language)
25
- return Haml::Engine.new(code.strip).render(template_rendering_scope, {})
26
- else
27
- code
28
- end
21
+ def paragraph(text)
22
+ "<p class=\"#{css_class_name}\">#{text}</p>"
23
+ end
24
+
25
+ def table(header, body)
26
+ "<table class=\"#{css_class_name}\"> #{header} #{body} </table>"
27
+ end
28
+
29
+ def codespan(code)
30
+ "<code class=\"#{css_class_name}\">#{html_escape(code)}</code>"
31
+ end
32
+
33
+ def link(link, title, content)
34
+ "<a class=\"#{css_class_name}\" href=\"#{link}\" title=\"#{title || link}\">#{content}</a>"
29
35
  end
30
36
 
31
- def template_rendering_scope
32
- Object.new
37
+ def block_code(code, language)
38
+ BlockCodeRenderer.new(code, language).render
33
39
  end
34
40
 
35
- def get_lexer(language)
36
- case language
37
- when 'haml_example'
38
- 'haml'
39
- else
40
- 'html'
41
+ def preprocess(full_document)
42
+ if link_helper
43
+ link_defs + "\n" + full_document
44
+ else
45
+ full_document
41
46
  end
42
47
  end
43
48
 
44
- def safe_require(templating_library, language)
45
- begin
46
- require templating_library
47
- rescue LoadError
48
- raise "#{templating_library} must be present for you to use #{language}"
49
+ def postprocess(full_document)
50
+ invalid_links = full_document.scan(/(?: \[ [\s\w]+ \]){2}/x)
51
+
52
+ invalid_links.each do |invalid_link|
53
+ component = /\[.+\]/.match(invalid_link)[1]
54
+ DisplayMessage.warning("Invalid reference link - #{invalid_link}." +
55
+ "Presumably the component #{component} does not exist.")
49
56
  end
57
+
58
+ full_document
59
+ end
60
+
61
+ def css_class_name
62
+ 'styleguide'
63
+ end
64
+
65
+ private
66
+
67
+ attr_reader :link_helper
68
+
69
+ def link_defs
70
+ @_link_defs ||= link_helper.all_links.map { |c_name, link| "[#{c_name}]: #{link}" }.join("\n")
50
71
  end
51
72
  end
52
73
  end
@@ -25,5 +25,5 @@
25
25
  # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
26
 
27
27
  module Hologram
28
- VERSION = "1.2.0"
28
+ VERSION = "1.3.0"
29
29
  end
@@ -0,0 +1,7 @@
1
+ <script><%= rendered_example %></script>
2
+ <div class="codeBlock jsExample">
3
+ <div class="highlight">
4
+ <pre><%= code_example %></pre>
5
+ </div>
6
+ </div>
7
+
@@ -0,0 +1,7 @@
1
+ <script type='text/jsx'><%= rendered_example %></script>
2
+ <div class="codeBlock jsExample">
3
+ <div class="highlight">
4
+ <pre><%= code_example %></pre>
5
+ </div>
6
+ </div>
7
+
@@ -0,0 +1,10 @@
1
+ <div class="codeExample">
2
+ <div class="exampleOutput">
3
+ <%= rendered_example %>
4
+ </div>
5
+ <div class="codeBlock">
6
+ <div class="highlight">
7
+ <pre><%= code_example %></pre>
8
+ </div>
9
+ </div>
10
+ </div>
@@ -0,0 +1,23 @@
1
+ <div class="codeTable">
2
+ <table>
3
+ <tbody>
4
+ <% examples.each do |example| %>
5
+ <tr>
6
+ <th>
7
+ <div class="exampleOutput">
8
+ <%= example.rendered_example %>
9
+ </div>
10
+ </th>
11
+ <td>
12
+ <div class="codeBlock">
13
+ <div class="highlight">
14
+ <pre><%= example.code_example %></pre>
15
+ </div>
16
+ </div>
17
+ </td>
18
+ </tr>
19
+ <% end %>
20
+ </tbody>
21
+ </table>
22
+ </div>
23
+
@@ -12,6 +12,17 @@ destination: ./docs
12
12
  # You may put doc related assets here too: images, css, etc.
13
13
  documentation_assets: ./doc_assets
14
14
 
15
+ # The folder that contains templates for rendering code examples.
16
+ # If you want to change the way code examples appear in the styleguide,
17
+ # modify the files in this folder
18
+ code_example_templates: ./code_example_templates
19
+
20
+ # The folder that contains custom code example renderers.
21
+ # If you want to create additional renderers that are not provided
22
+ # by Hologram (i.e. coffeescript renderer, jade renderer, etc)
23
+ # place them in this folder
24
+ code_example_renderers: ./code_example_renderers
25
+
15
26
  # Any other asset folders that need to be copied to the destination
16
27
  # folder. Typically this will include the css that you are trying to
17
28
  # document. May also include additional folders as needed.
@@ -22,3 +33,15 @@ dependencies:
22
33
  # Alternatively, you may have an index.md in the documentation assets
23
34
  # folder instead of specifying this config.
24
35
  index: basics
36
+
37
+ # To additionally output navigation for top level sections, set the value to
38
+ # 'section'. To output navigation for sub-sections,
39
+ # set the value to `all`
40
+ nav_level: all
41
+
42
+ # Hologram displays warnings when there are issues with your docs
43
+ # (e.g. if a component's parent is not found, if the _header.html and/or
44
+ # _footer.html files aren't found)
45
+ # If you want Hologram to exit on these warnings, set the value to 'true'
46
+ # (Default value is 'false')
47
+ exit_on_warnings: false
@@ -0,0 +1,279 @@
1
+ require 'spec_helper'
2
+ require 'hologram/block_code_renderer'
3
+ require 'haml'
4
+ require 'securerandom'
5
+
6
+ Hologram::CodeExampleRenderer.load_renderers_and_templates
7
+
8
+ describe Hologram::BlockCodeRenderer do
9
+ describe '#render' do
10
+ subject { Hologram::BlockCodeRenderer.new(code, markdown_language).render.strip }
11
+
12
+ context 'expected language' do
13
+ context 'react' do
14
+ let(:language) { 'react' }
15
+ let(:code) { '<ReactExample property="value">Example</ReactExample>' }
16
+
17
+ context 'when the language is a react_example' do
18
+ let(:markdown_language) { 'react_example' }
19
+ let(:div_id) { 'randomId' }
20
+
21
+ before :each do
22
+ SecureRandom.stub('hex').and_return(div_id);
23
+ end
24
+
25
+ it { is_expected.to eq [
26
+ "<div class=\"codeExample\">",
27
+ " <div class=\"exampleOutput\">",
28
+ " <div id=\"#{div_id}\"></div>",
29
+ "<script type=\"text/jsx\">",
30
+ " React.render(",
31
+ " <ReactExample property=\"value\">Example</ReactExample>,",
32
+ " document.getElementById('#{div_id}')",
33
+ " );",
34
+ "</script>",
35
+ " </div>",
36
+ " <div class=\"codeBlock\">",
37
+ " <div class=\"highlight\">",
38
+ " <pre><span class=\"nt\">&lt;ReactExample</span> <span class=\"na\">property=</span><span class=\"s\">\"value\"</span><span class=\"nt\">&gt;</span>Example<span class=\"nt\">&lt;/ReactExample&gt;</span></pre>",
39
+ " </div>",
40
+ " </div>",
41
+ "</div>"
42
+ ].join("\n") }
43
+ end
44
+ end
45
+
46
+ context 'slim' do
47
+ let(:language) { 'slim' }
48
+ let(:code) { 'h1 Markup Example' }
49
+
50
+ context 'when the language is a slim_example' do
51
+ let(:markdown_language) { 'slim_example' }
52
+
53
+ it { is_expected.to eq [
54
+ "<div class=\"codeExample\">",
55
+ " <div class=\"exampleOutput\">",
56
+ " <h1>Markup Example</h1>",
57
+ " </div>",
58
+ " <div class=\"codeBlock\">",
59
+ " <div class=\"highlight\">",
60
+ " <pre><span class=\"nt\">h1</span><span class=\"w\"> </span>Markup<span class=\"w\"> </span>Example</pre>",
61
+ " </div>",
62
+ " </div>",
63
+ "</div>",
64
+ ].join("\n") }
65
+ end
66
+ end
67
+
68
+ context 'haml' do
69
+ let(:language) { 'haml' }
70
+ let(:code) { '%h1 Example' }
71
+
72
+ context 'when the language is a haml_example' do
73
+ let(:markdown_language) { 'haml_example' }
74
+
75
+ it { is_expected.to eq [
76
+ "<div class=\"codeExample\">",
77
+ " <div class=\"exampleOutput\">",
78
+ " <h1>Example</h1>\n",
79
+ " </div>",
80
+ " <div class=\"codeBlock\">",
81
+ " <div class=\"highlight\">",
82
+ " <pre><span class=\"nt\">%h1</span> Example</pre>",
83
+ " </div>",
84
+ " </div>",
85
+ "</div>",
86
+ ].join("\n") }
87
+ end
88
+
89
+ context 'when the language is a haml_example_table' do
90
+ let(:markdown_language) { 'haml_example_table' }
91
+ let(:code) { [
92
+ ".spinner-lg",
93
+ " %i.fa.fa-spin",
94
+ "",
95
+ "%h1 Example"
96
+ ].join("\n") }
97
+
98
+ it { is_expected.to eq [
99
+ "<div class=\"codeTable\">",
100
+ " <table>",
101
+ " <tbody>",
102
+ " ",
103
+ " <tr>",
104
+ " <th>",
105
+ " <div class=\"exampleOutput\">",
106
+ " <div class='spinner-lg'>",
107
+ " <i class='fa fa-spin'></i>",
108
+ "</div>",
109
+ "",
110
+ " </div>",
111
+ " </th>",
112
+ " <td>",
113
+ " <div class=\"codeBlock\">",
114
+ " <div class=\"highlight\">",
115
+ " <pre><span class=\"nc\">.spinner-lg</span>",
116
+ " <span class=\"nt\">%i</span><span class=\"nc\">.fa.fa-spin</span></pre>",
117
+ " </div>",
118
+ " </div>",
119
+ " </td>",
120
+ " </tr>",
121
+ " ",
122
+ " <tr>",
123
+ " <th>",
124
+ " <div class=\"exampleOutput\">",
125
+ " <h1>Example</h1>",
126
+ "",
127
+ " </div>",
128
+ " </th>",
129
+ " <td>",
130
+ " <div class=\"codeBlock\">",
131
+ " <div class=\"highlight\">",
132
+ " <pre><span class=\"nt\">%h1</span> Example</pre>",
133
+ " </div>",
134
+ " </div>",
135
+ " </td>",
136
+ " </tr>",
137
+ " ",
138
+ " </tbody>",
139
+ " </table>",
140
+ "</div>"
141
+ ].join("\n") }
142
+ end
143
+ end
144
+
145
+ context 'html' do
146
+ let(:language) { 'html' }
147
+ let(:code) { '<h2></h2>' }
148
+ let(:formatted_code) { 'formatted h2' }
149
+ context 'when the language is html_example' do
150
+ let(:markdown_language) { 'html_example' }
151
+
152
+ it { is_expected.to eq [
153
+ "<div class=\"codeExample\">",
154
+ " <div class=\"exampleOutput\">",
155
+ " <h2></h2>",
156
+ " </div>",
157
+ " <div class=\"codeBlock\">",
158
+ " <div class=\"highlight\">",
159
+ " <pre><span class=\"nt\">&lt;h2&gt;&lt;/h2&gt;</span></pre>",
160
+ " </div>",
161
+ " </div>",
162
+ "</div>",
163
+ ].join("\n") }
164
+ end
165
+
166
+ context 'when the language is a html_example_table' do
167
+ let(:markdown_language) { 'html_example_table' }
168
+ let(:code) { [
169
+ "<div class='spinner-lg'></div>",
170
+ "",
171
+ "<h1>Example</h1>"
172
+ ].join("\n") }
173
+
174
+ it { is_expected.to eq [
175
+ "<div class=\"codeTable\">",
176
+ " <table>",
177
+ " <tbody>",
178
+ " ",
179
+ " <tr>",
180
+ " <th>",
181
+ " <div class=\"exampleOutput\">",
182
+ " <div class='spinner-lg'></div>",
183
+ " </div>",
184
+ " </th>",
185
+ " <td>",
186
+ " <div class=\"codeBlock\">",
187
+ " <div class=\"highlight\">",
188
+ " <pre><span class=\"nt\">&lt;div</span> <span class=\"na\">class=</span><span class=\"s\">'spinner-lg'</span><span class=\"nt\">&gt;&lt;/div&gt;</span></pre>",
189
+ " </div>",
190
+ " </div>",
191
+ " </td>",
192
+ " </tr>",
193
+ " ",
194
+ " <tr>",
195
+ " <th>",
196
+ " <div class=\"exampleOutput\">",
197
+ " <h1>Example</h1>",
198
+ " </div>",
199
+ " </th>",
200
+ " <td>",
201
+ " <div class=\"codeBlock\">",
202
+ " <div class=\"highlight\">",
203
+ " <pre><span class=\"nt\">&lt;h1&gt;</span>Example<span class=\"nt\">&lt;/h1&gt;</span></pre>",
204
+ " </div>",
205
+ " </div>",
206
+ " </td>",
207
+ " </tr>",
208
+ " ",
209
+ " </tbody>",
210
+ " </table>",
211
+ "</div>"
212
+ ].join("\n") }
213
+ end
214
+ end
215
+
216
+ context 'js_example' do
217
+ let(:language) { 'js' }
218
+ let(:markdown_language) { 'js_example' }
219
+ let(:code) { '$(document).ready(function() {});' }
220
+
221
+ it "inserts the code into the docs so that it will run and make the example work" do
222
+ expect(subject).to include "<script>$(document).ready(function() {});</script>"
223
+ end
224
+
225
+ it { is_expected.to include [
226
+ "<div class=\"codeBlock jsExample\">",
227
+ " <div class=\"highlight\">",
228
+ " <pre><span class=\"nx\">$</span><span class=\"p\">(</span><span class=\"nb\">document</span><span class=\"p\">).</span><span class=\"nx\">ready</span><span class=\"p\">(</span><span class=\"kd\">function</span><span class=\"p\">()</span> <span class=\"p\">{});</span></pre>",
229
+ " </div>",
230
+ "</div>",
231
+ ].join("\n") }
232
+ end
233
+
234
+ context 'jsx_example' do
235
+ let(:language) { 'jsx' }
236
+ let(:markdown_language) { 'jsx_example' }
237
+ let(:code) { '$(document).ready(function () { React.render(<div className="foo"></div>) });' }
238
+
239
+ it "inserts the code into the docs so that it will run and make the example work" do
240
+ expect(subject).to include "<script type='text/jsx'>$(document).ready(function () { React.render(<div className=\"foo\"></div>) });</script>"
241
+ end
242
+
243
+ it { is_expected.to include [
244
+ "<div class=\"codeBlock jsExample\">",
245
+ " <div class=\"highlight\">",
246
+ " <pre>$(document).ready(function () { React.render(<span class=\"nt\">&lt;div</span> <span class=\"na\">className=</span><span class=\"s\">\"foo\"</span><span class=\"nt\">&gt;&lt;/div&gt;</span>) });</pre>",
247
+ " </div>",
248
+ "</div>",
249
+ ].join("\n") }
250
+ end
251
+ end
252
+
253
+ context 'unexpected language' do
254
+ let(:markdown_language) { 'fortran' }
255
+ let(:code) { 'goto 12' }
256
+
257
+ it { is_expected.to eq [
258
+ "<div class=\"codeBlock\">",
259
+ " <div class=\"highlight\">",
260
+ " <pre>goto 12</pre>",
261
+ " </div>",
262
+ "</div>",
263
+ ].join("\n") }
264
+ end
265
+
266
+ context 'no language' do
267
+ let(:markdown_language) { nil }
268
+ let(:code) { 'unknown code' }
269
+
270
+ it { is_expected.to eq [
271
+ "<div class=\"codeBlock\">",
272
+ " <div class=\"highlight\">",
273
+ " <pre>unknown code</pre>",
274
+ " </div>",
275
+ "</div>",
276
+ ].join("\n") }
277
+ end
278
+ end
279
+ end