Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(adaptive-cards): enable default browser validation of input fields #739

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/components/adaptive-cards/AdaptiveCard/AdaptiveCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,25 @@ export default function AdaptiveCard({
};

const setInput = useCallback((input) => {
setInputs((prevInputs) => ({...prevInputs, [input.id]: input}));
setInputs((prevInputs) => {
const prevInput = prevInputs[input.id];

return {
...prevInputs,
[input.id]: {...prevInput, ...input},
};
});
}, [setInputs]);

const setNativeInputRef = useCallback((id, nativeRef) => {
setInputs((prevInputs) => {
const input = prevInputs[id];

return {
...prevInputs,
[id]: {...input, nativeRef},
};
});
}, [setInputs]);

const getValue = (id, defval = '') => ((id in inputs && inputs[id].value !== undefined) ? inputs[id].value : defval);
Expand Down Expand Up @@ -197,6 +215,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.nativeRef && !input.nativeRef.checkValidity()) {
error = 'The value you entered is invalid.';
}

return {...input, error};
Expand All @@ -220,6 +240,7 @@ export default function AdaptiveCard({
getError,
validate,
submit,
setNativeInputRef,
invalidSubmit,
setElement,
setIsVisible,
Expand Down
8 changes: 7 additions & 1 deletion src/components/adaptive-cards/InputDate/InputDate.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useContext, useEffect} from 'react';
import React, {useCallback, useContext, useEffect} from 'react';
import PropTypes from 'prop-types';
import webexComponentClasses from '../../helpers';
import {formatDateTime} from '../util';
Expand All @@ -22,6 +22,7 @@ export default function InputDate({data, className, style}) {
setValue,
getValue,
setInput,
setNativeInputRef,
getError,
} = useContext(AdaptiveCardContext);

Expand All @@ -44,6 +45,10 @@ export default function InputDate({data, className, style}) {
setInput,
]);

const nativeRefCallback = useCallback((nativeRef) => {
setNativeInputRef(data.id, nativeRef);
}, [data.id, setNativeInputRef]);

return (
<DateInput
className={cssClasses}
Expand All @@ -54,6 +59,7 @@ export default function InputDate({data, className, style}) {
error={getError(data.id)}
required={data.isRequired}
label={formatDateTime(data.label)}
nativeRef={nativeRefCallback}
onChange={(value) => setValue(data.id, value)}
/>
);
Expand Down
8 changes: 7 additions & 1 deletion src/components/adaptive-cards/InputNumber/InputNumber.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useContext, useEffect} from 'react';
import React, {useCallback, useContext, useEffect} from 'react';
import PropTypes from 'prop-types';
import webexComponentClasses from '../../helpers';
import AdaptiveCardContext from '../context/adaptive-card-context';
Expand All @@ -22,6 +22,7 @@ export default function InputNumber({data, className, style}) {
setValue,
getValue,
setInput,
setNativeInputRef,
getError,
} = useContext(AdaptiveCardContext);

Expand All @@ -44,13 +45,18 @@ export default function InputNumber({data, className, style}) {
setInput,
]);

const nativeRefCallback = useCallback((nativeRef) => {
setNativeInputRef(data.id, nativeRef);
}, [data.id, setNativeInputRef]);

return (
<NumberInput
className={cssClasses}
error={getError(data.id)}
label={formatDateTime(data.label)}
max={data.max}
min={data.min}
nativeRef={nativeRefCallback}
onChange={(value) => setValue(data.id, value)}
placeholder={data.placeholder}
required={data.isRequired}
Expand Down
8 changes: 7 additions & 1 deletion src/components/adaptive-cards/InputText/InputText.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useContext, useEffect} from 'react';
import React, {useCallback, useContext, useEffect} from 'react';
import PropTypes from 'prop-types';
import webexComponentClasses from '../../helpers';
import AdaptiveCardContext from '../context/adaptive-card-context';
Expand All @@ -24,6 +24,7 @@ export default function InputText({data, className, style}) {
setValue,
getValue,
setInput,
setNativeInputRef,
getError,
} = useContext(AdaptiveCardContext);
const [cssClasses, sc] = webexComponentClasses('adaptive-cards-input-text', className);
Expand All @@ -49,6 +50,10 @@ export default function InputText({data, className, style}) {
setInput,
]);

const nativeRefCallback = useCallback((nativeRef) => {
setNativeInputRef(data.id, nativeRef);
}, [data.id, setNativeInputRef]);

return (
<div className={cssClasses} style={style}>
{!data.isMultiline ? (
Expand All @@ -57,6 +62,7 @@ export default function InputText({data, className, style}) {
error={getError(data.id)}
label={formatDateTime(data.label)}
maxLength={data.maxLength}
nativeRef={nativeRefCallback}
onChange={(value) => setValue(data.id, value)}
pattern={data.regex}
placeholder={data.placeholder}
Expand Down
8 changes: 7 additions & 1 deletion src/components/adaptive-cards/InputTime/InputTime.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useContext, useEffect} from 'react';
import React, {useCallback, useContext, useEffect} from 'react';
import PropTypes from 'prop-types';
import webexComponentClasses from '../../helpers';
import {acPropTypes, registerComponent} from '../Component/Component';
Expand All @@ -22,6 +22,7 @@ export default function InputTime({data, className, style}) {
setValue,
getValue,
setInput,
setNativeInputRef,
getError,
} = useContext(AdaptiveCardContext);

Expand All @@ -44,6 +45,10 @@ export default function InputTime({data, className, style}) {
setInput,
]);

const nativeRefCallback = useCallback((nativeRef) => {
setNativeInputRef(data.id, nativeRef);
}, [data.id, setNativeInputRef]);

return (
<TimeInput
className={cssClasses}
Expand All @@ -54,6 +59,7 @@ export default function InputTime({data, className, style}) {
error={getError(data.id)}
required={data.isRequired}
label={formatDateTime(data.label)}
nativeRef={nativeRefCallback}
onChange={(value) => setValue(data.id, value)}
/>
);
Expand Down
13 changes: 11 additions & 2 deletions src/components/generic/InputField/InputField.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, {useCallback} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import webexComponentClasses from '../../helpers';
Expand All @@ -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.nativeRef] Action to perform to obtain the native 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
Expand All @@ -46,6 +47,7 @@ export default function InputField({
maxLength,
min,
name,
nativeRef,
onChange,
pattern,
placeholder,
Expand Down Expand Up @@ -74,6 +76,11 @@ export default function InputField({
}
};

const ref2 = useCallback((node) => {
inputRef(node);
nativeRef(node);
}, [nativeRef]);

useAutoFocus(inputRef, autoFocus);

return (
Expand Down Expand Up @@ -105,7 +112,7 @@ export default function InputField({
onKeyDown={onKeyDown}
pattern={pattern}
placeholder={placeholder}
ref={inputRef}
ref={ref2}
required={required}
tabIndex={tabIndex}
type={type}
Expand Down Expand Up @@ -138,6 +145,7 @@ InputField.propTypes = {
maxLength: PropTypes.number,
min: PropTypes.number,
name: PropTypes.string,
nativeRef: PropTypes.func,
onChange: PropTypes.func,
pattern: PropTypes.string,
placeholder: PropTypes.string,
Expand Down Expand Up @@ -168,6 +176,7 @@ InputField.defaultProps = {
maxLength: undefined,
min: undefined,
name: undefined,
nativeRef: () => {},
onChange: undefined,
pattern: undefined,
placeholder: undefined,
Expand Down
5 changes: 5 additions & 0 deletions src/components/inputs/DateInput/DateInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {InputField} from '../../generic';
* @param {boolean} [props.required=false] Flag indicating input required
* @param {string} [props.error] Error text
* @param {string} [props.label] Label text
* @param {Function} [props.nativeRef] Action to perform to obtain the native input ref
* @param {Function} props.onChange Action to perform on input change
* @returns {object} JSX of the component
*/
Expand All @@ -27,6 +28,7 @@ export default function DateInput({
required,
error,
label,
nativeRef,
onChange,
}) {
const [cssClasses] = webexComponentClasses('date-input', className);
Expand All @@ -41,6 +43,7 @@ export default function DateInput({
required={required}
error={error}
onChange={onChange}
nativeRef={nativeRef}
value={value}
label={label}
/>
Expand All @@ -56,6 +59,7 @@ DateInput.propTypes = {
required: PropTypes.bool,
error: PropTypes.string,
label: PropTypes.string,
nativeRef: PropTypes.func,
onChange: PropTypes.func.isRequired,
};

Expand All @@ -68,4 +72,5 @@ DateInput.defaultProps = {
required: false,
error: undefined,
label: undefined,
nativeRef: undefined,
};
5 changes: 5 additions & 0 deletions src/components/inputs/NumberInput/NumberInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const HINTS = {
* @param {number} [props.max] Maximum value for the input element
* @param {number} [props.min] Minimum value for the input element
* @param {string} [props.name] Input name
* @param {Function} [props.nativeRef] Action to perform to obtain the native input ref
* @param {Function} props.onChange Action to perform on input change
* @param {string} [props.placeholder] Input placeholder
* @param {boolean} [props.required=false] Flag indicating input required
Expand All @@ -41,6 +42,7 @@ export default function NumberInput({
max,
min,
name,
nativeRef,
onChange,
placeholder,
required,
Expand Down Expand Up @@ -92,6 +94,7 @@ export default function NumberInput({
min={min}
max={max}
name={name}
nativeRef={nativeRef}
onChange={onChange}
placeholder={placeholder}
required={required}
Expand All @@ -114,6 +117,7 @@ NumberInput.propTypes = {
max: PropTypes.number,
min: PropTypes.number,
name: PropTypes.string,
nativeRef: PropTypes.func,
onChange: PropTypes.func.isRequired,
placeholder: PropTypes.string,
required: PropTypes.bool,
Expand All @@ -134,6 +138,7 @@ NumberInput.defaultProps = {
max: undefined,
min: undefined,
name: undefined,
nativeRef: undefined,
placeholder: undefined,
required: false,
style: undefined,
Expand Down
5 changes: 5 additions & 0 deletions src/components/inputs/PasswordInput/PasswordInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const HINTS = {
* @param {string} [props.label] Label text
* @param {number} [props.maxLength] Maximum number of characters allowed
* @param {string} [props.name] Input name
* @param {Function} [props.nativeRef] Action to perform to obtain the native 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
Expand All @@ -36,6 +37,7 @@ export default function PasswordInput({
label,
maxLength,
name,
nativeRef,
onChange,
pattern,
placeholder,
Expand Down Expand Up @@ -71,6 +73,7 @@ export default function PasswordInput({
label={label}
maxLength={maxLength}
name={name}
nativeRef={nativeRef}
onChange={onChange}
pattern={pattern}
placeholder={placeholder}
Expand All @@ -92,6 +95,7 @@ PasswordInput.propTypes = {
label: PropTypes.string,
maxLength: PropTypes.number,
name: PropTypes.string,
nativeRef: PropTypes.func,
onChange: PropTypes.func.isRequired,
pattern: PropTypes.string,
placeholder: PropTypes.string,
Expand All @@ -109,6 +113,7 @@ PasswordInput.defaultProps = {
label: undefined,
maxLength: undefined,
name: undefined,
nativeRef: undefined,
pattern: undefined,
placeholder: undefined,
required: false,
Expand Down
5 changes: 5 additions & 0 deletions src/components/inputs/TextInput/TextInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const HINTS = {
* @param {string} [props.label] Label text
* @param {number} [props.maxLength] Maximum number of characters allowed
* @param {string} [props.name] Input name
* @param {Function} [props.nativeRef] Action to perform to obtain the native 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
Expand All @@ -37,6 +38,7 @@ export default function TextInput({
maxLength,
name,
onChange,
nativeRef,
pattern,
placeholder,
required,
Expand All @@ -63,6 +65,7 @@ export default function TextInput({
label={label}
maxLength={maxLength}
name={name}
nativeRef={nativeRef}
onChange={onChange}
pattern={pattern}
placeholder={placeholder}
Expand All @@ -84,6 +87,7 @@ TextInput.propTypes = {
label: PropTypes.string,
maxLength: PropTypes.number,
name: PropTypes.string,
nativeRef: PropTypes.func,
onChange: PropTypes.func.isRequired,
pattern: PropTypes.string,
placeholder: PropTypes.string,
Expand All @@ -102,6 +106,7 @@ TextInput.defaultProps = {
label: undefined,
maxLength: undefined,
name: undefined,
nativeRef: undefined,
pattern: undefined,
placeholder: undefined,
required: false,
Expand Down
Loading