@adamjkb/netlify-cms-widget-select-async

NetlifyCMS select widget from external resources

Usage no npm install needed!

<script type="module">
  import adamjkbNetlifyCmsWidgetSelectAsync from 'https://cdn.skypack.dev/@adamjkb/netlify-cms-widget-select-async';
</script>

README

Async Select Widget for NetlifyCMS

A select widget for NetlifyCMS widget that allows you to select a string value or an array of strings. The options are populated through asynchronous fetch requests.

Checkout the demo or try it out in a sandbox

Table of contents

Features

  • Support for custom fetch() request parameters like headers, method, and body
  • Supports GraphQL and REST APIs
  • Nested data structures
  • Grouped options
  • Fuzzy searching fetched options

Install

Via NPM:

npm install @adamjkb/netlify-cms-widget-async

You may install it through native JavaScript import through CDNs such as Skypack

import { Widget as AsyncSelectWidget } from '@adamjkb/netlify-cms-widget-async'
CMS.registerWidget(AsyncSelectWidget)

Via script tag:

<script src='https://unpkg.com/@adamjkb/netlify-cms-widget-select-async/dist/index.umd.js'></script>
<script>
    CMS.registerWidget(AsyncSelectWidget.Widget)
</script>

How to use

Simple usage

Add to your NetlifyCMS collection:

fields:
  - name: simple_usage
    label: Simple usage
    widget: select-async
    # widget options:
    url: https://fakestoreapi.com/products
    value_field: id
    display_field: title

Advanced usage

Add to your NetlifyCMS collection:

fields:
  - name: advanced_usage
    label: Advanced usage
    widget: select-async
    # widget options:
    url: https://graphql.myshopify.com/api/graphql
    value_field: node.value
    display_field: node.label
    data_path: data.products.edges
    grouped_options:
        data_path: node.options.edges
        value_field: node.id
        display_field: node.title
    multiple: true
    min: 1
    max: 4
    refetch_url: false
    fuzzy_search: true
    fetch_options:
        headers:
            Content-Type: application/json
            X-Shopify-Storefront-Access-Token: dd4d4dc146542ba7763305d71d1b3d38
        method: POST
        body: '{"query": "query allProducts{ products(first:10) { edges { node { label: title value: id options: variants(first:3) { edges { node { id title } } } } } } }"}'

Options

url string

Endpoint URL

Example: https://fakestoreapi.com/products


display_field string | default: "label"

Object key or path to object to be displayed.

Example: node.label or title


value_field string | default: "value"

Object key or path to object to be saved.

Example: node.value or id


data_path string?

Object key or path to object to the array of objects to be used as options.

If fetch() request does not return an array of object but an object this value can be used to access the array.

Example: data.products.edges or data


multiple boolean | default: false

Allows multiple options to be selected. Widget's output value is going to change to an string[]


min and max integer?

Minimum and maximum number of items allowed to be selected

ignored if multiple is false


fetch_options object?

The properties of this field are mapping to the main values of native fetch() request parameters. fetch() documentation on MDN

If you need to set parameters outside of the built-in method,headers, and body you can do so with by extending the passed in parameters using the fethc_options.params_function

fetch_options.method string | default: "GET"

Request method.

fetch_options.headers object | default: {}

Request headers.

Example:

fetch_options:
    headers:
        Content-Type: application/json
        X-Access-Token: <PUBLIC-API-KEY>
fetch_options.body string | default: undefined

Request body.

Note that GET methods does not have a body. Change request method if you are specifying this field.

fetch_options.params_function function?

⚠️ Only works if you are using a JavaScript config file to initialize NetlifyCMS. Refer to documentation how to set that up.

A JavaScript function that receives all all the other fetch_options values, the url, and the search input term. Must return a valid object with at least a url property.

function({term, url, method, headers, body}) {
    // ...
    return {
        url, // required
        options: {method, headers, body} // optional
    }
}
Example config using a GraphQL query with variables:
fetch_options: {
    // ...
    params_function: ({ term, url, ...rest }) => ({
        url,
        options: {
            ...rest,
            body: JSON.stringify({
                query: `
                    query allProducts(
                        $myCustomQuery: String
                    ) {
                        products(
                            first: 15
                            query: $myCustomQuery
                        ) {
                            edges {
                                node {
                                    label: title
                                    value: id
                                }
                            }
                        }
                    }
                `,
                variables: {
                    'myCustomQuery': `title:${term}*`
                }
            })
        }
    })    
}

grouped_options object?

If you have data structure where there is a second level of option you have the option to group them together. A sample data structure:

const data = [
    {
        label: 'Colors',
        options: [
            {
                label: 'Red',
                value: 'red'
            },
            // ...
        ]
    },
    {
        label: 'Flavors',
        options: [
            {
                label: 'Salted Caramel',
                value: 'salted-caramel'
            }
            // ...
        ]
    },
    // ...
]
grouped_options.display_field string | default: "label"

Object key or path to object to be displayed.


grouped_options.value_field string | default: "value"

Object key or path to object to be saved.


grouped_options.data_path string

Object key or path to object to the array of objects to be used as options.


grouped_options.flatten_singles boolean | default: true

If true, grouped options that contain only one option will be transformed into a top level options. Its label will be formatted as such {parent.label}: {child.label}.


refetch_url boolean | default: true

By default react-select will send a try to load new options through a new request whenever the search input changes, setting this field's value to false will prevent that and retain the options after the initial request.

Note that the fetched options are filtered with fuzzy search by default so the search input will affect the displayed options.


fuzzy_search boolean | default: true

The widget uses fuse.js to search the provided options. But if you are using filtering the data on the API level you might not want to filter the result on client side too. See fetch_options.params_function

Setting both fuzzy_search and refetch_url off will turn off input field search function.


Authors

Acknowledgments

The project was heavily inspired by @chrisboustead's asynchronous implementation of the original NetlifyCMS Select widget, netlify-cms-widget-select-async