lookbook 0.3.5 → 0.4.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d2f6afc23ec83a6ed2c19314540d26f83406da564385337fe29f9769253a432
4
- data.tar.gz: bc239282a1bbbca50a72da683e270efafd60f515cf59a9b56ffc98b73f5e53a6
3
+ metadata.gz: 870ef6a17d84bca0a9fbbc421e27c2d6d682965d96c646f44d3db817e5a5e4f2
4
+ data.tar.gz: 669ce3b709792be5bdf46ac806efdca269d694349e74f759b0997cbbb456792e
5
5
  SHA512:
6
- metadata.gz: '08d4cf77cc06512be08c1f1baf567b55ac4e8d0328281896ddb2dd7381051b9ed538e8c2333a4437629cfc3e7852f5398672d013a271cacfae4dad0bc8769d97'
7
- data.tar.gz: b748d37275d69d82522b7e7746d31ed54d296dac939c5610d3559834207fc0488d682ee86b161c42418af71a80eaee79ec10f16cd276b4d510a87e3446379253
6
+ metadata.gz: b0293806f0d57b130c07ae84b5d30fad2625df0cbf2477303d924dddfd42ece6e49b10f15fc0b395fa65adfca3de3ab1096e72565a175bdab14df2af1ba57050
7
+ data.tar.gz: 2e5ad4ca7957d2684dc367b3a7d89d9922024cfc01cdbcd2567d30b830fe891b815924d64f5b7048783efd42736f41924354255f847850498fd596ba1c25e977
data/README.md CHANGED
@@ -16,19 +16,17 @@
16
16
 
17
17
  It uses (and extends) the native [ViewComponent preview functionality](https://viewcomponent.org/guide/previews.html), so you don't need to learn a new DSL or create any extra files to get up and running.
18
18
 
19
- Lookbook uses [RDoc/Yard-style comment tags](https://rubydoc.info/gems/yard/file/docs/Tags.md) to extend the capabilities of ViewComponent's previews whilst maintaining compatability with the standard preview class format, so you can add or remove Lookbook at any time without having to rework your code.
19
+ Lookbook uses [RDoc/Yard-style comment tags](#annotating-preview-files) to extend the capabilities of ViewComponent's previews whilst maintaining compatability with the standard preview class format, so you can add or remove Lookbook at any time without having to rework your code.
20
20
 
21
21
  ![Lookbook UI](.github/assets/lookbook_screenshot.png)
22
22
 
23
23
  ### Features
24
24
 
25
- - Tree-style navigation menu
26
- - Live nav search/filter
25
+ - Tree-style navigation menu with live search/filter
27
26
  - Resizable preview window for responsive testing
28
27
  - Highlighted preview source code and HTML output
29
- - Add notes via comments in the preview file (markdown supported)
30
28
  - Auto-updating UI when component or preview files are updated _(Rails v6.0+ only)_
31
- - Hide, group and rename preview examples using comment tags
29
+ - Use comment tag annotations for granular customisation of the preview experience
32
30
  - Fully compatible with standard the ViewComponent preview system
33
31
 
34
32
  ## Lookbook demo
@@ -39,8 +37,6 @@ The [demo app repo](https://github.com/allmarkedup/lookbook-demo) contains instr
39
37
 
40
38
  ## Installing
41
39
 
42
- > ⚠️ **Please note:** Lookbook is still in the early stages of development and has not yet been well tested across a wide range of Rails/ViewComponent versions and setups. If you run into any problems please [open an issue](issues) with as much detail as possible. Thanks! ⚠️
43
-
44
40
  ### 1. Add as a dependency
45
41
 
46
42
  Add Lookbook to your `Gemfile` somewhere **after** the ViewComponent gem. For example:
@@ -74,12 +70,13 @@ You don't need to do anything special to see your ViewComponent previews and exa
74
70
 
75
71
  > If you are new to ViewComponent development, checkout the ViewComponent [documentation](https://viewcomponent.org/guide/) on how to get started developing your components and [creating previews](https://viewcomponent.org/guide/previews.html).
76
72
 
77
- ### Annotating preview files
73
+ ## Annotating preview files
78
74
 
79
75
  Lookbook parses [Yard-style comment tags](https://rubydoc.info/gems/yard/file/docs/Tags.md) in your preview classes to customise and extend the standard ViewComponent preview experience:
80
76
 
81
77
  ```ruby
82
78
  # @label Basic Button
79
+ # @display bg_color: "#fff"
83
80
  class ButtonComponentPreview < ViewComponent::Preview
84
81
 
85
82
  # Primary button
@@ -93,11 +90,13 @@ class ButtonComponentPreview < ViewComponent::Preview
93
90
  end
94
91
  end
95
92
 
96
- # Secondary button
93
+ # Inverted button
97
94
  # ---------------
98
- # This should be used for less important actions.
95
+ # For light-on-dark screens
96
+ #
97
+ # @display bg_color: "#000"
99
98
  def secondary
100
- render ButtonComponent.new(style: :secondary) do
99
+ render ButtonComponent.new(style: :inverted) do
101
100
  "Click me"
102
101
  end
103
102
  end
@@ -142,7 +141,12 @@ end
142
141
 
143
142
  The following Lookbook-specific tags are available for use:
144
143
 
145
- #### `@label <text>`
144
+ * `@label <label>` -[Customise navigation labels](#-label-text)
145
+ * `@hidden` - [Prevent items displaying in the navigation](#-hidden)
146
+ * `@display <key>:<value>` - [Specify params to pass into the preview template](#-display-key-value)
147
+ * `@!group <name> ... @!endgroup` - [Render examples in a group on the same page](#-group-name--endgroup)
148
+
149
+ ### 🔖 `@label <text>`
146
150
 
147
151
  Used to replace the auto-generated navigation label for the item with `<text>`.
148
152
 
@@ -158,7 +162,7 @@ class FooComponentPreview < ViewComponent::Preview
158
162
  end
159
163
  ```
160
164
 
161
- #### `@hidden`
165
+ ### 🔖 `@hidden`
162
166
 
163
167
  Used to temporarily exclude an item from the Lookbook navigation. The item will still be accessible via it's URL.
164
168
 
@@ -176,7 +180,91 @@ class FooComponentPreview < ViewComponent::Preview
176
180
  end
177
181
  ```
178
182
 
179
- #### `@!group <name> ... @!endgroup`
183
+ ### 🔖 `@display <key>: <value>`
184
+
185
+ The `@display` tag lets you pass custom parameters to your preview layout so that the component preview can be customised on a per-example basis.
186
+
187
+ ```ruby
188
+ # @display bg_color: "#eee"
189
+ class FooComponentPreview < ViewComponent::Preview
190
+
191
+ # @display max_width: "500px"
192
+ # @display wrapper: true
193
+ def default
194
+ end
195
+ end
196
+ ```
197
+
198
+ The `@display` tag can be applied at the preview (class) or at the example (method) level, and takes the following format:
199
+
200
+ ```ruby
201
+ # @display <key>: <value>
202
+ ```
203
+
204
+ - `<key>` must be a valid Ruby hash key name, without quotes or spaces
205
+ - `<value>` must be a valid JSON-parsable value. It can be a string (surrounded by **double** quotes), a boolean or an integer.
206
+
207
+ > [See below for some examples](#some-display-value-examples) of valid and invalid `@display` values.
208
+
209
+ These display parameters can then be accessed via the `params` hash in your preview layout using `params[:lookbook][:display][<key>]`:
210
+
211
+ ```html
212
+ <!DOCTYPE html>
213
+ <html style="background-color: <%= params[:lookbook][:display][:bg_color] %>">
214
+ <head>
215
+ <title>Preview Layout</title>
216
+ </head>
217
+ <body>
218
+ <div style="max-width: <%= params[:lookbook][:display][:max_width] || '100%' %>">
219
+ <% if params[:lookbook][:display][:wrapper] == true %>
220
+ <div class="wrapper"><%= yield %></div>
221
+ <% else %>
222
+ <%= yield %>
223
+ <% end %>
224
+ </div>
225
+ </body>
226
+ </html>
227
+ ```
228
+
229
+ > By default ViewComponent will use your default application layout for displaying the rendered example. However it's often better to create a seperate layout that you can customise and use specifically for previewing your components. See the ViewComponent [preview docs](https://viewcomponent.org/guide/previews.html) for instructions on how to set that up.
230
+
231
+ Any `@display` params set at the preview (class) level with be merged with those set on individual example methods.
232
+
233
+ #### Global display params
234
+
235
+ Global (fallback) display params can be defined via a configuration option:
236
+
237
+ ```ruby
238
+ # config/application.rb
239
+ config.lookbook.preview_display_params = {
240
+ bg_color: "#fff",
241
+ max_width: "100%"
242
+ }
243
+ ```
244
+
245
+ Globally defined display params will be available to all previews. Any preview or example-level `@display` values with the same name will take precedence and override a globally-set one.
246
+
247
+ #### Some `@display` value examples:
248
+
249
+ Valid:
250
+
251
+ ```ruby
252
+ # @display body_classes: "bg-red border border-4 border-green"
253
+ # @display wrap_in_container: true
254
+ # @display emojis_to_show: 4
255
+ # @display page_title: "Special example title"
256
+ ```
257
+
258
+ Invalid:
259
+
260
+ ```ruby
261
+ # @display body_classes: 'bg-red border border-4 border-green' [❌ single quotes]
262
+ # @display wrap_in_container: should_wrap [❌ unquoted string, perhaps trying to call a method]
263
+ # @display page title: "Special example title" [❌ space in key]
264
+ # @display bg_color: #fff [❌ colors need quotes around them, it's not CSS!]
265
+ ```
266
+
267
+ ### 🔖 `@!group <name> ... @!endgroup`
180
268
 
181
269
  For smaller components, it can often make sense to render a set of preview examples in a single window, rather than representing them as individual items in the navigation which can start to look a bit cluttered.
182
270
 
@@ -222,7 +310,7 @@ The example above would display the `Sizes` examples grouped together on a singl
222
310
 
223
311
  You can have as many groups as you like within a single preview class, but each example can only belong to one group.
224
312
 
225
- #### Adding notes
313
+ ### Adding notes
226
314
 
227
315
  All comment text other than tags will be treated as markdown and rendered in the **Notes** panel for that example in the Lookbook UI.
228
316
 
@@ -14,6 +14,17 @@ export default function navNode() {
14
14
  ? Array.from(this.$refs.items.querySelectorAll(":scope > li"))
15
15
  : [];
16
16
  },
17
+ navigateToFirstChild() {
18
+ if (this.open()) {
19
+ const child = this.firstVisibleChild();
20
+ if (child) {
21
+ const link = child.querySelector(":scope > a.nav-link");
22
+ if (link) {
23
+ this.navigate(link.getAttribute("href"));
24
+ }
25
+ }
26
+ }
27
+ },
17
28
  filter() {
18
29
  this.hidden = true;
19
30
  this.getChildren().forEach((child) => {
@@ -27,5 +38,12 @@ export default function navNode() {
27
38
  toggle() {
28
39
  this.$store.nav.open[this.id] = !this.$store.nav.open[this.id];
29
40
  },
41
+ firstVisibleChild() {
42
+ return this.getChildren().find((child) => {
43
+ return child._x_dataStack
44
+ ? child._x_dataStack[0].hidden === false
45
+ : false;
46
+ });
47
+ },
30
48
  };
31
49
  }
@@ -50,7 +50,7 @@ module Lookbook
50
50
  def find_preview
51
51
  candidates = []
52
52
  params[:path].to_s.scan(%r{/|$}) { candidates << $` }
53
- match = candidates.reverse.detect { |candidate| Lookbook::Preview.exists?(candidate) }
53
+ match = candidates.detect { |candidate| Lookbook::Preview.exists?(candidate) }
54
54
  @preview = match ? Lookbook::Preview.find(match) : nil
55
55
  end
56
56
 
@@ -95,14 +95,29 @@ module Lookbook
95
95
  html: preview_controller.render_example_to_string(@preview, example.name)
96
96
  }
97
97
  end
98
+ set_params
98
99
  joined = render_to_string "lookbook/preview_group", locals: {examples: examples}, layout: nil
99
- preview_controller.render_in_layout_to_string(joined, @preview.lookbook_layout)
100
+ preview_controller.render_in_layout_to_string(joined, @preview.lookbook_layout || current_layout)
100
101
  else
101
- preview_controller.request.params[:path] = "#{@preview.preview_name}/#{@example.name}".chomp("/")
102
+ set_params(@example)
103
+ preview_controller.params[:path] = "#{@preview.preview_name}/#{@example.name}".chomp("/")
102
104
  preview_controller.process(:previews)
103
105
  end
104
106
  end
105
107
 
108
+ def set_params(example = nil)
109
+ example_params = @preview.lookbook_display_params.deep_merge(example ? example.lookbook_display_params : {})
110
+ preview_controller.params.merge!({
111
+ lookbook: {
112
+ display: Lookbook.config.preview_display_params.deep_merge(example_params)
113
+ }
114
+ })
115
+ end
116
+
117
+ def current_layout
118
+ preview_controller.send :_layout, preview_controller.lookup_context, [:html]
119
+ end
120
+
106
121
  def assign_inspector
107
122
  @inspector = {
108
123
  panes: {
@@ -5,7 +5,7 @@ label ||= leaf.label
5
5
  %>
6
6
  <li x-data="navLeaf" :class="{hidden}" x-init="matchers = <%= leaf.matchers.to_json %>; path = '<%= path %>'; setActive()" @popstate.window="setActive">
7
7
  <a href="<%= path %>"
8
- class="pr-3 py-[5px] flex items-center w-full group transition hover:bg-gray-200 hover:bg-opacity-50"
8
+ class="nav-link pr-3 py-[5px] flex items-center w-full group transition hover:bg-gray-200 hover:bg-opacity-50"
9
9
  style="<%= nav_padding_style(depth) %>"
10
10
  :class="{'!bg-indigo-100':active}"
11
11
  @click.stop.prevent="navigate"
@@ -8,7 +8,7 @@
8
8
  <svg class="feather h-3.5 w-3.5 mr-1.5 flex-none text-indigo-500">
9
9
  <use xlink:href="/lookbook-assets/feather-sprite.svg#<%= node.type == :preview ? 'layers' : 'folder' %>" />
10
10
  </svg>
11
- <div class="truncate w-full whitespace-nowrap text-left <%= "font-bold" if node.type == :preview %>" @click.stop="toggle(); if (open()) { <%= "navigate('#{path}')" if defined?(path) %>}">
11
+ <div class="truncate w-full whitespace-nowrap text-left <%= "font-bold" if node.type == :preview %>" @click.stop="toggle(); navigateToFirstChild();">
12
12
  <%= node.label %>
13
13
  </div>
14
14
  </div>
@@ -30,6 +30,7 @@ module Lookbook
30
30
 
31
31
  options.preview_controller = vc_options.preview_controller if options.preview_controller.nil?
32
32
  options.preview_srcdoc = true if options.preview_srcdoc.nil?
33
+ options.preview_display_params ||= {}
33
34
 
34
35
  options.listen_paths = options.listen_paths.map(&:to_s)
35
36
  options.listen_paths += options.preview_paths
@@ -27,6 +27,7 @@ module Lookbook
27
27
  def define_tags
28
28
  YARD::Tags::Library.define_tag("Hidden status", :hidden)
29
29
  YARD::Tags::Library.define_tag("Label", :label)
30
+ YARD::Tags::Library.define_tag("Display", :display)
30
31
  end
31
32
  end
32
33
  end
@@ -20,6 +20,23 @@ module Lookbook
20
20
  code_object&.group
21
21
  end
22
22
 
23
+ def lookbook_display_params
24
+ display_params = {}
25
+ if code_object&.tags(:display).present?
26
+ code_object.tags(:display).each do |tag|
27
+ parts = tag.text.match(/^\s?([^:]*)\s?:\s?(.*)\s?$/)
28
+ if parts.present?
29
+ begin
30
+ display_params[parts[1]] = JSON.parse parts[2]
31
+ rescue JSON::ParserError => err
32
+ Rails.logger.error("\n👀 [Lookbook] Invalid JSON in @display tag.\n👀 [Lookbook] (#{err.to_s})\n")
33
+ end
34
+ end
35
+ end
36
+ end
37
+ display_params
38
+ end
39
+
23
40
  # private
24
41
 
25
42
  def code_object
@@ -1,3 +1,3 @@
1
1
  module Lookbook
2
- VERSION = "0.3.5"
2
+ VERSION = "0.4.0.beta.1"
3
3
  end
@@ -8523,6 +8523,17 @@ Expression: "${expression}"
8523
8523
  getChildren() {
8524
8524
  return this.$refs.items ? Array.from(this.$refs.items.querySelectorAll(":scope > li")) : [];
8525
8525
  },
8526
+ navigateToFirstChild() {
8527
+ if (this.open()) {
8528
+ const child = this.firstVisibleChild();
8529
+ if (child) {
8530
+ const link = child.querySelector(":scope > a.nav-link");
8531
+ if (link) {
8532
+ this.navigate(link.getAttribute("href"));
8533
+ }
8534
+ }
8535
+ }
8536
+ },
8526
8537
  filter() {
8527
8538
  this.hidden = true;
8528
8539
  this.getChildren().forEach((child) => {
@@ -8535,6 +8546,11 @@ Expression: "${expression}"
8535
8546
  },
8536
8547
  toggle() {
8537
8548
  this.$store.nav.open[this.id] = !this.$store.nav.open[this.id];
8549
+ },
8550
+ firstVisibleChild() {
8551
+ return this.getChildren().find((child) => {
8552
+ return child._x_dataStack ? child._x_dataStack[0].hidden === false : false;
8553
+ });
8538
8554
  }
8539
8555
  };
8540
8556
  }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lookbook
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.4.0.beta.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Perkins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-21 00:00:00.000000000 Z
11
+ date: 2021-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -259,9 +259,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
259
259
  version: '0'
260
260
  required_rubygems_version: !ruby/object:Gem::Requirement
261
261
  requirements:
262
- - - ">="
262
+ - - ">"
263
263
  - !ruby/object:Gem::Version
264
- version: '0'
264
+ version: 1.3.1
265
265
  requirements: []
266
266
  rubygems_version: 3.1.2
267
267
  signing_key: