Components:Tutorial: Input Component - bettyblocks/cli GitHub Wiki
In this tutorial we will create an input Component that can be used in the Betty Blocks Page Builder. This tutorial assumes that you've gone ahead and set up your own Component Set.
To create our Component, we'll follow these steps:
- Prepare the Prefab.
- Prepare the Component.
- Write the JSX.
- Add the Component to the Prefab.
- Allow the Component inside other Components.
- Test your Component.
- Customize Component Page Builder behavior.
- Add Component Options.
If you are unsure what a Prefab is, find out here.
Inside ./src/prefabs
, add a file called input.js
containing the following:
(() => ({
name: "Input",
}))();
The Prefab currently only contains a name, Input
, which corresponds to the filename input.js
. We'll add a couple more fields so that it can be used in the Page Builder.
First off, let's choose the icon to represent this Prefab in the Page Builder sidebar. A list of available icons can be found here. Let's go with "TextInputIcon"
for now:
(() => ({
name: "Input",
icon: "TextInputIcon"
}))();
We need to supply a category to determine how the Prefab will be grouped. For our input element, the "FORM"
category makes sense:
(() => ({
name: "Input",
icon: "TextInputIcon",
category: "FORMS"
}))();
The structure determines which Components belong to the Prefab, along with their options. For now, we add an empty list. We will return to this value after we've prepared the Component.
(() => ({
name: "Input",
icon: "TextInputIcon",
category: "FORMS",
structure: []
}))();
A Prefab without Components is literally useless. Let's fix this by adding a file called input.js
to ./src/components
containing the following:
(() => ({
name: "Input"
}))();
As with the Prefab, we start with just the name, which likewise mirrors the Component filename. This name can later be used to reference this Component inside the Prefab structure.
The type can be added to the list of allowed types in another Component. We recommend using the uppercased Component name as the value:
(() => ({
name: "Input",
type: "INPUT"
}))();
It doesn't make much sense yet for our input Component to contain children, so let's go ahead and add an empty list.
(() => ({
name: "Input",
type: "INPUT",
allowedTypes: []
}))();
In this case, we want our input Components to be stacked vertically, so let's go with a horizontal drop indicator:
(() => ({
name: "Input",
type: "INPUT",
allowedTypes: [],
orientation: "HORIZONTAL"
}))();
For now, add an empty curried function as the styles key. Don't worry for now if this looks funky to you, we'll come back to this later:
(() => ({
name: "Input",
type: "INPUT",
allowedTypes: [],
orientation: "HORIZONTAL",
styles: () => () => ({})
}))();
Now that our Prefab and Component are configured, let's get down to business and write the actual mark-up. Add JSX to the Component (./src/components/input.js
) so it ends up looking like this:
(() => ({
name: "Input",
type: "INPUT",
allowedTypes: [],
orientation: "HORIZONTAL",
styles: () => () => ({}),
jsx: <input type="text" />
}))();
To make the Component available inside the Page Builder, we add it to the Prefab we made earlier (./src/prefabs/input.js
):
(() => ({
name: "Input",
icon: "TextInputIcon",
category: "FORMS",
structure: [
{
name: "Input",
options: [],
descendants: [],
},
],
}))();
The important part is that the name inside the first structure object ("Input"
) matches the name in ./src/components/input.js
. Options and descendants have been left empty. We'll get to those in a bit.
Before we can use the Component inside the Page Builder, we'll need to specify which of the other Components may serve as a parent. We do this by adding the Component type to the list of allowed types in the desired parent Component, ie. this container Component:
(() => ({
name: 'Container',
allowedTypes: [
"INPUT",
],
// omitted for brevity
}
Let's see the result of our work so far. Open the root directory of your Component Set in your shell, and tell bb
to build and serve:
$ bb components build
Built component set.
$ bb components serve
Serving "Your Awesome" Component Set at http://localhost:5001
Load your Component Set into the Page Builder as described here. After refreshing the page, the input Component should be available in the sidebar. Now drag a container Component onto the canvas, and add an input Component by dragging it onto the container.
If all went well, you should be seeing something like this:
After clicking the Play button, your Component will be rendered:
You might have noticed that you're able to type inside the input while designing your page, whereas you only want to enable this inside the actual Application you're building. Let's fix this inside the JSX of the input Component (./src/components/input.js
) with the help of a Component Helper:
(() => ({
// omitted for brevity
jsx: <input type="text" readOnly={B.env === 'dev'} />
}))();
What we're saying is that the input should be read-only when the Component is shown inside the Page Builder (as opposed to your Application).
When we click the Component on the Page Builder canvas, the sidebar is empty. This is because we haven't added any Options.
One thing we would like to make configurable is the placeholder text.
Add an option to the Component Prefab (./src/prefabs/input.js
) structure:
// omitted for brevity
structure: [
{
name: "Input",
options: [
{
key: "placeholder",
label: "Placeholder",
type: "TEXT",
value: "",
},
],
descendants: [],
},
],
// ...
If you drag the input Component on the canvas again and click it, you should see the placeholder Option in the sidebar:
Right now the Option doesn't do anything. The value can be changed, but it isn't used. Go back to the Component (./src/prefabs/input.js
) and change the JSX so it looks like this:
jsx: (
<input
type="text"
readOnly={B.env === 'dev'}
placeholder={options.placeholder}
/>
),
The placeholder attribute will now use the value of the Option:
Leads to:
Our input needs a name attribute in the context of a form. In ./src/prefabs/input.js
, add an Option, just like we did for placeholder:
// omitted for brevity
{
key: "name",
label: "Name",
type: "TEXT",
value: "",
},
// ...
Use the name Option value to set the name attribute in the Component (./src/components/input.js
):
<input
type="text"
readOnly={B.env === 'dev'}
placeholder={options.placeholder}
name={options.name}
/>
The effect of the name value is not immediately visible in the Application, but will come in handy later when we have to reference the input.
Let's add a label Option, so we can specify what our input field means. In ./src/prefabs/input.js
:
// omitted for brevity
structure: [
{
name: "Input",
options: [
{
key: "label",
label: "Label",
type: "TEXT",
value: "",
},
],
descendants: [],
},
],
// ...
Change the Component (./src/components/input.js
) mark-up accordingly:
<label htmlFor={options.name}>
<span>{options.label}</span>
<input
type="text"
readOnly={B.env === 'dev'}
placeholder={options.placeholder}
name={options.label}
id={options.label}
/>
</label>