feat: Introduce InputCreator
This commit is contained in:
@@ -5,6 +5,7 @@ const appType = new GraphQLObjectType({
|
||||
name: 'App',
|
||||
fields: {
|
||||
name: { type: GraphQLString },
|
||||
key: { type: GraphQLString },
|
||||
iconUrl: { type: GraphQLString },
|
||||
docUrl: { type: GraphQLString },
|
||||
primaryColor: { type: GraphQLString },
|
||||
|
@@ -8,6 +8,7 @@ const fieldType = new GraphQLObjectType({
|
||||
type: { type: GraphQLString },
|
||||
required: { type: GraphQLBoolean},
|
||||
readOnly: { type: GraphQLBoolean},
|
||||
value: { type: GraphQLString},
|
||||
placeholder: { type: GraphQLString},
|
||||
description: { type: GraphQLString},
|
||||
docUrl: { type: GraphQLString},
|
||||
|
@@ -16,6 +16,7 @@
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"@types/react-router-dom": "^5.3.0",
|
||||
"clipboard-copy": "^4.0.1",
|
||||
"graphql": "^15.6.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
|
@@ -1,25 +1,54 @@
|
||||
import { useMutation } from '@apollo/client';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import DialogContentText from '@mui/material/DialogContentText';
|
||||
import Dialog from '@mui/material/Dialog';
|
||||
import Button from '@mui/material/Button';
|
||||
import { FieldValues, SubmitHandler } from 'react-hook-form';
|
||||
|
||||
import InputCreator from 'components/InputCreator';
|
||||
import { CREATE_CREDENTIALS } from 'graphql/mutations/create-credentials';
|
||||
import type { App } from 'types/app';
|
||||
import { Form } from './style';
|
||||
|
||||
type AddAppConnectionProps = {
|
||||
onClose: (value: string) => void;
|
||||
onClose: () => void;
|
||||
application: App;
|
||||
};
|
||||
|
||||
export default function AddAppConnection(props: AddAppConnectionProps){
|
||||
const { application, onClose } = props;
|
||||
const { name } = application;
|
||||
const { name, fields } = application;
|
||||
|
||||
const [createCredentials, { data: newCredentials }] = useMutation(CREATE_CREDENTIALS);
|
||||
console.log('newCredentials', newCredentials)
|
||||
|
||||
const submitHandler: SubmitHandler<FieldValues> = (data) => {
|
||||
const variables = {
|
||||
key: application.key,
|
||||
displayName: data.displayName,
|
||||
data: {
|
||||
consumerKey: data.consumerKey,
|
||||
consumerSecret: data.consumerSecret
|
||||
}
|
||||
};
|
||||
|
||||
createCredentials({ variables });
|
||||
|
||||
onClose?.();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={true} onClose={onClose}>
|
||||
<DialogTitle>Add connection</DialogTitle>
|
||||
|
||||
<DialogContent>
|
||||
<DialogContentText tabIndex={-1}>
|
||||
Add a connection to {name}
|
||||
<DialogContentText tabIndex={-1} component="div">
|
||||
<Form onSubmit={submitHandler}>
|
||||
{fields?.map(field => (<InputCreator key={field.key} schema={field} />))}
|
||||
|
||||
<Button type="submit" variant="contained" color="primary">Submit</Button>
|
||||
</Form>
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
9
packages/web/src/components/AddAppConnection/style.ts
Normal file
9
packages/web/src/components/AddAppConnection/style.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { styled } from "@mui/material/styles";
|
||||
import BaseForm from 'components/Form';
|
||||
|
||||
export const Form = styled(BaseForm)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: theme.spacing(2),
|
||||
paddingTop: theme.spacing(1),
|
||||
}));
|
20
packages/web/src/components/Form/index.tsx
Normal file
20
packages/web/src/components/Form/index.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { FormProvider, useForm, FieldValues, SubmitHandler, UseFormReturn } from "react-hook-form";
|
||||
|
||||
type FormProps = {
|
||||
children: React.ReactNode;
|
||||
onSubmit: SubmitHandler<FieldValues>;
|
||||
}
|
||||
|
||||
export default function Form(props: FormProps) {
|
||||
const { children, onSubmit, ...formProps } = props;
|
||||
const methods: UseFormReturn = useForm();
|
||||
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
<form onSubmit={methods.handleSubmit(onSubmit)} {...formProps}>
|
||||
{children}
|
||||
</form>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
47
packages/web/src/components/InputCreator/index.tsx
Normal file
47
packages/web/src/components/InputCreator/index.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { AppFields } from 'types/app';
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import TextField from 'components/TextField';
|
||||
|
||||
type InputCreatorProps = {
|
||||
onChange?: React.ChangeEventHandler;
|
||||
schema: AppFields;
|
||||
};
|
||||
|
||||
export default function InputCreator(props: InputCreatorProps) {
|
||||
const {
|
||||
onChange,
|
||||
schema,
|
||||
} = props;
|
||||
|
||||
const { control } = useFormContext();
|
||||
|
||||
const {
|
||||
key: name,
|
||||
label,
|
||||
type,
|
||||
required,
|
||||
readOnly,
|
||||
value,
|
||||
description,
|
||||
docUrl,
|
||||
clickToCopy,
|
||||
} = schema;
|
||||
|
||||
return (
|
||||
<TextField
|
||||
defaultValue={value}
|
||||
required={required}
|
||||
placeholder=""
|
||||
disabled={readOnly}
|
||||
readOnly={readOnly}
|
||||
onChange={onChange}
|
||||
name={name}
|
||||
label={label}
|
||||
fullWidth
|
||||
helperText={description}
|
||||
control={control}
|
||||
clickToCopy={clickToCopy}
|
||||
/>
|
||||
);
|
||||
};
|
@@ -1,20 +1,68 @@
|
||||
import React from "react";
|
||||
import { useRef } from "react";
|
||||
import { Controller, Control, FieldValues } from "react-hook-form";
|
||||
import MuiTextField from "@mui/material/TextField";
|
||||
import MuiTextField, { TextFieldProps as MuiTextFieldProps } from "@mui/material/TextField";
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import InputAdornment from '@mui/material/InputAdornment';
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||
|
||||
import copyInputValue from 'helpers/copyInputValue';
|
||||
|
||||
type TextFieldProps = {
|
||||
control: Control<FieldValues>;
|
||||
control?: Control<FieldValues>;
|
||||
shouldUnregister?: boolean;
|
||||
name: string;
|
||||
clickToCopy?: boolean;
|
||||
readOnly?: boolean;
|
||||
} & MuiTextFieldProps;
|
||||
|
||||
const createCopyAdornment = (ref: React.RefObject<HTMLInputElement | null>) => {
|
||||
return (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
onClick={() => copyInputValue(ref.current as HTMLInputElement)}
|
||||
edge="end"
|
||||
>
|
||||
<ContentCopyIcon />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TextField({ control, name }: TextFieldProps) {
|
||||
export default function TextField(props: TextFieldProps) {
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const {
|
||||
control,
|
||||
required,
|
||||
name,
|
||||
defaultValue,
|
||||
shouldUnregister,
|
||||
clickToCopy,
|
||||
readOnly,
|
||||
...textFieldProps
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Controller
|
||||
name="MyCheckbox"
|
||||
rules={{ required }}
|
||||
name={name}
|
||||
defaultValue={defaultValue || ''}
|
||||
control={control}
|
||||
defaultValue={false}
|
||||
rules={{ required: true }}
|
||||
render={({ field }) => <MuiTextField {...field} />}
|
||||
shouldUnregister={shouldUnregister}
|
||||
render={({ field: { ref, ...field } }) => (
|
||||
<MuiTextField
|
||||
{...textFieldProps}
|
||||
{...field}
|
||||
inputRef={(element) => { inputRef.current = element; ref(element); }}
|
||||
InputProps={{ readOnly, endAdornment: clickToCopy ? createCopyAdornment(inputRef) : null}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
TextField.defaultProps = {
|
||||
readOnly: false,
|
||||
disabled: false,
|
||||
clickToCopy: false,
|
||||
shouldUnregister: false,
|
||||
};
|
||||
|
14
packages/web/src/graphql/mutations/create-credentials.ts
Normal file
14
packages/web/src/graphql/mutations/create-credentials.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const CREATE_CREDENTIALS = gql`
|
||||
mutation CreateCredentials($displayName: String!, $key: String!, $data: twitterCredentialInput!) {
|
||||
createCredential(displayName: $displayName, key: $key, data: $data) {
|
||||
key
|
||||
displayName
|
||||
data {
|
||||
consumerKey
|
||||
consumerSecret
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
@@ -4,6 +4,7 @@ export const GET_APP = gql`
|
||||
query GetApp($name: String!) {
|
||||
getApp (name: $name) {
|
||||
name
|
||||
key
|
||||
iconUrl
|
||||
docUrl
|
||||
primaryColor
|
||||
@@ -13,7 +14,7 @@ export const GET_APP = gql`
|
||||
type
|
||||
required
|
||||
readOnly
|
||||
placeholder
|
||||
value
|
||||
description
|
||||
docUrl
|
||||
clickToCopy
|
||||
|
4
packages/web/src/helpers/copyInputValue.ts
Normal file
4
packages/web/src/helpers/copyInputValue.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import copy from 'clipboard-copy';
|
||||
export default function copyInputValue(element: HTMLInputElement) {
|
||||
copy(element.value);
|
||||
};
|
@@ -4,18 +4,19 @@ type AppFields = {
|
||||
type: string;
|
||||
required: boolean,
|
||||
readOnly: boolean,
|
||||
placeholder: string;
|
||||
value: string;
|
||||
description: string;
|
||||
docUrl: string;
|
||||
clickToCopy: boolean,
|
||||
};
|
||||
|
||||
type App = {
|
||||
key: string;
|
||||
name: string;
|
||||
iconUrl: string;
|
||||
docUrl: string;
|
||||
primaryColor: string;
|
||||
fields: AppFields;
|
||||
fields: AppFields[];
|
||||
};
|
||||
|
||||
export type { App, AppFields };
|
||||
|
@@ -5094,6 +5094,11 @@ cli-width@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
|
||||
integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
|
||||
|
||||
clipboard-copy@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/clipboard-copy/-/clipboard-copy-4.0.1.tgz#326ef9726d4ffe72d9a82a7bbe19379de692017d"
|
||||
integrity sha512-wOlqdqziE/NNTUJsfSgXmBMIrYmfd5V0HCGsR8uAKHcg+h9NENWINcfRjtWGU77wDHC8B8ijV4hMTGYbrKovng==
|
||||
|
||||
cliui@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5"
|
||||
|
Reference in New Issue
Block a user