feat: add new commit trigger in GitHub

This commit is contained in:
Ali BARIN
2022-04-24 01:23:59 +02:00
parent 2e2d371875
commit e651fd141b
32 changed files with 439 additions and 98 deletions

View File

@@ -88,6 +88,7 @@ function ChooseAppAndEventSubstep(props: ChooseAppAndEventSubstepProps): React.R
...step,
key: '',
appKey,
parameters: {},
},
});
}

View File

@@ -15,7 +15,7 @@ type Option = {
value: string;
}
const getOption = (options: readonly Option[], value: string) => options.find(option => option.value === value);
const getOption = (options: readonly Option[], value: string) => options.find(option => option.value === value) || null;
function ControlledAutocomplete(props: ControlledAutocompleteProps): React.ReactElement {
const { control } = useFormContext();
@@ -28,11 +28,10 @@ function ControlledAutocomplete(props: ControlledAutocompleteProps): React.React
onBlur,
onChange,
description,
options = [],
...autocompleteProps
} = props;
if (!autocompleteProps.options) return (<React.Fragment />);
return (
<Controller
rules={{ required }}
@@ -40,13 +39,14 @@ function ControlledAutocomplete(props: ControlledAutocompleteProps): React.React
defaultValue={defaultValue || ''}
control={control}
shouldUnregister={shouldUnregister}
render={({ field: { ref, onChange: controllerOnChange, onBlur: controllerOnBlur, ...field } }) => (
render={({ field: { ref, onChange: controllerOnChange, onBlur: controllerOnBlur, ...field }, fieldState }) => (
<div>
{/* encapsulated with an element such as div to vertical spacing delegated from parent */}
<Autocomplete
{...autocompleteProps}
{...field}
value={getOption(autocompleteProps.options, field.value)}
options={options}
value={getOption(options, field.value)}
onChange={(event, selectedOption, reason, details) => {
const typedSelectedOption = selectedOption as Option;
if (typedSelectedOption?.value) {
@@ -63,8 +63,9 @@ function ControlledAutocomplete(props: ControlledAutocompleteProps): React.React
<FormHelperText
variant="outlined"
error={Boolean(fieldState.isTouched && fieldState.error)}
>
{description}
{fieldState.isTouched ? fieldState.error?.message || description : description}
</FormHelperText>
</div>
)}

View File

@@ -10,7 +10,10 @@ import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
import IconButton from '@mui/material/IconButton';
import ErrorIcon from '@mui/icons-material/Error';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import type { IApp, IField, IStep } from '@automatisch/types';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import type { BaseSchema } from 'yup';
import type { IApp, IField, IStep, ISubstep } from '@automatisch/types';
import { StepExecutionsProvider } from 'contexts/StepExecutions';
import TestSubstep from 'components/TestSubstep';
@@ -43,10 +46,57 @@ type FlowStepProps = {
const validIcon = <CheckCircleIcon color="success" />;
const errorIcon = <ErrorIcon color="error" />;
function generateValidationSchema(substeps: ISubstep[]) {
const fieldValidations = substeps?.reduce((allValidations, { arguments: args }) => {
if (!args || !Array.isArray(args)) return allValidations;
const substepArgumentValidations: Record<string, BaseSchema> = {};
for (const arg of args) {
const { key, required, dependsOn } = arg;
// base validation for the field if not exists
if (!substepArgumentValidations[key]) {
substepArgumentValidations[key] = yup.string();
}
if (typeof substepArgumentValidations[key] === 'object') {
// if the field is required, add the required validation
if (required) {
substepArgumentValidations[key] = substepArgumentValidations[key].required(`${key} is required.`);
}
// if the field depends on another field, add the dependsOn required validation
if (dependsOn?.length > 0) {
for (const dependsOnKey of dependsOn) {
// TODO: make `dependsOnKey` agnostic to the field. However, nested validation schema is not supported.
// So the fields under the `parameters` key are subject to their siblings only and thus, `parameters.` is removed.
substepArgumentValidations[key] = substepArgumentValidations[key].when(`${dependsOnKey.replace('parameters.', '')}`, {
is: (value: string) => Boolean(value) === false,
then: (schema) => schema.required(`We're having trouble loading '${key}' data as required field '${dependsOnKey}' is missing.`),
});
}
}
}
}
return {
...allValidations,
...substepArgumentValidations,
}
}, {});
const validationSchema = yup.object({
parameters: yup.object(fieldValidations),
});
return yupResolver(validationSchema);
};
export default function FlowStep(
props: FlowStepProps
): React.ReactElement | null {
const { collapsed, index, onChange } = props;
const { collapsed, onChange } = props;
const contextButtonRef = React.useRef<HTMLButtonElement | null>(null);
const step: IStep = props.step;
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(
@@ -103,6 +153,8 @@ export default function FlowStep(
handleChange({ step: val as IStep });
};
const stepValidationSchema = React.useMemo(() => generateValidationSchema(substeps), [substeps]);
if (!apps) return null;
const onContextMenuClose = (event: React.SyntheticEvent) => {
@@ -171,7 +223,11 @@ export default function FlowStep(
stepWithTestExecutionsData?.getStepWithTestExecutions as IStep[]
}
>
<Form defaultValues={step} onSubmit={handleSubmit}>
<Form
defaultValues={step}
onSubmit={handleSubmit}
resolver={stepValidationSchema}
>
<ChooseAppAndEventSubstep
expanded={currentSubstep === 0}
substep={{ name: 'Choose app & event', arguments: [] }}

View File

@@ -1,5 +1,5 @@
import * as React from 'react';
import { FormProvider, useForm, FieldValues, SubmitHandler, UseFormReturn } from 'react-hook-form';
import { FormProvider, useForm, useWatch, FieldValues, SubmitHandler, UseFormReturn } from 'react-hook-form';
import type { UseFormProps } from 'react-hook-form';
type FormProps = {
@@ -20,15 +20,26 @@ export default function Form(props: FormProps): React.ReactElement {
defaultValues,
resolver,
render,
mode,
mode = 'all',
...formProps
} = props;
const methods: UseFormReturn = 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]);

View File

@@ -3,6 +3,7 @@ import { useLazyQuery } from '@apollo/client';
import MuiTextField from '@mui/material/TextField';
import type { IField, IFieldDropdown, IJSONObject } from '@automatisch/types';
import useDynamicData from 'hooks/useDynamicData';
import { GET_DATA } from 'graphql/queries/get-data';
import PowerInput from 'components/PowerInput';
import TextField from 'components/TextField';
@@ -26,7 +27,6 @@ type Option = {
value: string;
};
const computeArguments = (args: IFieldDropdown["source"]["arguments"]): IJSONObject => args.reduce((result, { name, value }) => ({ ...result, [name as string]: value as string }), {});
const optionGenerator = (options: RawOption[]): Option[] => options?.map(({ name, value }) => ({ label: name as string, value: value as string }));
const getOption = (options: Option[], value: string) => options?.find(option => option.value === value);
@@ -51,24 +51,11 @@ export default function InputCreator(props: InputCreatorProps): React.ReactEleme
type,
} = schema;
const [getData, { called, data }] = useLazyQuery(GET_DATA);
React.useEffect(() => {
if (schema.type === 'dropdown' && stepId && schema.source && !called) {
getData({
variables: {
stepId,
...computeArguments(schema.source.arguments),
}
})
}
}, [getData, called, stepId, schema]);
const { data, loading } = useDynamicData(stepId, schema);
const computedName = namePrefix ? `${namePrefix}.${name}` : name;
if (type === 'dropdown') {
const options = optionGenerator(data?.getData);
const options = optionGenerator(data);
return (
<ControlledAutocomplete
@@ -81,6 +68,7 @@ export default function InputCreator(props: InputCreatorProps): React.ReactEleme
value={getOption(options, value)}
onChange={console.log}
description={description}
loading={loading}
/>
);
}