import { Meta } from '@storybook/addon-docs';

<Meta title="Docs/Customization/Schema" />

# Schema
<br />

## Description

The schema is an important concept within `sfb-theme-components`. It determines whether components are customizable through the Storefront Builder visual editor.
For example, you can define branding properties like `height` or `background-colour` in a component that Marketplace Managers can later customize with the WYSIWYG. Therefore, you enable users to enrich existing theme components without the need for coding expertise.

The following table lists the properties that you can specify for your component's schema so your Marketplace Manager can customize your components with the visual editor.

<table>
    <tr>
        <th>Property name</th>
        <th>Description</th>
        <th>Type</th>
        <th>Availability</th>
    </tr>
    <tr>
        <td>name</td>
        <td>The name of the component. This name must be unique and identifies the component to use in the theme.</td>
        <td>String</td>
        <td>Early Availability</td>
    </tr>
    <tr>
        <td>title</td>
        <td>The title of the component to display in the visual editor.</td>
        <td>String</td>
        <td>Early Availability</td>
    </tr>
    <tr>
        <td>iconName</td>
        <td>The icon that will be displayed in the visual editor for this component.</td>
        <td>String</td>
        <td>Early Availability</td>
    </tr>
    <tr>
        <td>orderableSection</td>
        <td>The sections or storefront pages to which users can add this component with the visual editor.</td>
        <td>Array of <i>constants/namespaces.js</i> enum</td>
        <td>Early Availability</td>
    </tr>
    <tr>
        <td>menus</td>
        <td>The different menu(s) created in the visual editor that the component can consume.</td>
        <td>Array of <i>constants/menus.js</i> enum</td>
        <td>Early Availability</td>
    </tr>
    <tr>
        <td>hasChildrenComponents</td>
        <td>This property indicates if a component has children components or not.</td>
        <td>Boolean</td>
        <td>Early Availability</td>
    </tr>
    <tr>
        <td>form</td>
        <td>
            The branding properties that a user can customize in the visual editor for this component. Each branding property can
            be associated with a <a href="?path=/story/docs-customization-schema-component-types--page#schema-component-types">schema component type</a>.
        </td>
        <td>Object</td>
        <td>Early Availability</td>
    </tr>
</table>

## How do components consume the schema form?

Let's create a custom `Banner` component as an example. This `Banner` component displays some promotional text. 

```jsx
import React from 'react';
import PropTypes from 'prop-types';
import { COLORPICKER, INPUT, SLIDER } from '@appdirect/sfb-theme-components/src/constants/schemaComponentTypes';
import withListener from '@appdirect/sfb-theme-components/src/components/withListener';
import './styles/Banner.scss';

const Banner = props => {
    const { settings = {} } = props;
    const {
        title,
        backgroundColor,
        textColor,
        height,
    } = settings;

    const bannerStyle = {
        backgroundColor,
        color: textColor,
        height: `${height}px`
    };

    return (
        <div style={bannerStyle}>
            {title}
        </div>
    );
};

Banner.schema = () => ({
    name: 'Banner',
    title: 'Banner',
    iconName: 'carousel',
    form: {
        title: {
            title: 'Banner title',
            placeholder: 'Banner title',
            defaultValue: 'My awesome banner title',
            type: INPUT,
            required: true,
            validation: {
                pattern: /^[a-zA-Z0-9 ]+$/gm,
                message: 'Invalid text'
            }
        },
        backgroundColor: {
            title: 'Banner background color',
            defaultValue: '#0076ce',
            type: COLORPICKER
        },
        textColor: {
            title: 'Banner text color',
            defaultValue: '#ffffff',
            type: COLORPICKER
        },
        height: {
            title: 'Banner height',
            type: SLIDER,
            required: true,
            defaultValue: 35,
            options: {
                step: 1,
                min: 0,
                max: 100
            }
      }
    }
});

const SETTINGS = Banner.schema().form;
export default withListener(Banner);
```

As you can see, in this component the schema specifies `Banner` as the name, `Banner` as the title, and `carousel` as the iconName. The Marketplace Manager can customize these branding properties in the visual editor:
- Title
- Background colour
- Text colour
- Height

A Marketplace Manager can now customize this `Banner` component in the visual editor and save the form settings into the database. Every component has a `settings` prop that receives the form setting changes made by the Marketplace Manager in the visual editor, so the component can receive and render the form setting customizations. Note that this is done by server-side component rendering: the corresponding settings for each component are automatically injected.

This is how the settings props in the component are retrieved and the customizations are applied:

```js
const { settings = {} } = props;
const {
    title,
    backgroundColor,
    textColor,
    height,
} = settings;
```

## withListener HOC

Note that the `Banner` code snippet above uses the `withListener` HOC. This HOC enables you to make the component send a custom event in the visual editor preview panel on each form setting change. Therefore, you would be able to see your form setting changes live in the visual editor preview panel.

## Storybook Controls

The Storybook Controls feature enables you to edit props dynamically using the Storybook UI so that you can preview your schema form setting changes.

Each [schema component type](?path=/docs/docs-customization-schema-component-types--docs) is mapped to an appropriate Control. You can edit the value in Storybook UI with Controls for each schema component type.

<table>
    <tr>
        <th>Schema Component Type</th>
        <th>Controls</th>
    </tr>
    <tr>
        <td>INPUT</td>
        <td>text</td>
    </tr>
    <tr>
        <td>TEXTAREA</td>
        <td>text</td>
    </tr>
    <tr>
        <td>DROPDOWN</td>
        <td>select</td>
    </tr>
    <tr>
        <td>TOGGLE</td>
        <td>boolean</td>
    </tr>
    <tr>
        <td>TOGGLE_CHECK</td>
        <td>boolean</td>
    </tr>
    <tr>
        <td>COLORPICKER</td>
        <td>color</td>
    </tr>
    <tr>
        <td>RADIO</td>
        <td>radio</td>
    </tr>
    <tr>
        <td>CHECKBOX</td>
        <td>check</td>
    </tr>
    <tr>
        <td>IMAGE_UPLOADER</td>
        <td>object</td>
    </tr>
    <tr>
        <td>SLIDER</td>
        <td>range</td>
    </tr>
    <tr>
        <td>MENU</td>
        <td>object</td>
    </tr>
    <tr>
        <td>DRAWER_LIST</td>
        <td>object</td>
    </tr>
</table>


## Screenshot

<img src="/KNOBS.png" alt="Knobs" />

## Marketplace data

If you want to retrieve specific field data from the page object (available in the data viewer in the toolkit's toolbar), you can specify that in the schema object through the `marketplaceData` object.

<img src="/DATA_VIEWER.png" alt="Data Viewer" />

### Example

The `TabsMenu` component needs to get the following data from the page object:
<li>The list of items</li>
<li>The list of call to action buttons</li>
<li>The list of images</li>

In the schema object with the `marketplaceData` object, you need to map each attribute to a page object's field:

```js
marketplaceData: {
    summary: 'application.summary',
    callToActions: 'application.callToActions',
    items: 'application.navigation.items'

}
```

Then you can use the `marketplaceData` properties in your component through the `data` props:

```js
TabsMenuComponent.propTypes = {
    data: PropTypes.shape({
        items: PropTypes.arrayOf(
            PropTypes.shape({
                label: PropTypes.string,
                href: PropTypes.string,
                value: PropTypes.string,
                selected: PropTypes.bool
            })
        ),
        summary: PropTypes.shape({
            logoSrc: PropTypes.string,
            title: PropTypes.string
        }),
        callToActions: PropTypes.shape({
            items: PropTypes.arrayOf(
                PropTypes.shape({
                    label: PropTypes.string,
                    tooltip: PropTypes.string,
                    isPrimary: PropTypes.bool,
                    disabled: PropTypes.bool,
                    url: PropTypes.string,
                    actionType: PropTypes.string
                })
            )
        })
    })
}
```
