Widgets in Forms

This is the official documentation of the @forestadmin/agent Node.js agent.

Field Widgets empower your Actions Forms providing various options to display inputs and ease operators in their daily operations.

Widgets

WidgetSupported typesMinimal versionDescription

null

All types

@forestadmin/agent@*

Use the default widget

String

@forestadmin/agent@1.34.0

Display a text input with address autocomplete.

Boolean

@forestadmin/agent@1.18.0

Display a checkbox with true/false and possibly null values.

StringList, NumberList

@forestadmin/agent@1.24.0

Display a group of checkboxes to select multiple values.

String

@forestadmin/agent@1.27.0

Display a color picker to select a color.

Number

@forestadmin/agent@1.28.0

Display a currency input.

Date, Dateonly, String

@forestadmin/agent@1.29.0

Display a calendar date picker

Date, Dateonly, Number, String, StringList

@forestadmin/agent@1.17.0

Users can choose between a limited set of values.

File, FileList

@forestadmin/agent@1.35.0

Users can upload files.

Json

@forestadmin/agent@1.31.0

Display a Json editor with syntax highlighting.

Number

@forestadmin/agent@1.25.0

Display a standard number input.

NumberList

@forestadmin/agent@1.26.0

Display a standard number input to enter a list of number values.

Date, Dateonly, Number, String

@forestadmin/agent@1.23.0

Group of radio buttons to choose a value from.

String

@forestadmin/agent@1.22.0

Rich text area allowing to input formatted text.

String

@forestadmin/agent@1.21.0

Multi-line text area.

String

@forestadmin/agent@1.19.0

One-line text input.

StringList

@forestadmin/agent@1.20.0

One-line text input to enter a list of string values

Time

@forestadmin/agent@1.32.0

Input for entering a time

String, StringList

@forestadmin/agent@1.33.0

Dropdown containing the users available in the current project

Address autocomplete widget

The address autocomplete widget allows to input an address as a text value, autocompleted by the Google Maps API.

agent.customizeCollection('customer', collection => {
  collection.addAction('Update address', {
    scope: 'Single',
    form: [
      {
        label: 'Address',
        type: 'String',
        widget: 'AddressAutocomplete',
        placeholder: 'Type the address here',
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form (when empty):

And once the user starts typing:

Options

OptionTypeUsageMinimal versionDescription

placeholder

string

optional

@forestadmin/agent@1.34.0

Placeholder shown in the component.

Checkbox widget

The checkbox widget allows to activate or deactivate a boolean value.

agent.customizeCollection('product', collection => {
  collection.addAction('Refresh price', {
    scope: 'Single',
    form: [
      {
        label: 'Send a notification',
        type: 'Boolean',
        widget: 'Checkbox',
        isRequired: true,
        defaultValue: false,
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form:

Options

The Checkbox widget has no specific options, but its behavior can be customized using the following already existing options on fields:

OptionTypeUsageMinimal versionDescription

defaultValue

boolean or null

optional

@forestadmin/agent@*

Default value of the field. Define a value different from null in combination to isRequired to receive a not-null value even if the user did not click the widget.

isRequired

boolean

optional

@forestadmin/agent@*

If isRequired is set to false (default value), the checkbox widget allows users to choose between 3 states: true, false and null. Setting isRequired to true only allows 2 states: true and false. In both cases the default value will be null if not defined.

By default, the Checkbox widget allows 3 states:

  • null

  • true

  • false

To ensure having only 2 states, you need to:

  1. set the isRequired option to true,

  2. define a value for the defaultValue option.

Checkbox group widget

The checkbox group widget allows to select multiple values from a list of options.

agent.customizeCollection('product', collection => {
  collection.addAction('Leave a review', {
    scope: 'Single',
    form: [
      {
        label: 'Why do you like this product?',
        type: 'StringList',
        widget: 'CheckboxGroup',
        options: [
          { value: 'price', label: 'Good price' },
          { value: 'quality', label: 'Build quality' },
          { value: 'look', label: 'It looks good' },
        ],
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form:

Options

OptionTypeUsageMinimal versionDescription

options

An array if values, an array of value/label pairs, or a function receiving a context object returning this shape.

required

@forestadmin/agent@1.24.0

List of available options in the component. Supported formats:

  • ['Good', 'Very good']

  • [{value: 2, label: 'Good'}, {value: 3, label: 'Very good'}]

  • (context) => ['Good', 'Very good']

  • (context) => [{value: 2, label: 'Good'}, {value: 3, label: 'Very good'}]

Color picker widget

The color picker widget allows to select a color.

organization.addAction('Customize UI', {
  scope: 'Single',
  form: [
    {
      label: 'Background color',
      type: 'String',
      widget: 'ColorPicker',
      placeholder: 'Select the color',
      isRequired: true,
      enableOpacity: false,
      quickPalette: [
        '#6002ee',
        '#90ee02',
        '#021aee',
        '#d602ee',
        '#ee0290',
        '#ee6002',
      ],
    },
  ],
  execute: () => {},
});

The above code will produce the following form:

Options

OptionTypeUsageMinimal versionDescription

enableOpacity

boolean

optional

@forestadmin/agent@1.27.0

Allow users to select a color with an opacity.

placeholder

string

optional

@forestadmin/agent@1.27.0

Placeholder shown in the component.

quickPalette

string

optional

@forestadmin/agent@1.27.0

List of colors to display in the quick palette. The list can be an array of hex colors or rgba colors.

Currency input widget

The currency input widget allows to input a currency value.

agent.customizeCollection('product', collection => {
  collection.addAction('Change price', {
    scope: 'Single',
    form: [
      {
        label: 'Price',
        type: 'Number',
        widget: 'CurrencyInput',
        placeholder: 'Enter the new price',
        isRequired: true,
        min: 0,
        max: 1000,
        step: 1,
        currency: 'USD',
        base: 'Unit',
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form:

Options

OptionTypeUsageMinimal versionDescription

currency

string or a function returning a string (dynamic forms).

required

@forestadmin/agent@1.28.0

Currency code to display in the component. Valid currency ISO codes ↗ is required.

base

Unit (default value) or Cent

optional

@forestadmin/agent@1.28.0

Base unit to use for the value.unitValue is expressed in the currency's base unit.centValue is expressed as a hundredth of the base unit (typically in cents).

min

number or a function returning a number (dynamic forms).

optional

@forestadmin/agent@1.28.0

Minimum value allowed.

max

number or a function returning a number (dynamic forms).

optional

@forestadmin/agent@1.28.0

Maximum value allowed.

placeholder

string

optional

@forestadmin/agent@1.28.0

Placeholder shown in the component.

step

number or a function returning a number (dynamic forms).

optional

@forestadmin/agent@1.28.0

Step between two values.

DatePicker widget

The date picker widget allows to input a date in a form

agent.customizeCollection('order', collection => {
  collection.addAction('Set shipping date', {
    scope: 'Single',
    form: [
      {
        label: 'Shipping date',
        type: 'Date',
        widget: 'DatePicker',
        format: 'DD-MM-YYYY',
        min: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000),
        max: new Date(),
        placeholder: 'please indicate when you shipped the item',
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form :

Note: using a Dateonly field type will hide the time section from the date picker

Options

OptionTypeUsageMinimal versionDescription

format

string or function returning a string

optional

@forestadmin/agent@1.29.0

The date picker uses moment.js library under the hood. You can refer to this documentation ↗ to specify your date display format

placeholder

string

optional

@forestadmin/agent@1.29.0

Text will be shown as placeholder if the field is empty

min

Date or function returning a Date

optional

@forestadmin/agent@1.29.0

The minimum date allowed to be set in the field

max

Date or function returning a Date

optional

@forestadmin/agent@1.29.0

The maximum date allowed to be set in the field

The dates are sent as ISO 8601 formatted strings over the network. In order to use them as Date object in your hooks, you can parse them directly like so

const shippingDate = new Date(context.formValues['Shipping date']))

The dropdown widget allows to select a value from a list of options.

agent.customizeCollection('product', collection => {
  collection.addAction('Leave a review', {
    scope: 'Single',
    form: [
      {
        label: 'Rating',
        type: 'Number',
        widget: 'Dropdown',
        options: [
          { value: 0, label: 'Poor' },
          { value: 1, label: 'Fair' },
          { value: 2, label: 'Good' },
          { value: 3, label: 'Very good' },
          { value: 4, label: 'Excellent' },
        ],
        search: 'disabled',
        placeholder: 'Select a rating',
        isRequired: true,
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form:

In this next example, we call a remote api to get a list of products and then perform a custom filter based on given fields of the json response, and return the first 25. This example uses search: 'dynamic' dropdown widget, which provides you with the searchValue in the callback context.

Note: when a search is performed, only the field on which it is performed will have its options recomputed.

agent.customizeCollection('product', collection => {
  collection.addAction('Add more products', {
    execute: async (context, resultBuilder) => {
      // ...
    },
    scope: 'Bulk',
    form: [
      {
        label: 'Select some products',
        type: 'StringList',
        widget: 'Dropdown',
        search: 'dynamic',
        options: async (context, searchValue) => {
          const products = await (
            await fetch('https://api.fake-eshop.com/v2/products')
          ).json();
          return products
            .filter(product => {
              return (
                !searchValue ||
                ['description', 'name', 'features', 'keyword', 'category'].some(
                  key =>
                    product[key]?.toLowerCase().includes(searchValue.toLowerCase()),
                )
              );
            })
            .slice(0, 25)
            .sort((a, b) => a.name.localeCompare(b.name))
            .map(product => ({ label: product.name, value: product.id }));
        },
      },
    ],
  });
});

Options

OptionTypeUsageMinimal versionDescription

options

Either an array of values, or an array of value/label pairs, or a function returning one of those arrays

required

@forestadmin/agent@1.17.0 (@forestadmin/agent@1.20.1 for dynamic options)

List of available options in the component. Supported formats:

  • ['Good', 'Very good']

  • [{value: 2, label: 'Good'}, {value: 3, label: 'Very good'}]

placeholder

string

optional

@forestadmin/agent@1.17.0

Placeholder shown in the component.

search

static, dynamic or disabled(default)

optional

@forestadmin/agent@1.17.0, dynamic available from @forestadmin/agent@1.30.0

Allow users to input text to filter values displayed in the dropdown. - disabled: Users need to scroll to find the appropriate option. - static: Users can search a value by typing terms in an input. The search will be done in the browser, based on the label. - dynamic The search is performed on the agent side, using any custom logic needed to fetch the required options

File picker widget

The file picker allows to upload files

import { File } from '@forestadmin/datasource-toolkit';
import fs from 'fs/promises';
import path from 'path';

import { UserCustomizer } from '../typings';

async function readFile(filePath: string): Promise<File> {
  const data = await fs.readFile(filePath);

  return {
    mimeType: 'image/png',
    buffer: Buffer.from(data),
    name: path.basename(filePath),
  };
}

async function writeFile(file: File, directoryPath: string): Promise<void> {
  await fs.mkdir(directoryPath, { recursive: true });
  await fs.writeFile(
    path.join(directoryPath, path.basename(file.name)),
    file.buffer,
  );
}

export default async (collection: UserCustomizer) => {
  collection.addAction('Update user identification details', {
    scope: 'Single',
    execute: async (context, resultBuilder) => {
      try {
        const username = await context.getRecord(['username']).username;
        const userDirectory = path.join(
          __dirname,
          'data/documents',
          sanitize(username),
        );
        await Promise.all([
          await writeFile(context.formValues['Avatar picture'], userDirectory),
          ...context.formValues.Identification.map(async (file: File) => {
            await writeFile(file, path.join(userDirectory, 'identification'));
          }),
        ]);
        return resultBuilder.success(`Profile updated`);
      } catch (e) {
        return resultBuilder.error(
          `Upload failed with error: ${(e as Error).message}`,
        );
      }
    },
    form: [
      {
        label: 'Avatar picture',
        type: 'File',
        widget: 'FilePicker',
        description: 'Upload a profile picture or leave it to use the default one',
        extensions: ['png', 'jpg'],
        maxSizeMb: 20,
        defaultValue: readFile(
          path.join(__dirname, './data/avatars/default-avatar.png'),
        ),
      },
      {
        label: 'Identification',
        type: 'FileList',
        widget: 'FilePicker',
        description: 'Upload up to 4 documents to identify the user',
        extensions: ['png', 'jpg', 'bmp', 'pdf', 'gif'],
        maxSizeMb: 2,
        maxCount: 4,
        isRequired: true,
      },
    ],
  });
};

The above code will produce the following form:

Options

OptionTypeUsageMinimal versionDescription

extensions

An array of string values, or a function returning this shape

optional

@forestadmin/agent@1.35.0

The whitelist of file extensions allowed. If not specified, any file extension will be accepted

maxSizeMb

A number, or a function returning a number

optional

@forestadmin/agent@1.35.0

The max file size allowed to upload. Any file size is allowed if left empty. Make sure your server will be able to handle it.

maxCount

A positive integer number, or a function returning this shape

optional

@forestadmin/agent@1.35.0 (only available if the field type is FileList)

The max number of files allowed to be uploaded. If not specified, any number of files is allowed

Number input widget

The number input widget allows to input a number value.

agent.customizeCollection('product', collection => {
  collection.addAction('Change price', {
    scope: 'Single',
    form: [
      {
        label: 'Price',
        type: 'Number',
        widget: 'NumberInput',
        placeholder: 'Enter the new price',
        isRequired: true,
        min: 0,
        max: 1000,
        step: 1,
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form:

Options

OptionTypeUsageMinimal versionDescription

min

number or a function returning a number (dynamic forms).

optional

@forestadmin/agent@1.25.0

Minimum value allowed.

max

number or a function returning a number (dynamic forms).

optional

@forestadmin/agent@1.25.0

Maximum value allowed.

placeholder

string

optional

@forestadmin/agent@1.25.0

Placeholder shown in the component.

step

number or a function returning a number (dynamic forms).

optional

@forestadmin/agent@1.25.0

Step between two values.

JSON editor widget

The JSON editor widget display a rich editor with syntax highlighting for JSON.

agent.customizeCollection('product', collection => {
  collection.addAction('Set properties', {
    scope: 'Single',
    form: [
      {
        label: 'Properties',
        type: 'Json',
        widget: 'JsonEditor',
        defaultValue: { color: 'red' },
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form:

Options

This widget does not have any options.

Number input list widget

The number input list widget allows to input a list of number values.

agent.customizeCollection('product', collection => {
  collection.addAction('Change step values', {
    scope: 'Single',
    form: [
      {
        label: 'Step values',
        type: 'NumberList',
        widget: 'NumberInputList',
        placeholder: 'Enter the new step value',
        isRequired: true,
        min: 0,
        max: 1000,
        step: 1,
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form (once the user entered two step values):

Options

OptionTypeUsageMinimal versionDescription

allowDuplicates

boolean

optional

@forestadmin/agent@1.26.0

Allow users to enter duplicate values.

allowEmptyValues

boolean

optional

@forestadmin/agent@1.26.0

Allow users to enter empty values.

enableReorder

boolean

optional

@forestadmin/agent@1.26.0

Allow users to reorder values.

min

number

optional

@forestadmin/agent@1.26.0

Minimum value allowed.

max

number

optional

@forestadmin/agent@1.26.0

Maximum value allowed.

placeholder

string

optional

@forestadmin/agent@1.26.0

Placeholder shown in the component.

step

number

optional

@forestadmin/agent@1.26.0

Step between two values.

Radio group widget

The radio group widget allows to select a value from a list of options.

agent.customizeCollection('product', collection => {
  collection.addAction('Leave a review', {
    scope: 'Single',
    form: [
      {
        label: 'Appreciation',
        type: 'Number',
        widget: 'RadioGroup',
        options: [
          { value: 1, label: 'Good' },
          { value: 0, label: 'Average' },
          { value: -1, label: 'Bad' },
        ],
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form:

Options

OptionTypeUsageMinimal versionDescription

options

An array if values, an array of value/label pairs, or a function receiving a context object returning this shape.

required

@forestadmin/agent@1.23.0

List of available options in the component. Supported formats:

  • ['Good', 'Very good']

  • [{value: 2, label: 'Good'}, {value: 3, label: 'Very good'}]

  • (context) => ['Good', 'Very good']

  • (context) => [{value: 2, label: 'Good'}, {value: 3, label: 'Very good'}]

Using the options function, you can dynamically change the list of options based on the context, which contains information about the selected records and other values in the form. More info about dynamic configuration.

Rich text widget

The rich text widget allows to input a formatted text value.

agent.customizeCollection('product', collection => {
  collection.addAction('Leave a review', {
    scope: 'Single',
    form: [
      {
        label: 'Comment',
        type: 'String',
        widget: 'RichText',
        isRequired: true,
        placeholder: 'Type your comment here',
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form:

Options

OptionTypeUsageMinimal versionDescription

placeholder

string

optional

@forestadmin/agent@1.22.0

Placeholder shown in the component.

Text area widget

The text area widget allows to input a multiline string value.

agent.customizeCollection('product', collection => {
  collection.addAction('Leave a review', {
    scope: 'Single',
    form: [
      {
        label: 'Comment',
        type: 'String',
        widget: 'TextArea',
        isRequired: true,
        placeholder: 'Type your comment here...',
        rows: 10,
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form:

Options

OptionTypeUsageMinimal versionDescription

placeholder

string

optional

@forestadmin/agent@1.21.0

Placeholder shown in the component.

rows

number

optional

@forestadmin/agent@1.21.0

Widget's height expressed as a number of rows.

Text input widget

The text input widget allows to input a string value.

agent.customizeCollection('customer', collection => {
  collection.addAction('Send notification', {
    scope: 'Single',
    form: [
      {
        label: 'Message',
        type: 'String',
        widget: 'TextInput',
        placeholder: 'Enter your message here',
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form:

Options

OptionTypeUsageMinimal versionDescription

placeholder

string

optional

@forestadmin/agent@1.19.0

Placeholder shown in the component.

Text input list widget

The text input list widget allows to input a list of string values.

agent.customizeCollection('product', collection => {
  collection.addAction('Add tags', {
    scope: 'Single',
    form: [
      {
        label: 'Tag',
        type: 'StringList',
        widget: 'TextInputList',
        isRequired: true,
        placeholder: 'Enter a tag',
        allowDuplicates: false,
        allowEmptyValues: false,
        enableReorder: false,
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form (once the user entered two tags):

Options

OptionTypeUsageMinimal versionDescription

allowDuplicates

boolean

optional

@forestadmin/agent@1.20.0

Allow users to enter duplicate values.

allowEmptyValues

boolean

optional

@forestadmin/agent@1.20.0

Allow users to enter empty values.

enableReorder

boolean

optional

@forestadmin/agent@1.20.0

Allow users to reorder values.

placeholder

string

optional

@forestadmin/agent@1.20.0

Placeholder shown in the component.

Time picker widget

The time picker widget allows to select a time

agent.customizeCollection('store', collection => {
  collection.addAction('set opening and closing time', {
    scope: 'Single',
    form: [
      {
        label: 'Opening time',
        type: 'Time',
        widget: 'TimePicker',
      },
      {
        label: 'Closing time',
        type: 'Time',
        widget: 'TimePicker',
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form :

time picker does not support additional options

User dropdown widget

The user dropdown widget allows to input a user or list of users from the project

agent.customizeCollection('Ticket', collection => {
  collection.addAction('Assign to the record', {
    scope: 'Single',
    form: [
      {
        type: 'String',
        label: 'Manager',
        widget: 'UserDropdown',
        placeholder: 'Select the manager in charge',
      },
      {
        type: 'StringList',
        label: 'Operators',
        widget: 'UserDropdown',
        placeholder: 'Select operators for this record',
      },
    ],
    execute: async context => {
      /* ... perform work here ... */
    },
  });
});

The above code will produce the following form (once the user has selected some users)

Options

OptionTypeUsageMinimal versionDescription

placeholder

string

optional

@forestadmin/agent@1.33.0

Placeholder shown in the component.

Last updated