@ibm-adw/skill-toolkit
Version:
Developing your own skills with IBM Automation Digital Worker Skill Toolkit
1,177 lines (1,051 loc) • 34.6 kB
JavaScript
import React, { useCallback, useState, useEffect, useImperativeHandle, useRef } from 'react';
import { widgets as widgets$1, useDirtyInput, templates as templates$1, FormCommon, fields as fields$1 } from '@adw/form-common';
import { Link, Icon, RadioButtonGroup, RadioButton, TextInput, TextArea, NumberInput, Button, SkeletonPlaceholder, Loading } from 'carbon-components-react';
import { iconLaunch } from 'carbon-icons';
import StateDecorator, { ConflictPolicy } from 'state-decorator';
import axios from 'axios';
/*
Licensed Materials - Property of IBM
5737-I23
Copyright IBM Corp. 2019. All Rights Reserved.
U.S. Government Users Restricted Rights:
Use, duplication or disclosure restricted by GSA ADP Schedule
Contract with IBM Corp.
*/
function CarbonLinkField(props) {
const {
schema,
disabled
} = props;
return React.createElement("div", null, React.createElement(Link, {
className: "baiw--field-link",
href: schema.url,
disabled: disabled,
target: "_blank"
}, schema.title, React.createElement(Icon, {
icon: iconLaunch,
fill: "#0062ff",
className: "baiw--field-link-icon",
description: "",
height: "16px",
width: "16px"
})));
}
CarbonLinkField.defaultProps = {
autofocus: false
};
/*
Licensed Materials - Property of IBM
5737-I23
Copyright IBM Corp. 2019. All Rights Reserved.
U.S. Government Users Restricted Rights:
Use, duplication or disclosure restricted by GSA ADP Schedule
Contract with IBM Corp.
*/
const fields = {
LinkField: CarbonLinkField
};
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(source, true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(source).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _objectWithoutProperties(source, excluded) {
if (source == null) return {};
var target = _objectWithoutPropertiesLoose(source, excluded);
var key, i;
if (Object.getOwnPropertySymbols) {
var sourceSymbolKeys = Object.getOwnPropertySymbols(source);
for (i = 0; i < sourceSymbolKeys.length; i++) {
key = sourceSymbolKeys[i];
if (excluded.indexOf(key) >= 0) continue;
if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
target[key] = source[key];
}
}
return target;
}
function CarbonCheckboxWidgetWrapper(props) {
const {
formContext,
options,
value
} = props; // Checkbox's value are by default boolean
// We add the possibility to have checkbox with string values
// This provides the compatibility of checkbox with schema dependencies (oneOf)
let checked = typeof value === 'undefined' || typeof value === 'boolean' ? value : true;
let label = props.label;
if (props.schema && props.schema.type === 'string' && props.schema.enum && props.schema.enum.length === 2) {
if (value && typeof value === 'string') {
checked = value === props.schema.enum[0];
if (props.schema.enumNames && props.schema.enumNames.length === 2) {
label = props.schema.enumNames[props.schema.enum.indexOf(value)];
} else {
label = value;
}
} else {
checked = true;
label = props.schema.enum[0];
}
} // the onChange function called by Carbon's Checkbox has the value as first parameter
const _onChange = checked => {
if (options && options.triggerEvent) {
formContext.setTriggerEvent(options.triggerEvent);
}
let value = checked;
if (props.schema && props.schema.type === 'string' && props.schema.enum && props.schema.enum.length === 2) {
value = checked ? props.schema.enum[0] : props.schema.enum[1];
}
props.onChange(value);
};
return React.createElement(widgets$1.CheckboxWidget, _extends({}, props, {
onChange: _onChange,
value: checked,
label: label
}));
}
function CarbonSelectWidgetWrapper(props) {
const {
options,
formContext
} = props;
const _onChange = event => {
props.onChange(event);
if (options && options.triggerEvent) {
formContext.setTriggerEvent(options.triggerEvent);
}
};
return React.createElement(widgets$1.SelectWidget, _extends({}, props, {
onChange: _onChange
}));
}
/*
Licensed Materials - Property of IBM
5737-I23
Copyright IBM Corp. 2019. All Rights Reserved.
U.S. Government Users Restricted Rights:
Use, duplication or disclosure restricted by GSA ADP Schedule
Contract with IBM Corp.
*/
function CarbonRadioWidget(props) {
//TODO Carbon had problem with : value, valueSelected, checked pros
// check this is still valid
const {
options,
value,
required,
disabled,
readonly,
autofocus,
formContext,
schema,
onChange,
id
} = props; // Generating a unique field name to identify this set of radio buttons
const name = Math.random().toString();
const {
enumOptions,
enumDisabled
/*, inline*/
} = options; // checked={checked} has been moved above name={name}, As mentioned in #349;
// this is a temporary fix for radio button rendering bug in React, facebook/react#7630.
const _onGlobalChange = function _onGlobalChange(value) {
if (options.triggerEvent) {
formContext.setTriggerEvent(options.triggerEvent);
}
onChange(value === '' ? options.emptyValue : value);
};
return React.createElement("div", {
className: "bx--form-item"
}, React.createElement(RadioButtonGroup // TODO: temporary fix for carbon issue with orientation vertical
, {
className: "baiw--vertical-radio-button",
orientation: "vertical",
legend: "Group Legend",
name: id,
valueSelected: value,
onChange: _onGlobalChange
}, enumOptions.map((option, i) => {
const itemDisabled = enumDisabled && enumDisabled.indexOf(option.value) !== -1;
return React.createElement(RadioButton, {
name: name,
required: required,
value: option.value,
disabled: disabled || itemDisabled || readonly,
autoFocus: autofocus && i === 0 //onChange={() => onChange(option.value)}
,
labelText: option.label,
labelPosition: "right",
key: `id${i}` // TODO: temporary fix for carbon issue with orientation vertical
,
className: "baiw--vertical-radio-button-wrapper"
});
})), schema.invalid ? React.createElement("div", {
className: "baiw--vertical-radio-button-invalid-wrapper"
}, React.createElement("div", {
className: "baiw--vertical-radio-button-invalid bx--form-requirement"
}, schema.invalidText)) : null);
}
CarbonRadioWidget.defaultProps = {
autofocus: false
};
function CarbonBaseInput(props) {
const {
options,
formContext
} = props;
const _onChange = value => {
if (options && options.triggerEvent) {
formContext.setTriggerEvent(options.triggerEvent);
}
props.onChange(value);
};
return React.createElement(widgets$1.BaseInput, _extends({}, props, {
onChange: _onChange
}));
}
function CarbonPasswordWidget(props) {
const {
id,
value,
defaultValue,
readonly,
disabled,
autofocus,
onBlur,
onFocus,
onChange,
options,
schema,
formContext,
registry,
rawErrors
} = props,
inputProps = _objectWithoutProperties(props, ["id", "value", "defaultValue", "readonly", "disabled", "autofocus", "onBlur", "onFocus", "onChange", "options", "schema", "formContext", "registry", "rawErrors"]);
const _onChange = useCallback(({
target: {
value
}
}) => {
if (options.triggerEvent) {
formContext.setTriggerEvent(options.triggerEvent);
}
onChange(value === '' ? options.emptyValue : value);
}, [options, formContext, onChange]);
const [inputDirty, __onChange, _onBlur] = useDirtyInput(_onChange, onBlur, value, defaultValue);
const [invalid, setInvalid] = useState(false);
useEffect(() => {
if (formContext && formContext.ignoreDirty) {
setInvalid(schema.invalid);
} else {
setInvalid(inputDirty ? schema.invalid : false);
}
}, [formContext, inputDirty, schema.invalid]);
const [showPasswordLabel, setShowPasswordLabel] = useState();
const [hidePasswordLabel, setHidePasswordLabel] = useState();
useEffect(() => {
if (formContext && formContext.showPasswordLabel) {
setShowPasswordLabel(formContext.showPasswordLabel);
}
if (formContext && formContext.hidePasswordLabel) {
setHidePasswordLabel(formContext.hidePasswordLabel);
}
}, [formContext]);
return React.createElement(TextInput.PasswordInput, _extends({
id: id,
disabled: disabled,
invalid: invalid,
invalidText: schema.invalid ? schema.invalidText : '',
labelText: "",
showPasswordLabel: showPasswordLabel,
hidePasswordLabel: hidePasswordLabel,
value: value == null ? '' : value,
defaultValue: defaultValue,
hideLabel: true,
onChange: __onChange,
onBlur: _onBlur
}, inputProps));
}
CarbonPasswordWidget.defaultProps = {
required: true,
disabled: false,
readonly: false,
autofocus: false
};
/*
Licensed Materials - Property of IBM
5737-I23
Copyright IBM Corp. 2019,2020. All Rights Reserved.
U.S. Government Users Restricted Rights:
Use, duplication or disclosure restricted by GSA ADP Schedule
Contract with IBM Corp.
*/
function CarbonTextAreaWidget(props) {
const {
id,
options,
formContext,
schema,
value,
defaultValue,
disabled,
placeholder,
onChange,
onBlur
} = props;
const _onChange = useCallback(({
target: {
value
}
}) => {
if (options.triggerEvent) {
formContext.setTriggerEvent(options.triggerEvent);
}
onChange(value === '' ? options.emptyValue : value);
}, [onChange, options, formContext]);
const [inputDirty, __onChange, _onBlur] = useDirtyInput(_onChange, onBlur, value, defaultValue);
const [invalid, setInvalid] = useState(false);
useEffect(() => {
if (formContext && formContext.ignoreDirty) {
setInvalid(schema.invalid);
} else {
setInvalid(inputDirty ? schema.invalid : false);
}
}, [formContext, inputDirty, schema.invalid]);
return React.createElement(TextArea, {
hideLabel: true,
id: id,
onChange: __onChange,
onBlur: _onBlur,
labelText: "",
invalid: invalid,
invalidText: schema.invalid ? schema.invalidText : '',
disabled: disabled,
value: value == null ? '' : value,
defaultValue: defaultValue,
placeholder: placeholder
});
}
CarbonTextAreaWidget.defaultProps = {
autofocus: false,
options: {}
};
function CarbonEmailWidget(props) {
const {
BaseInput
} = props.registry.widgets;
return React.createElement(BaseInput, _extends({
type: "email"
}, props));
} //
function CarbonUpDownWidget(props) {
const {
value,
readonly,
disabled,
autofocus,
onBlur,
onFocus,
options,
schema,
formContext,
registry,
rawErrors
} = props,
inputProps = _objectWithoutProperties(props, ["value", "readonly", "disabled", "autofocus", "onBlur", "onFocus", "options", "schema", "formContext", "registry", "rawErrors"]);
const _onChange = function _onChange(_ref) {
const value = _ref.imaginaryTarget.value;
return props.onChange(value === '' ? options.emptyValue : value);
};
return React.createElement(NumberInput, _extends({
className: "form-control bx--fieldset",
readOnly: readonly,
disabled: disabled,
autoFocus: autofocus
}, inputProps, {
onChange: _onChange,
label: "Number Input label",
hideLabel: true //min={min}
//max={max}
,
value: parseInt(value),
step: 1,
isMobile: false,
invalidText: "Number is not valid",
helperText: "Optional helper text."
}));
}
CarbonUpDownWidget.defaultProps = {
required: false,
disabled: false,
readonly: false,
autofocus: false,
value: 0,
options: {}
};
/*
Licensed Materials - Property of IBM
5737-I23
Copyright IBM Corp. 2019. All Rights Reserved.
U.S. Government Users Restricted Rights:
Use, duplication or disclosure restricted by GSA ADP Schedule
Contract with IBM Corp.
*/
const widgets = {
BaseInput: CarbonBaseInput,
CheckboxWidget: CarbonCheckboxWidgetWrapper,
EmailWidget: CarbonEmailWidget,
PasswordWidget: CarbonPasswordWidget,
RadioWidget: CarbonRadioWidget,
SelectWidget: CarbonSelectWidgetWrapper,
TextareaWidget: CarbonTextAreaWidget,
UpDownWidget: CarbonUpDownWidget
};
function updateValidity(errors, schema) {
const updatedSchema = _objectSpread2({}, schema);
if (updatedSchema.error) {
// server side validation error
updatedSchema.invalid = true;
updatedSchema.invalidText = updatedSchema.error;
updatedSchema.error = '';
} else if (errors) {
// client side validation error
updatedSchema.invalid = true;
updatedSchema.invalidText = errors[0];
} else {
updatedSchema.invalid = false;
updatedSchema.invalidText = '';
}
return updatedSchema;
}
function CustomFieldTemplateWrapper(props) {
const {
children
} = props;
const displayLabel = props.displayLabel && typeof props.schema.title !== 'undefined';
return React.createElement(templates$1.FieldTemplate, _extends({
updateValidity: updateValidity
}, props, {
displayLabel: displayLabel
}), children); //
}
/*
Licensed Materials - Property of IBM
5737-I23
Copyright IBM Corp. 2019. All Rights Reserved.
U.S. Government Users Restricted Rights:
Use, duplication or disclosure restricted by GSA ADP Schedule
Contract with IBM Corp.
*/
const templates = {
FieldTemplate: CustomFieldTemplateWrapper
};
/*
Licensed Materials - Property of IBM
5737-I23
Copyright IBM Corp. 2019,2020. All Rights Reserved.
U.S. Government Users Restricted Rights:
Use, duplication or disclosure restricted by GSA ADP Schedule
Contract with IBM Corp.
*/
const LINK_TYPES = {
DOCUMENTATION: 'documentation',
SNIPPET: 'snippet',
INPUT_SCHEMA: 'inputSchema',
OUTPUT_SCHEMA: 'outputSchema'
};
const SkillFormLinks = React.forwardRef((props, ref) => {
const {
serverUrl,
labels,
supportedLocales,
saveConfig
} = props;
const [disabled, setDisabled] = useState(false);
useImperativeHandle(ref, () => ({
setDisabled: value => {
setDisabled(value);
}
}), []); // linkType: One of 'documentation', 'inputSchema', 'outputSchema' or 'snippet'
const buildLink = useCallback(linkType => {
if (linkType === LINK_TYPES.SNIPPET) {
return `${serverUrl}/snippet`;
} else if (linkType === LINK_TYPES.DOCUMENTATION) {
return `${serverUrl}/documentation?language=${supportedLocales}`;
}
return `${serverUrl}/schema?type=${linkType === LINK_TYPES.INPUT_SCHEMA ? 'input' : 'output'}&pretty=true`;
}, [serverUrl, supportedLocales]);
const handleClick = useCallback(linkType => {
return async () => {
if (saveConfig) {
// When saveConfig function is provided, try to save config before opening link
if (await saveConfig()) {
// Open link only when successfully saved config
window.open(buildLink(linkType), '_blank', 'noopener,noreferrer');
}
} else {
window.open(buildLink(linkType), '_blank', 'noopener,noreferrer');
}
};
}, [buildLink, saveConfig]);
const openDocumentation = useCallback(() => {
window.open(buildLink(LINK_TYPES.DOCUMENTATION), '_blank', 'noopener,noreferrer');
}, [buildLink]);
return React.createElement("div", {
className: "baiw--skills-form-links"
}, React.createElement(React.Fragment, null, React.createElement("div", {
className: 'bx--fieldset baiw--skills-form-links--fieldset'
}, React.createElement(Button, {
id: "skillDocumentationBtn",
className: "baiw--skills-form-links--button",
kind: "ghost",
onClick: openDocumentation
}, labels.buttons.skillDocumentation, React.createElement(Icon, {
icon: iconLaunch,
description: ""
}))), React.createElement("div", {
className: `bx--fieldset baiw--skills-form-links--fieldset ${disabled ? 'baiw--skills-form-links--fieldset-disabled' : ''}`
}, React.createElement(Button, {
id: "sampleInstructionsBtn",
className: "baiw--skills-form-links--button",
kind: "ghost",
disabled: disabled,
onClick: handleClick(LINK_TYPES.SNIPPET)
}, labels.buttons.sampleInstructions, React.createElement(Icon, {
icon: iconLaunch,
description: ""
}))), React.createElement("div", {
className: `bx--fieldset baiw--skills-form-links--fieldset ${disabled ? 'baiw--skills-form-links--fieldset-disabled' : ''}`
}, React.createElement(Button, {
id: "schemaInputBtn",
className: "baiw--skills-form-links--button",
kind: "ghost",
disabled: disabled,
onClick: handleClick(LINK_TYPES.INPUT_SCHEMA)
}, labels.buttons.schemaInput, React.createElement(Icon, {
icon: iconLaunch,
description: ""
}))), React.createElement("div", {
className: `bx--fieldset baiw--skills-form-links--fieldset ${disabled ? 'baiw--skills-form-links--fieldset-disabled' : ''}`
}, React.createElement(Button, {
id: "schemaOutputBtn",
className: "baiw--skills-form-links--button",
kind: "ghost",
disabled: disabled,
onClick: handleClick(LINK_TYPES.OUTPUT_SCHEMA)
}, labels.buttons.schemaOutput, React.createElement(Icon, {
icon: iconLaunch,
description: ""
})))));
});
/*
Licensed Materials - Property of IBM
5737-I23
Copyright IBM Corp. 2019,2020. All Rights Reserved.
U.S. Government Users Restricted Rights:
Use, duplication or disclosure restricted by GSA ADP Schedule
Contract with IBM Corp.
*/
const SkillFormButtons = React.forwardRef((props, ref) => {
const {
onClickCancel,
onClickSubmit,
buttonLabelCancel,
buttonLabelSubmit,
isFormSubmittable
} = props;
const [buttonDisabled, setButtonDisabled] = useState(!isFormSubmittable);
useImperativeHandle(ref, () => ({
setDisabled: value => {
setButtonDisabled(value);
}
}), []);
return React.createElement("div", {
className: "baiw--form_buttons"
}, React.createElement(Button, {
className: "baiw--form_button_left_column",
type: "button",
kind: "secondary",
onClick: onClickCancel
}, buttonLabelCancel), React.createElement(Button, {
className: "baiw--form_button_right_column",
type: "submit",
kind: "primary",
disabled: buttonDisabled,
onClick: onClickSubmit
}, buttonLabelSubmit));
});
/*
Licensed Materials - Property of IBM
5737-I23
Copyright IBM Corp. 2019, 2020. All Rights Reserved.
U.S. Government Users Restricted Rights:
Use, duplication or disclosure restricted by GSA ADP Schedule
Contract with IBM Corp.
*/
function SkillForm(props) {
const {
schema,
uiSchema,
formData,
isLastStaticStep,
isFormSubmittable,
onSubmit,
cancel,
labels,
serverUrl,
saveConfig,
loading,
runStep,
debounceRunStep,
clearDebounceTimeout,
isInitial,
supportedLocales,
transformErrors
} = props; // 'triggerEvent' is created with useRef so all children components get always the same ref object
const triggerEvent = useRef(null); // 'setTriggerEvent' is exposed in formContext so any child component can update its value
const setTriggerEvent = useCallback(event => {
triggerEvent.current = event;
}, []); // context exposed in formContext
const [context, setContext] = useState({});
useEffect(() => {
setContext({
setTriggerEvent,
optionalLabel: labels.skillForm.optional,
addItemLabel: labels.skillForm.buttons.addItem,
moveItemUpLabel: labels.skillForm.buttons.moveItemUp,
moveItemDownLabel: labels.skillForm.buttons.moveItemDown,
removeItemLabel: labels.skillForm.buttons.removeItem,
showPasswordLabel: labels.skillForm.password.showPasswordLabel,
hidePasswordLabel: labels.skillForm.password.hidePasswordLabel,
unsupportedFieldLabel: labels.skillForm.unsupportedField,
ignoreDirty: !isInitial
});
}, [setTriggerEvent, labels.skillForm.optional, isInitial, labels.skillForm.buttons, labels.skillForm.password, labels.skillForm.unsupportedField]);
const isInTimeOut = useRef(false);
const setIsInTimeOut = useCallback(boolean => {
isInTimeOut.current = boolean;
}, []);
let buttonLabel;
let action;
if (isLastStaticStep) {
buttonLabel = labels.skillForm.buttons.submit;
action = onSubmit;
} else {
buttonLabel = labels.skillForm.buttons.next;
action = runStep;
} // 'submitButtonRef' is exposed in skillFormButtons to update the submit button state
const submitButtonRef = useRef(null);
const setSubmitButtonRef = useCallback(button => {
submitButtonRef.current = button;
}, [submitButtonRef]); // 'skillLinksRef' is exposed in SkillFormLinks to update the links disable state
const skillLinksRef = useRef(null);
const setSkillLinksRef = useCallback(skillLinks => {
skillLinksRef.current = skillLinks;
}, [skillLinksRef]); // set the submit button and links 'disabled' state
const setButtonLinkDisabledState = useCallback(formErrorList => {
if (isInTimeOut.current || loading || !isFormSubmittable || Object.keys(formErrorList).length > 0) {
submitButtonRef.current.setDisabled(true);
skillLinksRef.current.setDisabled(true);
} else {
submitButtonRef.current.setDisabled(false);
skillLinksRef.current.setDisabled(false);
}
}, [loading, isFormSubmittable]);
const _onChange = useCallback(state => {
if (triggerEvent.current !== null) {
// At this point the form validation is already done
// Don't call nextStaticStep if there are any errors from validation
if (Object.keys(state.errors).length > 0) {
setTriggerEvent(null);
if (submitButtonRef.current && skillLinksRef.current) {
setButtonLinkDisabledState(state.errors);
}
if (isInTimeOut.current) {
clearDebounceTimeout();
}
return;
}
if (debounceRunStep) {
if (submitButtonRef.current && skillLinksRef.current) {
setIsInTimeOut(true);
setButtonLinkDisabledState(state.errors);
} // Defer execution of step in case there are lots of request in a short amount of time
debounceRunStep(state, triggerEvent.current, () => {
setIsInTimeOut(false);
});
setTriggerEvent(null);
}
} else if (submitButtonRef.current && skillLinksRef.current) {
setButtonLinkDisabledState(state.errors);
}
}, [debounceRunStep, setTriggerEvent, setButtonLinkDisabledState, clearDebounceTimeout, setIsInTimeOut]);
const _onSubmit = useCallback(action => {
return state => {
clearDebounceTimeout(); // Execute action (submit or nextStaticStep)
action(state);
};
}, [clearDebounceTimeout]);
const handlerKeyDownEnter = useCallback(event => {
if (event.key === 'Enter') {
event.preventDefault();
}
}, []); //Reference the form in order to externalize buttons
//See https://react-jsonschema-form.readthedocs.io/en/latest/#submit-form-programmatically
const formRef = useRef(null);
const refCallback = useCallback(form => {
if (form) {
formRef.current = form; // Disable the "Enter" keydown by adding an event listener
formRef.current.formElement.addEventListener('keydown', handlerKeyDownEnter);
}
}, [handlerKeyDownEnter]); // On unmount: remove the event listener
useEffect(() => () => {
if (formRef.current) {
formRef.current.formElement.removeEventListener('keydown', handlerKeyDownEnter);
}
}, [handlerKeyDownEnter]); // when isFormSubmittable changes we update the submit button state
useEffect(() => {
if (formRef.current && submitButtonRef.current && skillLinksRef.current) {
setButtonLinkDisabledState(formRef.current.state.errors);
}
}, [isFormSubmittable, loading, setButtonLinkDisabledState]);
const _saveConfig = useCallback(async () => {
try {
await saveConfig(formRef.current.state);
return true;
} catch (error) {
// This means that there was an error when trying to save the configuration. The errors are handled by SkillFormManager.
return false;
}
}, [saveConfig]);
const _transformErrors = useCallback(errors => {
//translate errors
if (transformErrors instanceof Function) {
errors = transformErrors(errors);
} // Update "disable" status of buttons based on errors
if (submitButtonRef.current && skillLinksRef.current) {
setButtonLinkDisabledState(errors);
}
return errors;
}, [setButtonLinkDisabledState, transformErrors]); // If schema has a type : Form is not empty
// Display the form
// Avoid displaying buttons without form
if (schema.type) {
return React.createElement("div", {
className: "baiw--skill-form"
}, React.createElement("div", {
className: "baiw--loading-skeleton"
}, loading ? React.createElement(SkeletonPlaceholder, {
className: "baiw--loading-skeleton-color"
}) : ''), React.createElement("div", {
className: "baiw--content"
}, React.createElement("div", {
className: "baiw--content-left_column"
}, React.createElement(FormCommon, {
schema: schema,
uiSchema: uiSchema,
formData: formData,
formContext: context,
onChange: _onChange,
liveValidate: true,
showErrorList: false,
onSubmit: _onSubmit(action),
ref: refCallback,
customWidgets: widgets,
customFields: fields,
customTemplates: templates,
transformErrors: _transformErrors
}, React.createElement("span", null))), React.createElement("div", {
className: "baiw--content-right_column"
}, React.createElement("div", {
id: 'right_column__info',
className: 'bx--fieldset'
}, React.createElement(fields$1.TitleField, {
id: 'right_column__title',
title: labels.skillForm.details.header
}), React.createElement(fields$1.DescriptionField, {
id: 'right_column__description',
description: labels.skillForm.details.description
})), React.createElement(SkillFormLinks, {
ref: setSkillLinksRef,
labels: labels.skillFormLinks,
serverUrl: serverUrl,
saveConfig: _saveConfig,
supportedLocales: supportedLocales
}))), React.createElement(SkillFormButtons, {
ref: setSubmitButtonRef,
onClickCancel: cancel,
onClickSubmit: () => formRef.current.submit(),
buttonLabelCancel: labels.skillForm.buttons.cancel,
buttonLabelSubmit: buttonLabel,
isFormSubmittable: isFormSubmittable
}));
} else {
return React.createElement("div", {
className: "baiw--skill-form"
}, React.createElement("div", {
className: "baiw--content"
}, React.createElement("div", {
className: "baiw--loading"
}, React.createElement(Loading, {
withOverlay: false
}))));
}
}
const FINAL_STEP = '__baiw_final';
let runStepSource;
let runStepPromise;
let debounceTimeout;
const DEBOUNCE_DELAY = 500;
const actions = (serverUrl, supportedLocales, {
currentLocale,
fallbackLocale
}, formTitle) => {
return {
clearDebounceTimeout: state => {
clearTimeout(debounceTimeout);
if (runStepSource) {
runStepSource.cancel();
}
return state;
},
debounceRunStep: {
action: (state, [formState]) => {
if (runStepPromise && runStepSource) {
runStepSource.cancel();
}
clearTimeout(debounceTimeout);
return _objectSpread2({}, state, {
formData: formState.formData
});
},
onActionDone: (state, [{
formData
}, step, callback], props, actions) => {
debounceTimeout = setTimeout(() => {
actions.runStep(formData, step);
callback();
}, DEBOUNCE_DELAY);
}
},
runStep: {
promise: ([formData, stepOverride], state) => {
runStepSource = axios.CancelToken.source();
const step = stepOverride === undefined ? state.nextStaticStep : stepOverride;
const options = {
method: 'POST',
headers: {
'Content-Language': supportedLocales
},
url: serverUrl,
data: formData,
params: {
event: step,
currentLocale: currentLocale,
fallbackLocale: fallbackLocale
},
cancelToken: runStepSource ? runStepSource.token : null
};
runStepPromise = axios(options);
return runStepPromise;
},
reducer: (state, {
data
}, [, stepOverride]) => {
const step = stepOverride === undefined ? state.nextStaticStep : stepOverride;
data.schema.title = formTitle;
return {
event: step,
nextStaticStep: data.nextStaticStep,
isLastStaticStep: !data.nextStaticStep,
isFormSubmittable: data.isFormSubmittable === undefined ? true : data.isFormSubmittable,
schema: data.schema,
uiSchema: data.uiSchema || {},
formData: data.formData,
isInitial: data.isInitial !== undefined ? data.isInitial : false
};
},
errorReducer: (state, error, args, props) => {
if (!axios.isCancel(error)) {
// Call onError function if it was passed (only when the error is not due to the request being cancelled)
props.onError(error);
}
return null;
},
conflictPolicy: ConflictPolicy.KEEP_LAST
},
saveConfig: {
preReducer: (state, [{
formData
}]) => {
// Update state with latest formData
return _objectSpread2({}, state, {
formData
});
},
promise: ([{
formData
}]) => {
const options = {
method: 'POST',
headers: {
'Content-Language': supportedLocales
},
url: serverUrl,
data: formData,
params: {
event: FINAL_STEP
}
};
return axios(options);
},
reducer: () => {
// Schema validation ok and data saved
return null;
},
errorReducer: (state, error, args, props) => {
// Call onError function if it was passed
props.onError(error);
return null;
},
rejectPromiseOnError: true
},
cancel: () => {
return null;
}
};
};
const initialState = {
event: '__baiw_init',
nextStaticStep: undefined,
isLastStaticStep: true,
isFormSubmittable: false,
schema: {},
uiSchema: {},
formData: {},
isInitial: false
};
const onMount = actions => {
// On initialization, the step 0 is requested
actions.runStep({}, initialState.event);
};
const SkillFormManager = ({
serverUrl,
supportedLocales,
locales,
onSubmit,
onCancel,
onError,
labels,
transformErrors
}) => {
const formActions = useRef(actions(`${serverUrl}/form-configuration`, supportedLocales, locales || {}, labels.skillForm.title));
const _onSubmit = useCallback(saveConfig => {
return async formState => {
// Run 'saveConfig' action
try {
await saveConfig(formState); // If passed in props, run onSubmit function (only when no errors)
onSubmit && onSubmit(formState);
} catch (err) {// Don't call user 'onSubmit' function when there are errors (errors are handled on the errorReducer of the 'saveConfig' action)
}
};
}, [onSubmit]);
const _onCancel = useCallback(cancel => {
return () => {
// Run 'cancel' action
cancel(); // If passed in props, run onCancel function
onCancel && onCancel();
};
}, [onCancel]);
const _onError = useCallback(error => {
// Call onError function if it was passed
onError && onError(error);
}, [onError]);
return React.createElement(StateDecorator, {
actions: formActions.current,
initialState: initialState,
onMount: onMount,
logEnabled: true,
props: {
onError: _onError
},
name: "SkillFormManager"
}, (state, actions, loading) => // TODO 'loading' could be used to show spinner when Saving or requesting next step
React.createElement(SkillForm, _extends({}, state, {
cancel: _onCancel(actions.cancel),
onSubmit: _onSubmit(actions.saveConfig),
debounceRunStep: actions.debounceRunStep,
runStep: actions.runStep,
clearDebounceTimeout: actions.clearDebounceTimeout,
labels: labels,
serverUrl: serverUrl,
saveConfig: actions.saveConfig,
loading: loading,
supportedLocales: supportedLocales,
transformErrors: transformErrors
})));
};
export { SkillForm, SkillFormLinks, SkillFormManager, fields, templates, widgets };
//# sourceMappingURL=index.es.js.map