Skip to content

Commit

Permalink
♻️ [#4859] Display copy toggle only if there are relevant backends
Browse files Browse the repository at this point in the history
The toggle is not displayed if there are no backends to copy from, so
the UI can be de-cluttered.

Additionally, the mechanism/slot for the extra controls has been
refactored to be more generic - anything can now be rendered there that
may be relevant, and the state is specific to the UI being rendered so
that has been removed from the generic parent component.

We use the formik status to track form-specific state, through a small
hook that normalizes the shape of the status (since it can be anything).
  • Loading branch information
sergei-maertens committed Dec 11, 2024
1 parent 7d48879 commit 7291cfa
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {Formik} from 'formik';
import PropTypes from 'prop-types';
import {useState} from 'react';
import {FormattedMessage} from 'react-intl';

import {SubmitAction} from 'components/admin/forms/ActionButton';
Expand Down Expand Up @@ -51,19 +50,10 @@ const PrefillConfigurationForm = ({
}}
>
{({handleSubmit, values}) => {
const PluginFormComponent =
PLUGIN_COMPONENT_MAPPING[values.plugin]?.component ??
PLUGIN_COMPONENT_MAPPING.default.component;
const ToggleCopyComponent =
PLUGIN_COMPONENT_MAPPING[values.plugin]?.toggleCopyComponent ??
PLUGIN_COMPONENT_MAPPING.default.toggleCopyComponent;

const [showCopyButton, setShowCopyButton] = useState(false);

const handleToggle = event => {
event.preventDefault();
setShowCopyButton(!showCopyButton);
};
const pluginConfiguration =
PLUGIN_COMPONENT_MAPPING[values.plugin] ?? PLUGIN_COMPONENT_MAPPING.default;
const {component: PluginFormComponent, pluginFieldExtra: PluginFieldExtra = null} =
pluginConfiguration;

return (
<>
Expand All @@ -80,22 +70,17 @@ const PrefillConfigurationForm = ({
>
<>
<PluginField />
{ToggleCopyComponent ? (
{PluginFieldExtra && (
<div style={{marginLeft: '10px', marginTop: '5px'}}>
<ToggleCopyComponent handleToggle={handleToggle} />
<PluginFieldExtra />
</div>
) : null}
)}
</>
</Field>
</FormRow>
</Fieldset>

<PluginFormComponent
{...(ToggleCopyComponent && {
showCopyButton: showCopyButton,
setShowCopyButton: setShowCopyButton,
})}
/>
<PluginFormComponent />

<SubmitRow>
<SubmitAction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,14 @@ import ObjectsAPIFields from './objects_api/ObjectsAPIFields';
import ToggleCopyButton from './objects_api/ToggleCopyButton';

const PLUGIN_COMPONENT_MAPPING = {
objects_api: {component: ObjectsAPIFields, toggleCopyComponent: ToggleCopyButton},
default: {component: DefaultFields, toggleCopyComponent: null},
objects_api: {
component: ObjectsAPIFields,
pluginFieldExtra: ToggleCopyButton,
},
default: {
component: DefaultFields,
pluginFieldExtra: null,
},
};

export default PLUGIN_COMPONENT_MAPPING;
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Field from 'components/admin/forms/Field';
import FormRow from 'components/admin/forms/FormRow';
import ReactSelect from 'components/admin/forms/ReactSelect';

const CopyConfigurationFromRegistrationBackend = ({backends, setShowCopyButton}) => {
const CopyConfigurationFromRegistrationBackend = ({backends, onCopyDone}) => {
const name = 'copyConfigurationFromBackend';
const {setFieldValue, setValues} = useFormikContext();
const options = backends.map(elem => ({value: elem.key, label: elem.name}));
Expand Down Expand Up @@ -67,9 +67,7 @@ const CopyConfigurationFromRegistrationBackend = ({backends, setShowCopyButton})
variablesMapping: selectedBackend.options.variablesMapping,
},
}));

// Collapse the registration backend selection row
setShowCopyButton(false);
onCopyDone();
}
}}
disabled={!selectedBackend}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* Most other plugins can be configured with the generic form in `./DefaultFields`.
*/
import {useFormikContext} from 'formik';
import PropTypes from 'prop-types';
import {useContext, useEffect} from 'react';
import {FormattedMessage, useIntl} from 'react-intl';
import useAsync from 'react-use/esm/useAsync';
Expand All @@ -15,6 +14,7 @@ import Fieldset from 'components/admin/forms/Fieldset';
import FormRow from 'components/admin/forms/FormRow';
import {LOADING_OPTION} from 'components/admin/forms/Select';
import {ValidationErrorContext} from 'components/admin/forms/ValidationErrors';
import ValidationErrorsProvider from 'components/admin/forms/ValidationErrors';
import VariableMapping from 'components/admin/forms/VariableMapping';
import {
AuthAttributePath,
Expand All @@ -26,8 +26,8 @@ import {FAIcon} from 'components/admin/icons';
import ErrorBoundary from 'components/errors/ErrorBoundary';
import {get} from 'utils/fetch';

import ValidationErrorsProvider from '../../../../forms/ValidationErrors';
import CopyConfigurationFromRegistrationBackend from './CopyConfigurationFromRegistrationBackend';
import useStatus from './useStatus';

const PLUGIN_ID = 'objects_api';

Expand Down Expand Up @@ -69,7 +69,7 @@ const getProperties = async (objectsApiGroup, objecttypeUuid, objecttypeVersion)
return response.data.map(property => [property.targetPath, property.targetPath.join(' > ')]);
};

const ObjectsAPIFields = ({showCopyButton, setShowCopyButton}) => {
const ObjectsAPIFields = () => {
const intl = useIntl();
// Object with keys the plugin/attribute/options, we process these further to set up
// the required context for the fields.
Expand All @@ -84,6 +84,7 @@ const ObjectsAPIFields = ({showCopyButton, setShowCopyButton}) => {
plugin,
options: {objecttypeUuid, objecttypeVersion, objectsApiGroup},
} = values;
const {showCopyButton, toggleShowCopyButton} = useStatus();

const defaults = {
objectsApiGroup: null,
Expand Down Expand Up @@ -138,12 +139,12 @@ const ObjectsAPIFields = ({showCopyButton, setShowCopyButton}) => {

return (
<ValidationErrorsProvider errors={optionsErrors}>
{showCopyButton ? (
{showCopyButton && (
<CopyConfigurationFromRegistrationBackend
backends={backends}
setShowCopyButton={setShowCopyButton}
onCopyDone={toggleShowCopyButton}
/>
) : null}
)}
<Fieldset>
<ObjectsAPIGroup
apiGroupChoices={apiGroups}
Expand Down Expand Up @@ -296,9 +297,6 @@ const ObjectsAPIFields = ({showCopyButton, setShowCopyButton}) => {
);
};

ObjectsAPIFields.propTypes = {
showCopyButton: PropTypes.bool.isRequired,
setShowCopyButton: PropTypes.func.isRequired,
};
ObjectsAPIFields.propTypes = {};

export default ObjectsAPIFields;
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import {useContext} from 'react';
import {FormattedMessage} from 'react-intl';

const ToggleCopyButton = ({handleToggle}) => {
import {FormContext} from 'components/admin/form_design/Context';

import useStatus from './useStatus';

const ToggleCopyButton = () => {
const {registrationBackends = []} = useContext(FormContext);
const {toggleShowCopyButton} = useStatus();
const backends = registrationBackends.filter(elem => elem.backend === 'objects_api');
// don't render a toggle if there's nothing to copy from
if (!backends.length) return null;

return (
<a href="#" onClick={handleToggle}>
<a
href="#"
onClick={event => {
event.preventDefault();
toggleShowCopyButton();
}}
>
<FormattedMessage
description={'Objects API prefill options: link to show copy from registration button'}
defaultMessage={'Copy configuration from registration'}
Expand All @@ -11,4 +28,6 @@ const ToggleCopyButton = ({handleToggle}) => {
);
};

ToggleCopyButton.propTypes = {};

export default ToggleCopyButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {useFormikContext} from 'formik';

/**
* Convenience hook that wraps around Formik's status.
*
* This centralizes the shape of the status tracked in the Formik state. We use status
* to track some configuration/state that affects the configuration modal without it
* directly being a form field or requiring to be managed at a higher level.
*/
const useStatus = () => {
const {status = {}, setStatus} = useFormikContext();
const {showCopyButton = false} = status;
const toggleShowCopyButton = () => {
setStatus({...status, showCopyButton: !showCopyButton});
};
return {
showCopyButton,
toggleShowCopyButton,
};
};

export default useStatus;

0 comments on commit 7291cfa

Please sign in to comment.