lookbook 0.8.2 → 0.9.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b2895b167e1e2dc38f33927d4dfab553e0fe58e11b3558330d95eb139011c25
4
- data.tar.gz: c7c62607dc8634d4883fde5a4495d5bfcf7c2fb30edbb37ba4ab228bf0735cd2
3
+ metadata.gz: e7dfafad463df617a30ac6baff60536568771276737340b18f094bb4ad29b90a
4
+ data.tar.gz: 24221d0a990d8181f7fec06bd2acc24d21599b33778b25591054a1d279317d6c
5
5
  SHA512:
6
- metadata.gz: 510e98f6284e5d374347612e4c724bb5745b09cf82945443d2f37c6a54dde996298c31598ba2a5188ee325fb7ee550857bf66b1f9ecf260fb0ea4db0963f50b1
7
- data.tar.gz: 2d41d201fd170a206b91638cc6eb51d638ade8f8d8c30206ce5e4ecbb0e53023c68379fc93428b161e4b5efeb28ead5b4d6908d9e689b493b58175f0016ab4a9
6
+ metadata.gz: a84d78ab4e4bd953f0f3b012a6cf8056efe57f37656ed9ba9839ed99c65cf824a510b1286eede9a4b3640cd9ee25575f0c58fd6998752169303fa5a29df10ec8
7
+ data.tar.gz: 0d516c5a151d34929594abb832827783fa8ceba7a95c2bbea7113a094a1320a5e8b394cab441f551cf6f9d0d4c715a72f00d14a414684e6995362344bc19de08
data/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  ---
14
14
 
15
15
  <div align="center">
16
- <a href="#installing">Installing</a> • <a href="#previews">Previews</a> • <a href="#pages">Pages</a> • <a href="#deployment">Deployment</a> • <a href="#config">Config</a>
16
+ <a href="#installing">Installing</a> • <a href="#previews">Previews</a> • <a href="#pages">Pages</a> • <a href="#deployment">Deployment</a> • <a href="#config">Config</a>
17
17
  </div>
18
18
 
19
19
  ---
@@ -481,10 +481,10 @@ end
481
481
  If you need to add more long-form documentation to live alongside your component previews you can do so using Lookbook's markdown-powered `pages` system.
482
482
 
483
483
  > ⚠️ This feature is currently flagged as an **experimental** feature which requires [feature opt-in](#experimental-features) to use. Its API and implementation may change before it is released.
484
- >
484
+ >
485
485
  > To enable support for pages in your project, add `config.lookbook.experimental_features = [:pages]` into your application configuration file.
486
486
 
487
- ### Pages demo
487
+ ### Pages demo
488
488
 
489
489
  For an example of some pages in Lookbook, check out the [example pages](https://lookbook-demo-app.herokuapp.com/lookbook) in the Lookbook demo app and the associated [page files](https://github.com/allmarkedup/lookbook-demo/tree/main/test/components/docs) in the demo repo.
490
490
 
@@ -496,7 +496,7 @@ Pages must have either a `.html.erb` or a `.md.erb` file extension. All pages a
496
496
 
497
497
  Pages can optionally make use of a **YAML frontmatter block** to customise the behaviour and content of the page itself.
498
498
 
499
- An example page might look like this:
499
+ An example page might look like this:
500
500
 
501
501
  ```markdown
502
502
  ---
@@ -510,7 +510,7 @@ contents will be run through a Markdown parser/renderer before display.
510
510
  Fenced code blocks are fully supported and will be highlighted appropriately.
511
511
 
512
512
  ERB can be used in here.
513
- The template will be rendered **before** being parsed as Markdown.
513
+ The template will be rendered **before** being parsed as Markdown.
514
514
 
515
515
  You can can access data about the page using the `@page` variable.
516
516
  The title of this page is "<%= @page.title %>".
@@ -656,6 +656,35 @@ The default language is `ruby`. To highlight a different language you need to sp
656
656
 
657
657
  > Lookbook uses [Rouge](https://github.com/rouge-ruby/rouge) for syntax highlighting. You can find a [full list of supported languages here](https://github.com/rouge-ruby/rouge/blob/master/docs/Languages.md).
658
658
 
659
+ ### Tabs
660
+
661
+ You can break up your page's content into tabs. If your avatar page is named `01_avatar.md.erb`, by declaring a page named `01_avatar[web].md.erb` it will create a **web** tab on the page. Tabs like normal Pages can contain embedded previews and code examples.
662
+
663
+ ```
664
+ test/components/docs/
665
+ ├── 01_avatar.md.erb
666
+ ├── 01_avatar[design].md.erb
667
+ ├── 01_avatar[mobile].md.erb
668
+ ├── 01_avatar[web].md.erb
669
+ ```
670
+
671
+ By declaring the `label` frontmatter you can change the label shown on the tab:
672
+
673
+ ```
674
+ ---
675
+ label: Website
676
+ ---
677
+ ```
678
+
679
+ If you want the tabs in a different order, you can use the `position` frontmatter:
680
+
681
+ ```
682
+ ---
683
+ label: Web
684
+ position: 1
685
+ ---
686
+ ```
687
+
659
688
  ---
660
689
 
661
690
  ### Pages configuration
@@ -743,10 +772,10 @@ If for some reason you wish to enable file watching or runtime preview annotatio
743
772
  # config/environments/production.rb
744
773
 
745
774
  # enable file-change listening
746
- config.lookbook.listen = true
775
+ config.lookbook.listen = true
747
776
 
748
777
  # enable runtime preview parsing
749
- config.lookbook.runtime_parsing = true
778
+ config.lookbook.runtime_parsing = true
750
779
  ```
751
780
 
752
781
  <h2 id="config">General Configuration</h2>
@@ -778,7 +807,7 @@ config.lookbook.listen_paths << Rails.root.join('app/other/directory')
778
807
  If you want to change the favicon used by the Lookbook UI, you can provide a path to your own (or a data-uri string) using the `ui_favicon` option:
779
808
 
780
809
  ```ruby
781
- config.lookbook.ui_favicon = "/path/to/my/favicon.png"
810
+ config.lookbook.ui_favicon = "/path/to/my/favicon.png"
782
811
  ```
783
812
 
784
813
  > To disable the favicon entirely, set the value to `false`.
@@ -788,7 +817,7 @@ config.lookbook.ui_favicon = "/path/to/my/favicon.png"
788
817
  Specify a project name to display in the top left of the UI (instead of the default "Lookbook"):
789
818
 
790
819
  ```ruby
791
- config.lookbook.project_name = "My Project"
820
+ config.lookbook.project_name = "My Project"
792
821
  ```
793
822
 
794
823
  > If you don't want to display a project name at all, set the value to `false`.
@@ -58,7 +58,7 @@
58
58
  }
59
59
 
60
60
  @layer components {
61
- .unsectioned > #nav > ul > li > div {
61
+ .unsectioned > nav > ul > li > div {
62
62
  @apply py-1 border-b border-gray-300;
63
63
  }
64
64
 
@@ -20,6 +20,7 @@ import copy from "./components/copy";
20
20
  import code from "./components/code";
21
21
  import sizes from "./components/sizes";
22
22
  import embed from "./components/embed";
23
+ import pageTabs from "./components/page-tabs";
23
24
 
24
25
  import initFilterStore from "./stores/filter";
25
26
  import initLayoutStore from "./stores/layout";
@@ -61,6 +62,7 @@ Alpine.data("tabs", tabs);
61
62
  Alpine.data("navItem", navItem);
62
63
  Alpine.data("navGroup", navGroup);
63
64
  Alpine.data("embed", embed);
65
+ Alpine.data("pageTabs", pageTabs);
64
66
 
65
67
  // Init
66
68
 
@@ -0,0 +1,9 @@
1
+ export default function pageTabs() {
2
+ return {
3
+ active: "",
4
+
5
+ changeTab(tabName) {
6
+ this.active = tabName;
7
+ },
8
+ };
9
+ }
@@ -14,7 +14,7 @@ module Lookbook
14
14
  if feature_enabled? :pages
15
15
  landing = Lookbook.pages.find(&:landing) || Lookbook.pages.first
16
16
  if landing.present?
17
- redirect_to page_path(landing.lookup_path)
17
+ redirect_to lookbook_page_path(landing.lookup_path)
18
18
  end
19
19
  end
20
20
  end
@@ -1,5 +1,7 @@
1
1
  module Lookbook
2
2
  class PagesController < ApplicationController
3
+ helper_method :page_controller
4
+
3
5
  def self.controller_path
4
6
  "lookbook/pages"
5
7
  end
@@ -7,7 +9,7 @@ module Lookbook
7
9
  def index
8
10
  landing = Lookbook.pages.find(&:landing) || Lookbook.pages.first
9
11
  if landing.present?
10
- redirect_to page_path landing.lookup_path
12
+ redirect_to lookbook_page_path landing.lookup_path
11
13
  else
12
14
  @title = "Not found"
13
15
  render "not_found"
@@ -28,8 +28,8 @@ module Lookbook
28
28
  begin
29
29
  set_params
30
30
  @examples = examples_data
31
- @drawer_panels = drawer_panels.filter { |name, panel| panel[:show] }
32
- @preview_panels = preview_panels.filter { |name, panel| panel[:show] }
31
+ @drawer_panels = drawer_panels.select { |name, panel| panel[:show] }
32
+ @preview_panels = preview_panels.select { |name, panel| panel[:show] }
33
33
  rescue => exception
34
34
  render_in_layout "lookbook/error", error: prettify_error(exception)
35
35
  end
@@ -45,11 +45,11 @@ module Lookbook
45
45
  if @example
46
46
  @preview = @example.preview
47
47
  if params[:path] == @preview&.lookup_path
48
- redirect_to show_path "#{params[:path]}/#{@preview.default_example.name}"
48
+ redirect_to lookbook_inspect_path "#{params[:path]}/#{@preview.default_example.name}"
49
49
  end
50
50
  else
51
51
  first_example = Lookbook.previews.find(params[:path])&.examples&.first
52
- redirect_to show_path(first_example.lookup_path) if first_example
52
+ redirect_to lookbook_inspect_path(first_example.lookup_path) if first_example
53
53
  end
54
54
  end
55
55
 
@@ -133,7 +133,7 @@ module Lookbook
133
133
  template: "lookbook/previews/panels/notes",
134
134
  hotkey: "n",
135
135
  show: true,
136
- disabled: @examples.filter { |e| e[:notes].present? }.none?
136
+ disabled: @examples.select { |e| e[:notes].present? }.none?
137
137
  },
138
138
  params: {
139
139
  label: "Params",
@@ -11,9 +11,9 @@ module Lookbook
11
11
  def landing_path
12
12
  landing = feature_enabled?(:pages) ? Lookbook.pages.find(&:landing) || Lookbook.pages.first : nil
13
13
  if landing.present?
14
- page_path(landing.lookup_path)
14
+ lookbook_page_path landing.lookup_path
15
15
  else
16
- home_path
16
+ lookbook_home_path
17
17
  end
18
18
  end
19
19
  end
@@ -4,7 +4,7 @@ module Lookbook
4
4
 
5
5
  def page_path(id)
6
6
  page = id.is_a?(Page) ? id : Lookbook.pages.find(id)
7
- lookbook.page_path page.lookup_path
7
+ lookbook_page_path page.lookup_path
8
8
  end
9
9
 
10
10
  def embed(*args, params: {}, type: :preview, **opts)
@@ -1,14 +1,14 @@
1
- <div id="<%= id %>" x-data="embed" class="not-prose embed border border-gray-300 rounded-md overflow-hidden" @page:morphed.window="recaclulateIframeHeight">
1
+ <div id="<%= id %>" x-data="embed" class="not-prose embed border border-gray-300 rounded-md overflow-hidden" @page:morphed.window="recaclulateIframeHeight" @tab-switch.window="recaclulateIframeHeight">
2
2
  <header class="px-3 py-2 flex items-center text-xs text-gray-400 bg-white border-b border-gray-300">
3
3
  <div class="text-gray-500">
4
4
  <%= example.preview.label %> (<%= example.label %>)
5
5
  </div>
6
6
  <div class="ml-auto flex items-center space-x-3">
7
- <a href="<%= lookbook.show_path(example.path, params) %>" class="" x-tooltip.theme.lookbook="`View in Inspector`">
7
+ <a href="<%= lookbook_inspect_path(example.path, params) %>" class="" x-tooltip.theme.lookbook="`View in Inspector`">
8
8
  <%= icon "eye", size: 4, class: "hover:text-indigo-800" %>
9
9
  </a>
10
10
  <a
11
- href="<%= lookbook.preview_path(example.path, params) %>"
11
+ href="<%= lookbook_preview_path(example.path, params) %>"
12
12
  target="_blank"
13
13
  class="-top-px relative"
14
14
  x-tooltip.theme.lookbook="`Open in new window`">
@@ -21,7 +21,7 @@
21
21
  <iframe seamless
22
22
  id="<%= id %>-iframe"
23
23
  x-ref="iframe"
24
- src="<%= lookbook.preview_path(example.path, params.merge(lookbook_embed: true)) %>"
24
+ src="<%= lookbook_preview_path(example.path, params.merge(lookbook_embed: true)) %>"
25
25
  class="min-w-full h-full w-px"
26
26
  :class="{ 'pointer-events-none': reflowing }"
27
27
  @load="recaclulateIframeHeight">
@@ -1,6 +1,6 @@
1
- <nav id="nav" x-data="nav(<%= filterable %>)">
1
+ <nav id="nav-<%= items.class.describe_as %>" x-data="nav(<%= filterable %>)">
2
2
  <% if items.any? %>
3
- <ul x-ref="items" id="nav-<%= items.class.describe_as %>">
3
+ <ul x-ref="items" id="nav-<%= items.class.describe_as %>-items">
4
4
  <% items.each do |item| %>
5
5
  <%= component "nav_#{item.type}", node: item %>
6
6
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <%
2
- path = show_path(item.lookup_path)
2
+ path = lookbook_inspect_path(item.lookup_path)
3
3
  display ||= :item
4
4
  label ||= item.label
5
5
  item_icon = display == :node ? "layers" : "eye"
@@ -1,5 +1,5 @@
1
1
  <%
2
- path = page_path(node.lookup_path)
2
+ path = lookbook_page_path(node.lookup_path)
3
3
  depth = node.hierarchy_depth + 1
4
4
  %>
5
5
  <li key="nav-page-<%= node.id %>-item">
@@ -1,6 +1,6 @@
1
1
  <% examples = node.examples.reject(&:hidden?) %>
2
2
  <% if examples.many? %>
3
- <%= component "nav_group", node: node, path: show_path(examples.first.path) do %>
3
+ <%= component "nav_group", node: node, path: lookbook_inspect_path(examples.first.path) do %>
4
4
  <% examples.each do |example| %>
5
5
  <%= component "nav_item", item: example, depth: example.hierarchy_depth + 1 %>
6
6
  <% end %>
@@ -47,7 +47,7 @@
47
47
  <%= icon "refresh-cw", size: 3.5, class: "hover:text-indigo-800" %>
48
48
  </button>
49
49
  <a
50
- href="<%= preview_path %>"
50
+ href="<%= lookbook_preview_path %>"
51
51
  target="_blank"
52
52
  x-tooltip.theme.lookbook="`Open in new window`"
53
53
  data-hotkey="w">
@@ -43,6 +43,32 @@
43
43
  <div id="page-main-<%= @page.id %>" data-morph-strategy="replace">
44
44
  <div class="prose max-w-none prose-a:text-indigo-900">
45
45
  <%= @page_content %>
46
+
47
+ <% if @page.tabs.any? %>
48
+ <div
49
+ id="page-tabs"
50
+ x-data="pageTabs"
51
+ x-init="active = '<%= @page.tabs.first.tab %>'"
52
+ class="">
53
+ <nav class="flex border-b border-gray-300">
54
+ <% @page.tabs.each do |tab| %>
55
+ <button
56
+ class="px-5 py-2 font-bold border-b-2 hover:border-gray-500"
57
+ :class="active === '<%= tab.tab %>' ? '!border-gray-500 !cursor-default' : 'border-gray-50'"
58
+ x-on:click="changeTab('<%= tab.tab %>');$dispatch('tab-switch')">
59
+ <%= tab.label %>
60
+ </button>
61
+ <% end %>
62
+ </nav>
63
+
64
+ <% @page.tabs.each do |tab| %>
65
+ <div id="tab-<%= tab.tab %>" x-show="active === '<%= tab.tab %>'">
66
+ <%= page_controller.render_page(tab) %>
67
+ </div>
68
+ <% end %>
69
+ </div>
70
+ <% end %>
71
+
46
72
  </div>
47
73
  </div>
48
74
  <% if @page.footer? %>
@@ -1 +1 @@
1
- <iframe id="error-iframe" src="<%= url_for lookbook.preview_path %>" frameborder="0" class=" h-screen w-full" seamless></iframe>
1
+ <iframe id="error-iframe" src="<%= url_for lookbook_preview_path %>" frameborder="0" class=" h-screen w-full" seamless></iframe>
@@ -1,4 +1,4 @@
1
- <% items = examples.filter { |example| example[:notes].present? } %>
1
+ <% items = examples.select { |example| example[:notes].present? } %>
2
2
  <div class="text-gray-600 bg-gray-50 h-full overflow-auto" data-morph-strategy="replace">
3
3
  <% if items.many? %>
4
4
  <div class="divide-y divide-dashed divide-gray-300">
@@ -22,4 +22,4 @@
22
22
  <% end %>
23
23
  </div>
24
24
  <% end %>
25
- </div>
25
+ </div>
@@ -9,7 +9,7 @@
9
9
  <iframe seamless
10
10
  class="h-full w-full border border-gray-300"
11
11
  :class="{ 'pointer-events-none': $store.layout.reflowing }"
12
- src="<%= lookbook.preview_path(request.query_parameters.merge(lookbook_timestamp: Time.now)) %>"
12
+ src="<%= lookbook_preview_path(request.query_parameters.merge(lookbook_timestamp: Time.now)) %>"
13
13
  <% if config.preview_srcdoc %>srcdoc="<%== srcdoc %>"<% end %>
14
14
  frameborder="0"
15
15
  x-data="sizes"
data/config/routes.rb CHANGED
@@ -3,13 +3,13 @@ Lookbook::Engine.routes.draw do
3
3
  mount Lookbook::Engine.websocket => Lookbook.config.cable_mount_path
4
4
  end
5
5
 
6
- root to: "application#index", as: :home
6
+ root to: "application#index", as: :lookbook_home
7
7
 
8
8
  if Lookbook::Features.enabled?(:pages)
9
- get "/#{Lookbook.config.page_route}", to: "pages#index", as: :page_index
10
- get "/#{Lookbook.config.page_route}/*path", to: "pages#show", as: :page
9
+ get "/#{Lookbook.config.page_route}", to: "pages#index", as: :lookbook_page_index
10
+ get "/#{Lookbook.config.page_route}/*path", to: "pages#show", as: :lookbook_page
11
11
  end
12
12
 
13
- get "/preview/*path", to: "previews#preview", as: :preview
14
- get "/*path", to: "previews#show", as: :show
13
+ get "/preview/*path", to: "previews#preview", as: :lookbook_preview
14
+ get "/*path", to: "previews#show", as: :lookbook_inspect
15
15
  end
@@ -53,7 +53,7 @@ module Lookbook
53
53
  options.listen_paths = options.listen_paths.map(&:to_s)
54
54
  options.listen_paths += options.preview_paths
55
55
  options.listen_paths << (vc_options.view_component_path || Rails.root.join("app/components"))
56
- options.listen_paths.filter! { |path| Dir.exist? path }
56
+ options.listen_paths.select! { |path| Dir.exist? path }
57
57
 
58
58
  options.cable_mount_path ||= "/lookbook-cable"
59
59
  options.cable_logger ||= Rails.logger
@@ -95,7 +95,7 @@ module Lookbook
95
95
  @preview_listener.start
96
96
 
97
97
  if Lookbook::Features.enabled?(:pages)
98
- @page_listener = Listen.to(*config.lookbook.page_paths.filter { |dir| Dir.exist? dir }, only: /\.(html.*|md.*)$/) do |modified, added, removed|
98
+ @page_listener = Listen.to(*config.lookbook.page_paths.select { |dir| Dir.exist? dir }, only: /\.(html.*|md.*)$/) do |modified, added, removed|
99
99
  Lookbook::Engine.websocket&.broadcast("reload", {
100
100
  modified: modified,
101
101
  removed: removed,
data/lib/lookbook/page.rb CHANGED
@@ -16,17 +16,22 @@ module Lookbook
16
16
  ]
17
17
 
18
18
  attr_reader :errors
19
+ attr_accessor :tabs
19
20
 
20
21
  def initialize(path, base_path)
21
22
  @pathname = Pathname.new path
22
- @base_path = base_path
23
+ @base_path = Pathname.new base_path
23
24
  @options = nil
24
25
  @errors = []
26
+ @tabs = []
25
27
  end
26
28
 
27
29
  def path
28
30
  rel_path = @pathname.relative_path_from(@base_path)
29
- (rel_path.dirname.to_s == "." ? name : "#{rel_path.dirname}/#{name}")
31
+
32
+ _path = (rel_path.dirname.to_s == "." ? name : "#{rel_path.dirname}/#{name}")
33
+ _path.gsub!("[#{tab}]", "") if tab?
34
+ _path
30
35
  end
31
36
 
32
37
  def lookup_path
@@ -78,7 +83,16 @@ module Lookbook
78
83
  end
79
84
 
80
85
  def type
81
- :page
86
+ tab? ? :tab : :page
87
+ end
88
+
89
+ def tab
90
+ matches = full_path.to_s.match(%r{\[(?<tab>\w+)\]})
91
+ matches ? remove_position_prefix(matches[:tab]) : nil
92
+ end
93
+
94
+ def tab?
95
+ tab.present?
82
96
  end
83
97
 
84
98
  def method_missing(method_name, *args, &block)
@@ -114,7 +128,7 @@ module Lookbook
114
128
  end
115
129
  @options = Lookbook.config.page_options.deep_merge(frontmatter).with_indifferent_access
116
130
  @options[:id] = @options[:id] ? generate_id(@options[:id]) : generate_id(lookup_path)
117
- @options[:label] ||= name.titleize
131
+ @options[:label] ||= (tab? ? tab : name).titleize
118
132
  @options[:title] ||= @options[:label]
119
133
  @options[:hidden] ||= false
120
134
  @options[:landing] ||= false
@@ -143,17 +157,29 @@ module Lookbook
143
157
  end
144
158
 
145
159
  def all
146
- pages = Array(page_paths).map do |dir|
147
- Dir["#{dir}/**/*.html.*", "#{dir}/**/*.md.*"].sort.map do |page|
148
- Lookbook::Page.new(page, dir)
149
- end
160
+ pages, tabs =
161
+ Array(page_paths).flat_map do |dir|
162
+ Dir["#{dir}/**/*.html.*", "#{dir}/**/*.md.*"].sort.map do |page|
163
+ page = Lookbook::Page.new(page, dir)
164
+ end
165
+ end.partition { |page| page.type == :page }
166
+
167
+ sorted_pages = pages
168
+ .uniq { |page| page.path }
169
+ .sort_by { |page| [page.position, page.label] }
170
+
171
+ page_dict = sorted_pages.index_by(&:path)
172
+ sorted_tabs = tabs.sort_by { |tab| [tab.position, tab.label] }
173
+
174
+ sorted_tabs.each do |tab|
175
+ page_dict[tab.path].tabs << tab
150
176
  end
151
- sorted_pages = pages.flatten.uniq { |p| p.path }.sort_by { |page| [page.position, page.label] }
177
+
152
178
  PageCollection.new(sorted_pages)
153
179
  end
154
180
 
155
181
  def page_paths
156
- Lookbook.config.page_paths.filter { |dir| Dir.exist? dir }
182
+ Lookbook.config.page_paths.select { |dir| Dir.exist? dir }
157
183
  end
158
184
  end
159
185
  end
@@ -33,7 +33,7 @@ module Lookbook
33
33
  def examples
34
34
  return @examples if @examples.present?
35
35
  public_methods = @preview.public_instance_methods(false)
36
- public_method_objects = @preview_inspector&.methods&.filter { |m| public_methods.include?(m.name) }
36
+ public_method_objects = @preview_inspector&.methods&.select { |m| public_methods.include?(m.name) }
37
37
  examples = (public_method_objects || []).map { |m| PreviewExample.new(m.name.to_s, self) }
38
38
  sorted = Lookbook.config.sort_examples ? examples.sort_by(&:label) : examples
39
39
  @examples = []
@@ -6,7 +6,7 @@ module Lookbook
6
6
  protected
7
7
 
8
8
  def generate_id(*args)
9
- parts = args.map { |arg| arg.to_s.parameterize.underscore }
9
+ parts = args.map { |arg| arg.to_s.force_encoding("UTF-8").parameterize.underscore }
10
10
  parts.join("-").tr("/", "-").tr("_", "-").delete_prefix("-").delete_suffix("-").gsub("--", "-")
11
11
  end
12
12
 
@@ -1,3 +1,3 @@
1
1
  module Lookbook
2
- VERSION = "0.8.2"
2
+ VERSION = "0.9.1"
3
3
  end