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 = {
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,
};

View File

@@ -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,

View File

@@ -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 (
<ListItemButton
sx={{ pl: 4 }}
divider
onClick={(event) => onOptionClick(event, 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.value}
secondaryTypographyProps={{
variant: 'subtitle2',
title: 'Sample value',
noWrap: true,
}}
/>
</ListItemButton>
);
};
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 (
<ListItemButton
sx={{ pl: 4 }}
divider
onClick={(event) => onOptionClick(event, 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.value}
secondaryTypographyProps={{
variant: 'subtitle2',
title: 'Sample value',
noWrap: true,
}}
/>
</ListItemButton>
);
<Item onOptionClick={onOptionClick} {...props} />;
};
const Options = (props) => {

View File

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

View File

@@ -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;

View File

@@ -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 (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)} {...formProps}>
@@ -35,3 +43,14 @@ export default function Form(props) {
</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 Slide from '@mui/material/Slide';
import useScrollTrigger from '@mui/material/useScrollTrigger';
export default function HideOnScroll(props) {
const trigger = useScrollTrigger();
return <Slide appear={false} direction="down" in={!trigger} {...props} />;
}

View File

@@ -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) {
<React.Fragment>
<PowerInput
key={computedName}
label={label}
// label={label}
label="PowerInput"
description={description}
name={computedName}
required={required}
@@ -204,3 +206,16 @@ export default function InputCreator(props) {
}
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 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 (
<Container>
<Typography variant="subtitle1" sx={{}}>
@@ -11,3 +15,9 @@ export default function IntermediateStepCount(props) {
</Container>
);
}
IntermediateStepCount.propTypes = {
count: PropTypes.number.isRequired,
};
export default IntermediateStepCount;

View File

@@ -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 (
<BaseIntlProvider
@@ -11,4 +14,9 @@ const IntlProvider = ({ children }) => {
</BaseIntlProvider>
);
};
IntlProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default IntlProvider;

View File

@@ -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. <?php ?>
base0F: '#a16946',
};
function JSONViewer(props) {
const { data } = props;
return (
<JSONTree
hideRoot
@@ -48,4 +52,9 @@ function JSONViewer(props) {
/>
);
}
JSONViewer.propTypes = {
data: PropTypes.object.isRequired,
};
export default JSONViewer;

View File

@@ -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;

View File

@@ -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 (
<li>
<ListItem
@@ -47,3 +51,14 @@ export default function ListItemLink(props) {
</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 {
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;

View File

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

View File

@@ -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;

View File

@@ -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) {
</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 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 (
<Card>
<CardActionArea component={'a'} href={documentationUrl} target="_blank">
@@ -36,3 +41,12 @@ export default function NotificationCard(props) {
</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 Typography from '@mui/material/Typography';
export default function PageTitle(props) {
return <Typography variant="h3" data-test="page-title" {...props} />;
}

View File

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

View File

@@ -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 (
<Dialog
open={open}
@@ -133,3 +138,26 @@ export default function PermissionSettings(props) {
</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 IconButton from '@mui/material/IconButton';
import Paper from '@mui/material/Paper';
@@ -110,4 +111,10 @@ const PermissionCatalogField = ({
</TableContainer>
);
};
PermissionCatalogField.propTypes = {
name: PropTypes.string,
disabled: PropTypes.bool,
defaultChecked: PropTypes.bool,
};
export default PermissionCatalogField;

View File

@@ -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;

View File

@@ -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 (
<MuiPopper
open={open}
@@ -34,4 +37,15 @@ const Popper = (props) => {
</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;

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 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 (
<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>
);
};
(props) => (
<SuggestionItem {...props} onSuggestionClick={onSuggestionClick} />
);
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 (
<Paper elevation={0} sx={{ width: '100%' }}>
<Box px={2} pb={2}>
@@ -182,4 +170,16 @@ const Suggestions = (props) => {
</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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 (
<Paper sx={{ px: 2, py: 4 }}>
<Typography

View File

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

View File

@@ -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 (
<>
<Box my={2}>
@@ -38,4 +42,9 @@ const SearchableJSONViewer = ({ data }) => {
</>
);
};
SearchableJSONViewer.propTypes = {
data: PropTypes.object.isRequired,
};
export default SearchableJSONViewer;

View File

@@ -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 (
<>
<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';
export default function Element(props) {
const { attributes, children, element, disabled } = props;
switch (element.type) {
case 'variable':
return <Variable {...props} disabled={disabled} />;
@@ -8,3 +11,12 @@ export default function Element(props) {
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 { 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;

View File

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

View File

@@ -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)));
};

View File

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

View File

@@ -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 (
<Controller
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 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) {
</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 { 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) {
</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;

View File

@@ -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 (
<InputAdornment position="end">
@@ -14,7 +16,8 @@ const createCopyAdornment = (ref) => {
</InputAdornment>
);
};
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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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 (
<Box sx={{ flexShrink: 0, ml: 2.5 }}>
<IconButton
@@ -65,3 +72,12 @@ export default function TablePaginationActions(props) {
</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 { 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) {
</Alert>
);
}
WebhookUrlInfo.propTypes = {
webhookUrl: PropTypes.string.isRequired,
};
export default WebhookUrlInfo;

View File

@@ -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) => {
</AuthenticationContext.Provider>
);
};
AuthenticationProvider.propTypes = {
children: PropTypes.node.isRequired,
};

View File

@@ -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 (
<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 { useNavigate } from 'react-router-dom';
@@ -100,3 +101,7 @@ export const PaddleProvider = (props) => {
<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 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) => {
</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 {
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'));

View File

@@ -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;

View File

@@ -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 }) {
</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;