hologram 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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