feat: add missing propTypes

This commit is contained in:
kasia.oczkowska
2024-06-05 13:26:56 +01:00
parent 725b38c697
commit 3f5df118a0
53 changed files with 597 additions and 81 deletions

View File

@@ -68,7 +68,10 @@ function AccountDropdownMenu(props) {
AccountDropdownMenu.propTypes = { AccountDropdownMenu.propTypes = {
open: PropTypes.bool.isRequired, open: PropTypes.bool.isRequired,
onClose: PropTypes.func.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, id: PropTypes.string.isRequired,
}; };

View File

@@ -76,7 +76,10 @@ const CustomOptions = (props) => {
CustomOptions.propTypes = { CustomOptions.propTypes = {
open: PropTypes.bool.isRequired, 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( data: PropTypes.arrayOf(
PropTypes.shape({ PropTypes.shape({
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,

View File

@@ -18,10 +18,8 @@ const computeListHeight = (currentLength) => {
return LIST_ITEM_HEIGHT * numberOfRenderedItems; return LIST_ITEM_HEIGHT * numberOfRenderedItems;
}; };
const renderItemFactory = const Item = (props) => {
({ onOptionClick }) => const { index, style, data, onOptionClick } = props;
(props) => {
const { index, style, data } = props;
const suboption = data[index]; const suboption = data[index];
return ( return (
<ListItemButton <ListItemButton
@@ -50,6 +48,19 @@ const renderItemFactory =
); );
}; };
Item.propTypes = {
index: PropTypes.number.isRequired,
style: PropTypes.object,
data: PropTypes.array.isRequired,
onOptionClick: PropTypes.func.isRequired,
};
const renderItemFactory =
({ onOptionClick }) =>
(props) => {
<Item onOptionClick={onOptionClick} {...props} />;
};
const Options = (props) => { const Options = (props) => {
const formatMessage = useFormatMessage(); const formatMessage = useFormatMessage();
const { data, onOptionClick } = props; const { data, onOptionClick } = props;

View File

@@ -364,6 +364,7 @@ FlowStep.propTypes = {
onClose: PropTypes.func, onClose: PropTypes.func,
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
onContinue: PropTypes.func, onContinue: PropTypes.func,
flowId: PropTypes.string.isRequired,
}; };
export default FlowStep; export default FlowStep;

View File

@@ -43,8 +43,12 @@ function FlowStepContextMenu(props) {
FlowStepContextMenu.propTypes = { FlowStepContextMenu.propTypes = {
stepId: PropTypes.string.isRequired, stepId: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired, onClose: PropTypes.func.isRequired,
anchorEl: PropTypes.element.isRequired, anchorEl: PropTypes.oneOfType([
PropTypes.func,
PropTypes.shape({ current: PropTypes.instanceOf(Element) }),
]),
deletable: PropTypes.bool.isRequired, deletable: PropTypes.bool.isRequired,
flowId: PropTypes.string.isRequired,
}; };
export default FlowStepContextMenu; export default FlowStepContextMenu;

View File

@@ -1,7 +1,10 @@
import * as React from 'react'; import * as React from 'react';
import { FormProvider, useForm, useWatch } from 'react-hook-form'; import { FormProvider, useForm, useWatch } from 'react-hook-form';
import PropTypes from 'prop-types';
const noop = () => null; const noop = () => null;
export default function Form(props) {
function Form(props) {
const { const {
children, children,
onSubmit = noop, onSubmit = noop,
@@ -11,22 +14,27 @@ export default function Form(props) {
mode = 'all', mode = 'all',
...formProps ...formProps
} = props; } = props;
const methods = useForm({ const methods = useForm({
defaultValues, defaultValues,
reValidateMode: 'onBlur', reValidateMode: 'onBlur',
resolver, resolver,
mode, mode,
}); });
const form = useWatch({ control: methods.control }); const form = useWatch({ control: methods.control });
/** /**
* For fields having `dependsOn` fields, we need to re-validate the form. * For fields having `dependsOn` fields, we need to re-validate the form.
*/ */
React.useEffect(() => { React.useEffect(() => {
methods.trigger(); methods.trigger();
}, [methods.trigger, form]); }, [methods.trigger, form]);
React.useEffect(() => { React.useEffect(() => {
methods.reset(defaultValues); methods.reset(defaultValues);
}, [defaultValues]); }, [defaultValues]);
return ( return (
<FormProvider {...methods}> <FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)} {...formProps}> <form onSubmit={methods.handleSubmit(onSubmit)} {...formProps}>
@@ -35,3 +43,14 @@ export default function Form(props) {
</FormProvider> </FormProvider>
); );
} }
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;

View File

@@ -1,7 +1,9 @@
import * as React from 'react'; import * as React from 'react';
import Slide from '@mui/material/Slide'; import Slide from '@mui/material/Slide';
import useScrollTrigger from '@mui/material/useScrollTrigger'; import useScrollTrigger from '@mui/material/useScrollTrigger';
export default function HideOnScroll(props) { export default function HideOnScroll(props) {
const trigger = useScrollTrigger(); const trigger = useScrollTrigger();
return <Slide appear={false} direction="down" in={!trigger} {...props} />; return <Slide appear={false} direction="down" in={!trigger} {...props} />;
} }

View File

@@ -1,6 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import MuiTextField from '@mui/material/TextField'; import MuiTextField from '@mui/material/TextField';
import CircularProgress from '@mui/material/CircularProgress'; import CircularProgress from '@mui/material/CircularProgress';
import PropTypes from 'prop-types';
import useDynamicFields from 'hooks/useDynamicFields'; import useDynamicFields from 'hooks/useDynamicFields';
import useDynamicData from 'hooks/useDynamicData'; import useDynamicData from 'hooks/useDynamicData';
@@ -9,11 +10,11 @@ import TextField from 'components/TextField';
import ControlledAutocomplete from 'components/ControlledAutocomplete'; import ControlledAutocomplete from 'components/ControlledAutocomplete';
import ControlledCustomAutocomplete from 'components/ControlledCustomAutocomplete'; import ControlledCustomAutocomplete from 'components/ControlledCustomAutocomplete';
import DynamicField from 'components/DynamicField'; import DynamicField from 'components/DynamicField';
import { FieldPropType } from 'propTypes/propTypes';
const optionGenerator = (options) => const optionGenerator = (options) =>
options?.map(({ name, value }) => ({ label: name, value: value })); options?.map(({ name, value }) => ({ label: name, value: value }));
function InputCreator(props) {
export default function InputCreator(props) {
const { const {
onChange, onChange,
onBlur, onBlur,
@@ -131,7 +132,8 @@ export default function InputCreator(props) {
<React.Fragment> <React.Fragment>
<PowerInput <PowerInput
key={computedName} key={computedName}
label={label} // label={label}
label="PowerInput"
description={description} description={description}
name={computedName} name={computedName}
required={required} required={required}
@@ -204,3 +206,16 @@ export default function InputCreator(props) {
} }
return <React.Fragment />; return <React.Fragment />;
} }
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;

View File

@@ -1,8 +1,12 @@
import * as React from 'react'; import * as React from 'react';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import PropTypes from 'prop-types';
import { Container } from './style'; import { Container } from './style';
export default function IntermediateStepCount(props) {
function IntermediateStepCount(props) {
const { count } = props; const { count } = props;
return ( return (
<Container> <Container>
<Typography variant="subtitle1" sx={{}}> <Typography variant="subtitle1" sx={{}}>
@@ -11,3 +15,9 @@ export default function IntermediateStepCount(props) {
</Container> </Container>
); );
} }
IntermediateStepCount.propTypes = {
count: PropTypes.number.isRequired,
};
export default IntermediateStepCount;

View File

@@ -1,5 +1,8 @@
import { IntlProvider as BaseIntlProvider } from 'react-intl'; import { IntlProvider as BaseIntlProvider } from 'react-intl';
import PropTypes from 'prop-types';
import englishMessages from 'locales/en.json'; import englishMessages from 'locales/en.json';
const IntlProvider = ({ children }) => { const IntlProvider = ({ children }) => {
return ( return (
<BaseIntlProvider <BaseIntlProvider
@@ -11,4 +14,9 @@ const IntlProvider = ({ children }) => {
</BaseIntlProvider> </BaseIntlProvider>
); );
}; };
IntlProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default IntlProvider; export default IntlProvider;

View File

@@ -1,5 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import { JSONTree } from 'react-json-tree'; import { JSONTree } from 'react-json-tree';
import PropTypes from 'prop-types';
const theme = { const theme = {
scheme: 'inspector', scheme: 'inspector',
author: 'Alexander Kuznetsov (alexkuz@gmail.com)', author: 'Alexander Kuznetsov (alexkuz@gmail.com)',
@@ -36,8 +38,10 @@ const theme = {
// base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. <?php ?> // base0F - Deprecated, Opening/Closing Embedded Language Tags, e.g. <?php ?>
base0F: '#a16946', base0F: '#a16946',
}; };
function JSONViewer(props) { function JSONViewer(props) {
const { data } = props; const { data } = props;
return ( return (
<JSONTree <JSONTree
hideRoot hideRoot
@@ -48,4 +52,9 @@ function JSONViewer(props) {
/> />
); );
} }
JSONViewer.propTypes = {
data: PropTypes.object.isRequired,
};
export default JSONViewer; export default JSONViewer;

View File

@@ -9,6 +9,7 @@ import SwapCallsIcon from '@mui/icons-material/SwapCalls';
import HistoryIcon from '@mui/icons-material/History'; import HistoryIcon from '@mui/icons-material/History';
import NotificationsIcon from '@mui/icons-material/Notifications'; import NotificationsIcon from '@mui/icons-material/Notifications';
import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew'; import ArrowBackIosNew from '@mui/icons-material/ArrowBackIosNew';
import PropTypes from 'prop-types';
import * as URLS from 'config/urls'; import * as URLS from 'config/urls';
import useFormatMessage from 'hooks/useFormatMessage'; import useFormatMessage from 'hooks/useFormatMessage';
@@ -75,7 +76,7 @@ const generateDrawerBottomLinks = async ({
return links; return links;
}; };
export default function PublicLayout({ children }) { function PublicLayout({ children }) {
const version = useVersion(); const version = useVersion();
const { data: configData, isLoading } = useAutomatischConfig(); const { data: configData, isLoading } = useAutomatischConfig();
const config = configData?.data; const config = configData?.data;
@@ -135,3 +136,9 @@ export default function PublicLayout({ children }) {
</> </>
); );
} }
PublicLayout.propTypes = {
children: PropTypes.node.isRequired,
};
export default PublicLayout;

View File

@@ -4,9 +4,12 @@ import ListItem from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon'; import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText'; import ListItemText from '@mui/material/ListItemText';
import { Link } from 'react-router-dom'; 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 { icon, primary, to, onClick, 'data-test': dataTest, target } = props;
const selected = useMatch({ path: to, end: true }); const selected = useMatch({ path: to, end: true });
const CustomLink = React.useMemo( const CustomLink = React.useMemo(
() => () =>
React.forwardRef(function InLineLink(linkProps, ref) { React.forwardRef(function InLineLink(linkProps, ref) {
@@ -28,6 +31,7 @@ export default function ListItemLink(props) {
}), }),
[to], [to],
); );
return ( return (
<li> <li>
<ListItem <ListItem
@@ -47,3 +51,14 @@ export default function ListItemLink(props) {
</li> </li>
); );
} }
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;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import { import {
IconButton, IconButton,
Skeleton, Skeleton,
@@ -7,6 +8,7 @@ import {
} from '@mui/material'; } from '@mui/material';
import EditIcon from '@mui/icons-material/Edit'; import EditIcon from '@mui/icons-material/Edit';
import DeleteIcon from '@mui/icons-material/Delete'; import DeleteIcon from '@mui/icons-material/Delete';
const ListLoader = ({ rowsNumber, columnsNumber, 'data-test': dataTest }) => { const ListLoader = ({ rowsNumber, columnsNumber, 'data-test': dataTest }) => {
return ( 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; export default ListLoader;

View File

@@ -1,6 +1,8 @@
import * as React from 'react'; import * as React from 'react';
import { ReactComponent as MationLogoSvg } from './assets/mation-logo.svg'; import { ReactComponent as MationLogoSvg } from './assets/mation-logo.svg';
const MationLogo = () => { const MationLogo = () => {
return <MationLogoSvg />; return <MationLogoSvg />;
}; };
export default MationLogo; export default MationLogo;

View File

@@ -1,4 +1,5 @@
import * as React from 'react'; import * as React from 'react';
import PropTypes from 'prop-types';
import useAutomatischConfig from 'hooks/useAutomatischConfig'; import useAutomatischConfig from 'hooks/useAutomatischConfig';
@@ -27,10 +28,13 @@ const MetadataProvider = ({ children }) => {
newFaviconElement.href = '/browser-tab.ico'; newFaviconElement.href = '/browser-tab.ico';
} }
} }
}, [config?.disableFavicon]); }, [config?.disableFavicon]);
return <>{children}</>; return <>{children}</>;
}; };
MetadataProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default MetadataProvider; export default MetadataProvider;

View File

@@ -4,9 +4,11 @@ import Card from '@mui/material/Card';
import AddCircleIcon from '@mui/icons-material/AddCircle'; import AddCircleIcon from '@mui/icons-material/AddCircle';
import CardActionArea from '@mui/material/CardActionArea'; import CardActionArea from '@mui/material/CardActionArea';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import PropTypes from 'prop-types';
import { CardContent } from './style'; import { CardContent } from './style';
export default function NoResultFound(props) { function NoResultFound(props) {
const { text, to } = props; const { text, to } = props;
const ActionAreaLink = React.useMemo( const ActionAreaLink = React.useMemo(
@@ -29,3 +31,10 @@ export default function NoResultFound(props) {
</Card> </Card>
); );
} }
NoResultFound.propTypes = {
text: PropTypes.string,
to: PropTypes.string,
};
export default NoResultFound;

View File

@@ -5,16 +5,21 @@ import CardActionArea from '@mui/material/CardActionArea';
import CardContent from '@mui/material/CardContent'; import CardContent from '@mui/material/CardContent';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
import PropTypes from 'prop-types';
import useFormatMessage from 'hooks/useFormatMessage'; import useFormatMessage from 'hooks/useFormatMessage';
const getHumanlyDate = (timestamp) => const getHumanlyDate = (timestamp) =>
DateTime.fromMillis(timestamp).toRelative(); DateTime.fromMillis(timestamp).toRelative();
export default function NotificationCard(props) {
function NotificationCard(props) {
const { name, createdAt, documentationUrl, description } = props; const { name, createdAt, documentationUrl, description } = props;
const formatMessage = useFormatMessage(); const formatMessage = useFormatMessage();
const relativeCreatedAt = getHumanlyDate(new Date(createdAt).getTime()); const relativeCreatedAt = getHumanlyDate(new Date(createdAt).getTime());
const subheader = formatMessage('notification.releasedAt', { const subheader = formatMessage('notification.releasedAt', {
relativeDate: relativeCreatedAt, relativeDate: relativeCreatedAt,
}); });
return ( return (
<Card> <Card>
<CardActionArea component={'a'} href={documentationUrl} target="_blank"> <CardActionArea component={'a'} href={documentationUrl} target="_blank">
@@ -36,3 +41,12 @@ export default function NotificationCard(props) {
</Card> </Card>
); );
} }
NotificationCard.propTypes = {
name: PropTypes.string.isRequired,
createdAt: PropTypes.string.isRequired,
documentationUrl: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
};
export default NotificationCard;

View File

@@ -1,5 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
export default function PageTitle(props) { export default function PageTitle(props) {
return <Typography variant="h3" data-test="page-title" {...props} />; return <Typography variant="h3" data-test="page-title" {...props} />;
} }

View File

@@ -11,7 +11,9 @@ import {
Typography, Typography,
} from '@mui/material'; } from '@mui/material';
import SettingsIcon from '@mui/icons-material/Settings'; import SettingsIcon from '@mui/icons-material/Settings';
import ControlledCheckbox from 'components/ControlledCheckbox'; import ControlledCheckbox from 'components/ControlledCheckbox';
const PermissionCatalogFieldLoader = () => { const PermissionCatalogFieldLoader = () => {
return ( return (
<TableContainer> <TableContainer>
@@ -56,4 +58,5 @@ const PermissionCatalogFieldLoader = () => {
</TableContainer> </TableContainer>
); );
}; };
export default PermissionCatalogFieldLoader; export default PermissionCatalogFieldLoader;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog'; import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions'; import DialogActions from '@mui/material/DialogActions';
@@ -15,7 +16,8 @@ import * as React from 'react';
import { useFormContext } from 'react-hook-form'; import { useFormContext } from 'react-hook-form';
import ControlledCheckbox from 'components/ControlledCheckbox'; import ControlledCheckbox from 'components/ControlledCheckbox';
import useFormatMessage from 'hooks/useFormatMessage'; import useFormatMessage from 'hooks/useFormatMessage';
export default function PermissionSettings(props) {
function PermissionSettings(props) {
const { const {
onClose, onClose,
open = false, open = false,
@@ -27,6 +29,7 @@ export default function PermissionSettings(props) {
} = props; } = props;
const formatMessage = useFormatMessage(); const formatMessage = useFormatMessage();
const { getValues, resetField } = useFormContext(); const { getValues, resetField } = useFormContext();
const cancel = () => { const cancel = () => {
for (const action of actions) { for (const action of actions) {
for (const condition of conditions) { for (const condition of conditions) {
@@ -36,6 +39,7 @@ export default function PermissionSettings(props) {
} }
onClose(); onClose();
}; };
const apply = () => { const apply = () => {
for (const action of actions) { for (const action of actions) {
for (const condition of conditions) { for (const condition of conditions) {
@@ -46,6 +50,7 @@ export default function PermissionSettings(props) {
} }
onClose(); onClose();
}; };
return ( return (
<Dialog <Dialog
open={open} open={open}
@@ -133,3 +138,26 @@ export default function PermissionSettings(props) {
</Dialog> </Dialog>
); );
} }
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;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import SettingsIcon from '@mui/icons-material/Settings'; import SettingsIcon from '@mui/icons-material/Settings';
import IconButton from '@mui/material/IconButton'; import IconButton from '@mui/material/IconButton';
import Paper from '@mui/material/Paper'; import Paper from '@mui/material/Paper';
@@ -110,4 +111,10 @@ const PermissionCatalogField = ({
</TableContainer> </TableContainer>
); );
}; };
PermissionCatalogField.propTypes = {
name: PropTypes.string,
disabled: PropTypes.bool,
defaultChecked: PropTypes.bool,
};
export default PermissionCatalogField; export default PermissionCatalogField;

View File

@@ -1,7 +1,9 @@
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
const Portal = ({ children }) => { const Portal = ({ children }) => {
return typeof document === 'object' return typeof document === 'object'
? ReactDOM.createPortal(children, document.body) ? ReactDOM.createPortal(children, document.body)
: null; : null;
}; };
export default Portal; export default Portal;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import Paper from '@mui/material/Paper'; import Paper from '@mui/material/Paper';
import MuiPopper from '@mui/material/Popper'; import MuiPopper from '@mui/material/Popper';
import Tab from '@mui/material/Tab'; import Tab from '@mui/material/Tab';
@@ -5,8 +6,10 @@ import * as React from 'react';
import Suggestions from 'components/PowerInput/Suggestions'; import Suggestions from 'components/PowerInput/Suggestions';
import TabPanel from 'components/TabPanel'; import TabPanel from 'components/TabPanel';
import { Tabs } from './style'; import { Tabs } from './style';
const Popper = (props) => { const Popper = (props) => {
const { open, anchorEl, data, onSuggestionClick } = props; const { open, anchorEl, data, onSuggestionClick } = props;
return ( return (
<MuiPopper <MuiPopper
open={open} open={open}
@@ -34,4 +37,15 @@ const Popper = (props) => {
</MuiPopper> </MuiPopper>
); );
}; };
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; export default Popper;

View File

@@ -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 (
<ListItemButton
sx={{ pl: 4 }}
divider
onClick={() => onSuggestionClick(suboption)}
data-test="power-input-suggestion-item"
key={index}
style={style}
>
<ListItemText
primary={suboption.label}
primaryTypographyProps={{
variant: 'subtitle1',
title: 'Property name',
sx: { fontWeight: 700 },
}}
secondary={suboption.sampleValue || ''}
secondaryTypographyProps={{
variant: 'subtitle2',
title: 'Sample value',
noWrap: true,
}}
/>
</ListItemButton>
);
};
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;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import ExpandLess from '@mui/icons-material/ExpandLess'; import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore'; import ExpandMore from '@mui/icons-material/ExpandMore';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
@@ -13,52 +14,33 @@ import * as React from 'react';
import { FixedSizeList } from 'react-window'; import { FixedSizeList } from 'react-window';
import SearchInput from 'components/SearchInput'; import SearchInput from 'components/SearchInput';
import useFormatMessage from 'hooks/useFormatMessage'; import useFormatMessage from 'hooks/useFormatMessage';
import SuggestionItem from './SuggestionItem';
const SHORT_LIST_LENGTH = 4; const SHORT_LIST_LENGTH = 4;
const LIST_ITEM_HEIGHT = 64; const LIST_ITEM_HEIGHT = 64;
const computeListHeight = (currentLength) => { const computeListHeight = (currentLength) => {
const numberOfRenderedItems = Math.min(SHORT_LIST_LENGTH, currentLength); const numberOfRenderedItems = Math.min(SHORT_LIST_LENGTH, currentLength);
return LIST_ITEM_HEIGHT * numberOfRenderedItems; return LIST_ITEM_HEIGHT * numberOfRenderedItems;
}; };
const getPartialArray = (array, length = array.length) => { const getPartialArray = (array, length = array.length) => {
return array.slice(0, length); return array.slice(0, length);
}; };
const renderItemFactory = const renderItemFactory =
({ onSuggestionClick }) => ({ onSuggestionClick }) =>
(props) => { (props) => (
const { index, style, data } = props; <SuggestionItem {...props} onSuggestionClick={onSuggestionClick} />
const suboption = data[index];
return (
<ListItemButton
sx={{ pl: 4 }}
divider
onClick={() => onSuggestionClick(suboption)}
data-test="power-input-suggestion-item"
key={index}
style={style}
>
<ListItemText
primary={suboption.label}
primaryTypographyProps={{
variant: 'subtitle1',
title: 'Property name',
sx: { fontWeight: 700 },
}}
secondary={suboption.sampleValue || ''}
secondaryTypographyProps={{
variant: 'subtitle2',
title: 'Sample value',
noWrap: true,
}}
/>
</ListItemButton>
); );
};
const Suggestions = (props) => { const Suggestions = (props) => {
const formatMessage = useFormatMessage(); const formatMessage = useFormatMessage();
const { data, onSuggestionClick = () => null } = props; const { data, onSuggestionClick = () => null } = props;
const [current, setCurrent] = React.useState(0); const [current, setCurrent] = React.useState(0);
const [listLength, setListLength] = React.useState(SHORT_LIST_LENGTH); const [listLength, setListLength] = React.useState(SHORT_LIST_LENGTH);
const [filteredData, setFilteredData] = React.useState(data); const [filteredData, setFilteredData] = React.useState(data);
React.useEffect( React.useEffect(
function syncOptions() { function syncOptions() {
setFilteredData((filteredData) => { setFilteredData((filteredData) => {
@@ -70,6 +52,7 @@ const Suggestions = (props) => {
}, },
[data], [data],
); );
const renderItem = React.useMemo( const renderItem = React.useMemo(
() => () =>
renderItemFactory({ renderItemFactory({
@@ -77,15 +60,19 @@ const Suggestions = (props) => {
}), }),
[onSuggestionClick], [onSuggestionClick],
); );
const expandList = () => { const expandList = () => {
setListLength(Infinity); setListLength(Infinity);
}; };
const collapseList = () => { const collapseList = () => {
setListLength(SHORT_LIST_LENGTH); setListLength(SHORT_LIST_LENGTH);
}; };
React.useEffect(() => { React.useEffect(() => {
setListLength(SHORT_LIST_LENGTH); setListLength(SHORT_LIST_LENGTH);
}, [current]); }, [current]);
const onSearchChange = React.useMemo( const onSearchChange = React.useMemo(
() => () =>
throttle((event) => { throttle((event) => {
@@ -111,6 +98,7 @@ const Suggestions = (props) => {
}, 400), }, 400),
[data], [data],
); );
return ( return (
<Paper elevation={0} sx={{ width: '100%' }}> <Paper elevation={0} sx={{ width: '100%' }}>
<Box px={2} pb={2}> <Box px={2} pb={2}>
@@ -182,4 +170,16 @@ const Suggestions = (props) => {
</Paper> </Paper>
); );
}; };
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; export default Suggestions;

View File

@@ -1,5 +1,6 @@
const joinBy = (delimiter = '.', ...args) => const joinBy = (delimiter = '.', ...args) =>
args.filter(Boolean).join(delimiter); args.filter(Boolean).join(delimiter);
const process = ({ data, parentKey, index, parentLabel = '' }) => { const process = ({ data, parentKey, index, parentLabel = '' }) => {
if (typeof data !== 'object') { if (typeof data !== 'object') {
return [ return [
@@ -10,10 +11,13 @@ const process = ({ data, parentKey, index, parentLabel = '' }) => {
}, },
]; ];
} }
const entries = Object.entries(data); const entries = Object.entries(data);
return entries.flatMap(([name, sampleValue]) => { return entries.flatMap(([name, sampleValue]) => {
const label = joinBy('.', parentLabel, index?.toString(), name); const label = joinBy('.', parentLabel, index?.toString(), name);
const value = joinBy('.', parentKey, index?.toString(), name); const value = joinBy('.', parentKey, index?.toString(), name);
if (Array.isArray(sampleValue)) { if (Array.isArray(sampleValue)) {
return sampleValue.flatMap((item, index) => return sampleValue.flatMap((item, index) =>
process({ process({
@@ -21,9 +25,10 @@ const process = ({ data, parentKey, index, parentLabel = '' }) => {
parentKey: value, parentKey: value,
index, index,
parentLabel: label, parentLabel: label,
}) }),
); );
} }
if (typeof sampleValue === 'object' && sampleValue !== null) { if (typeof sampleValue === 'object' && sampleValue !== null) {
return process({ return process({
data: sampleValue, data: sampleValue,
@@ -40,8 +45,10 @@ const process = ({ data, parentKey, index, parentLabel = '' }) => {
]; ];
}); });
}; };
export const processStepWithExecutions = (steps) => { export const processStepWithExecutions = (steps) => {
if (!steps) return []; if (!steps) return [];
return steps return steps
.filter((step) => { .filter((step) => {
const hasExecutionSteps = !!step.executionSteps?.length; const hasExecutionSteps = !!step.executionSteps?.length;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import ClickAwayListener from '@mui/base/ClickAwayListener'; import ClickAwayListener from '@mui/base/ClickAwayListener';
import FormHelperText from '@mui/material/FormHelperText'; import FormHelperText from '@mui/material/FormHelperText';
import InputLabel from '@mui/material/InputLabel'; 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; export default PowerInput;

View File

@@ -12,6 +12,7 @@ import TextField from 'components/TextField';
import * as URLS from 'config/urls'; import * as URLS from 'config/urls';
import { RESET_PASSWORD } from 'graphql/mutations/reset-password.ee'; import { RESET_PASSWORD } from 'graphql/mutations/reset-password.ee';
import useFormatMessage from 'hooks/useFormatMessage'; import useFormatMessage from 'hooks/useFormatMessage';
const validationSchema = yup.object().shape({ const validationSchema = yup.object().shape({
password: yup.string().required('resetPasswordForm.mandatoryInput'), password: yup.string().required('resetPasswordForm.mandatoryInput'),
confirmPassword: yup confirmPassword: yup
@@ -19,6 +20,7 @@ const validationSchema = yup.object().shape({
.required('resetPasswordForm.mandatoryInput') .required('resetPasswordForm.mandatoryInput')
.oneOf([yup.ref('password')], 'resetPasswordForm.passwordsMustMatch'), .oneOf([yup.ref('password')], 'resetPasswordForm.passwordsMustMatch'),
}); });
export default function ResetPasswordForm() { export default function ResetPasswordForm() {
const enqueueSnackbar = useEnqueueSnackbar(); const enqueueSnackbar = useEnqueueSnackbar();
const formatMessage = useFormatMessage(); const formatMessage = useFormatMessage();
@@ -26,6 +28,7 @@ export default function ResetPasswordForm() {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const [resetPassword, { data, loading }] = useMutation(RESET_PASSWORD); const [resetPassword, { data, loading }] = useMutation(RESET_PASSWORD);
const token = searchParams.get('token'); const token = searchParams.get('token');
const handleSubmit = async (values) => { const handleSubmit = async (values) => {
await resetPassword({ await resetPassword({
variables: { variables: {
@@ -43,6 +46,7 @@ export default function ResetPasswordForm() {
}); });
navigate(URLS.LOGIN); navigate(URLS.LOGIN);
}; };
return ( return (
<Paper sx={{ px: 2, py: 4 }}> <Paper sx={{ px: 2, py: 4 }}>
<Typography <Typography

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import InputLabel from '@mui/material/InputLabel'; import InputLabel from '@mui/material/InputLabel';
import OutlinedInput from '@mui/material/OutlinedInput'; import OutlinedInput from '@mui/material/OutlinedInput';
@@ -5,6 +6,7 @@ import InputAdornment from '@mui/material/InputAdornment';
import FormControl from '@mui/material/FormControl'; import FormControl from '@mui/material/FormControl';
import SearchIcon from '@mui/icons-material/Search'; import SearchIcon from '@mui/icons-material/Search';
import useFormatMessage from 'hooks/useFormatMessage'; import useFormatMessage from 'hooks/useFormatMessage';
export default function SearchInput({ onChange }) { export default function SearchInput({ onChange }) {
const formatMessage = useFormatMessage(); const formatMessage = useFormatMessage();
return ( return (
@@ -29,3 +31,7 @@ export default function SearchInput({ onChange }) {
</FormControl> </FormControl>
); );
} }
SearchInput.propTypes = {
onChange: PropTypes.func,
};

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import throttle from 'lodash/throttle'; import throttle from 'lodash/throttle';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
@@ -6,9 +7,11 @@ import JSONViewer from 'components/JSONViewer';
import SearchInput from 'components/SearchInput'; import SearchInput from 'components/SearchInput';
import useFormatMessage from 'hooks/useFormatMessage'; import useFormatMessage from 'hooks/useFormatMessage';
import filterObject from 'helpers/filterObject'; import filterObject from 'helpers/filterObject';
const SearchableJSONViewer = ({ data }) => { const SearchableJSONViewer = ({ data }) => {
const [filteredData, setFilteredData] = React.useState(data); const [filteredData, setFilteredData] = React.useState(data);
const formatMessage = useFormatMessage(); const formatMessage = useFormatMessage();
const onSearchChange = React.useMemo( const onSearchChange = React.useMemo(
() => () =>
throttle((event) => { throttle((event) => {
@@ -26,6 +29,7 @@ const SearchableJSONViewer = ({ data }) => {
}, 400), }, 400),
[data], [data],
); );
return ( return (
<> <>
<Box my={2}> <Box my={2}>
@@ -38,4 +42,9 @@ const SearchableJSONViewer = ({ data }) => {
</> </>
); );
}; };
SearchableJSONViewer.propTypes = {
data: PropTypes.object.isRequired,
};
export default SearchableJSONViewer; export default SearchableJSONViewer;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar'; import Toolbar from '@mui/material/Toolbar';
@@ -11,6 +12,7 @@ import useAutomatischInfo from 'hooks/useAutomatischInfo';
import useFormatMessage from 'hooks/useFormatMessage'; import useFormatMessage from 'hooks/useFormatMessage';
import AppBar from 'components/AppBar'; import AppBar from 'components/AppBar';
import Drawer from 'components/Drawer'; import Drawer from 'components/Drawer';
function createDrawerLinks({ isCloud }) { function createDrawerLinks({ isCloud }) {
const items = [ const items = [
{ {
@@ -28,7 +30,8 @@ function createDrawerLinks({ isCloud }) {
} }
return items; return items;
} }
export default function SettingsLayout({ children }) {
function SettingsLayout({ children }) {
const { data: automatischInfo } = useAutomatischInfo(); const { data: automatischInfo } = useAutomatischInfo();
const isCloud = automatischInfo?.data.isCloud; const isCloud = automatischInfo?.data.isCloud;
const theme = useTheme(); const theme = useTheme();
@@ -45,6 +48,7 @@ export default function SettingsLayout({ children }) {
to: '/', to: '/',
}, },
]; ];
return ( return (
<> <>
<AppBar <AppBar
@@ -71,3 +75,9 @@ export default function SettingsLayout({ children }) {
</> </>
); );
} }
SettingsLayout.propTypes = {
children: PropTypes.node.isRequired,
};
export default SettingsLayout;

View File

@@ -1,6 +1,9 @@
import PropTypes from 'prop-types';
import Variable from './Variable'; import Variable from './Variable';
export default function Element(props) { export default function Element(props) {
const { attributes, children, element, disabled } = props; const { attributes, children, element, disabled } = props;
switch (element.type) { switch (element.type) {
case 'variable': case 'variable':
return <Variable {...props} disabled={disabled} />; return <Variable {...props} disabled={disabled} />;
@@ -8,3 +11,12 @@ export default function Element(props) {
return <p {...attributes}>{children}</p>; return <p {...attributes}>{children}</p>;
} }
} }
Element.propTypes = {
attributes: PropTypes.object.isRequired,
children: PropTypes.node.isRequired,
element: PropTypes.shape({
type: PropTypes.string,
}),
disabled: PropTypes.bool,
};

View File

@@ -1,6 +1,8 @@
import PropTypes from 'prop-types';
import Chip from '@mui/material/Chip'; import Chip from '@mui/material/Chip';
import { useSelected, useFocused } from 'slate-react'; import { useSelected, useFocused } from 'slate-react';
export default function Variable({ attributes, children, element, disabled }) {
function Variable({ attributes, children, element, disabled }) {
const selected = useSelected(); const selected = useSelected();
const focused = useFocused(); const focused = useFocused();
const label = ( 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;

View File

@@ -1,2 +1,3 @@
import { Slate } from 'slate-react'; import { Slate } from 'slate-react';
export default Slate; export default Slate;

View File

@@ -1,39 +1,45 @@
import { Text } from 'slate'; import { Text } from 'slate';
import { withHistory } from 'slate-history'; import { withHistory } from 'slate-history';
import { ReactEditor, withReact } from 'slate-react'; import { ReactEditor, withReact } from 'slate-react';
function isCustomText(value) { function isCustomText(value) {
const isText = Text.isText(value); const isText = Text.isText(value);
const hasValueProperty = 'value' in value; const hasValueProperty = 'value' in value;
if (isText && hasValueProperty) return true; if (isText && hasValueProperty) return true;
return false; return false;
} }
function getStepPosition(id, stepsWithVariables) { function getStepPosition(id, stepsWithVariables) {
const stepIndex = stepsWithVariables.findIndex((stepWithVariables) => { const stepIndex = stepsWithVariables.findIndex((stepWithVariables) => {
return stepWithVariables.id === id; return stepWithVariables.id === id;
}); });
return stepIndex + 1; return stepIndex + 1;
} }
function getVariableName(variable) { function getVariableName(variable) {
return variable.replace(/{{|}}/g, ''); return variable.replace(/{{|}}/g, '');
} }
function getVariableStepId(variable) { function getVariableStepId(variable) {
const nameWithoutCurlies = getVariableName(variable); const nameWithoutCurlies = getVariableName(variable);
const stepId = nameWithoutCurlies.match(stepIdRegExp)?.[1] || ''; const stepId = nameWithoutCurlies.match(stepIdRegExp)?.[1] || '';
return stepId; return stepId;
} }
function getVariableSampleValue(variable, stepsWithVariables) { function getVariableSampleValue(variable, stepsWithVariables) {
const variableStepId = getVariableStepId(variable); const variableStepId = getVariableStepId(variable);
const stepWithVariables = stepsWithVariables.find( const stepWithVariables = stepsWithVariables.find(
({ id }) => id === variableStepId ({ id }) => id === variableStepId,
); );
if (!stepWithVariables) return null; if (!stepWithVariables) return null;
const variableName = getVariableName(variable); const variableName = getVariableName(variable);
const variableData = stepWithVariables.output.find( const variableData = stepWithVariables.output.find(
({ value }) => variableName === value ({ value }) => variableName === value,
); );
if (!variableData) return null; if (!variableData) return null;
return variableData.sampleValue; return variableData.sampleValue;
} }
function getVariableDetails(variable, stepsWithVariables) { function getVariableDetails(variable, stepsWithVariables) {
const variableName = getVariableName(variable); const variableName = getVariableName(variable);
const stepId = getVariableStepId(variableName); const stepId = getVariableStepId(variableName);
@@ -45,12 +51,15 @@ function getVariableDetails(variable, stepsWithVariables) {
label, label,
}; };
} }
const variableRegExp = /({{.*?}})/; const variableRegExp = /({{.*?}})/;
const stepIdRegExp = /^step.([\da-zA-Z-]*)/; const stepIdRegExp = /^step.([\da-zA-Z-]*)/;
export const deserialize = (value, options, stepsWithVariables) => { export const deserialize = (value, options, stepsWithVariables) => {
const selectedNativeOption = options?.find( const selectedNativeOption = options?.find(
(option) => value === option.value (option) => value === option.value,
); );
if (selectedNativeOption) { if (selectedNativeOption) {
return [ return [
{ {
@@ -60,6 +69,7 @@ export const deserialize = (value, options, stepsWithVariables) => {
}, },
]; ];
} }
if (value === null || value === undefined || value === '') if (value === null || value === undefined || value === '')
return [ return [
{ {
@@ -67,6 +77,7 @@ export const deserialize = (value, options, stepsWithVariables) => {
children: [{ text: '' }], children: [{ text: '' }],
}, },
]; ];
return value return value
.toString() .toString()
.split('\n') .split('\n')
@@ -79,7 +90,7 @@ export const deserialize = (value, options, stepsWithVariables) => {
if (node.match(variableRegExp)) { if (node.match(variableRegExp)) {
const variableDetails = getVariableDetails( const variableDetails = getVariableDetails(
node, node,
stepsWithVariables stepsWithVariables,
); );
return { return {
type: 'variable', type: 'variable',
@@ -101,6 +112,7 @@ export const deserialize = (value, options, stepsWithVariables) => {
}; };
}); });
}; };
export const serialize = (value) => { export const serialize = (value) => {
const serializedNodes = value.map((node) => serializeNode(node)); const serializedNodes = value.map((node) => serializeNode(node));
const hasSingleNode = value.length === 1; const hasSingleNode = value.length === 1;
@@ -114,6 +126,7 @@ export const serialize = (value) => {
const serializedValue = serializedNodes.join('\n'); const serializedValue = serializedNodes.join('\n');
return serializedValue; return serializedValue;
}; };
const serializeNode = (node) => { const serializeNode = (node) => {
if (isCustomText(node)) { if (isCustomText(node)) {
return node.value; return node.value;
@@ -134,6 +147,7 @@ const serializeNode = (node) => {
} }
return node.children.map((n) => serializeNode(n)).join(''); return node.children.map((n) => serializeNode(n)).join('');
}; };
export const withVariables = (editor) => { export const withVariables = (editor) => {
const { isInline, isVoid } = editor; const { isInline, isVoid } = editor;
editor.isInline = (element) => { editor.isInline = (element) => {
@@ -144,10 +158,11 @@ export const withVariables = (editor) => {
}; };
return editor; return editor;
}; };
export const insertVariable = (editor, variableData, stepsWithVariables) => { export const insertVariable = (editor, variableData, stepsWithVariables) => {
const variableDetails = getVariableDetails( const variableDetails = getVariableDetails(
`{{${variableData.value}}}`, `{{${variableData.value}}}`,
stepsWithVariables stepsWithVariables,
); );
const variable = { const variable = {
type: 'variable', type: 'variable',
@@ -159,10 +174,12 @@ export const insertVariable = (editor, variableData, stepsWithVariables) => {
editor.insertNodes(variable, { select: false }); editor.insertNodes(variable, { select: false });
focusEditor(editor); focusEditor(editor);
}; };
export const focusEditor = (editor) => { export const focusEditor = (editor) => {
ReactEditor.focus(editor); ReactEditor.focus(editor);
editor.move(); editor.move();
}; };
export const resetEditor = (editor, options) => { export const resetEditor = (editor, options) => {
const focus = options?.focus || false; const focus = options?.focus || false;
editor.removeNodes({ editor.removeNodes({
@@ -177,6 +194,7 @@ export const resetEditor = (editor, options) => {
focusEditor(editor); focusEditor(editor);
} }
}; };
export const overrideEditorValue = (editor, options) => { export const overrideEditorValue = (editor, options) => {
const { option, focus } = options; const { option, focus } = options;
const variable = { const variable = {
@@ -201,6 +219,7 @@ export const overrideEditorValue = (editor, options) => {
} }
}); });
}; };
export const createTextNode = (text) => ({ export const createTextNode = (text) => ({
type: 'paragraph', type: 'paragraph',
children: [ children: [
@@ -209,6 +228,7 @@ export const createTextNode = (text) => ({
}, },
], ],
}); });
export const customizeEditor = (editor) => { export const customizeEditor = (editor) => {
return withVariables(withReact(withHistory(editor))); return withVariables(withReact(withHistory(editor)));
}; };

View File

@@ -1,5 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import { SnackbarProvider as BaseSnackbarProvider } from 'notistack'; import { SnackbarProvider as BaseSnackbarProvider } from 'notistack';
const SnackbarProvider = (props) => { const SnackbarProvider = (props) => {
return ( return (
<BaseSnackbarProvider <BaseSnackbarProvider
@@ -12,4 +13,5 @@ const SnackbarProvider = (props) => {
/> />
); );
}; };
export default SnackbarProvider; export default SnackbarProvider;

View File

@@ -1,8 +1,10 @@
import PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import { Controller, useFormContext } from 'react-hook-form'; import { Controller, useFormContext } from 'react-hook-form';
import FormControlLabel from '@mui/material/FormControlLabel'; import FormControlLabel from '@mui/material/FormControlLabel';
import MuiSwitch from '@mui/material/Switch'; import MuiSwitch from '@mui/material/Switch';
export default function Switch(props) {
function Switch(props) {
const { control } = useFormContext(); const { control } = useFormContext();
const inputRef = React.useRef(null); const inputRef = React.useRef(null);
const { const {
@@ -18,6 +20,7 @@ export default function Switch(props) {
className, className,
...switchProps ...switchProps
} = props; } = props;
return ( return (
<Controller <Controller
rules={{ required }} rules={{ required }}
@@ -63,3 +66,18 @@ export default function Switch(props) {
/> />
); );
} }
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;

View File

@@ -1,4 +1,6 @@
import * as React from 'react'; import * as React from 'react';
import PropTypes from 'prop-types';
export default function TabPanel(props) { export default function TabPanel(props) {
const { children, value, index, ...other } = props; const { children, value, index, ...other } = props;
return ( return (
@@ -7,3 +9,9 @@ export default function TabPanel(props) {
</div> </div>
); );
} }
TabPanel.propTypes = {
children: PropTypes.element.isRequired,
value: PropTypes.number.isRequired,
index: PropTypes.number.isRequired,
};

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import { useMutation } from '@apollo/client'; import { useMutation } from '@apollo/client';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
@@ -14,6 +15,7 @@ import JSONViewer from 'components/JSONViewer';
import WebhookUrlInfo from 'components/WebhookUrlInfo'; import WebhookUrlInfo from 'components/WebhookUrlInfo';
import FlowSubstepTitle from 'components/FlowSubstepTitle'; import FlowSubstepTitle from 'components/FlowSubstepTitle';
import { useQueryClient } from '@tanstack/react-query'; import { useQueryClient } from '@tanstack/react-query';
import { StepPropType, SubstepPropType } from 'propTypes/propTypes';
function serializeErrors(graphQLErrors) { function serializeErrors(graphQLErrors) {
return graphQLErrors?.map((error) => { return graphQLErrors?.map((error) => {
@@ -154,4 +156,18 @@ function TestSubstep(props) {
</React.Fragment> </React.Fragment>
); );
} }
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; export default TestSubstep;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import { Controller, useFormContext } from 'react-hook-form'; import { Controller, useFormContext } from 'react-hook-form';
import MuiTextField from '@mui/material/TextField'; import MuiTextField from '@mui/material/TextField';
@@ -5,6 +6,7 @@ import IconButton from '@mui/material/IconButton';
import InputAdornment from '@mui/material/InputAdornment'; import InputAdornment from '@mui/material/InputAdornment';
import ContentCopyIcon from '@mui/icons-material/ContentCopy'; import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import copyInputValue from 'helpers/copyInputValue'; import copyInputValue from 'helpers/copyInputValue';
const createCopyAdornment = (ref) => { const createCopyAdornment = (ref) => {
return ( return (
<InputAdornment position="end"> <InputAdornment position="end">
@@ -14,7 +16,8 @@ const createCopyAdornment = (ref) => {
</InputAdornment> </InputAdornment>
); );
}; };
export default function TextField(props) {
function TextField(props) {
const { control } = useFormContext(); const { control } = useFormContext();
const inputRef = React.useRef(null); const inputRef = React.useRef(null);
const { 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;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import CssBaseline from '@mui/material/CssBaseline'; import CssBaseline from '@mui/material/CssBaseline';
import { ThemeProvider as BaseThemeProvider } from '@mui/material/styles'; import { ThemeProvider as BaseThemeProvider } from '@mui/material/styles';
import clone from 'lodash/clone'; import clone from 'lodash/clone';
@@ -24,6 +25,7 @@ const customizeTheme = (theme, config) => {
return shallowDefaultTheme; return shallowDefaultTheme;
}; };
const ThemeProvider = ({ children, ...props }) => { const ThemeProvider = ({ children, ...props }) => {
const { data: automatischInfo, isPending: isAutomatischInfoPending } = const { data: automatischInfo, isPending: isAutomatischInfoPending } =
useAutomatischInfo(); useAutomatischInfo();
@@ -53,4 +55,8 @@ const ThemeProvider = ({ children, ...props }) => {
); );
}; };
ThemeProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default ThemeProvider; export default ThemeProvider;

View File

@@ -1,5 +1,6 @@
import { styled } from '@mui/material/styles'; import { styled } from '@mui/material/styles';
import MuiChip, { chipClasses } from '@mui/material/Chip'; import MuiChip, { chipClasses } from '@mui/material/Chip';
export const Chip = styled(MuiChip)` export const Chip = styled(MuiChip)`
&.${chipClasses.root} { &.${chipClasses.root} {
font-weight: 500; font-weight: 500;

View File

@@ -10,6 +10,7 @@ import CardContent from '@mui/material/CardContent';
import Divider from '@mui/material/Divider'; import Divider from '@mui/material/Divider';
import Grid from '@mui/material/Grid'; import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import PropTypes from 'prop-types';
import TrialOverAlert from 'components/TrialOverAlert/index.ee'; import TrialOverAlert from 'components/TrialOverAlert/index.ee';
import SubscriptionCancelledAlert from 'components/SubscriptionCancelledAlert/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) { function Action(props) {
const { action, text } = props; const { action, text } = props;
@@ -80,6 +88,11 @@ function Action(props) {
); );
} }
Action.propTypes = {
action: PropTypes.string,
text: PropTypes.string,
};
export default function UsageDataInformation() { export default function UsageDataInformation() {
const formatMessage = useFormatMessage(); const formatMessage = useFormatMessage();
const queryClient = useQueryClient(); const queryClient = useQueryClient();

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import { useTheme } from '@mui/material'; import { useTheme } from '@mui/material';
import IconButton from '@mui/material/IconButton'; import IconButton from '@mui/material/IconButton';
import FirstPageIcon from '@mui/icons-material/FirstPage'; 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 KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight';
import LastPageIcon from '@mui/icons-material/LastPage'; import LastPageIcon from '@mui/icons-material/LastPage';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
export default function TablePaginationActions(props) {
function TablePaginationActions(props) {
const theme = useTheme(); const theme = useTheme();
const { count, page, rowsPerPage, onPageChange } = props; const { count, page, rowsPerPage, onPageChange } = props;
const handleFirstPageButtonClick = (event) => { const handleFirstPageButtonClick = (event) => {
onPageChange(event, 0); onPageChange(event, 0);
}; };
const handleBackButtonClick = (event) => { const handleBackButtonClick = (event) => {
onPageChange(event, page - 1); onPageChange(event, page - 1);
}; };
const handleNextButtonClick = (event) => { const handleNextButtonClick = (event) => {
onPageChange(event, page + 1); onPageChange(event, page + 1);
}; };
const handleLastPageButtonClick = (event) => { const handleLastPageButtonClick = (event) => {
onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1)); onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1));
}; };
return ( return (
<Box sx={{ flexShrink: 0, ml: 2.5 }}> <Box sx={{ flexShrink: 0, ml: 2.5 }}>
<IconButton <IconButton
@@ -65,3 +72,12 @@ export default function TablePaginationActions(props) {
</Box> </Box>
); );
} }
TablePaginationActions.propTypes = {
count: PropTypes.number.isRequired,
page: PropTypes.number.isRequired,
rowsPerPage: PropTypes.number.isRequired,
onPageChange: PropTypes.func.isRequired,
};
export default TablePaginationActions;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
@@ -5,6 +6,7 @@ import { generateExternalLink } from '../../helpers/translationValues';
import { WEBHOOK_DOCS } from '../../config/urls'; import { WEBHOOK_DOCS } from '../../config/urls';
import TextField from '../TextField'; import TextField from '../TextField';
import { Alert } from './style'; import { Alert } from './style';
function WebhookUrlInfo(props) { function WebhookUrlInfo(props) {
const { webhookUrl, ...alertProps } = props; const { webhookUrl, ...alertProps } = props;
return ( return (
@@ -33,4 +35,8 @@ function WebhookUrlInfo(props) {
</Alert> </Alert>
); );
} }
WebhookUrlInfo.propTypes = {
webhookUrl: PropTypes.string.isRequired,
};
export default WebhookUrlInfo; export default WebhookUrlInfo;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import { getItem, removeItem, setItem } from 'helpers/storage'; import { getItem, removeItem, setItem } from 'helpers/storage';
import api from 'helpers/api.js'; import api from 'helpers/api.js';
@@ -36,3 +37,7 @@ export const AuthenticationProvider = (props) => {
</AuthenticationContext.Provider> </AuthenticationContext.Provider>
); );
}; };
AuthenticationProvider.propTypes = {
children: PropTypes.node.isRequired,
};

View File

@@ -1,10 +1,18 @@
import * as React from 'react'; import * as React from 'react';
import PropTypes from 'prop-types';
export const EditorContext = React.createContext({ export const EditorContext = React.createContext({
readOnly: false, readOnly: false,
}); });
export const EditorProvider = (props) => { export const EditorProvider = (props) => {
const { children, value } = props; const { children, value } = props;
return ( return (
<EditorContext.Provider value={value}>{children}</EditorContext.Provider> <EditorContext.Provider value={value}>{children}</EditorContext.Provider>
); );
}; };
EditorProvider.propTypes = {
children: PropTypes.node.isRequired,
value: PropTypes.shape({ readOnly: PropTypes.bool.isRequired }).isRequired,
};

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
@@ -100,3 +101,7 @@ export const PaddleProvider = (props) => {
<PaddleContext.Provider value={value}>{children}</PaddleContext.Provider> <PaddleContext.Provider value={value}>{children}</PaddleContext.Provider>
); );
}; };
PaddleProvider.propTypes = {
children: PropTypes.node.isRequired,
};

View File

@@ -1,5 +1,9 @@
import * as React from 'react'; import * as React from 'react';
import PropTypes from 'prop-types';
import { StepPropType } from 'propTypes/propTypes';
export const StepExecutionsContext = React.createContext([]); export const StepExecutionsContext = React.createContext([]);
export const StepExecutionsProvider = (props) => { export const StepExecutionsProvider = (props) => {
const { children, value } = props; const { children, value } = props;
return ( return (
@@ -8,3 +12,8 @@ export const StepExecutionsProvider = (props) => {
</StepExecutionsContext.Provider> </StepExecutionsContext.Provider>
); );
}; };
StepExecutionsProvider.propTypes = {
children: PropTypes.node.isRequired,
value: PropTypes.arrayOf(StepPropType),
};

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import * as React from 'react'; import * as React from 'react';
import { import {
Link, Link,
@@ -31,6 +32,7 @@ import Container from 'components/Container';
import PageTitle from 'components/PageTitle'; import PageTitle from 'components/PageTitle';
import useApp from 'hooks/useApp'; import useApp from 'hooks/useApp';
import Can from 'components/Can'; import Can from 'components/Can';
import { AppPropType } from 'propTypes/propTypes';
const ReconnectConnection = (props) => { const ReconnectConnection = (props) => {
const { application, onClose } = 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() { export default function Application() {
const theme = useTheme(); const theme = useTheme();
const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md')); const matchSmallScreens = useMediaQuery(theme.breakpoints.down('md'));

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import { useMutation } from '@apollo/client'; import { useMutation } from '@apollo/client';
import LoadingButton from '@mui/lab/LoadingButton'; import LoadingButton from '@mui/lab/LoadingButton';
import Divider from '@mui/material/Divider'; 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; export default RoleMappings;

View File

@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import { useMutation } from '@apollo/client'; import { useMutation } from '@apollo/client';
import LoadingButton from '@mui/lab/LoadingButton'; import LoadingButton from '@mui/lab/LoadingButton';
import Stack from '@mui/material/Stack'; import Stack from '@mui/material/Stack';
@@ -192,4 +193,22 @@ function SamlConfiguration({ provider, providerLoading }) {
</Form> </Form>
); );
} }
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; export default SamlConfiguration;