7. Create a block using BlockDataForm – Effective Volto – Add-ons

Create a block using BlockDataForm

7. Create a block using BlockDataForm#

The BlockDataForm component renders a form fully integrated with Volto's blocks extensions mechanisms: variations and block styles. To use it, instantiate the schema and pass it down to the component. A full Volto block edit component could be like:

export const myBlockSchema = ({data, intl}) => {
  // notice that we are pass the data, so the schema is "dynamic", we can
  // compose it based on the data.

  return {
    title: "My block",
    fieldsets: [
      {
        id: 'default',
        title: 'Default',
        fields: ['myfield']
      }
    ],
    properties: {
      myfield: {
        title: "My field",
        widget: "number",
      }
    }
    required: [],
  };
}

export const MyBlockEdit = (props) => {
  const {
    block,
    onChangeBlock,
    data,
    selected,
    intl,
  } = props;

  let schema = Schema({ data, intl });

  return (
    <>
      <MyBlockViewComponent
        {...props}
        path={getBaseUrl(props.pathname)}
        mode="edit"
      />
      <SidebarPortal selected={selected}>
        <BlockDataForm
          schema={schema}
          onChangeField={(id, value) => {
            onChangeBlock(block, {
              ...data,
              [id]: value,
            });
          }}
          formData={data}
          onChangeBlock={onChangeBlock}
          block={block}
          blocksConfig={blocksConfig}
        />
      </SidebarPortal>
    </>
  );
};

If there's any registered variations for this block, they will be displayed as a Select control for the active variation, in the sidebar.

  myblock: {
    id: 'myblock',
    title: 'My Block',
    icon: heroSVG,
    group: 'common',
    view: MyBlockView,
    edit: MyBlockEdit,
    restricted: false,
    mostUsed: false,
    blockHasOwnFocusManagement: true,
    sidebarTab: 1,
    blockSchema: myBlockSchema,
    variations: [
      {
        id: 'leftSideView',
        isDefault: true,
        view: MyLeftSideView,
      },
      {
        id: 'rightSideView',
        isDefault: true,
        view: MyRighSideView,
      }
    ]
  },

Note: you can assign the schema to blockSchema in the block configuration. It is used to extract the block default values.

Volto also has a "generic block edit component", the EditDefaultBlock which you get by simply not setting the edit field of the block configuration registration. This component uses the blockSchema field, so you are required to set that.

7.1. Using a blockSchema and the dataAdapter pattern#

You can declare the schema that the block is using internally in the blockSchema property in the block configuration.

Sometimes is also useful to adapt the incoming data to other data format, structure or type. You can use the dataAdapter pattern in BlockDataForm as this:

Given a block with the config:

  config.blocks.blocksConfig.myBlock = {
    // ...
    schemaEnhancer: myBlockSchemaEnhancer,
    blockSchema: myBlockSchema,
    dataAdapter: myBlockDataAdapter,
    // ...
  };
  const schema = blocksConfig.myBlock.blockSchema({ intl });
  const dataAdapter = blocksConfig.myBlock.dataAdapter;

    <BlockDataForm
      schema={schema}
      title={schema.title}
      onChangeField={(id, value) => {
        dataAdapter({
          block,
          data,
          id,
          onChangeBlock,
          value,
        });
      }}
      onChangeBlock={onChangeBlock}
      formData={data}
      block={block}
      blocksConfig={blocksConfig}
    />

and define dataAdapter as this:

import { isEmpty } from 'lodash';

export const myBlockDataAdapter = ({
  block,
  data,
  id,
  onChangeBlock,
  value,
}) => {
  let dataSaved = {
    ...data,
    [id]: value,
  };

  // The adaptation that I need to inject to the block value(s)
  if (id === 'href' && !isEmpty(value) && !data.title && !data.description) {
    dataSaved = {
      ...dataSaved,
      title: value[0].Title,
      description: value[0].Description,
      head_title: value[0].head_title,
    };
  }

  // Then we save it to the current block data
  onChangeBlock(block, dataSaved);
};