From f50c09ed377781f6abf6e81ba04992078504fb53 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Mon, 11 Oct 2021 23:22:12 +0200 Subject: [PATCH] feat: Introduce InputCreator --- packages/backend/src/graphql/types/app.ts | 1 + packages/backend/src/graphql/types/field.ts | 1 + packages/web/package.json | 1 + .../src/components/AddAppConnection/index.tsx | 37 +++++++++-- .../src/components/AddAppConnection/style.ts | 9 +++ packages/web/src/components/Form/index.tsx | 20 ++++++ .../web/src/components/InputCreator/index.tsx | 47 ++++++++++++++ .../web/src/components/TextField/index.tsx | 64 ++++++++++++++++--- .../graphql/mutations/create-credentials.ts | 14 ++++ packages/web/src/graphql/queries/get-app.ts | 3 +- packages/web/src/helpers/copyInputValue.ts | 4 ++ packages/web/src/types/app.ts | 5 +- yarn.lock | 5 ++ 13 files changed, 196 insertions(+), 15 deletions(-) create mode 100644 packages/web/src/components/AddAppConnection/style.ts create mode 100644 packages/web/src/components/Form/index.tsx create mode 100644 packages/web/src/components/InputCreator/index.tsx create mode 100644 packages/web/src/graphql/mutations/create-credentials.ts create mode 100644 packages/web/src/helpers/copyInputValue.ts diff --git a/packages/backend/src/graphql/types/app.ts b/packages/backend/src/graphql/types/app.ts index 26699be9..fa28253e 100644 --- a/packages/backend/src/graphql/types/app.ts +++ b/packages/backend/src/graphql/types/app.ts @@ -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 }, diff --git a/packages/backend/src/graphql/types/field.ts b/packages/backend/src/graphql/types/field.ts index 19e08d73..3903508a 100644 --- a/packages/backend/src/graphql/types/field.ts +++ b/packages/backend/src/graphql/types/field.ts @@ -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}, diff --git a/packages/web/package.json b/packages/web/package.json index 243a27b6..41b20dbc 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -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", diff --git a/packages/web/src/components/AddAppConnection/index.tsx b/packages/web/src/components/AddAppConnection/index.tsx index d8f5bb91..56453f57 100644 --- a/packages/web/src/components/AddAppConnection/index.tsx +++ b/packages/web/src/components/AddAppConnection/index.tsx @@ -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 = (data) => { + const variables = { + key: application.key, + displayName: data.displayName, + data: { + consumerKey: data.consumerKey, + consumerSecret: data.consumerSecret + } + }; + + createCredentials({ variables }); + + onClose?.(); + }; return ( Add connection - - Add a connection to {name} + +
+ {fields?.map(field => ())} + + +
diff --git a/packages/web/src/components/AddAppConnection/style.ts b/packages/web/src/components/AddAppConnection/style.ts new file mode 100644 index 00000000..8a8949ea --- /dev/null +++ b/packages/web/src/components/AddAppConnection/style.ts @@ -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), +})); diff --git a/packages/web/src/components/Form/index.tsx b/packages/web/src/components/Form/index.tsx new file mode 100644 index 00000000..9beeb1dd --- /dev/null +++ b/packages/web/src/components/Form/index.tsx @@ -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; +} + +export default function Form(props: FormProps) { + const { children, onSubmit, ...formProps } = props; + const methods: UseFormReturn = useForm(); + + return ( + +
+ {children} +
+
+ ); +}; diff --git a/packages/web/src/components/InputCreator/index.tsx b/packages/web/src/components/InputCreator/index.tsx new file mode 100644 index 00000000..330aa958 --- /dev/null +++ b/packages/web/src/components/InputCreator/index.tsx @@ -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 ( + + ); +}; diff --git a/packages/web/src/components/TextField/index.tsx b/packages/web/src/components/TextField/index.tsx index 36bd484a..8ad9f0bb 100644 --- a/packages/web/src/components/TextField/index.tsx +++ b/packages/web/src/components/TextField/index.tsx @@ -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; + control?: Control; + shouldUnregister?: boolean; name: string; + clickToCopy?: boolean; + readOnly?: boolean; +} & MuiTextFieldProps; + +const createCopyAdornment = (ref: React.RefObject) => { + return ( + + copyInputValue(ref.current as HTMLInputElement)} + edge="end" + > + + + +); } -export default function TextField({ control, name }: TextFieldProps) { +export default function TextField(props: TextFieldProps) { + const inputRef = useRef(null); + const { + control, + required, + name, + defaultValue, + shouldUnregister, + clickToCopy, + readOnly, + ...textFieldProps + } = props; + return ( } + shouldUnregister={shouldUnregister} + render={({ field: { ref, ...field } }) => ( + { inputRef.current = element; ref(element); }} + InputProps={{ readOnly, endAdornment: clickToCopy ? createCopyAdornment(inputRef) : null}} + /> + )} /> ); }; + +TextField.defaultProps = { + readOnly: false, + disabled: false, + clickToCopy: false, + shouldUnregister: false, +}; diff --git a/packages/web/src/graphql/mutations/create-credentials.ts b/packages/web/src/graphql/mutations/create-credentials.ts new file mode 100644 index 00000000..ed9841a1 --- /dev/null +++ b/packages/web/src/graphql/mutations/create-credentials.ts @@ -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 + } + } + } +`; diff --git a/packages/web/src/graphql/queries/get-app.ts b/packages/web/src/graphql/queries/get-app.ts index 2999b2cf..96e72ebc 100644 --- a/packages/web/src/graphql/queries/get-app.ts +++ b/packages/web/src/graphql/queries/get-app.ts @@ -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 diff --git a/packages/web/src/helpers/copyInputValue.ts b/packages/web/src/helpers/copyInputValue.ts new file mode 100644 index 00000000..8cecfa91 --- /dev/null +++ b/packages/web/src/helpers/copyInputValue.ts @@ -0,0 +1,4 @@ +import copy from 'clipboard-copy'; +export default function copyInputValue(element: HTMLInputElement) { + copy(element.value); +}; diff --git a/packages/web/src/types/app.ts b/packages/web/src/types/app.ts index 98655e1a..dccbf0b1 100644 --- a/packages/web/src/types/app.ts +++ b/packages/web/src/types/app.ts @@ -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 }; diff --git a/yarn.lock b/yarn.lock index 2ab38478..76aece98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"