Skip to content

Commit

Permalink
Merge pull request #3552 from open-formulieren/issue/2629-dynamic-opt…
Browse files Browse the repository at this point in the history
…ions

Confirm ability to use dynamic options in selectboxes
  • Loading branch information
CharString authored Oct 26, 2023
2 parents 7a78870 + 49a188b commit 1a33fad
Show file tree
Hide file tree
Showing 11 changed files with 300 additions and 5 deletions.
Binary file not shown.
1 change: 1 addition & 0 deletions docs/manual/forms/examples/dynamic_options.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.. _example_logic_dynamic_options:

===============================
Formulier met dynamische opties
===============================

Expand Down
1 change: 1 addition & 0 deletions docs/manual/forms/examples/dynamic_options_2.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.. _example_logic_dynamic_options_2:

=============================================================
Formulier met dynamische opties op basis van het huidige jaar
=============================================================

Expand Down
125 changes: 125 additions & 0 deletions docs/manual/forms/examples/dynamic_options_3.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
.. _example_logic_dynamic_options_3:

==========================================================================
Formulier met (extra) opties in selectievakjes, keuzelijst en radio-velden
==========================================================================

In dit voorbeeld maken we een deel-formulier waarbij een vaste groep keuzeopties
dynamisch uitgebreid wordt op basis van logica-regels.

Deze functionaliteit kan op eenzelfde manier worden gebruikt voor de "radio"-,
"keuzelijst"- en de "selectievakjes"-componenten.

.. note::

U kunt dit voorbeeld downloaden en :ref:`importeren <manual_export_import>`
in Open Formulieren.

Download: :download:`dynamische-keuzelijsten.zip <_assets/dynamische-keuzelijsten.zip>`

Formulier maken
===============

#. Maak een formulier aan met de volgende gegevens:

* **Naam**: Dynamische keuzelijsten

#. Klik op het tabblad **Variabelen**. Klik hierbinnen op het tabblad
**Gebruikersvariabelen**.

#. Voeg een variabele toe door op **Variabele toevoegen** link te klikken, en voer in:

* **Naam**: Checkbox options
* **Datatype**: Lijst (array)
* **Beginwaarde**: vink *"Gebruik ruwe JSON-invoer"* aan en voer de JSON-array in:

.. code-block:: json
[
[
"option1",
"Option 1"
],
[
"option2",
"Option 2"
]
]
#. Klik op het tabblad **Stappen en velden**.

#. Klik aan de linkerkant op **Stap toevoegen** en selecteer **Maak een nieuwe
formulierdefinitie**.

#. Onder de sectie **(Herbruikbare) stapgegevens** vul het volgende in:

* **Naam**: Variabele selectievakjesopties

#. Scroll naar de sectie **Velden**.

#. Sleep een **Selectievakje** component op het witte vlak, vul de volgende
gegevens in en druk daarna op **Opslaan**:

* **Label**: Toon optie 3

#. Sleep een **Selectievakjes** component op het witte vlak, vul de volgende
gegevens in en druk daarna op **Opslaan**:

* **Label**: Variabele opties selectievakjes
* **Beschrijving**: De beschikbare opties zijn afhankelijk van eerdere antwoorden.

* **Keuzeopties**: *Variabele*
* **Opties-expressie**:

.. code-block:: json
{
"var": "checkboxOptions"
}
Deze referentie komt overeen met de sleutel van de eerder toegevoegde
gebruikersvariabele.

#. Klik op het tabblad **Logica** en voeg een eenvoudige regel toe.

#. Kies als triggervoorwaarde:

* Als "Variabele selectievakjesoptie: Toon optie 3 (toonOptie3)"
* "is gelijk aan"
* "de waarde"
* "Ja"

#. Voeg een actie toe:

* dan "zet de waarde van een variabele/component"
* "Checkbox options (checkboxOptions)"
* voer de volgende JsonLogic in:

.. code-block:: json
{
"merge": [
{
"var": "checkboxOptions"
},
[
[
"option3",
"Option 3"
]
]
]
}
#. Klik onderaan op **Opslaan** om het formulier volledig op te slaan.

U kunt nu het formulier bekijken.

.. note::

Deze expressie gebruik de JsonLogic `merge`_ operatie om de bestaande
gebruikersvariabele uit te breiden met extra keuzeopties. U kunt vrij
gebruikersvariabelen combineren zolang de variabele voor de selectievakjes component
een lijst van opties (waarde en label) bevat.

.. _merge: https://jsonlogic.com/operations.html#merge
1 change: 1 addition & 0 deletions docs/manual/forms/examples/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Voorbeelden
decision_tree
dynamic_options
dynamic_options_2
dynamic_options_3
service_fetch

======================
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import uniqueId from 'lodash/uniqueId';
import PropTypes from 'prop-types';
import React from 'react';
import React, {useState} from 'react';
import {FormattedMessage} from 'react-intl';

import ArrayInput from 'components/admin/forms/ArrayInput';
import {DateInput, DateTimeInput, NumberInput, TextInput} from 'components/admin/forms/Inputs';
import {Checkbox} from 'components/admin/forms/Inputs';
import JsonWidget from 'components/admin/forms/JsonWidget';
import Select from 'components/admin/forms/Select';

Expand All @@ -28,7 +31,18 @@ CheckboxChoices.propTypes = {
};

const WrapperArrayInput = ({name, value, onChange}) => {
return (
const [useRawJSON, setUseRawJSON] = useState(false);

// if the value is not an array of strings, the text inputs break the existing data
// structures. In that case, pin the input to use raw JSON.
const anyItemNotString = value && value.some(item => typeof item !== 'string');
if (anyItemNotString && !useRawJSON) {
setUseRawJSON(true);
}

const actualInput = useRawJSON ? (
<WrappedJsonWidget name={name} value={value} onChange={onChange} />
) : (
<ArrayInput
name={name}
values={value}
Expand All @@ -39,11 +53,37 @@ const WrapperArrayInput = ({name, value, onChange}) => {
inputType="text"
/>
);

return (
<>
<Checkbox
name={uniqueId()}
label={
<FormattedMessage
description="Toggle array input raw JSON input mode"
defaultMessage="Use raw JSON input"
/>
}
checked={useRawJSON}
onChange={() => setUseRawJSON(!useRawJSON)}
disabled={anyItemNotString}
/>
{actualInput}
</>
);
};

WrapperArrayInput.propTypes = {
name: PropTypes.string,
value: PropTypes.arrayOf(PropTypes.string),
/**
* Value is an array of items.
*
* The most common use-case is an array of strings, for which a friendly user
* interface is rendered to manage individual items. However, any nested (JSON) data
* type is supported. As soon as any item is not a string, the UI locks into "raw JSON"
* edit mode.
*/
value: PropTypes.array,
onChange: PropTypes.func,
};

Expand All @@ -53,7 +93,7 @@ const WrappedJsonWidget = ({name, value, onChange}) => {

WrappedJsonWidget.propTypes = {
name: PropTypes.string,
value: PropTypes.object,
value: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
onChange: PropTypes.func,
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {FormDecorator} from '../story-decorators';
import LiteralValueInput from './LiteralValueInput';

export default {
title: 'Form design / LiteralValueInput',
component: LiteralValueInput,
decorators: [FormDecorator],
args: {
name: 'aValue',
type: 'string',
value: undefined,
},
};

export const Text = {
name: 'String',
args: {
type: 'string',
},
};

export const NumFloat = {
name: 'Float',
args: {
type: 'float',
},
};

export const NumInt = {
name: 'Int',
args: {
type: 'int',
},
};

export const DateTime = {
args: {
type: 'datetime',
},
};

export const Date_ = {
name: 'Date',
args: {
type: 'date',
},
};

export const Bool = {
name: 'Boolean',
args: {
type: 'boolean',
},
};

export const Arr = {
name: 'Array',
args: {
type: 'array',
},
};

export const ArrWithValues = {
name: 'Array with values',
args: {
type: 'array',
value: ['Item 1', 'Item 2'],
},
};

export const Obj = {
name: 'Object',
args: {
type: 'object',
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const FormDecorator = (Story, {args}) => (
selectedAuthPlugins: args.selectedAuthPlugins || [],
plugins: {
availableAuthPlugins: args.availableAuthPlugins || [],
availablePrefillPlugins: args.availablePrefillPlugins || [],
},
components: args.availableComponents || {},
}}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {FormDecorator} from '../story-decorators';
import VariablesEditor from './VariablesEditor';

export default {
title: 'Form design / Variables editor',
component: VariablesEditor,
decorators: [FormDecorator],
args: {
variables: [
{
form: 'http://localhost:8000/api/v2/forms/36612390',
formDefinition: 'http://localhost:8000/api/v2/form-definitions/6de1ea5a',
name: 'Form.io component',
key: 'formioComponent',
source: 'component',
prefillPlugin: '',
prefillAttribute: '',
prefillIdentifierRole: 'main',
dataType: 'string',
dataFormat: undefined,
isSensitiveData: false,
serviceFetchConfiguration: undefined,
initialValue: '',
},
{
form: 'http://localhost:8000/api/v2/forms/36612390',
formDefinition: undefined,
name: 'User defined',
key: 'userDefined',
source: 'user_defined',
prefillPlugin: '',
prefillAttribute: '',
prefillIdentifierRole: 'main',
dataType: 'array',
dataFormat: undefined,
isSensitiveData: false,
serviceFetchConfiguration: undefined,
initialValue: [],
},
],
},
argTypes: {
onChange: {action: true},
onAdd: {action: true},
onDelete: {action: true},
},
};

export const Default = {};
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ const EditableVariableRow = ({index, variable, onDelete, onChange}) => {
<td>
<Field name="initialValue" errors={variable.errors?.initialValue}>
<LiteralValueInput
key={`initialValue-${index}`}
name="initialValue"
type={variable.dataType}
value={initialValue}
Expand Down
2 changes: 1 addition & 1 deletion src/openforms/js/components/admin/forms/Inputs.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ const Checkbox = ({name, label, helpText, ...extraProps}) => {
const idFor = disabled ? undefined : `id_${name}`;
return (
<div className={classNames('checkbox-row', {'checkbox-row--disabled': disabled})}>
<input type="checkbox" name={name} id={idFor} {...extraProps} />
<input type="checkbox" name={name} id={idFor} {...extraProps} />{' '}
<label className="vCheckboxLabel inline" htmlFor={idFor}>
{label}
</label>
Expand Down

0 comments on commit 1a33fad

Please sign in to comment.