Dynamic Params
Lookbook builds on ViewComponent’s dynamic preview values functionality to provide the ability to specify editable preview parameters. These can be changed by the end user in the Lookbook UI in order to customise rendered preview output on the fly, much like the Controls (knobs) addon for Storybook.
A basic example
We can create a preview with editable text by adding an argument to the preview example method, using the value of that argument when rendering our component
and then adding a @param
comment tag to tell Lookbook to generate a form field in the UI so it can be edited:
class HeaderComponentPreview < ViewComponent::Preview
# @param text
def default(text: "Some header text")
render Elements::HeaderComponent.new do
text
end
end
end
The editable text field will appear under the ‘Params’ tab in the preview inspector. Editing the text in this field will re-render the preview with the new content:
Param tag format
The @param
tag takes the following format:
@param <name> <input_type> <description?> <opts?>
<name> | Name of the preview param (should match example method argument key) |
<input_type> | Input field type to generate in the UI |
<description?> | Optional short description of what the param is used for, supplied as a double-quoted string. |
<opts?> | Optional hash of options for customising the field display. See the param options section for more info. |
class ButtonComponentPreview < ViewComponent::Preview
# @param arrow toggle
# @param theme select { choices: [primary, secondary, danger] }
# @param content text "The text to display in the button"
def default(content: 'Click me', theme: 'primary', arrow: true)
render Elements::ButtonComponent.new(theme: theme, arrow: arrow) do
content
end
end
end
Input types
The available input field types are listed below.
If you require a type of input that is not provided by Lookbook (or wish to override an existing one) then you can also add your own custom inputs quickly and easily.
📝 Text-style inputs
Single line fields, useful for short strings of text or numbers.
@param <name> text
@param <name> email
@param <name> number
@param <name> url
@param <name> tel
@param <name> date
@param <name> datetime-local
📝 Textarea
Multi-line textarea field for longer-form content.
@param <name> textarea
📝 Select box
Dropdown select field for selecting from a list of known items.
@param <name> select <opts>
# Basic options:
# @param theme select { choices: [primary, secondary, danger] }
# With custom labels (each item itself an array of [label, value]):
# @param theme select { choices: [[Primary theme, primary], [Secondary theme, secondary], [Danger theme, danger]] }
# With empty option (`~` in YAML)
# @param theme select { choices: [~, primary, secondary, danger] }
📝 Toggle
On/off switch for toggling boolean values.
@param <name> toggle
📝 Color
Color picker input. Provides a six-letter hex code in the format #ff0000
.
@param <name> color
📝 Range
Range slider input. min
, max
and step
values should be provided via the options hash.
@param <name> range { min: 0, max: 10, step: 1 }
Options
@param
options provide a way to further customise the display of each field in the Lookbook UI.
See the options reference section for details of available options. Any ‘unknown’ options will be used to generate HTML attributes for the relevant input element.
Options can be provided inline (in YAML hash format), dynamically generated via a preview class instance method or evaluated Ruby statement, or loaded from a file.
# Inline:
# @param theme select { choices: [primary, secondary, danger] }
# Dynamic - method reference:
# @param theme select :name_of_method_that_returns_options
# Dynamic - evaluated Ruby statement:
# @param theme select {{ FooComponent::OPTIONS }}
# File:
# @param theme select ./path/to/options.yml
Note that the options hash, method or file reference should always be placed at the very end of the @param
tag annotation.
Inline options
The simplest way to specify options for a param field is to hard-code it as a YAML-formatted hash. For example, as in the case for the list of select
options in the example below:
# @param theme select { choices: [primary, secondary, danger] }
This is straightforward and useful for simple cases, but if you have a long list of choices or you want to reference values elsewhere to prevent duplication then hard-coding the data might not be the ideal solution
Dynamic options (since v1.1)
For more flexibility it is possible to generate the options hash dynamically from Ruby code.
Dynamic options depend on runtime code evaluation and require enabling in your config before they can be used:
config.lookbook.preview_params_options_eval = true
Use of eval
to evaluate arbitrary strings can be a security concern. However Lookbook never eval
’s any user-inputed content - only comments that have been added to the preview file source code itself.
Using a method
You can use a private method (in your preview class) that returns a hash of param options, and reference it via it’s symbolized name:
# @param theme select :method_that_returns_options
Note that you cannot pass any arguments to the method.
class ButtonComponent::Preview < ViewComponent::Preview
# @param theme select :theme_options
def button(theme: :danger)
# ...
end
private
def theme_options
{
choices: %i[primary secondary danger],
include_blank: true
}
end
end
Using a Ruby statement
For maximum flexibility it is also possible to evaluate arbitrary Ruby statements to generate the options.
The statement must be placed within double curly brackets and will be evaluated in the context of the current preview class, as with the method reference technique above.
# @param theme select {{ ButtonComponent::THEMES }}
File options
It is possible to import YAML/JSON data from a file by providing the file path:
# @param theme select data/theme-select-data.yml
# data/theme-select-data.yml
choices:
- primary
- secondary
- danger
Files must have a .json
or .yml
extension, and by default paths are resolved relative to the application root directory.
However, if the path starts with ./
or ../
then the path will be resolved relative to the current preview file. For example:
# @param theme select ./theme-select-data.json
# @param theme select ../data/theme-select-data.json
Options reference
All of the below options are optional, although specific inputs may require or rely on additional options (such as the choices
option for select inputs).
label | Custom label text |
description | Short description of what the param is used for. An alternative to providing the description in the main body of the annotation. |
hint | Help text. Displayed as a tooltip when hovering over a ‘?’ icon next to the label. |
Default values
Default values are specified as part of the preview example method parameters in the usual Ruby way:
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 booleansnumber
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
can be specified in the @param
definition to automatically cast the dynamic value to a different type.
The type can either be provided in the body of the @param
tag, surrounded by square brackets:
# @param <name> [<type>] <input_type> <opts?>
or as the value of the value_type
key in the param options:
# @param <name> <input_type> { value_type: "<type>" }
Both examples below are equivalent, and the value of the theme
param (by default a string) will be automatically cast to a Symbol, ready for use in instantiating the component.
# @param theme [Symbol] select { choices: [primary, secondary, danger] }
def default(theme: :primary)
render Elements::ButtonComponent.new(theme: theme) do
'Click me'
end
end
# @param theme select { choices: [primary, secondary, danger], value_type: "Symbol" }
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 excepttoggle
inputsBoolean
- default fortoggle
inputsSymbol
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 parserArray
- value string converted to Array using the Ruby YAML parser