diff --git a/.storybook/main.ts b/.storybook/main.ts index af7b0f60dd..700f989c69 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -7,6 +7,19 @@ import type { StorybookConfig } from "@storybook/react-webpack5"; const config: StorybookConfig = { webpackFinal: async (config) => { + if (!config.resolve) { + config.resolve = {}; + } + if (!config.resolve.alias) { + config.resolve.alias = {}; + } + (config.resolve.alias as Record)[ + "node-sql-parser" + ] = path.resolve( + __dirname, + "../node_modules/@sgress454/node-sql-parser/umd/sqlite.umd.js" + ); + config.module?.rules?.push({ test: /\.scss$/, use: [ diff --git a/changes/input-field-to-ts b/changes/input-field-to-ts new file mode 100644 index 0000000000..5a930457ae --- /dev/null +++ b/changes/input-field-to-ts @@ -0,0 +1 @@ +* Moved some core UI form components from TypeScript for better predictability/reliability diff --git a/frontend/components/AddHostsModal/PlatformWrapper/AndroidPanel/AndroidPanel.tsx b/frontend/components/AddHostsModal/PlatformWrapper/AndroidPanel/AndroidPanel.tsx index 92156c2266..6990a65db1 100644 --- a/frontend/components/AddHostsModal/PlatformWrapper/AndroidPanel/AndroidPanel.tsx +++ b/frontend/components/AddHostsModal/PlatformWrapper/AndroidPanel/AndroidPanel.tsx @@ -6,7 +6,6 @@ import { AppContext } from "context/app"; import CustomLink from "components/CustomLink"; import Radio from "components/forms/fields/Radio"; -// @ts-ignore import InputField from "components/forms/fields/InputField"; type EnrollmentType = "workProfile" | "fullyManaged"; diff --git a/frontend/components/AddHostsModal/PlatformWrapper/IosIpadosPanel/IosIpadosPanel.tsx b/frontend/components/AddHostsModal/PlatformWrapper/IosIpadosPanel/IosIpadosPanel.tsx index 88095bc20c..07e387b93d 100644 --- a/frontend/components/AddHostsModal/PlatformWrapper/IosIpadosPanel/IosIpadosPanel.tsx +++ b/frontend/components/AddHostsModal/PlatformWrapper/IosIpadosPanel/IosIpadosPanel.tsx @@ -4,7 +4,6 @@ import CustomLink from "components/CustomLink"; import PATHS from "router/paths"; import { AppContext } from "context/app"; -// @ts-ignore import InputField from "components/forms/fields/InputField"; const generateUrl = (serverUrl: string, enrollSecret: string) => { diff --git a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx index 7345798b75..acecc01982 100644 --- a/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx +++ b/frontend/components/AddHostsModal/PlatformWrapper/PlatformWrapper.tsx @@ -9,7 +9,6 @@ import { LEARN_MORE_ABOUT_BASE_LINK } from "utilities/constants"; import Button from "components/buttons/Button"; import Icon from "components/Icon/Icon"; import RevealButton from "components/buttons/RevealButton"; -// @ts-ignore import InputField from "components/forms/fields/InputField"; import TooltipWrapper from "components/TooltipWrapper"; import TabNav from "components/TabNav"; diff --git a/frontend/components/EnrollSecrets/SecretEditorModal/SecretEditorModal.tsx b/frontend/components/EnrollSecrets/SecretEditorModal/SecretEditorModal.tsx index 801f430f71..0ea7565669 100644 --- a/frontend/components/EnrollSecrets/SecretEditorModal/SecretEditorModal.tsx +++ b/frontend/components/EnrollSecrets/SecretEditorModal/SecretEditorModal.tsx @@ -5,7 +5,6 @@ import { IEnrollSecret } from "interfaces/enroll_secret"; import Modal from "components/Modal"; import Button from "components/buttons/Button"; -// @ts-ignore import InputField from "components/forms/fields/InputField"; interface ISecretEditorModalProps { diff --git a/frontend/components/TargetsInput/TargetsInput.tsx b/frontend/components/TargetsInput/TargetsInput.tsx index c38ba8d5e4..f54fe30a69 100644 --- a/frontend/components/TargetsInput/TargetsInput.tsx +++ b/frontend/components/TargetsInput/TargetsInput.tsx @@ -89,7 +89,6 @@ const TargetsInput = ({ type="search" iconSvg="search" value={searchText} - iconPosition="start" label={label} placeholder={placeholder} onChange={setSearchText} diff --git a/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.tsx b/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.tsx index 5dd97d654e..6e9b5bdd41 100644 --- a/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.tsx +++ b/frontend/components/forms/ConfirmInviteForm/ConfirmInviteForm.tsx @@ -3,7 +3,6 @@ import React, { useCallback, useState } from "react"; import validateEquality from "components/forms/validators/validate_equality"; import Button from "components/buttons/Button"; -// @ts-ignore import InputField from "components/forms/fields/InputField"; import { IInputFieldParseTarget } from "interfaces/form_field"; @@ -111,7 +110,7 @@ const ConfirmInviteForm = ({ value={name} error={formErrors.name} parseTarget - maxLength={80} + inputOptions={{ maxLength: 80 }} /> | JSX.Element | string; + label: React.ReactNode; name: string; - helpText?: Array | JSX.Element | string; + helpText?: React.ReactNode; type?: string; - error?: string; + error?: string | null; className?: string; tooltip?: React.ReactNode; labelTooltipPosition?: PlacesType; diff --git a/frontend/components/forms/fields/InputField/InputField.jsx b/frontend/components/forms/fields/InputField/InputField.jsx deleted file mode 100644 index 6fa0121ed0..0000000000 --- a/frontend/components/forms/fields/InputField/InputField.jsx +++ /dev/null @@ -1,291 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import classnames from "classnames"; -import { noop, pick } from "lodash"; - -import { stringToClipboard } from "utilities/copy_text"; - -import FormField from "components/forms/FormField"; -import Button from "components/buttons/Button"; -import Icon from "components/Icon"; - -const baseClass = "input-field"; - -class InputField extends Component { - static propTypes = { - autofocus: PropTypes.bool, - /** readOnly displays a non-editable field */ - readOnly: PropTypes.bool, - /** disabled displays a greyed out non-editable field */ - disabled: PropTypes.bool, - error: PropTypes.string, - inputClassName: PropTypes.string, // eslint-disable-line react/forbid-prop-types - inputWrapperClass: PropTypes.string, - inputOptions: PropTypes.object, // eslint-disable-line react/forbid-prop-types - name: PropTypes.string, - onChange: PropTypes.func, - onBlur: PropTypes.func, - onFocus: PropTypes.func, - placeholder: PropTypes.string, - type: PropTypes.string, - blockAutoComplete: PropTypes.bool, - value: PropTypes.oneOfType([ - PropTypes.bool, - PropTypes.string, - PropTypes.number, - ]).isRequired, - /** Returns both name and value */ - parseTarget: PropTypes.bool, - tooltip: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), - labelTooltipPosition: PropTypes.string, - helpText: PropTypes.oneOfType([ - PropTypes.string, - PropTypes.arrayOf(PropTypes.string), - PropTypes.object, - ]), - /** Use in conjunction with type "password" and enableCopy to see eye icon to view */ - enableShowSecret: PropTypes.bool, - enableCopy: PropTypes.bool, - ignore1password: PropTypes.bool, - // Accepts string or number for HTML compatibility, (e.g., step="0.1", step={0.1}) - /** Only effective on input type number */ - step: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - /** Only effective on input type number */ - min: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - /** Only effective on input type number */ - max: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), - }; - - static defaultProps = { - autofocus: false, - inputWrapperClass: "", - inputOptions: {}, - label: null, - labelClassName: "", - onFocus: noop, - onBlur: noop, - type: "text", - blockAutoComplete: false, - value: "", - parseTarget: false, - tooltip: "", - labelTooltipPosition: undefined, - helpText: "", - enableCopy: false, - enableShowSecret: false, - ignore1password: false, - step: undefined, - min: undefined, - max: undefined, - }; - - constructor() { - super(); - this.state = { - copied: false, - }; - } - - componentDidMount() { - const { autofocus } = this.props; - const { input } = this; - - if (autofocus) { - input.focus(); - } - - return false; - } - - onInputChange = (evt) => { - evt.preventDefault(); - - const { value, name } = evt.target; - const { onChange, parseTarget } = this.props; - - if (parseTarget) { - // Returns both name and value - return onChange({ value, name }); - } - - return onChange(value); - }; - - onToggleSecret = (evt) => { - evt.preventDefault(); - - this.setState({ showSecret: !this.state.showSecret }); - return false; - }; - - onClickCopy = (e) => { - e.preventDefault(); - stringToClipboard(this.props.value).then(() => { - this.setState({ copied: true }); - setTimeout(() => { - this.setState({ copied: false }); - }, 2000); - }); - }; - - renderShowSecretButton = () => { - const { onToggleSecret } = this; - - return ( - - ); - }; - - renderCopyButton = () => { - const { onClickCopy } = this; - - const copyButtonValue = ; - const wrapperClasses = classnames(`${baseClass}__copy-wrapper`, { - [`${baseClass}__copy-wrapper__text-area`]: this.props.type === "textarea", - }); - - const copiedConfirmationClasses = classnames( - `${baseClass}__copied-confirmation` - ); - - return ( -
- {this.state.copied && ( - Copied! - )} - - {this.props.enableShowSecret && this.renderShowSecretButton()} -
- ); - }; - - render() { - const { - readOnly, - disabled, - error, - inputClassName, - inputOptions, - inputWrapperClass, - name, - onFocus, - onBlur, - placeholder, - type, - blockAutoComplete, - value, - ignore1password, - enableCopy, - enableShowSecret, - step, - min, - max, - } = this.props; - - const { onInputChange } = this; - const shouldShowPasswordClass = - type === "password" && !this.state.showSecret; - const inputClasses = classnames(baseClass, inputClassName, { - [`${baseClass}--password`]: shouldShowPasswordClass, - [`${baseClass}--read-only`]: readOnly || disabled, - [`${baseClass}--disabled`]: disabled, - [`${baseClass}--error`]: error, - [`${baseClass}__textarea`]: type === "textarea", - }); - - const inputWrapperClasses = classnames(inputWrapperClass, { - [`input-field--read-only`]: readOnly || disabled, - [`input-field--disabled`]: disabled, - }); - - const formFieldProps = pick(this.props, [ - "helpText", - "label", - "error", - "name", - "tooltip", - "labelTooltipPosition", - ]); - - const inputContainerClasses = classnames(`${baseClass}__input-container`, { - "copy-enabled": enableCopy, - }); - - if (type === "textarea") { - return ( - -
-