diff --git a/packages/web/src/components/AccountDropdownMenu/index.jsx b/packages/web/src/components/AccountDropdownMenu/index.jsx index e4889c89..4c69a8be 100644 --- a/packages/web/src/components/AccountDropdownMenu/index.jsx +++ b/packages/web/src/components/AccountDropdownMenu/index.jsx @@ -68,7 +68,10 @@ function AccountDropdownMenu(props) { AccountDropdownMenu.propTypes = { open: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, - anchorEl: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), + anchorEl: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.instanceOf(Element) }), + ]), id: PropTypes.string.isRequired, }; diff --git a/packages/web/src/components/ControlledCustomAutocomplete/CustomOptions.jsx b/packages/web/src/components/ControlledCustomAutocomplete/CustomOptions.jsx index bf3c3748..3de84a9a 100644 --- a/packages/web/src/components/ControlledCustomAutocomplete/CustomOptions.jsx +++ b/packages/web/src/components/ControlledCustomAutocomplete/CustomOptions.jsx @@ -76,7 +76,10 @@ const CustomOptions = (props) => { CustomOptions.propTypes = { open: PropTypes.bool.isRequired, - anchorEl: PropTypes.oneOfType([PropTypes.element, PropTypes.func]).isRequired, + anchorEl: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.instanceOf(Element) }), + ]), data: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.string.isRequired, diff --git a/packages/web/src/components/ControlledCustomAutocomplete/Options.jsx b/packages/web/src/components/ControlledCustomAutocomplete/Options.jsx index 2d1cb3c8..c5d1321a 100644 --- a/packages/web/src/components/ControlledCustomAutocomplete/Options.jsx +++ b/packages/web/src/components/ControlledCustomAutocomplete/Options.jsx @@ -18,36 +18,47 @@ const computeListHeight = (currentLength) => { return LIST_ITEM_HEIGHT * numberOfRenderedItems; }; +const Item = (props) => { + const { index, style, data, onOptionClick } = props; + const suboption = data[index]; + return ( + onOptionClick(event, suboption)} + data-test="power-input-suggestion-item" + key={index} + style={style} + > + + + ); +}; + +Item.propTypes = { + index: PropTypes.number.isRequired, + style: PropTypes.object, + data: PropTypes.array.isRequired, + onOptionClick: PropTypes.func.isRequired, +}; + const renderItemFactory = ({ onOptionClick }) => (props) => { - const { index, style, data } = props; - const suboption = data[index]; - return ( - onOptionClick(event, suboption)} - data-test="power-input-suggestion-item" - key={index} - style={style} - > - - - ); + ; }; const Options = (props) => { diff --git a/packages/web/src/components/FlowStep/index.jsx b/packages/web/src/components/FlowStep/index.jsx index 853c24bb..200d0fec 100644 --- a/packages/web/src/components/FlowStep/index.jsx +++ b/packages/web/src/components/FlowStep/index.jsx @@ -364,6 +364,7 @@ FlowStep.propTypes = { onClose: PropTypes.func, onChange: PropTypes.func.isRequired, onContinue: PropTypes.func, + flowId: PropTypes.string.isRequired, }; export default FlowStep; diff --git a/packages/web/src/components/FlowStepContextMenu/index.jsx b/packages/web/src/components/FlowStepContextMenu/index.jsx index 9fedb469..23a40970 100644 --- a/packages/web/src/components/FlowStepContextMenu/index.jsx +++ b/packages/web/src/components/FlowStepContextMenu/index.jsx @@ -43,8 +43,12 @@ function FlowStepContextMenu(props) { FlowStepContextMenu.propTypes = { stepId: PropTypes.string.isRequired, onClose: PropTypes.func.isRequired, - anchorEl: PropTypes.element.isRequired, + anchorEl: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.instanceOf(Element) }), + ]), deletable: PropTypes.bool.isRequired, + flowId: PropTypes.string.isRequired, }; export default FlowStepContextMenu; diff --git a/packages/web/src/components/Form/index.jsx b/packages/web/src/components/Form/index.jsx index 061e10d8..68a1b1db 100644 --- a/packages/web/src/components/Form/index.jsx +++ b/packages/web/src/components/Form/index.jsx @@ -1,7 +1,10 @@ import * as React from 'react'; import { FormProvider, useForm, useWatch } from 'react-hook-form'; +import PropTypes from 'prop-types'; + const noop = () => null; -export default function Form(props) { + +function Form(props) { const { children, onSubmit = noop, @@ -11,22 +14,27 @@ export default function Form(props) { mode = 'all', ...formProps } = props; + const methods = useForm({ defaultValues, reValidateMode: 'onBlur', resolver, mode, }); + const form = useWatch({ control: methods.control }); + /** * For fields having `dependsOn` fields, we need to re-validate the form. */ React.useEffect(() => { methods.trigger(); }, [methods.trigger, form]); + React.useEffect(() => { methods.reset(defaultValues); }, [defaultValues]); + return (
@@ -35,3 +43,14 @@ export default function Form(props) { ); } + +Form.propTypes = { + children: PropTypes.node, + defaultValues: PropTypes.object, + onSubmit: PropTypes.func, + render: PropTypes.func, + resolver: PropTypes.func, + mode: PropTypes.oneOf(['onChange', 'onBlur', 'onSubmit', 'onTouched', 'all']), +}; + +export default Form; diff --git a/packages/web/src/components/HideOnScroll/index.jsx b/packages/web/src/components/HideOnScroll/index.jsx index b93f91da..4c8a103e 100644 --- a/packages/web/src/components/HideOnScroll/index.jsx +++ b/packages/web/src/components/HideOnScroll/index.jsx @@ -1,7 +1,9 @@ import * as React from 'react'; import Slide from '@mui/material/Slide'; import useScrollTrigger from '@mui/material/useScrollTrigger'; + export default function HideOnScroll(props) { const trigger = useScrollTrigger(); + return ; } diff --git a/packages/web/src/components/InputCreator/index.jsx b/packages/web/src/components/InputCreator/index.jsx index 613244cf..cb9a4f0f 100644 --- a/packages/web/src/components/InputCreator/index.jsx +++ b/packages/web/src/components/InputCreator/index.jsx @@ -1,6 +1,7 @@ import * as React from 'react'; import MuiTextField from '@mui/material/TextField'; import CircularProgress from '@mui/material/CircularProgress'; +import PropTypes from 'prop-types'; import useDynamicFields from 'hooks/useDynamicFields'; import useDynamicData from 'hooks/useDynamicData'; @@ -9,11 +10,11 @@ import TextField from 'components/TextField'; import ControlledAutocomplete from 'components/ControlledAutocomplete'; import ControlledCustomAutocomplete from 'components/ControlledCustomAutocomplete'; import DynamicField from 'components/DynamicField'; +import { FieldPropType } from 'propTypes/propTypes'; const optionGenerator = (options) => options?.map(({ name, value }) => ({ label: name, value: value })); - -export default function InputCreator(props) { +function InputCreator(props) { const { onChange, onBlur, @@ -131,7 +132,8 @@ export default function InputCreator(props) { ; } + +InputCreator.propTypes = { + onChange: PropTypes.func, + onBlur: PropTypes.func, + schema: FieldPropType.isRequired, + namePrefix: PropTypes.string, + stepId: PropTypes.string, + disabled: PropTypes.bool, + showOptionValue: PropTypes.bool, + shouldUnregister: PropTypes.bool, +}; + +export default InputCreator; diff --git a/packages/web/src/components/IntermediateStepCount/index.jsx b/packages/web/src/components/IntermediateStepCount/index.jsx index eae814cb..01e6cfbd 100644 --- a/packages/web/src/components/IntermediateStepCount/index.jsx +++ b/packages/web/src/components/IntermediateStepCount/index.jsx @@ -1,8 +1,12 @@ import * as React from 'react'; import Typography from '@mui/material/Typography'; +import PropTypes from 'prop-types'; + import { Container } from './style'; -export default function IntermediateStepCount(props) { + +function IntermediateStepCount(props) { const { count } = props; + return ( @@ -11,3 +15,9 @@ export default function IntermediateStepCount(props) { ); } + +IntermediateStepCount.propTypes = { + count: PropTypes.number.isRequired, +}; + +export default IntermediateStepCount; diff --git a/packages/web/src/components/IntlProvider/index.jsx b/packages/web/src/components/IntlProvider/index.jsx index e3a1a35f..bb33a89a 100644 --- a/packages/web/src/components/IntlProvider/index.jsx +++ b/packages/web/src/components/IntlProvider/index.jsx @@ -1,5 +1,8 @@ import { IntlProvider as BaseIntlProvider } from 'react-intl'; +import PropTypes from 'prop-types'; + import englishMessages from 'locales/en.json'; + const IntlProvider = ({ children }) => { return ( { ); }; + +IntlProvider.propTypes = { + children: PropTypes.node.isRequired, +}; + export default IntlProvider; diff --git a/packages/web/src/components/JSONViewer/index.jsx b/packages/web/src/components/JSONViewer/index.jsx index bde7a28c..89ed2878 100644 --- a/packages/web/src/components/JSONViewer/index.jsx +++ b/packages/web/src/components/JSONViewer/index.jsx @@ -1,5 +1,7 @@ import * as React from 'react'; import { JSONTree } from 'react-json-tree'; +import PropTypes from 'prop-types'; + const theme = { scheme: 'inspector', author: 'Alexander Kuznetsov (alexkuz@gmail.com)', @@ -36,8 +38,10 @@ const theme = { // base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. base0F: '#a16946', }; + function JSONViewer(props) { const { data } = props; + return ( ); } + +JSONViewer.propTypes = { + data: PropTypes.object.isRequired, +}; + export default JSONViewer; diff --git a/packages/web/src/components/Layout/index.jsx b/packages/web/src/components/Layout/index.jsx index 8f0532de..c36026d8 100644 --- a/packages/web/src/components/Layout/index.jsx +++ b/packages/web/src/components/Layout/index.jsx @@ -9,6 +9,7 @@ import SwapCallsIcon from '@mui/icons-material/SwapCalls'; import HistoryIcon from '@mui/icons-material/History'; import NotificationsIcon from '@mui/icons-material/Notifications'; import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew'; +import PropTypes from 'prop-types'; import * as URLS from 'config/urls'; import useFormatMessage from 'hooks/useFormatMessage'; @@ -75,7 +76,7 @@ const generateDrawerBottomLinks = async ({ return links; }; -export default function PublicLayout({ children }) { +function PublicLayout({ children }) { const version = useVersion(); const { data: configData, isLoading } = useAutomatischConfig(); const config = configData?.data; @@ -135,3 +136,9 @@ export default function PublicLayout({ children }) { ); } + +PublicLayout.propTypes = { + children: PropTypes.node.isRequired, +}; + +export default PublicLayout; diff --git a/packages/web/src/components/ListItemLink/index.jsx b/packages/web/src/components/ListItemLink/index.jsx index 0e4e6b6e..9cfd15bc 100644 --- a/packages/web/src/components/ListItemLink/index.jsx +++ b/packages/web/src/components/ListItemLink/index.jsx @@ -4,9 +4,12 @@ import ListItem from '@mui/material/ListItemButton'; import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemText from '@mui/material/ListItemText'; import { Link } from 'react-router-dom'; -export default function ListItemLink(props) { +import PropTypes from 'prop-types'; + +function ListItemLink(props) { const { icon, primary, to, onClick, 'data-test': dataTest, target } = props; const selected = useMatch({ path: to, end: true }); + const CustomLink = React.useMemo( () => React.forwardRef(function InLineLink(linkProps, ref) { @@ -28,6 +31,7 @@ export default function ListItemLink(props) { }), [to], ); + return (
  • ); } + +ListItemLink.propTypes = { + icon: PropTypes.node.isRequired, + primary: PropTypes.string.isRequired, + to: PropTypes.string.isRequired, + target: PropTypes.oneOf(['_blank']), + onClick: PropTypes.func, + 'data-test': PropTypes.string, +}; + +export default ListItemLink; diff --git a/packages/web/src/components/ListLoader/index.jsx b/packages/web/src/components/ListLoader/index.jsx index ddc64728..2001d22e 100644 --- a/packages/web/src/components/ListLoader/index.jsx +++ b/packages/web/src/components/ListLoader/index.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import { IconButton, Skeleton, @@ -7,6 +8,7 @@ import { } from '@mui/material'; import EditIcon from '@mui/icons-material/Edit'; import DeleteIcon from '@mui/icons-material/Delete'; + const ListLoader = ({ rowsNumber, columnsNumber, 'data-test': dataTest }) => { return ( <> @@ -38,4 +40,11 @@ const ListLoader = ({ rowsNumber, columnsNumber, 'data-test': dataTest }) => { ); }; + +ListLoader.propTypes = { + rowsNumber: PropTypes.number.isRequired, + columnsNumber: PropTypes.number.isRequired, + 'data-test': PropTypes.string, +}; + export default ListLoader; diff --git a/packages/web/src/components/MationLogo/index.jsx b/packages/web/src/components/MationLogo/index.jsx index 34240749..c83b91a6 100644 --- a/packages/web/src/components/MationLogo/index.jsx +++ b/packages/web/src/components/MationLogo/index.jsx @@ -1,6 +1,8 @@ import * as React from 'react'; import { ReactComponent as MationLogoSvg } from './assets/mation-logo.svg'; + const MationLogo = () => { return ; }; + export default MationLogo; diff --git a/packages/web/src/components/MetadataProvider/index.jsx b/packages/web/src/components/MetadataProvider/index.jsx index b6453492..85e47914 100644 --- a/packages/web/src/components/MetadataProvider/index.jsx +++ b/packages/web/src/components/MetadataProvider/index.jsx @@ -1,4 +1,5 @@ import * as React from 'react'; +import PropTypes from 'prop-types'; import useAutomatischConfig from 'hooks/useAutomatischConfig'; @@ -27,10 +28,13 @@ const MetadataProvider = ({ children }) => { newFaviconElement.href = '/browser-tab.ico'; } } - }, [config?.disableFavicon]); return <>{children}; }; +MetadataProvider.propTypes = { + children: PropTypes.node.isRequired, +}; + export default MetadataProvider; diff --git a/packages/web/src/components/NoResultFound/index.jsx b/packages/web/src/components/NoResultFound/index.jsx index c2c47fa1..be634bdc 100644 --- a/packages/web/src/components/NoResultFound/index.jsx +++ b/packages/web/src/components/NoResultFound/index.jsx @@ -4,9 +4,11 @@ import Card from '@mui/material/Card'; import AddCircleIcon from '@mui/icons-material/AddCircle'; import CardActionArea from '@mui/material/CardActionArea'; import Typography from '@mui/material/Typography'; +import PropTypes from 'prop-types'; + import { CardContent } from './style'; -export default function NoResultFound(props) { +function NoResultFound(props) { const { text, to } = props; const ActionAreaLink = React.useMemo( @@ -29,3 +31,10 @@ export default function NoResultFound(props) { ); } + +NoResultFound.propTypes = { + text: PropTypes.string, + to: PropTypes.string, +}; + +export default NoResultFound; diff --git a/packages/web/src/components/NotificationCard/index.jsx b/packages/web/src/components/NotificationCard/index.jsx index 727d80bd..49f5bb6e 100644 --- a/packages/web/src/components/NotificationCard/index.jsx +++ b/packages/web/src/components/NotificationCard/index.jsx @@ -5,16 +5,21 @@ import CardActionArea from '@mui/material/CardActionArea'; import CardContent from '@mui/material/CardContent'; import Typography from '@mui/material/Typography'; import { DateTime } from 'luxon'; +import PropTypes from 'prop-types'; + import useFormatMessage from 'hooks/useFormatMessage'; + const getHumanlyDate = (timestamp) => DateTime.fromMillis(timestamp).toRelative(); -export default function NotificationCard(props) { + +function NotificationCard(props) { const { name, createdAt, documentationUrl, description } = props; const formatMessage = useFormatMessage(); const relativeCreatedAt = getHumanlyDate(new Date(createdAt).getTime()); const subheader = formatMessage('notification.releasedAt', { relativeDate: relativeCreatedAt, }); + return ( @@ -36,3 +41,12 @@ export default function NotificationCard(props) { ); } + +NotificationCard.propTypes = { + name: PropTypes.string.isRequired, + createdAt: PropTypes.string.isRequired, + documentationUrl: PropTypes.string.isRequired, + description: PropTypes.string.isRequired, +}; + +export default NotificationCard; diff --git a/packages/web/src/components/PageTitle/index.jsx b/packages/web/src/components/PageTitle/index.jsx index ab34a0be..1ddc0895 100644 --- a/packages/web/src/components/PageTitle/index.jsx +++ b/packages/web/src/components/PageTitle/index.jsx @@ -1,5 +1,6 @@ import * as React from 'react'; import Typography from '@mui/material/Typography'; + export default function PageTitle(props) { return ; } diff --git a/packages/web/src/components/PermissionCatalogField/PermissionCatalogFieldLoader/index.jsx b/packages/web/src/components/PermissionCatalogField/PermissionCatalogFieldLoader/index.jsx index 1eca4b15..50903dbf 100644 --- a/packages/web/src/components/PermissionCatalogField/PermissionCatalogFieldLoader/index.jsx +++ b/packages/web/src/components/PermissionCatalogField/PermissionCatalogFieldLoader/index.jsx @@ -11,7 +11,9 @@ import { Typography, } from '@mui/material'; import SettingsIcon from '@mui/icons-material/Settings'; + import ControlledCheckbox from 'components/ControlledCheckbox'; + const PermissionCatalogFieldLoader = () => { return ( @@ -56,4 +58,5 @@ const PermissionCatalogFieldLoader = () => { ); }; + export default PermissionCatalogFieldLoader; diff --git a/packages/web/src/components/PermissionCatalogField/PermissionSettings.ee.jsx b/packages/web/src/components/PermissionCatalogField/PermissionSettings.ee.jsx index 4ff1f43b..14237a20 100644 --- a/packages/web/src/components/PermissionCatalogField/PermissionSettings.ee.jsx +++ b/packages/web/src/components/PermissionCatalogField/PermissionSettings.ee.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; @@ -15,7 +16,8 @@ import * as React from 'react'; import { useFormContext } from 'react-hook-form'; import ControlledCheckbox from 'components/ControlledCheckbox'; import useFormatMessage from 'hooks/useFormatMessage'; -export default function PermissionSettings(props) { + +function PermissionSettings(props) { const { onClose, open = false, @@ -27,6 +29,7 @@ export default function PermissionSettings(props) { } = props; const formatMessage = useFormatMessage(); const { getValues, resetField } = useFormContext(); + const cancel = () => { for (const action of actions) { for (const condition of conditions) { @@ -36,6 +39,7 @@ export default function PermissionSettings(props) { } onClose(); }; + const apply = () => { for (const action of actions) { for (const condition of conditions) { @@ -46,6 +50,7 @@ export default function PermissionSettings(props) { } onClose(); }; + return ( ); } + +PermissionSettings.propTypes = { + onClose: PropTypes.func.isRequired, + fieldPrefix: PropTypes.string.isRequired, + subject: PropTypes.string.isRequired, + open: PropTypes.bool, + defaultChecked: PropTypes.bool, + actions: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string, + key: PropTypes.string, + subjects: PropTypes.arrayOf(PropTypes.string), + }), + ).isRequired, + conditions: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string, + key: PropTypes.string, + }), + ).isRequired, +}; + +export default PermissionSettings; diff --git a/packages/web/src/components/PermissionCatalogField/index.ee.jsx b/packages/web/src/components/PermissionCatalogField/index.ee.jsx index d298cb75..92bcb1a5 100644 --- a/packages/web/src/components/PermissionCatalogField/index.ee.jsx +++ b/packages/web/src/components/PermissionCatalogField/index.ee.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import SettingsIcon from '@mui/icons-material/Settings'; import IconButton from '@mui/material/IconButton'; import Paper from '@mui/material/Paper'; @@ -110,4 +111,10 @@ const PermissionCatalogField = ({ ); }; +PermissionCatalogField.propTypes = { + name: PropTypes.string, + disabled: PropTypes.bool, + defaultChecked: PropTypes.bool, +}; + export default PermissionCatalogField; diff --git a/packages/web/src/components/Portal/index.jsx b/packages/web/src/components/Portal/index.jsx index f4339748..e519dc7e 100644 --- a/packages/web/src/components/Portal/index.jsx +++ b/packages/web/src/components/Portal/index.jsx @@ -1,7 +1,9 @@ import ReactDOM from 'react-dom'; + const Portal = ({ children }) => { return typeof document === 'object' ? ReactDOM.createPortal(children, document.body) : null; }; + export default Portal; diff --git a/packages/web/src/components/PowerInput/Popper.jsx b/packages/web/src/components/PowerInput/Popper.jsx index 3509fe08..76e188ba 100644 --- a/packages/web/src/components/PowerInput/Popper.jsx +++ b/packages/web/src/components/PowerInput/Popper.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import Paper from '@mui/material/Paper'; import MuiPopper from '@mui/material/Popper'; import Tab from '@mui/material/Tab'; @@ -5,8 +6,10 @@ import * as React from 'react'; import Suggestions from 'components/PowerInput/Suggestions'; import TabPanel from 'components/TabPanel'; import { Tabs } from './style'; + const Popper = (props) => { const { open, anchorEl, data, onSuggestionClick } = props; + return ( { ); }; + +Popper.propTypes = { + open: PropTypes.bool.isRequired, + anchorEl: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.shape({ current: PropTypes.instanceOf(Element) }), + ]), + data: PropTypes.array.isRequired, + onSuggestionClick: PropTypes.func, +}; + export default Popper; diff --git a/packages/web/src/components/PowerInput/SuggestionItem.jsx b/packages/web/src/components/PowerInput/SuggestionItem.jsx new file mode 100644 index 00000000..4d04e36f --- /dev/null +++ b/packages/web/src/components/PowerInput/SuggestionItem.jsx @@ -0,0 +1,49 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import ListItemButton from '@mui/material/ListItemButton'; +import ListItemText from '@mui/material/ListItemText'; + +const SuggestionItem = (props) => { + const { index, style, data, onSuggestionClick } = props; + const suboption = data[index]; + return ( + onSuggestionClick(suboption)} + data-test="power-input-suggestion-item" + key={index} + style={style} + > + + + ); +}; + +SuggestionItem.propTypes = { + index: PropTypes.number.isRequired, + style: PropTypes.object, + data: PropTypes.arrayOf( + PropTypes.shape({ + label: PropTypes.string, + sampleValue: PropTypes.string, + }), + ).isRequired, + onSuggestionClick: PropTypes.func.isRequired, +}; + +export default SuggestionItem; diff --git a/packages/web/src/components/PowerInput/Suggestions.jsx b/packages/web/src/components/PowerInput/Suggestions.jsx index 0ea5f280..f88b15f7 100644 --- a/packages/web/src/components/PowerInput/Suggestions.jsx +++ b/packages/web/src/components/PowerInput/Suggestions.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import ExpandLess from '@mui/icons-material/ExpandLess'; import ExpandMore from '@mui/icons-material/ExpandMore'; import Box from '@mui/material/Box'; @@ -13,52 +14,33 @@ import * as React from 'react'; import { FixedSizeList } from 'react-window'; import SearchInput from 'components/SearchInput'; import useFormatMessage from 'hooks/useFormatMessage'; +import SuggestionItem from './SuggestionItem'; + const SHORT_LIST_LENGTH = 4; const LIST_ITEM_HEIGHT = 64; + const computeListHeight = (currentLength) => { const numberOfRenderedItems = Math.min(SHORT_LIST_LENGTH, currentLength); return LIST_ITEM_HEIGHT * numberOfRenderedItems; }; + const getPartialArray = (array, length = array.length) => { return array.slice(0, length); }; + const renderItemFactory = ({ onSuggestionClick }) => - (props) => { - const { index, style, data } = props; - const suboption = data[index]; - return ( - onSuggestionClick(suboption)} - data-test="power-input-suggestion-item" - key={index} - style={style} - > - - - ); - }; + (props) => ( + + ); + const Suggestions = (props) => { const formatMessage = useFormatMessage(); const { data, onSuggestionClick = () => null } = props; const [current, setCurrent] = React.useState(0); const [listLength, setListLength] = React.useState(SHORT_LIST_LENGTH); const [filteredData, setFilteredData] = React.useState(data); + React.useEffect( function syncOptions() { setFilteredData((filteredData) => { @@ -70,6 +52,7 @@ const Suggestions = (props) => { }, [data], ); + const renderItem = React.useMemo( () => renderItemFactory({ @@ -77,15 +60,19 @@ const Suggestions = (props) => { }), [onSuggestionClick], ); + const expandList = () => { setListLength(Infinity); }; + const collapseList = () => { setListLength(SHORT_LIST_LENGTH); }; + React.useEffect(() => { setListLength(SHORT_LIST_LENGTH); }, [current]); + const onSearchChange = React.useMemo( () => throttle((event) => { @@ -111,6 +98,7 @@ const Suggestions = (props) => { }, 400), [data], ); + return ( @@ -182,4 +170,16 @@ const Suggestions = (props) => { ); }; + +Suggestions.propTypes = { + data: PropTypes.arrayOf( + PropTypes.shape({ + id: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, + output: PropTypes.arrayOf(PropTypes.object).isRequired, + }), + ).isRequired, + onSuggestionClick: PropTypes.func, +}; + export default Suggestions; diff --git a/packages/web/src/components/PowerInput/data.js b/packages/web/src/components/PowerInput/data.js index 8657cecb..3aec60bb 100644 --- a/packages/web/src/components/PowerInput/data.js +++ b/packages/web/src/components/PowerInput/data.js @@ -1,5 +1,6 @@ const joinBy = (delimiter = '.', ...args) => args.filter(Boolean).join(delimiter); + const process = ({ data, parentKey, index, parentLabel = '' }) => { if (typeof data !== 'object') { return [ @@ -10,10 +11,13 @@ const process = ({ data, parentKey, index, parentLabel = '' }) => { }, ]; } + const entries = Object.entries(data); + return entries.flatMap(([name, sampleValue]) => { const label = joinBy('.', parentLabel, index?.toString(), name); const value = joinBy('.', parentKey, index?.toString(), name); + if (Array.isArray(sampleValue)) { return sampleValue.flatMap((item, index) => process({ @@ -21,9 +25,10 @@ const process = ({ data, parentKey, index, parentLabel = '' }) => { parentKey: value, index, parentLabel: label, - }) + }), ); } + if (typeof sampleValue === 'object' && sampleValue !== null) { return process({ data: sampleValue, @@ -40,8 +45,10 @@ const process = ({ data, parentKey, index, parentLabel = '' }) => { ]; }); }; + export const processStepWithExecutions = (steps) => { if (!steps) return []; + return steps .filter((step) => { const hasExecutionSteps = !!step.executionSteps?.length; diff --git a/packages/web/src/components/PowerInput/index.jsx b/packages/web/src/components/PowerInput/index.jsx index 15b69298..f2d9ad96 100644 --- a/packages/web/src/components/PowerInput/index.jsx +++ b/packages/web/src/components/PowerInput/index.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import ClickAwayListener from '@mui/base/ClickAwayListener'; import FormHelperText from '@mui/material/FormHelperText'; import InputLabel from '@mui/material/InputLabel'; @@ -147,4 +148,21 @@ const PowerInput = (props) => { /> ); }; + +PowerInput.propTypes = { + onChange: PropTypes.func, + onBlur: PropTypes.func, + defaultValue: PropTypes.string, + name: PropTypes.string.isRequired, + label: PropTypes.string, + type: PropTypes.string, + required: PropTypes.bool, + readOnly: PropTypes.bool, + description: PropTypes.string, + docUrl: PropTypes.string, + clickToCopy: PropTypes.bool, + disabled: PropTypes.bool, + shouldUnregister: PropTypes.bool, +}; + export default PowerInput; diff --git a/packages/web/src/components/ResetPasswordForm/index.ee.jsx b/packages/web/src/components/ResetPasswordForm/index.ee.jsx index f7c874c5..996311ab 100644 --- a/packages/web/src/components/ResetPasswordForm/index.ee.jsx +++ b/packages/web/src/components/ResetPasswordForm/index.ee.jsx @@ -12,6 +12,7 @@ import TextField from 'components/TextField'; import * as URLS from 'config/urls'; import { RESET_PASSWORD } from 'graphql/mutations/reset-password.ee'; import useFormatMessage from 'hooks/useFormatMessage'; + const validationSchema = yup.object().shape({ password: yup.string().required('resetPasswordForm.mandatoryInput'), confirmPassword: yup @@ -19,6 +20,7 @@ const validationSchema = yup.object().shape({ .required('resetPasswordForm.mandatoryInput') .oneOf([yup.ref('password')], 'resetPasswordForm.passwordsMustMatch'), }); + export default function ResetPasswordForm() { const enqueueSnackbar = useEnqueueSnackbar(); const formatMessage = useFormatMessage(); @@ -26,6 +28,7 @@ export default function ResetPasswordForm() { const [searchParams] = useSearchParams(); const [resetPassword, { data, loading }] = useMutation(RESET_PASSWORD); const token = searchParams.get('token'); + const handleSubmit = async (values) => { await resetPassword({ variables: { @@ -43,6 +46,7 @@ export default function ResetPasswordForm() { }); navigate(URLS.LOGIN); }; + return ( ); } + +SearchInput.propTypes = { + onChange: PropTypes.func, +}; diff --git a/packages/web/src/components/SearchableJSONViewer/index.jsx b/packages/web/src/components/SearchableJSONViewer/index.jsx index 5d977f51..4ac2b91e 100644 --- a/packages/web/src/components/SearchableJSONViewer/index.jsx +++ b/packages/web/src/components/SearchableJSONViewer/index.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import * as React from 'react'; import throttle from 'lodash/throttle'; import isEmpty from 'lodash/isEmpty'; @@ -6,9 +7,11 @@ import JSONViewer from 'components/JSONViewer'; import SearchInput from 'components/SearchInput'; import useFormatMessage from 'hooks/useFormatMessage'; import filterObject from 'helpers/filterObject'; + const SearchableJSONViewer = ({ data }) => { const [filteredData, setFilteredData] = React.useState(data); const formatMessage = useFormatMessage(); + const onSearchChange = React.useMemo( () => throttle((event) => { @@ -26,6 +29,7 @@ const SearchableJSONViewer = ({ data }) => { }, 400), [data], ); + return ( <> @@ -38,4 +42,9 @@ const SearchableJSONViewer = ({ data }) => { ); }; + +SearchableJSONViewer.propTypes = { + data: PropTypes.object.isRequired, +}; + export default SearchableJSONViewer; diff --git a/packages/web/src/components/SettingsLayout/index.jsx b/packages/web/src/components/SettingsLayout/index.jsx index 8cb31a91..7e7ed167 100644 --- a/packages/web/src/components/SettingsLayout/index.jsx +++ b/packages/web/src/components/SettingsLayout/index.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import * as React from 'react'; import Box from '@mui/material/Box'; import Toolbar from '@mui/material/Toolbar'; @@ -11,6 +12,7 @@ import useAutomatischInfo from 'hooks/useAutomatischInfo'; import useFormatMessage from 'hooks/useFormatMessage'; import AppBar from 'components/AppBar'; import Drawer from 'components/Drawer'; + function createDrawerLinks({ isCloud }) { const items = [ { @@ -28,7 +30,8 @@ function createDrawerLinks({ isCloud }) { } return items; } -export default function SettingsLayout({ children }) { + +function SettingsLayout({ children }) { const { data: automatischInfo } = useAutomatischInfo(); const isCloud = automatischInfo?.data.isCloud; const theme = useTheme(); @@ -45,6 +48,7 @@ export default function SettingsLayout({ children }) { to: '/', }, ]; + return ( <> ); } + +SettingsLayout.propTypes = { + children: PropTypes.node.isRequired, +}; + +export default SettingsLayout; diff --git a/packages/web/src/components/Slate/Element.jsx b/packages/web/src/components/Slate/Element.jsx index f28d9e99..1b9d17a8 100644 --- a/packages/web/src/components/Slate/Element.jsx +++ b/packages/web/src/components/Slate/Element.jsx @@ -1,6 +1,9 @@ +import PropTypes from 'prop-types'; import Variable from './Variable'; + export default function Element(props) { const { attributes, children, element, disabled } = props; + switch (element.type) { case 'variable': return ; @@ -8,3 +11,12 @@ export default function Element(props) { return

    {children}

    ; } } + +Element.propTypes = { + attributes: PropTypes.object.isRequired, + children: PropTypes.node.isRequired, + element: PropTypes.shape({ + type: PropTypes.string, + }), + disabled: PropTypes.bool, +}; diff --git a/packages/web/src/components/Slate/Variable.jsx b/packages/web/src/components/Slate/Variable.jsx index 4aff7628..4e895838 100644 --- a/packages/web/src/components/Slate/Variable.jsx +++ b/packages/web/src/components/Slate/Variable.jsx @@ -1,6 +1,8 @@ +import PropTypes from 'prop-types'; import Chip from '@mui/material/Chip'; import { useSelected, useFocused } from 'slate-react'; -export default function Variable({ attributes, children, element, disabled }) { + +function Variable({ attributes, children, element, disabled }) { const selected = useSelected(); const focused = useFocused(); const label = ( @@ -24,3 +26,15 @@ export default function Variable({ attributes, children, element, disabled }) { /> ); } + +Variable.propTypes = { + attributes: PropTypes.object.isRequired, + children: PropTypes.node.isRequired, + element: PropTypes.shape({ + name: PropTypes.string.isRequired, + sampleValue: PropTypes.string.isRequired, + }), + disabled: PropTypes.bool, +}; + +export default Variable; diff --git a/packages/web/src/components/Slate/index.jsx b/packages/web/src/components/Slate/index.jsx index 93d3fdfd..86c238b9 100644 --- a/packages/web/src/components/Slate/index.jsx +++ b/packages/web/src/components/Slate/index.jsx @@ -1,2 +1,3 @@ import { Slate } from 'slate-react'; + export default Slate; diff --git a/packages/web/src/components/Slate/utils.js b/packages/web/src/components/Slate/utils.js index bf83c1c5..006d3efc 100644 --- a/packages/web/src/components/Slate/utils.js +++ b/packages/web/src/components/Slate/utils.js @@ -1,39 +1,45 @@ import { Text } from 'slate'; import { withHistory } from 'slate-history'; import { ReactEditor, withReact } from 'slate-react'; + function isCustomText(value) { const isText = Text.isText(value); const hasValueProperty = 'value' in value; if (isText && hasValueProperty) return true; return false; } + function getStepPosition(id, stepsWithVariables) { const stepIndex = stepsWithVariables.findIndex((stepWithVariables) => { return stepWithVariables.id === id; }); return stepIndex + 1; } + function getVariableName(variable) { return variable.replace(/{{|}}/g, ''); } + function getVariableStepId(variable) { const nameWithoutCurlies = getVariableName(variable); const stepId = nameWithoutCurlies.match(stepIdRegExp)?.[1] || ''; return stepId; } + function getVariableSampleValue(variable, stepsWithVariables) { const variableStepId = getVariableStepId(variable); const stepWithVariables = stepsWithVariables.find( - ({ id }) => id === variableStepId + ({ id }) => id === variableStepId, ); if (!stepWithVariables) return null; const variableName = getVariableName(variable); const variableData = stepWithVariables.output.find( - ({ value }) => variableName === value + ({ value }) => variableName === value, ); if (!variableData) return null; return variableData.sampleValue; } + function getVariableDetails(variable, stepsWithVariables) { const variableName = getVariableName(variable); const stepId = getVariableStepId(variableName); @@ -45,12 +51,15 @@ function getVariableDetails(variable, stepsWithVariables) { label, }; } + const variableRegExp = /({{.*?}})/; const stepIdRegExp = /^step.([\da-zA-Z-]*)/; + export const deserialize = (value, options, stepsWithVariables) => { const selectedNativeOption = options?.find( - (option) => value === option.value + (option) => value === option.value, ); + if (selectedNativeOption) { return [ { @@ -60,6 +69,7 @@ export const deserialize = (value, options, stepsWithVariables) => { }, ]; } + if (value === null || value === undefined || value === '') return [ { @@ -67,6 +77,7 @@ export const deserialize = (value, options, stepsWithVariables) => { children: [{ text: '' }], }, ]; + return value .toString() .split('\n') @@ -79,7 +90,7 @@ export const deserialize = (value, options, stepsWithVariables) => { if (node.match(variableRegExp)) { const variableDetails = getVariableDetails( node, - stepsWithVariables + stepsWithVariables, ); return { type: 'variable', @@ -101,6 +112,7 @@ export const deserialize = (value, options, stepsWithVariables) => { }; }); }; + export const serialize = (value) => { const serializedNodes = value.map((node) => serializeNode(node)); const hasSingleNode = value.length === 1; @@ -114,6 +126,7 @@ export const serialize = (value) => { const serializedValue = serializedNodes.join('\n'); return serializedValue; }; + const serializeNode = (node) => { if (isCustomText(node)) { return node.value; @@ -134,6 +147,7 @@ const serializeNode = (node) => { } return node.children.map((n) => serializeNode(n)).join(''); }; + export const withVariables = (editor) => { const { isInline, isVoid } = editor; editor.isInline = (element) => { @@ -144,10 +158,11 @@ export const withVariables = (editor) => { }; return editor; }; + export const insertVariable = (editor, variableData, stepsWithVariables) => { const variableDetails = getVariableDetails( `{{${variableData.value}}}`, - stepsWithVariables + stepsWithVariables, ); const variable = { type: 'variable', @@ -159,10 +174,12 @@ export const insertVariable = (editor, variableData, stepsWithVariables) => { editor.insertNodes(variable, { select: false }); focusEditor(editor); }; + export const focusEditor = (editor) => { ReactEditor.focus(editor); editor.move(); }; + export const resetEditor = (editor, options) => { const focus = options?.focus || false; editor.removeNodes({ @@ -177,6 +194,7 @@ export const resetEditor = (editor, options) => { focusEditor(editor); } }; + export const overrideEditorValue = (editor, options) => { const { option, focus } = options; const variable = { @@ -201,6 +219,7 @@ export const overrideEditorValue = (editor, options) => { } }); }; + export const createTextNode = (text) => ({ type: 'paragraph', children: [ @@ -209,6 +228,7 @@ export const createTextNode = (text) => ({ }, ], }); + export const customizeEditor = (editor) => { return withVariables(withReact(withHistory(editor))); }; diff --git a/packages/web/src/components/SnackbarProvider/index.jsx b/packages/web/src/components/SnackbarProvider/index.jsx index ee2a1007..219eeced 100644 --- a/packages/web/src/components/SnackbarProvider/index.jsx +++ b/packages/web/src/components/SnackbarProvider/index.jsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { SnackbarProvider as BaseSnackbarProvider } from 'notistack'; + const SnackbarProvider = (props) => { return ( { /> ); }; + export default SnackbarProvider; diff --git a/packages/web/src/components/Switch/index.jsx b/packages/web/src/components/Switch/index.jsx index 42645b89..f98689bd 100644 --- a/packages/web/src/components/Switch/index.jsx +++ b/packages/web/src/components/Switch/index.jsx @@ -1,8 +1,10 @@ +import PropTypes from 'prop-types'; import * as React from 'react'; import { Controller, useFormContext } from 'react-hook-form'; import FormControlLabel from '@mui/material/FormControlLabel'; import MuiSwitch from '@mui/material/Switch'; -export default function Switch(props) { + +function Switch(props) { const { control } = useFormContext(); const inputRef = React.useRef(null); const { @@ -18,6 +20,7 @@ export default function Switch(props) { className, ...switchProps } = props; + return ( ); } + +Switch.propTypes = { + required: PropTypes.bool, + name: PropTypes.string.isRequired, + defaultChecked: PropTypes.bool, + disabled: PropTypes.bool, + label: PropTypes.string.isRequired, + shouldUnregister: PropTypes.bool, + onBlur: PropTypes.func, + onChange: PropTypes.func, + FormControlLabelProps: PropTypes.object, + className: PropTypes.string, +}; + +export default Switch; diff --git a/packages/web/src/components/TabPanel/index.jsx b/packages/web/src/components/TabPanel/index.jsx index 5ad8db72..526a0860 100644 --- a/packages/web/src/components/TabPanel/index.jsx +++ b/packages/web/src/components/TabPanel/index.jsx @@ -1,4 +1,6 @@ import * as React from 'react'; +import PropTypes from 'prop-types'; + export default function TabPanel(props) { const { children, value, index, ...other } = props; return ( @@ -7,3 +9,9 @@ export default function TabPanel(props) { ); } + +TabPanel.propTypes = { + children: PropTypes.element.isRequired, + value: PropTypes.number.isRequired, + index: PropTypes.number.isRequired, +}; diff --git a/packages/web/src/components/TestSubstep/index.jsx b/packages/web/src/components/TestSubstep/index.jsx index 92ee99ad..2d9f9e4d 100644 --- a/packages/web/src/components/TestSubstep/index.jsx +++ b/packages/web/src/components/TestSubstep/index.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import * as React from 'react'; import { useMutation } from '@apollo/client'; import Box from '@mui/material/Box'; @@ -14,6 +15,7 @@ import JSONViewer from 'components/JSONViewer'; import WebhookUrlInfo from 'components/WebhookUrlInfo'; import FlowSubstepTitle from 'components/FlowSubstepTitle'; import { useQueryClient } from '@tanstack/react-query'; +import { StepPropType, SubstepPropType } from 'propTypes/propTypes'; function serializeErrors(graphQLErrors) { return graphQLErrors?.map((error) => { @@ -154,4 +156,18 @@ function TestSubstep(props) { ); } + +TestSubstep.propTypes = { + substep: SubstepPropType.isRequired, + expanded: PropTypes.bool, + showWebhookUrl: PropTypes.bool, + onExpand: PropTypes.func.isRequired, + onCollapse: PropTypes.func.isRequired, + onChange: PropTypes.func, + onSubmit: PropTypes.func, + onContinue: PropTypes.func, + step: StepPropType.isRequired, + flowId: PropTypes.string.isRequired, +}; + export default TestSubstep; diff --git a/packages/web/src/components/TextField/index.jsx b/packages/web/src/components/TextField/index.jsx index cd62889a..20ec3aeb 100644 --- a/packages/web/src/components/TextField/index.jsx +++ b/packages/web/src/components/TextField/index.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import * as React from 'react'; import { Controller, useFormContext } from 'react-hook-form'; import MuiTextField from '@mui/material/TextField'; @@ -5,6 +6,7 @@ import IconButton from '@mui/material/IconButton'; import InputAdornment from '@mui/material/InputAdornment'; import ContentCopyIcon from '@mui/icons-material/ContentCopy'; import copyInputValue from 'helpers/copyInputValue'; + const createCopyAdornment = (ref) => { return ( @@ -14,7 +16,8 @@ const createCopyAdornment = (ref) => { ); }; -export default function TextField(props) { + +function TextField(props) { const { control } = useFormContext(); const inputRef = React.useRef(null); const { @@ -74,3 +77,18 @@ export default function TextField(props) { /> ); } + +TextField.propTypes = { + required: PropTypes.bool, + defaultValue: PropTypes.string, + shouldUnregister: PropTypes.bool, + name: PropTypes.string.isRequired, + clickToCopy: PropTypes.bool, + readOnly: PropTypes.bool, + 'data-test': PropTypes.string, + disabled: PropTypes.bool, + onBlur: PropTypes.func, + onChange: PropTypes.func, +}; + +export default TextField; diff --git a/packages/web/src/components/ThemeProvider/index.jsx b/packages/web/src/components/ThemeProvider/index.jsx index ebdb882a..cb9fca12 100644 --- a/packages/web/src/components/ThemeProvider/index.jsx +++ b/packages/web/src/components/ThemeProvider/index.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import CssBaseline from '@mui/material/CssBaseline'; import { ThemeProvider as BaseThemeProvider } from '@mui/material/styles'; import clone from 'lodash/clone'; @@ -24,6 +25,7 @@ const customizeTheme = (theme, config) => { return shallowDefaultTheme; }; + const ThemeProvider = ({ children, ...props }) => { const { data: automatischInfo, isPending: isAutomatischInfoPending } = useAutomatischInfo(); @@ -53,4 +55,8 @@ const ThemeProvider = ({ children, ...props }) => { ); }; +ThemeProvider.propTypes = { + children: PropTypes.node.isRequired, +}; + export default ThemeProvider; diff --git a/packages/web/src/components/TrialStatusBadge/style.ee.jsx b/packages/web/src/components/TrialStatusBadge/style.ee.jsx index 98f82b2b..d1dfa0a7 100644 --- a/packages/web/src/components/TrialStatusBadge/style.ee.jsx +++ b/packages/web/src/components/TrialStatusBadge/style.ee.jsx @@ -1,5 +1,6 @@ import { styled } from '@mui/material/styles'; import MuiChip, { chipClasses } from '@mui/material/Chip'; + export const Chip = styled(MuiChip)` &.${chipClasses.root} { font-weight: 500; diff --git a/packages/web/src/components/UsageDataInformation/index.ee.jsx b/packages/web/src/components/UsageDataInformation/index.ee.jsx index 5d0cf661..300c1482 100644 --- a/packages/web/src/components/UsageDataInformation/index.ee.jsx +++ b/packages/web/src/components/UsageDataInformation/index.ee.jsx @@ -10,6 +10,7 @@ import CardContent from '@mui/material/CardContent'; import Divider from '@mui/material/Divider'; import Grid from '@mui/material/Grid'; import Typography from '@mui/material/Typography'; +import PropTypes from 'prop-types'; import TrialOverAlert from 'components/TrialOverAlert/index.ee'; import SubscriptionCancelledAlert from 'components/SubscriptionCancelledAlert/index.ee'; @@ -54,6 +55,13 @@ function BillingCard(props) { ); } +BillingCard.propTypes = { + name: PropTypes.string.isRequired, + title: PropTypes.string, + action: PropTypes.string, + text: PropTypes.string, +}; + function Action(props) { const { action, text } = props; @@ -80,6 +88,11 @@ function Action(props) { ); } +Action.propTypes = { + action: PropTypes.string, + text: PropTypes.string, +}; + export default function UsageDataInformation() { const formatMessage = useFormatMessage(); const queryClient = useQueryClient(); diff --git a/packages/web/src/components/UserList/TablePaginationActions/index.jsx b/packages/web/src/components/UserList/TablePaginationActions/index.jsx index c611d005..96930182 100644 --- a/packages/web/src/components/UserList/TablePaginationActions/index.jsx +++ b/packages/web/src/components/UserList/TablePaginationActions/index.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import { useTheme } from '@mui/material'; import IconButton from '@mui/material/IconButton'; import FirstPageIcon from '@mui/icons-material/FirstPage'; @@ -5,21 +6,27 @@ import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft'; import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight'; import LastPageIcon from '@mui/icons-material/LastPage'; import Box from '@mui/material/Box'; -export default function TablePaginationActions(props) { + +function TablePaginationActions(props) { const theme = useTheme(); const { count, page, rowsPerPage, onPageChange } = props; + const handleFirstPageButtonClick = (event) => { onPageChange(event, 0); }; + const handleBackButtonClick = (event) => { onPageChange(event, page - 1); }; + const handleNextButtonClick = (event) => { onPageChange(event, page + 1); }; + const handleLastPageButtonClick = (event) => { onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)); }; + return ( ); } + +TablePaginationActions.propTypes = { + count: PropTypes.number.isRequired, + page: PropTypes.number.isRequired, + rowsPerPage: PropTypes.number.isRequired, + onPageChange: PropTypes.func.isRequired, +}; + +export default TablePaginationActions; diff --git a/packages/web/src/components/WebhookUrlInfo/index.jsx b/packages/web/src/components/WebhookUrlInfo/index.jsx index 6cb98b5d..7740c203 100644 --- a/packages/web/src/components/WebhookUrlInfo/index.jsx +++ b/packages/web/src/components/WebhookUrlInfo/index.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import Typography from '@mui/material/Typography'; @@ -5,6 +6,7 @@ import { generateExternalLink } from '../../helpers/translationValues'; import { WEBHOOK_DOCS } from '../../config/urls'; import TextField from '../TextField'; import { Alert } from './style'; + function WebhookUrlInfo(props) { const { webhookUrl, ...alertProps } = props; return ( @@ -33,4 +35,8 @@ function WebhookUrlInfo(props) { ); } + +WebhookUrlInfo.propTypes = { + webhookUrl: PropTypes.string.isRequired, +}; export default WebhookUrlInfo; diff --git a/packages/web/src/contexts/Authentication.jsx b/packages/web/src/contexts/Authentication.jsx index f4bd4a23..611de0b4 100644 --- a/packages/web/src/contexts/Authentication.jsx +++ b/packages/web/src/contexts/Authentication.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import * as React from 'react'; import { getItem, removeItem, setItem } from 'helpers/storage'; import api from 'helpers/api.js'; @@ -36,3 +37,7 @@ export const AuthenticationProvider = (props) => { ); }; + +AuthenticationProvider.propTypes = { + children: PropTypes.node.isRequired, +}; diff --git a/packages/web/src/contexts/Editor.jsx b/packages/web/src/contexts/Editor.jsx index edc600cb..bb554fb8 100644 --- a/packages/web/src/contexts/Editor.jsx +++ b/packages/web/src/contexts/Editor.jsx @@ -1,10 +1,18 @@ import * as React from 'react'; +import PropTypes from 'prop-types'; + export const EditorContext = React.createContext({ readOnly: false, }); + export const EditorProvider = (props) => { const { children, value } = props; return ( {children} ); }; + +EditorProvider.propTypes = { + children: PropTypes.node.isRequired, + value: PropTypes.shape({ readOnly: PropTypes.bool.isRequired }).isRequired, +}; diff --git a/packages/web/src/contexts/Paddle.ee.jsx b/packages/web/src/contexts/Paddle.ee.jsx index b37b5d13..b4cad62e 100644 --- a/packages/web/src/contexts/Paddle.ee.jsx +++ b/packages/web/src/contexts/Paddle.ee.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import * as React from 'react'; import { useNavigate } from 'react-router-dom'; @@ -100,3 +101,7 @@ export const PaddleProvider = (props) => { {children} ); }; + +PaddleProvider.propTypes = { + children: PropTypes.node.isRequired, +}; diff --git a/packages/web/src/contexts/StepExecutions.jsx b/packages/web/src/contexts/StepExecutions.jsx index d8ae1fc2..2664c0ea 100644 --- a/packages/web/src/contexts/StepExecutions.jsx +++ b/packages/web/src/contexts/StepExecutions.jsx @@ -1,5 +1,9 @@ import * as React from 'react'; +import PropTypes from 'prop-types'; +import { StepPropType } from 'propTypes/propTypes'; + export const StepExecutionsContext = React.createContext([]); + export const StepExecutionsProvider = (props) => { const { children, value } = props; return ( @@ -8,3 +12,8 @@ export const StepExecutionsProvider = (props) => { ); }; + +StepExecutionsProvider.propTypes = { + children: PropTypes.node.isRequired, + value: PropTypes.arrayOf(StepPropType), +}; diff --git a/packages/web/src/pages/Application/index.jsx b/packages/web/src/pages/Application/index.jsx index e8c4ec5e..9c6818f9 100644 --- a/packages/web/src/pages/Application/index.jsx +++ b/packages/web/src/pages/Application/index.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import * as React from 'react'; import { Link, @@ -31,6 +32,7 @@ import Container from 'components/Container'; import PageTitle from 'components/PageTitle'; import useApp from 'hooks/useApp'; import Can from 'components/Can'; +import { AppPropType } from 'propTypes/propTypes'; const ReconnectConnection = (props) => { const { application, onClose } = props; @@ -45,6 +47,11 @@ const ReconnectConnection = (props) => { ); }; +ReconnectConnection.propTypes = { + application: AppPropType.isRequired, + onClose: PropTypes.func.isRequired, +}; + export default function Application() { const theme = useTheme(); const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md')); diff --git a/packages/web/src/pages/Authentication/RoleMappings.jsx b/packages/web/src/pages/Authentication/RoleMappings.jsx index 78ee244e..a2605eef 100644 --- a/packages/web/src/pages/Authentication/RoleMappings.jsx +++ b/packages/web/src/pages/Authentication/RoleMappings.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import { useMutation } from '@apollo/client'; import LoadingButton from '@mui/lab/LoadingButton'; import Divider from '@mui/material/Divider'; @@ -105,4 +106,12 @@ function RoleMappings({ provider, providerLoading }) { ); } + +RoleMappings.propTypes = { + provider: PropTypes.shape({ + id: PropTypes.oneOf([PropTypes.number, PropTypes.string]).isRequired, + }), + providerLoading: PropTypes.bool, +}; + export default RoleMappings; diff --git a/packages/web/src/pages/Authentication/SamlConfiguration.jsx b/packages/web/src/pages/Authentication/SamlConfiguration.jsx index 30bef0a0..6a99b6bb 100644 --- a/packages/web/src/pages/Authentication/SamlConfiguration.jsx +++ b/packages/web/src/pages/Authentication/SamlConfiguration.jsx @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types'; import { useMutation } from '@apollo/client'; import LoadingButton from '@mui/lab/LoadingButton'; import Stack from '@mui/material/Stack'; @@ -192,4 +193,22 @@ function SamlConfiguration({ provider, providerLoading }) { ); } + +SamlConfiguration.propTypes = { + provider: PropTypes.shape({ + active: PropTypes.bool, + name: PropTypes.string, + certificate: PropTypes.string, + signatureAlgorithm: PropTypes.oneOf(['sha1', 'sha256', 'sha512']), + issuer: PropTypes.string, + entryPoint: PropTypes.string, + firstnameAttributeName: PropTypes.string, + surnameAttributeName: PropTypes.string, + emailAttributeName: PropTypes.string, + roleAttributeName: PropTypes.string, + defaultRoleId: PropTypes.string, + }), + providerLoading: PropTypes.bool, +}; + export default SamlConfiguration;