QueryForm.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. import React, { useCallback, useRef } from 'react';
  2. import { FormError } from 'components/common/Input/Input.styled';
  3. import { ErrorMessage } from '@hookform/error-message';
  4. import { useForm, Controller, useFieldArray } from 'react-hook-form';
  5. import { Button } from 'components/common/Button/Button';
  6. import IconButtonWrapper from 'components/common/Icons/IconButtonWrapper';
  7. import CloseIcon from 'components/common/Icons/CloseIcon';
  8. import { yupResolver } from '@hookform/resolvers/yup';
  9. import yup from 'lib/yupExtended';
  10. import PlusIcon from 'components/common/Icons/PlusIcon';
  11. import ReactAce from 'react-ace/lib/ace';
  12. import * as S from './QueryForm.styled';
  13. export interface Props {
  14. fetching: boolean;
  15. hasResults: boolean;
  16. handleClearResults: () => void;
  17. handleSSECancel: () => void;
  18. submitHandler: (values: FormValues) => void;
  19. }
  20. type StreamsPropertiesType = {
  21. key: string;
  22. value: string;
  23. };
  24. export type FormValues = {
  25. ksql: string;
  26. streamsProperties: StreamsPropertiesType[];
  27. };
  28. const streamsPropertiesSchema = yup.object().shape({
  29. key: yup.string().trim(),
  30. value: yup.string().trim(),
  31. });
  32. const validationSchema = yup.object({
  33. ksql: yup.string().trim().required(),
  34. streamsProperties: yup.array().of(streamsPropertiesSchema),
  35. });
  36. const QueryForm: React.FC<Props> = ({
  37. fetching,
  38. hasResults,
  39. handleClearResults,
  40. handleSSECancel,
  41. submitHandler,
  42. }) => {
  43. const {
  44. handleSubmit,
  45. setValue,
  46. getValues,
  47. control,
  48. formState: { errors },
  49. } = useForm<FormValues>({
  50. mode: 'onTouched',
  51. resolver: yupResolver(validationSchema),
  52. defaultValues: {
  53. ksql: '',
  54. streamsProperties: [{ key: '', value: '' }],
  55. },
  56. });
  57. const { fields, append, remove } = useFieldArray<
  58. FormValues,
  59. 'streamsProperties'
  60. >({
  61. control,
  62. name: 'streamsProperties',
  63. });
  64. const handleAddNewProperty = useCallback(() => {
  65. if (
  66. getValues().streamsProperties.every((prop) => {
  67. return prop.key;
  68. })
  69. ) {
  70. append({ key: '', value: '' });
  71. }
  72. }, []);
  73. const inputRef = useRef<ReactAce>(null);
  74. const handleFocus = () => {
  75. // eslint-disable-next-line @typescript-eslint/no-explicit-any
  76. const textInput = inputRef?.current?.editor?.textInput as any;
  77. if (textInput) {
  78. textInput.focus();
  79. }
  80. };
  81. return (
  82. <S.QueryWrapper>
  83. <form onSubmit={handleSubmit(submitHandler)}>
  84. <S.KSQLInputsWrapper>
  85. <S.Fieldset aria-labelledby="ksqlLabel">
  86. <S.KSQLInputHeader>
  87. <label id="ksqlLabel">KSQL</label>
  88. <Button
  89. onClick={() => setValue('ksql', '')}
  90. buttonType="primary"
  91. buttonSize="S"
  92. isInverted
  93. >
  94. Clear
  95. </Button>
  96. </S.KSQLInputHeader>
  97. <Controller
  98. control={control}
  99. name="ksql"
  100. render={({ field }) => (
  101. <S.SQLEditor
  102. {...field}
  103. commands={[
  104. {
  105. // commands is array of key bindings.
  106. // name for the key binding.
  107. name: 'commandName',
  108. // key combination used for the command.
  109. bindKey: { win: 'Ctrl-Enter', mac: 'Command-Enter' },
  110. // function to execute when keys are pressed.
  111. exec: () => {
  112. handleSubmit(submitHandler)();
  113. },
  114. },
  115. ]}
  116. readOnly={fetching}
  117. ref={inputRef}
  118. />
  119. )}
  120. />
  121. <FormError>
  122. <ErrorMessage errors={errors} name="ksql" />
  123. </FormError>
  124. </S.Fieldset>
  125. <S.StreamPropertiesContainer>
  126. Stream properties:
  127. {fields.map((item, index) => (
  128. <S.InputsContainer key={item.id}>
  129. <S.StreamPropertiesInputWrapper>
  130. <Controller
  131. control={control}
  132. name={`streamsProperties.${index}.key`}
  133. render={({ field }) => (
  134. <input
  135. {...field}
  136. placeholder="Key"
  137. aria-label="key"
  138. type="text"
  139. autoComplete="off"
  140. />
  141. )}
  142. />
  143. <FormError>
  144. <ErrorMessage
  145. errors={errors}
  146. name={`streamsProperties.${index}.key`}
  147. />
  148. </FormError>
  149. </S.StreamPropertiesInputWrapper>
  150. <S.StreamPropertiesInputWrapper>
  151. <Controller
  152. control={control}
  153. name={`streamsProperties.${index}.value`}
  154. render={({ field }) => (
  155. <input
  156. {...field}
  157. placeholder="Value"
  158. aria-label="value"
  159. type="text"
  160. autoComplete="off"
  161. />
  162. )}
  163. />
  164. <FormError>
  165. <ErrorMessage
  166. errors={errors}
  167. name={`streamsProperties.${index}.value`}
  168. />
  169. </FormError>
  170. </S.StreamPropertiesInputWrapper>
  171. <S.DeleteButtonWrapper onClick={() => remove(index)}>
  172. <IconButtonWrapper aria-label="deleteProperty">
  173. <CloseIcon aria-hidden />
  174. </IconButtonWrapper>
  175. </S.DeleteButtonWrapper>
  176. </S.InputsContainer>
  177. ))}
  178. <Button
  179. type="button"
  180. buttonSize="M"
  181. buttonType="secondary"
  182. onClick={handleAddNewProperty}
  183. >
  184. <PlusIcon />
  185. Add Stream Property
  186. </Button>
  187. </S.StreamPropertiesContainer>
  188. </S.KSQLInputsWrapper>
  189. <S.KSQLButtons>
  190. <Button
  191. buttonType="primary"
  192. buttonSize="M"
  193. type="submit"
  194. disabled={fetching}
  195. onClick={handleFocus}
  196. >
  197. Execute
  198. </Button>
  199. <Button
  200. buttonType="secondary"
  201. buttonSize="M"
  202. disabled={!fetching}
  203. onClick={handleSSECancel}
  204. >
  205. Stop query
  206. </Button>
  207. <Button
  208. buttonType="secondary"
  209. buttonSize="M"
  210. disabled={fetching || !hasResults}
  211. onClick={handleClearResults}
  212. >
  213. Clear results
  214. </Button>
  215. </S.KSQLButtons>
  216. </form>
  217. </S.QueryWrapper>
  218. );
  219. };
  220. export default QueryForm;