Editing Components
Last updated
Last updated
Components are edited through the fields and forms that are declared in their schema.yml
file. This means that developers have maximum flexibility with the template and styling of their components, but end users get a consistent and manageable editing experience.
The simplest possible form is an inline WYSIWYG field. It consists of a property in the schema, and a data-editable
attribute on the editable element in the template.
When someone clicks the data-editable
title, it will open an inline WYSIWYG form. The field will inherit styles from your component, for a proper WYSYWIG experience.
data-editable
may reference any field or group of fields, even fields inside of complex lists! This is perfectly valid syntax:
Complex-list items can be reordered and deleted inline without opening a settings modal if their template markup allows for it.
The parent element that contains the list items must have a data-reorderable
attribute where the value is the name of the complex-list field (similar to data-editable
on inline fields) and a data-reorderable-prop
attribute where the value is a unique property that all complex-list items have. Each list item element should be a direct child of the data-reorderable
element and should have a data-reorderable-item
attribute where the value is the value of that item's data-reorderable-prop
property.
Placeholders display when fields are empty, and are used to prompt end users to add information. They can be added to any field or group.
The placeholder will display inside of the element with the relevant data-editable
attribute.
The placeholder height specified in the config is the minimum height of the displayed placeholder. The placeholder will be taller if its parent element has an explicitly set height. When deciding what height to set in the config, remember that it should reflect the height of the content that goes in the field.
If you want to display a placeholder for some field, but don't want to allow users to click into it, use the data-placeholder
attribute. This is useful for situations where a component doesn't have anything to display in edit mode (like ads), but shouldn't be relegated to an invisible component list (like...ads).
You must also add the permanent
property to the placeholder config to force the placeholder to always display. Permanent placeholders are styled more subtly than regular placeholders, since end users don't click on them to edit anything.
Placeholders can display the value of a field, which is useful for components not visible during edit mode, e.g. third-party embeds. The syntax for displaying a value in the placeholder text is ${fieldName}
, which is similar to JavaScript template literals. Note that permanent
must be enabled.
Inline WYSIWYG fields can accomplish a lot, but not every bit of data in a component is a string of formatted text. For everything else, there's overlay forms.
Any fields and groups that aren't inline WYSIWYG inputs will display in overlays. Kiln provides an ever-growing variety of fields that can be used to edit almost any kind of data you desire. To open these forms, add a data-editable
attribute on the relevant element and point it to a field or group.
Overlay forms frequently consist of a single field. This is a perfectly acceptable user experience, because overlay forms will open where the user clicks.
For forms with multiple fields, those fields must be combined into groups. A group must have a fields
property, and may have _label
and _placeholder
properties.
When end users click the data-editable
attribute that points to this group, it will open an overlay form with all of the group's fields, in order.
If you add a _placeholder
to a group, you must either make it permanent or specify what fields it should check with ifEmpty
.
Placeholders in groups may check multiple fields with ifEmpty
. This is useful for components with editable links, as you'll usually want to display a placeholder when either the url or the link text are empty. Operators are case-insensitive, and you can use AND
, OR
, or XOR
.
Placeholders in groups may use deep field comparison to calculate emptiness of complex-lists. For example, ifEmpty: 'byline.names'
will look for emptiness in the prop names
in all items in the byline
complex-list. Note that deep field comparison is only supported up to two levels deep, so using isEmpty: byline.names.text
will throw an error.
The Component Settings Form is a special type of overlay form that's created when you add a group called settings
to your schema. Components with settings
groups get a button in their component selector to open the settings form, so don't add data-editable="settings"
to any elements in the template. Settings groups must not have _label
(their form will be titled "[Component Name] Settings"), though they may have _placeholder
(this is useful for permanent placeholders).
Components in <head>
or invisible lists must have all of their fields available in settings forms, as they don't have any visible elements that can be clicked. The settings forms are accesible from the Find on Page drawer
Settings forms are useful for <head>
and invisible components, as well as optional fields that aren't frequently accessed. They're also useful to provide manual fallbacks for automatic component logic, such as fields that are generated in model.js
files.
Overlay and settings forms may split their fields into tabs. This is useful to group related fields by feature, or to provide a consistent tab for fields that override others. Sections are declared by wrapping the section name in parenthesis in the fields
property.
If you specify sections for some fields but leave the other fields blank, they'll be added to a General section. Fields and sections will appear in the form in the order they've been listed.
All fields in Kiln have a standard configuration, no matter what kind of input they use. As noted above, the most basic properties of the field config are _label
and _has
. These denote the label that appears when looking at the form and what kind of input the field uses. _has
can be a string (if the input has no arguments) or an object with an input
property.
Fields (and certain input options) may also have a special _reveal
property, which allows you to hide or show the field based on some data.
In this example, the Original Article URL field would only be displayed if the Article Syndication Type was set to copy
, since that would mean the article was being syndicated from some other place. You can compare against other fields or even against the site you're on.
field - to compare against, and/or...
sites - to compare against (will be true if the current site is one of them)
operator - optional operator to use for the comparison (defaults to ===
)
value - optional value to compare against, when you want to compare against a single value
values - array of optional values to compare against, when you want to compare against multiple values
If neither operator
, value
, nor values
are specified, the field will be shown when the compared field has any data (the same as the not-empty
operator). The syntax for comparing against sites is the same as site-specific components in lists.
When comparing against multiple values
, the comparison will be true if at least one of the values matches.
Operators:
===
!==
<
>
<=
>=
typeof
regex
length-equals
(compares the length of strings and arrays)
length-less-than
(compares the length of strings and arrays)
length-greater-than
(compares the length of strings and arrays)
empty
(only checks field data, no value needed)
not-empty
(only checks field data, no value needed)
truthy
(only checks field data, no value needed)
falsy
(only checks field data, no value needed)
You can compare against deep fields (like checkbox-group
or complex-list
) by using dot-separated paths, e.g. featureTypes.New York Magazine Story
. Don't worry about the spaces, as Kiln will parse it correctly to pull the relevant data. Note that this only works against proper fields, not any arbitrary non-kiln property.
Kiln's inputs are highly customized to the type of data they handle, but there are some shared arguments that work with most inputs.
help - description / helper text for the field, which generally appears below it
attachedButton - an icon button that may be attached to the field, to allow additional functionality
validate - an object that contains pre-publish validation rules:
validate.required - either true
or an object that described the conditions that should make this field required (using the same properties as _reveal
)
validate.min - minimum number (for numerical inputs) or length (for text and array inputs) that the field must meet
validate.max - maximum number (for numerical inputs) or length (for text and array inputs) that the field must not exceed
validate.pattern - regex pattern, for text inputs
min
and max
validation only runs if the field is filled out. This differentiates it from the required
validation, which specifies that a field must be filled out to be valid. Thus, it is possible to have an optional field that, if it is filled out, must have more/less than x items or a value greater/less than y.
Validation rules may also have custom error messages, that will appear in the same place as the help text. If you do not specify a message, default error messages will appear.
validate.requiredMessage - will appear when required validation fails
validate.minMessage - will appear when minimum validation fails
validate.maxMessage - will appear when maximum validation fails
validate.patternMessage - will appear when pattern validation fails (very handy to set, as the default message is vague)
Attached buttons are very useful when combined with custom Kiln inputs, but Kiln comes with two built-in: lock
and mediaplay-picker
.
Lock is a button that disables a field until the user clicks the button. This provides a small amount of friction before editing important (and rarely-edited) fields, similar to macOS's system preferences.
Clicking the lock toggles the input between disabled and enabled state.
Magic Button is a button that fetches data, allowing quick population of fields based on other fields, other component data, or the result of api calls.
For more information about magic-button
, please see its specific documentation.
Components can affect other components on the page by publishing and subscribing to properties (such as title
, description
, ledeImage
, etc). This allows many-to-many communication in Kiln, as multiple components can publish or subscribe to the same property, and components can publish or subscribe to multiple different properties. There are no restrictions on pubsub property names, so be sure to avoid unintended collisions!
To publish to a property, add _publish
to your field. The component will send data to that property after their model.save()
logic is run (if they have any). This allows that component to perform logic before propagating its data, such as generating extra fields.
There are some situations where a component wants to limit the data they're sending to a certain subset of components on the page, rather than all components that listen to a property. For example, a slideshow that sets the width of its children shouldn't also set the width of any other slideshow's children on the same page. To limit which components are affected by a property, you may use scoped publishing.
There are currently three scopes you may publish to: global
(default), children
, and parents
.
Components that want to subscribe to properties should have _subscribe
on the field(s) they want to update, with the name of the property it should subscribe to. Subscribed properties will trigger a component to save, thus allowing the subscribed component's model.save()
method to do logic on the incoming data before it's sent to the server.
Both _publish
and _subscribe
can receive a single property (as a string) or an array of properties. This extremely powerful tool allows you to create complicated many-to-many communication workflows in a safe and standardized way.
Scoped publishing may be used in many-to-many communication, and you may intersperse scoped and unscoped properties as needed.
Kiln intelligently prevents a component from publishing more than once during pubsub, so you may safely create circular pubsub logic. For example, a tags
component may update an article
's primaryTag
field, and the article
may also allow editing of that field (and propagating those changes back to tags
).
As cross-component updating is a feature of Kiln, it only works client-side. For third-party microservices or manual API calls, please make use of cascading PUTs to affect multiple components.
If you have data you want to propagate when publishing (which happens server-side), have your publishing service add that data to the locals object, which will be passed in to all components' model.save()
method.
Kiln itself subscribes to a few specific properties, which it uses to update its index of pages. Publishing to kilnTitle
will update the page title, and kilnAuthors
will update the page authors. You can see the results in the Clay Menu.