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.