README.md in lookbook-0.4.4 vs README.md in lookbook-0.4.5
- old
+ new
@@ -26,10 +26,11 @@
- Resizable preview window for responsive testing
- Highlighted preview source code and HTML output
- Auto-updating UI when component or preview files are updated _(Rails v6.0+ only)_
- Use comment tag annotations for granular customisation of the preview experience
- Fully compatible with standard the ViewComponent preview system
+- [**Experimental**] In-browser live editable preview parameters (similar to Storybook Controls/Knobs)
## Lookbook demo
If you want to have a quick play with Lookbook, the easiest way is to [give the demo app](https://github.com/allmarkedup/lookbook-demo) a spin. It's a basic Rails/ViewComponent app with a few test components included to tinker with.
@@ -74,11 +75,11 @@
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:
```ruby
# @label Basic Button
-# @display bg_color "#fff"
+# @display bg_color #fff
class ButtonComponentPreview < ViewComponent::Preview
# Primary button
# ---------------
# This is the button style you should use for most things.
@@ -88,15 +89,28 @@
render ButtonComponent.new do
"Click me"
end
end
+ # Button with icon
+ # ----------------
+ # This example uses dynamic preview parameters
+ # which can be edited live in the Lookbook UI
+ #
+ # @param text
+ # @param icon select [heart, cog, alert]
+ def icon(text: "Spread the love", icon: "heart")
+ render ButtonComponent.new(icon: icon) do
+ text
+ end
+ end
+
# Inverted button
# ---------------
# For light-on-dark screens
#
- # @display bg_color "#000"
+ # @display bg_color #000
def secondary
render ButtonComponent.new(style: :inverted) do
"Click me"
end
end
@@ -139,19 +153,24 @@
**Tags** are just strings identified by their `@` prefix - for example `@hidden`. Tags are always placed in a comment above the relevant preview class or example method.
The following Lookbook-specific tags are available for use:
-* `@label <label>` -[Customise navigation labels](#-label-text)
-* `@hidden` - [Prevent items displaying in the navigation](#-hidden)
-* `@display <key> <value>` - [Specify params to pass into the preview template](#-display-key-value)
-* `@!group <name> ... @!endgroup` - [Render examples in a group on the same page](#-group-name--endgroup)
+* [`@label`](#label-tag)
+* [`@display`](#display-tag)
+* [`@!group ... @!endgroup`](#group-tag)
+* [`@hidden`](#hidden-tag)
+* [`@param`](#param-tag) [⚠️ **experimental!** - requires [feature opt-in](#experimental-features) ⚠️]
-### 🔖 `@label <text>`
+<h3 id="label-tag">🏷 @label</h3>
Used to replace the auto-generated navigation label for the item with `<text>`.
+```ruby
+@label <text>
+```
+
> Available for preview classes & example methods.
```ruby
# @label Preview Label
class FooComponentPreview < ViewComponent::Preview
@@ -160,54 +179,34 @@
def default
end
end
```
-### 🔖 `@hidden`
+<h3 id="display-tag">🏷 @display</h3>
-Used to temporarily exclude an item from the Lookbook navigation. The item will still be accessible via it's URL.
-
-Can be useful when a component (or a variant of a component) is still in development and is not ready to be shared with the wider team.
-
-> Available for both preview classes & example methods.
-
-```ruby
-# @hidden
-class FooComponentPreview < ViewComponent::Preview
-
- # @hidden
- def default
- end
-end
-```
-
-### 🔖 `@display <key> <value>`
-
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.
```ruby
-# @display bg_color "#eee"
+# @display bg_color #eee
class FooComponentPreview < ViewComponent::Preview
- # @display max_width "500px"
+ # @display max_width 500px
# @display wrapper true
def default
end
end
```
The `@display` tag can be applied at the preview (class) or at the example (method) level, and takes the following format:
```ruby
-# @display <key> <value>
+@display <key> <value>
```
- `<key>` must be a valid Ruby hash key name, without quotes or spaces
-- `<value>` must be a valid JSON-parsable value. It can be a string (surrounded by **double** quotes), a boolean or an integer.
+- `<value>` will be parsed using the [Ruby YAML parser](https://yaml.org/YAML_for_ruby.html) to resolve the value
-> [See below for some examples](#some-display-value-examples) of valid and invalid `@display` values.
-
These display parameters can then be accessed via the `params` hash in your preview layout using `params[:lookbook][:display][<key>]`:
```html
<!DOCTYPE html>
<html style="background-color: <%= params[:lookbook][:display][:bg_color] %>">
@@ -242,32 +241,12 @@
}
```
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.
-#### Some `@display` value examples:
+<h3 id="group-tag">🔖 `@!group ... @!endgroup`</h3>
-Valid:
-
-```ruby
-# @display body_classes "bg-red border border-4 border-green"
-# @display wrap_in_container true
-# @display emojis_to_show 4
-# @display page_title "Special example title"
-```
-
-Invalid:
-
-```ruby
-# @display body_classes 'bg-red border border-4 border-green' [❌ single quotes]
-# @display wrap_in_container should_wrap [❌ unquoted string, perhaps trying to call a method]
-# @display page title "Special example title" [❌ space in key]
-# @display bg_color #fff [❌ colors need quotes around them, it's not CSS!]
-```
-
-### 🔖 `@!group <name> ... @!endgroup`
-
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.
You can group a set of examples by wrapping them in `@!group <name>` / `@!endgroup` tags within your preview file:
```ruby
@@ -308,10 +287,171 @@
<img src=".github/assets/nav_group.png">
You can have as many groups as you like within a single preview class, but each example can only belong to one group.
+<h3 id="hidden-tag">🏷 `@hidden`</h3>
+
+Used to temporarily exclude an item from the Lookbook navigation. The item will still be accessible via it's URL.
+
+Can be useful when a component (or a variant of a component) is still in development and is not ready to be shared with the wider team.
+
+> Available for both preview classes & example methods.
+
+```ruby
+# @hidden
+class FooComponentPreview < ViewComponent::Preview
+
+ # @hidden
+ def default
+ end
+end
+```
+
+<h3 id="param-tag"> 🚧 @param (experimental)</h3>
+
+> ⚠️ This feature is currently flagged as an **experimental** feature which requires [feature opt-in](#experimental-features) to use. Its API and implementation may change in the future.
+
+The `@param` tag provides the ability to specify **editable preview parameters** which can be changed in the Lookbook UI in order to customise the rendered output on the fly, much like the [Controls (knobs) addon](https://storybook.js.org/addons/@storybook/addon-controls) for Storybook.
+
+Each `@param` will have an associated form field generated for it. The values for each field will be handled as [dynamic preview params](https://viewcomponent.org/guide/previews.html#:~:text=It%E2%80%99s%20also%20possible%20to%20set%20dynamic%20values%20from%20the%20params%20by%20setting%20them%20as%20arguments%3A) when rendering the example.
+
+The `@param` tag takes the following format:
+
+```ruby
+@param <name> <input_type> <opts?>
+```
+
+- `<name>` - name of the dynamic preview param
+- `<input_type>` - input field type to generate in the UI
+- `<opts?>` - YAML-encoded field options, used for some field types
+
+#### Input types
+
+The following **input field types** are available for use:
+
+📝 **Text-style inputs** - Single line fields, useful for short strings of text or numbers.
+
+```ruby
+@param <name> text
+@param <name> email
+@param <name> number
+@param <name> url
+@param <name> tel
+```
+
+> The above types only differ in the validation constraints they impose on the input field.
+
+📝 **Textarea** - Multi-line textarea field for longer-form content.
+
+```ruby
+@param <name> textarea
+```
+
+📝 **Select box** - Dropdown select field for selecting from a list of known options.
+
+```ruby
+@param <name> select <options>
+```
+
+`<options>` should be a [YAML array](https://yaml.org/YAML_for_ruby.html#simple_inline_array) of options which must be formatted in the same style as the input for Rails' [`options_for_select`](https://apidock.com/rails/v6.0.0/ActionView/Helpers/FormOptionsHelper/options_for_select) helper:
+
+```ruby
+# Basic options:
+# @param theme select [primary, secondary, danger]
+
+# With custom labels (each item itself an array of [label, value]):
+# @param theme select [[Primary theme, primary], [Secondary theme, secondary], [Danger theme, danger]]
+
+# With empty option (`~` in YAML)
+# @param theme select [~, primary, secondary, danger]
+```
+
+> **Note**: In most cases YAML does not require quoting of strings, however if you are running into issues check out the [Ruby YAML docs](https://yaml.org/YAML_for_ruby.html) for a complete syntax reference.
+
+📝 **Toggle** - On/off switch for toggling boolean values.
+
+```ruby
+@param <name> toggle
+```
+
+#### Default values
+
+Default values are specified as part of the preview example method parameters in the usual Ruby way:
+
+```ruby
+def button(content: "Click me", theme: "primary", arrow: false)
+ # ...
+end
+```
+
+These will be used as the default values for the param fields.
+
+> Note that the default values are **not** evaluated at runtime, so you cannot use method calls to generate the defaults. Only static default values are supported.
+
+#### Type casting values
+
+Most dynamic param values are passed to the example method as strings, with the following exceptions:
+
+- `toggle` input - values are cast to booleans
+- `number` input - values are cast to integers
+
+In some cases, you may want to type cast the parameter value to something else (for example a `Symbol`) before using it when initializing the component.
+
+To help with this, a `type` option can be specified in the `@param` definition to automatically cast the dynamic value to a different type:
+
+```ruby
+# @param <name> [<type>] <input_type> <opts?>
+```
+
+In the example below, the value of the `theme` param (by default a string) will be automatically cast to a Symbol, ready for use in instantiating the component.
+
+```ruby
+# @param theme [Symbol] select [primary, secondary, danger]
+def default(theme: :primary)
+ render Elements::ButtonComponent.new(theme: theme) do
+ "Click me"
+ end
+end
+```
+
+The supported types to cast to are:
+
+- `String` - *default for all except `toggle` inputs*
+- `Boolean` - *default for `toggle` inputs*
+- `Symbol`
+- `Date`
+- `DateTime`
+- `Integer`
+- `Float`
+
+The following structured types are also available but should be considered **experimental** - you may run into bugs!
+
+- `Hash` - *value string converted to Hash using the Ruby YAML parser*
+- `Array` - *value string converted to Array using the Ruby YAML parser*
+
+#### Full example:
+
+```ruby
+class ButtonComponentPreview < ViewComponent::Preview
+
+ # The params defined below will be editable in the UI:
+ #
+ # @param content text
+ # @param theme select [primary, secondary, danger]
+ # @param arrow toggle
+ def default(content: "Click me", theme: "primary", arrow: true)
+ render Elements::ButtonComponent.new(theme: theme, arrow: arrow) do
+ content
+ end
+ end
+
+end
+```
+
+<img src=".github/assets/dynamic_params.png">
+
### Adding notes
All comment text other than tags will be treated as markdown and rendered in the **Notes** panel for that example in the Lookbook UI.
```ruby
@@ -349,9 +489,36 @@
If you wish to add additional paths to listen for changes in, you can use the `listen_paths` option:
```ruby
config.lookbook.listen_paths << Rails.root.join('app/other/directory')
```
+
+<h3 id="experimental-features">Experimental features opt-in</h3>
+
+Some features may occasionally be released behind a 'experimental' feature flag while they are being tested and refined, to allow people to try them out and provide feedback.
+
+> ⚠️ **Please note:** Experimental features should be considered to be **subject to extensive change** and breaking changes to them may be made within point releases - these features are **not** considered to be covered by [semver](https://semver.org/) whilst flagged as 'experimental'. ⚠️
+
+#### Opting into specific features (recommended)
+
+To opt into individual experimental features, include the name of the feature in the `experimental_features` config option:
+
+```ruby
+config.lookbook.experimental_features = ["feature_name"]
+```
+
+The current experimental features that can be opted into are:
+
+- `params`: Live-editable, dynamic preview parameters ([read more](#param-tag)). Include `"params"` in the `experimental_features` config option to opt in.
+
+#### Opting into all experimental features (not recommended!)
+
+If you want to live life on the bleeding-edge you can opt-in to all current **and future** experimental features (usual caveats apply):
+
+```ruby
+config.lookbook.experimental_features = true
+```
+
## Keyboard shortcuts
Lookbook provides a few keyboard shortcuts to help you quickly move around the UI.