diff --git a/src/components/adaptive-cards/AdaptiveCard/AdaptiveCard.jsx b/src/components/adaptive-cards/AdaptiveCard/AdaptiveCard.jsx index 1c15ea4bb..84bb56e6d 100644 --- a/src/components/adaptive-cards/AdaptiveCard/AdaptiveCard.jsx +++ b/src/components/adaptive-cards/AdaptiveCard/AdaptiveCard.jsx @@ -170,6 +170,17 @@ export default function AdaptiveCard({ setInputs((prevInputs) => ({...prevInputs, [input.id]: input})); }, [setInputs]); + const setInputBrowserControlRef = useCallback((id, browserRef) => { + setInputs((prevInputs) => { + const input = prevInputs[id]; + + return { + ...prevInputs, + [id]: {...input, browserRef}, + }; + }); + }, [setInputs]); + const getValue = (id, defval = '') => ((id in inputs && inputs[id].value !== undefined) ? inputs[id].value : defval); const getAllValues = () => mapValues(inputs, (input) => (input.value)); @@ -197,6 +208,8 @@ export default function AdaptiveCard({ error = input.errorMessage || `The value you entered must match the pattern ${input.regex}`; } else if (String(input.value).length > input.maxLength) { error = `Maximum length is ${input.maxLength}`; + } else if (input.browserRef && !input.browserRef.checkValidity()) { + error = 'The value you entered is invalid.'; } return {...input, error}; @@ -220,6 +233,7 @@ export default function AdaptiveCard({ getError, validate, submit, + setInputBrowserControlRef, invalidSubmit, setElement, setIsVisible, diff --git a/src/components/adaptive-cards/InputDate/InputDate.jsx b/src/components/adaptive-cards/InputDate/InputDate.jsx index 235d0e588..592ba3360 100644 --- a/src/components/adaptive-cards/InputDate/InputDate.jsx +++ b/src/components/adaptive-cards/InputDate/InputDate.jsx @@ -1,4 +1,4 @@ -import React, {useContext, useEffect} from 'react'; +import React, {useContext, useEffect, useState} from 'react'; import PropTypes from 'prop-types'; import webexComponentClasses from '../../helpers'; import {formatDateTime} from '../util'; @@ -22,9 +22,12 @@ export default function InputDate({data, className, style}) { setValue, getValue, setInput, + setInputBrowserControlRef, getError, } = useContext(AdaptiveCardContext); + const [browserControlRef, setBrowserControlRef] = useState(undefined); + useEffect(() => { setInput({ id: data.id, @@ -44,6 +47,10 @@ export default function InputDate({data, className, style}) { setInput, ]); + useEffect(() => { + setInputBrowserControlRef(data.id, browserControlRef); + }, [browserControlRef, data.id, setInputBrowserControlRef]); + return ( setBrowserControlRef(inputRef)} onChange={(value) => setValue(data.id, value)} /> ); diff --git a/src/components/adaptive-cards/InputNumber/InputNumber.jsx b/src/components/adaptive-cards/InputNumber/InputNumber.jsx index 42ad0a29d..652ae2e3b 100644 --- a/src/components/adaptive-cards/InputNumber/InputNumber.jsx +++ b/src/components/adaptive-cards/InputNumber/InputNumber.jsx @@ -1,4 +1,4 @@ -import React, {useContext, useEffect} from 'react'; +import React, {useContext, useEffect, useState} from 'react'; import PropTypes from 'prop-types'; import webexComponentClasses from '../../helpers'; import AdaptiveCardContext from '../context/adaptive-card-context'; @@ -22,9 +22,12 @@ export default function InputNumber({data, className, style}) { setValue, getValue, setInput, + setInputBrowserControlRef, getError, } = useContext(AdaptiveCardContext); + const [browserControlRef, setBrowserControlRef] = useState(undefined); + useEffect(() => { setInput({ id: data.id, @@ -44,6 +47,10 @@ export default function InputNumber({data, className, style}) { setInput, ]); + useEffect(() => { + setInputBrowserControlRef(data.id, browserControlRef); + }, [browserControlRef, data.id, setInputBrowserControlRef]); + return ( setBrowserControlRef(inputRef)} onChange={(value) => setValue(data.id, value)} placeholder={data.placeholder} required={data.isRequired} diff --git a/src/components/adaptive-cards/InputText/InputText.jsx b/src/components/adaptive-cards/InputText/InputText.jsx index 216d1840d..467787788 100644 --- a/src/components/adaptive-cards/InputText/InputText.jsx +++ b/src/components/adaptive-cards/InputText/InputText.jsx @@ -1,4 +1,4 @@ -import React, {useContext, useEffect} from 'react'; +import React, {useContext, useEffect, useState} from 'react'; import PropTypes from 'prop-types'; import webexComponentClasses from '../../helpers'; import AdaptiveCardContext from '../context/adaptive-card-context'; @@ -24,11 +24,13 @@ export default function InputText({data, className, style}) { setValue, getValue, setInput, + setInputBrowserControlRef, getError, } = useContext(AdaptiveCardContext); const [cssClasses, sc] = webexComponentClasses('adaptive-cards-input-text', className); const Input = data.style === 'password' ? PasswordInput : TextInput; const inlineAction = useAction(data.inlineAction); + const [browserControlRef, setBrowserControlRef] = useState(undefined); useEffect(() => { setInput({ @@ -49,6 +51,10 @@ export default function InputText({data, className, style}) { setInput, ]); + useEffect(() => { + setInputBrowserControlRef(data.id, browserControlRef); + }, [browserControlRef, data.id, setInputBrowserControlRef]); + return (
{!data.isMultiline ? ( @@ -57,6 +63,7 @@ export default function InputText({data, className, style}) { error={getError(data.id)} label={formatDateTime(data.label)} maxLength={data.maxLength} + onBrowserControlRef={(inputRef) => setBrowserControlRef(inputRef)} onChange={(value) => setValue(data.id, value)} pattern={data.regex} placeholder={data.placeholder} diff --git a/src/components/adaptive-cards/InputTime/InputTime.jsx b/src/components/adaptive-cards/InputTime/InputTime.jsx index 77bec4ca6..67c31fb34 100644 --- a/src/components/adaptive-cards/InputTime/InputTime.jsx +++ b/src/components/adaptive-cards/InputTime/InputTime.jsx @@ -1,4 +1,4 @@ -import React, {useContext, useEffect} from 'react'; +import React, {useContext, useEffect, useState} from 'react'; import PropTypes from 'prop-types'; import webexComponentClasses from '../../helpers'; import {acPropTypes, registerComponent} from '../Component/Component'; @@ -22,9 +22,12 @@ export default function InputTime({data, className, style}) { setValue, getValue, setInput, + setInputBrowserControlRef, getError, } = useContext(AdaptiveCardContext); + const [browserControlRef, setBrowserControlRef] = useState(undefined); + useEffect(() => { setInput({ id: data.id, @@ -44,6 +47,10 @@ export default function InputTime({data, className, style}) { setInput, ]); + useEffect(() => { + setInputBrowserControlRef(data.id, browserControlRef); + }, [browserControlRef, data.id, setInputBrowserControlRef]); + return ( setBrowserControlRef(inputRef)} onChange={(value) => setValue(data.id, value)} /> ); diff --git a/src/components/generic/InputField/InputField.jsx b/src/components/generic/InputField/InputField.jsx index 59e7313fd..72b553ae4 100644 --- a/src/components/generic/InputField/InputField.jsx +++ b/src/components/generic/InputField/InputField.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useEffect} from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import webexComponentClasses from '../../helpers'; @@ -22,6 +22,7 @@ import Label from '../../inputs/Label/Label'; * @param {number} [props.maxLength] Maximum number of characters allowed * @param {number} [props.min] Minimum value for the input element * @param {string} [props.name] Input name + * @param {Function} [props.onBrowserControlRef] Action to perform to take the input ref * @param {Function} [props.onChange] Action to perform on input change * @param {string} [props.pattern] Specifies a regular expression that the element's value is checked against * @param {string} [props.placeholder] Input placeholder @@ -47,6 +48,7 @@ export default function InputField({ min, name, onChange, + onBrowserControlRef, pattern, placeholder, required, @@ -76,6 +78,10 @@ export default function InputField({ useAutoFocus(inputRef, autoFocus); + useEffect(() => { + onBrowserControlRef(inputRef.current); + }, [inputRef, onBrowserControlRef]); + return (