Repository: provectus/kafka-ui
Issue: Space sensitive syntax for Create Connector
Labels: type/bug
scope/frontend
status/accepted
status/confirmed

**Describe the bug**
If there is a space before a code of connector the button Submit is inactive and the warning 'config is not JSON object' is displayed. If you delete the space the code is acceptable. This behavior is confusing because it`s not obvious that the problem is just in space (for instance for Schema it works well with space before the code)


**Set up**
(How do you run the app? 
Which version of the app are you running? Provide either docker image version or check commit hash at the top left corner. We won't be able to help you without this information.)
http://redesign.internal.kafka-ui.provectus.io/

**Steps to Reproduce**
Steps to reproduce the behavior:

1. Navigate to item menu Kafka Connect
2. Click the button Create connector
3. Input Name and Config with a space before code

**Expected behavior**
A connector is created regardless of the space in syntax  

**Screenshots**
With space
![image](https://user-images.githubusercontent.com/92585878/145425328-bc5ca911-f61a-4a27-b1fd-e029013c4866.png)
Without space (the same code)
![image](https://user-images.githubusercontent.com/92585878/145426734-91e7a900-749d-4d42-b0d6-b85be188ea2c.png)


**Additional context**
(Add any other context about the problem here)
The same bug in the old and redesigned versions https://www.kafka-ui.provectus.io/ui http://redesign.internal.kafka-ui.provectus.io/

Issue comments:

Pull request review comments:
We have either to remove `aria-label="form"` or change `aria-label` to something meaningful 
In your case its something like `Connector config edit` (I'm not sure on this text)

Reasoning: Aria label has to be meaningful and unique 
[MDN role form](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/form_role)
[MDN aria-label](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label)
I thought we are getting rid of snapshots
@workshur can clarify this
```js
await waitFor(() => fireEvent.submit(screen.getByRole('form')));
```
Is use of `fireEvent.submit` - desired approach?

I thought about case where someone deletes `Submit` button and this test won't fail

I'm not sure about this
```js
userEvent.click(screen.getByText('Submit'));
```
got a lot of problems with submitting react hook form because of connector
this is the only approach which works

Patch that resolved the issue:
diff --git a/kafka-ui-react-app/src/components/Connect/Edit/Edit.tsx b/kafka-ui-react-app/src/components/Connect/Edit/Edit.tsx
index 3e8cb1dce97..baf933cfdef 100644
--- a/kafka-ui-react-app/src/components/Connect/Edit/Edit.tsx
+++ b/kafka-ui-react-app/src/components/Connect/Edit/Edit.tsx
@@ -88,7 +88,7 @@ const Edit: React.FC<EditProps> = ({
         clusterName,
         connectName,
         connectorName,
-        JSON.parse(values.config)
+        JSON.parse(values.config.trim())
       );
       if (connector) {
         history.push(
@@ -116,7 +116,7 @@ const Edit: React.FC<EditProps> = ({
           accidentally breaking your connector config!
         </ConnectEditWarningMessageStyled>
       )}
-      <form onSubmit={handleSubmit(onSubmit)}>
+      <form onSubmit={handleSubmit(onSubmit)} aria-label="Edit connect form">
         <div>
           <Controller
             control={control}
diff --git a/kafka-ui-react-app/src/components/Connect/Edit/__tests__/Edit.spec.tsx b/kafka-ui-react-app/src/components/Connect/Edit/__tests__/Edit.spec.tsx
index 44e4268fe4e..54c45619c67 100644
--- a/kafka-ui-react-app/src/components/Connect/Edit/__tests__/Edit.spec.tsx
+++ b/kafka-ui-react-app/src/components/Connect/Edit/__tests__/Edit.spec.tsx
@@ -1,8 +1,5 @@
 import React from 'react';
-import { create } from 'react-test-renderer';
-import { mount } from 'enzyme';
-import { act } from 'react-dom/test-utils';
-import { containerRendersView, TestRouterWrapper } from 'lib/testHelpers';
+import { containerRendersView, render } from 'lib/testHelpers';
 import {
   clusterConnectConnectorConfigPath,
   clusterConnectConnectorEditPath,
@@ -10,8 +7,9 @@ import {
 import EditContainer from 'components/Connect/Edit/EditContainer';
 import Edit, { EditProps } from 'components/Connect/Edit/Edit';
 import { connector } from 'redux/reducers/connect/__test__/fixtures';
-import { ThemeProvider } from 'styled-components';
-import theme from 'theme/theme';
+import { Route } from 'react-router';
+import { waitFor } from '@testing-library/dom';
+import { fireEvent, screen } from '@testing-library/react';
 
 jest.mock('components/common/PageLoader/PageLoader', () => 'mock-PageLoader');
 
@@ -38,12 +36,9 @@ describe('Edit', () => {
     const connectName = 'my-connect';
     const connectorName = 'my-connector';
 
-    const setupWrapper = (props: Partial<EditProps> = {}) => (
-      <ThemeProvider theme={theme}>
-        <TestRouterWrapper
-          pathname={pathname}
-          urlParams={{ clusterName, connectName, connectorName }}
-        >
+    const renderComponent = (props: Partial<EditProps> = {}) =>
+      render(
+        <Route path={pathname}>
           <Edit
             fetchConfig={jest.fn()}
             isConfigFetching={false}
@@ -51,30 +46,19 @@ describe('Edit', () => {
             updateConfig={jest.fn()}
             {...props}
           />
-        </TestRouterWrapper>
-      </ThemeProvider>
-    );
-
-    it('matches snapshot', () => {
-      const wrapper = create(setupWrapper());
-      expect(wrapper.toJSON()).toMatchSnapshot();
-    });
-
-    it('matches snapshot when fetching config', () => {
-      const wrapper = create(setupWrapper({ isConfigFetching: true }));
-      expect(wrapper.toJSON()).toMatchSnapshot();
-    });
-
-    it('matches snapshot when config has credentials', () => {
-      const wrapper = create(
-        setupWrapper({ config: { ...connector.config, password: '******' } })
+        </Route>,
+        {
+          pathname: clusterConnectConnectorEditPath(
+            clusterName,
+            connectName,
+            connectorName
+          ),
+        }
       );
-      expect(wrapper.toJSON()).toMatchSnapshot();
-    });
 
     it('fetches config on mount', () => {
       const fetchConfig = jest.fn();
-      mount(setupWrapper({ fetchConfig }));
+      renderComponent({ fetchConfig });
       expect(fetchConfig).toHaveBeenCalledTimes(1);
       expect(fetchConfig).toHaveBeenCalledWith(
         clusterName,
@@ -85,10 +69,8 @@ describe('Edit', () => {
 
     it('calls updateConfig on form submit', async () => {
       const updateConfig = jest.fn();
-      const wrapper = mount(setupWrapper({ updateConfig }));
-      await act(async () => {
-        wrapper.find('form').simulate('submit');
-      });
+      renderComponent({ updateConfig });
+      await waitFor(() => fireEvent.submit(screen.getByRole('form')));
       expect(updateConfig).toHaveBeenCalledTimes(1);
       expect(updateConfig).toHaveBeenCalledWith(
         clusterName,
@@ -100,10 +82,8 @@ describe('Edit', () => {
 
     it('redirects to connector config view on successful submit', async () => {
       const updateConfig = jest.fn().mockResolvedValueOnce(connector);
-      const wrapper = mount(setupWrapper({ updateConfig }));
-      await act(async () => {
-        wrapper.find('form').simulate('submit');
-      });
+      renderComponent({ updateConfig });
+      await waitFor(() => fireEvent.submit(screen.getByRole('form')));
       expect(mockHistoryPush).toHaveBeenCalledTimes(1);
       expect(mockHistoryPush).toHaveBeenCalledWith(
         clusterConnectConnectorConfigPath(
@@ -116,10 +96,8 @@ describe('Edit', () => {
 
     it('does not redirect to connector config view on unsuccessful submit', async () => {
       const updateConfig = jest.fn().mockResolvedValueOnce(undefined);
-      const wrapper = mount(setupWrapper({ updateConfig }));
-      await act(async () => {
-        wrapper.find('form').simulate('submit');
-      });
+      renderComponent({ updateConfig });
+      await waitFor(() => fireEvent.submit(screen.getByRole('form')));
       expect(mockHistoryPush).not.toHaveBeenCalled();
     });
   });
diff --git a/kafka-ui-react-app/src/components/Connect/Edit/__tests__/__snapshots__/Edit.spec.tsx.snap b/kafka-ui-react-app/src/components/Connect/Edit/__tests__/__snapshots__/Edit.spec.tsx.snap
deleted file mode 100644
index f8dd0c4fb6b..00000000000
--- a/kafka-ui-react-app/src/components/Connect/Edit/__tests__/__snapshots__/Edit.spec.tsx.snap
+++ /dev/null
@@ -1,209 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Edit view matches snapshot 1`] = `
-.c1 {
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: -ms-flexbox;
-  display: flex;
-  -webkit-flex-direction: row;
-  -ms-flex-direction: row;
-  flex-direction: row;
-  -webkit-align-items: center;
-  -webkit-box-align: center;
-  -ms-flex-align: center;
-  align-items: center;
-  -webkit-box-pack: center;
-  -webkit-justify-content: center;
-  -ms-flex-pack: center;
-  justify-content: center;
-  padding: 0px 12px;
-  border: none;
-  border-radius: 4px;
-  white-space: nowrap;
-  background: #4F4FFF;
-  color: #FFFFFF;
-  font-size: 14px;
-  font-weight: 500;
-  height: 32px;
-}
-
-.c1:hover:enabled {
-  background: #1717CF;
-  color: #FFFFFF;
-  cursor: pointer;
-}
-
-.c1:active:enabled {
-  background: #1414B8;
-  color: #FFFFFF;
-}
-
-.c1:disabled {
-  opacity: 0.5;
-  cursor: not-allowed;
-}
-
-.c1 a {
-  color: white;
-}
-
-.c1 i {
-  margin-right: 7px;
-}
-
-.c0 {
-  margin: 16px;
-}
-
-.c0 form > *:last-child {
-  margin-top: 16px;
-}
-
-<div
-  className="c0"
->
-  <form
-    onSubmit={[Function]}
-  >
-    <div>
-      <mock-Editor
-        name="config"
-        onBlur={[Function]}
-        onChange={[Function]}
-        readOnly={false}
-        value="{
-	\\"connector.class\\": \\"FileStreamSource\\",
-	\\"tasks.max\\": \\"10\\",
-	\\"topic\\": \\"test-topic\\",
-	\\"file\\": \\"/some/file\\"
-}"
-      />
-    </div>
-    <div />
-    <button
-      className="c1"
-      disabled={true}
-      type="submit"
-    >
-      Submit
-    </button>
-  </form>
-</div>
-`;
-
-exports[`Edit view matches snapshot when config has credentials 1`] = `
-.c2 {
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: -ms-flexbox;
-  display: flex;
-  -webkit-flex-direction: row;
-  -ms-flex-direction: row;
-  flex-direction: row;
-  -webkit-align-items: center;
-  -webkit-box-align: center;
-  -ms-flex-align: center;
-  align-items: center;
-  -webkit-box-pack: center;
-  -webkit-justify-content: center;
-  -ms-flex-pack: center;
-  justify-content: center;
-  padding: 0px 12px;
-  border: none;
-  border-radius: 4px;
-  white-space: nowrap;
-  background: #4F4FFF;
-  color: #FFFFFF;
-  font-size: 14px;
-  font-weight: 500;
-  height: 32px;
-}
-
-.c2:hover:enabled {
-  background: #1717CF;
-  color: #FFFFFF;
-  cursor: pointer;
-}
-
-.c2:active:enabled {
-  background: #1414B8;
-  color: #FFFFFF;
-}
-
-.c2:disabled {
-  opacity: 0.5;
-  cursor: not-allowed;
-}
-
-.c2 a {
-  color: white;
-}
-
-.c2 i {
-  margin-right: 7px;
-}
-
-.c0 {
-  margin: 16px;
-}
-
-.c0 form > *:last-child {
-  margin-top: 16px;
-}
-
-.c1 {
-  height: 48px;
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: -ms-flexbox;
-  display: flex;
-  -webkit-align-items: center;
-  -webkit-box-align: center;
-  -ms-flex-align: center;
-  align-items: center;
-  background-color: #FFEECC;
-  border-radius: 8px;
-  padding: 8px;
-  margin-bottom: 16px;
-}
-
-<div
-  className="c0"
->
-  <div
-    className="c1"
-  >
-    Please replace ****** with the real credential values to avoid accidentally breaking your connector config!
-  </div>
-  <form
-    onSubmit={[Function]}
-  >
-    <div>
-      <mock-Editor
-        name="config"
-        onBlur={[Function]}
-        onChange={[Function]}
-        readOnly={false}
-        value="{
-	\\"connector.class\\": \\"FileStreamSource\\",
-	\\"tasks.max\\": \\"10\\",
-	\\"topic\\": \\"test-topic\\",
-	\\"file\\": \\"/some/file\\",
-	\\"password\\": \\"******\\"
-}"
-      />
-    </div>
-    <div />
-    <button
-      className="c2"
-      disabled={true}
-      type="submit"
-    >
-      Submit
-    </button>
-  </form>
-</div>
-`;
-
-exports[`Edit view matches snapshot when fetching config 1`] = `<mock-PageLoader />`;
diff --git a/kafka-ui-react-app/src/components/Connect/New/New.styled.ts b/kafka-ui-react-app/src/components/Connect/New/New.styled.ts
new file mode 100644
index 00000000000..c24eff561a3
--- /dev/null
+++ b/kafka-ui-react-app/src/components/Connect/New/New.styled.ts
@@ -0,0 +1,12 @@
+import styled from 'styled-components';
+
+export const NewConnectFormStyled = styled.form`
+  padding: 0 16px 16px;
+  display: flex;
+  flex-direction: column;
+  gap: 16px;
+
+  & > button:last-child {
+    align-self: flex-start;
+  }
+`;
diff --git a/kafka-ui-react-app/src/components/Connect/New/New.tsx b/kafka-ui-react-app/src/components/Connect/New/New.tsx
index 205acb50013..0e25e934cbc 100644
--- a/kafka-ui-react-app/src/components/Connect/New/New.tsx
+++ b/kafka-ui-react-app/src/components/Connect/New/New.tsx
@@ -14,9 +14,10 @@ import Select from 'components/common/Select/Select';
 import { FormError } from 'components/common/Input/Input.styled';
 import Input from 'components/common/Input/Input';
 import { Button } from 'components/common/Button/Button';
-import styled from 'styled-components';
 import PageHeading from 'components/common/PageHeading/PageHeading';
 
+import * as S from './New.styled';
+
 const validationSchema = yup.object().shape({
   name: yup.string().required(),
   config: yup.string().required().isJsonObject(),
@@ -26,17 +27,6 @@ interface RouterParams {
   clusterName: ClusterName;
 }
 
-const NewConnectFormStyled = styled.form`
-  padding: 16px;
-  padding-top: 0;
-  display: flex;
-  flex-direction: column;
-  gap: 16px;
-  & > button:last-child {
-    align-self: flex-start;
-  }
-`;
-
 export interface NewProps {
   fetchConnects(clusterName: ClusterName): void;
   areConnectsFetching: boolean;
@@ -99,7 +89,7 @@ const New: React.FC<NewProps> = ({
     async (values: FormValues) => {
       const connector = await createConnector(clusterName, values.connectName, {
         name: values.name,
-        config: JSON.parse(values.config),
+        config: JSON.parse(values.config.trim()),
       });
       if (connector) {
         history.push(
@@ -125,7 +115,10 @@ const New: React.FC<NewProps> = ({
   return (
     <FormProvider {...methods}>
       <PageHeading text="Create new connector" />
-      <NewConnectFormStyled onSubmit={handleSubmit(onSubmit)}>
+      <S.NewConnectFormStyled
+        onSubmit={handleSubmit(onSubmit)}
+        aria-label="Create connect form"
+      >
         <div className={['field', connectNameFieldClassName].join(' ')}>
           <InputLabel>Connect *</InputLabel>
           <Select selectSize="M" name="connectName" disabled={isSubmitting}>
@@ -175,7 +168,7 @@ const New: React.FC<NewProps> = ({
         >
           Submit
         </Button>
-      </NewConnectFormStyled>
+      </S.NewConnectFormStyled>
     </FormProvider>
   );
 };
diff --git a/kafka-ui-react-app/src/components/Connect/New/__tests__/New.spec.tsx b/kafka-ui-react-app/src/components/Connect/New/__tests__/New.spec.tsx
index aa9b73d440a..f333197ae92 100644
--- a/kafka-ui-react-app/src/components/Connect/New/__tests__/New.spec.tsx
+++ b/kafka-ui-react-app/src/components/Connect/New/__tests__/New.spec.tsx
@@ -1,8 +1,5 @@
 import React from 'react';
-import { create, act as rendererAct } from 'react-test-renderer';
-import { mount, ReactWrapper } from 'enzyme';
-import { act } from 'react-dom/test-utils';
-import { containerRendersView, TestRouterWrapper } from 'lib/testHelpers';
+import { containerRendersView, render } from 'lib/testHelpers';
 import {
   clusterConnectConnectorPath,
   clusterConnectorNewPath,
@@ -10,12 +7,19 @@ import {
 import NewContainer from 'components/Connect/New/NewContainer';
 import New, { NewProps } from 'components/Connect/New/New';
 import { connects, connector } from 'redux/reducers/connect/__test__/fixtures';
-import { ThemeProvider } from 'styled-components';
-import theme from 'theme/theme';
+import { Route } from 'react-router';
+import { act, fireEvent, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { waitFor } from '@testing-library/dom';
+import { ControllerRenderProps } from 'react-hook-form';
 
 jest.mock('components/common/PageLoader/PageLoader', () => 'mock-PageLoader');
-
-jest.mock('components/common/Editor/Editor', () => 'mock-Editor');
+jest.mock(
+  'components/common/Editor/Editor',
+  () => (props: ControllerRenderProps) => {
+    return <textarea {...props} placeholder="json" />;
+  }
+);
 
 const mockHistoryPush = jest.fn();
 jest.mock('react-router-dom', () => ({
@@ -29,22 +33,27 @@ describe('New', () => {
   containerRendersView(<NewContainer />, New);
 
   describe('view', () => {
-    const pathname = clusterConnectorNewPath(':clusterName');
     const clusterName = 'my-cluster';
-    const simulateFormSubmit = (wrapper: ReactWrapper) =>
-      act(async () => {
-        wrapper.find('input[name="name"]').simulate('change', {
-          target: { name: 'name', value: 'my-connector' },
-        });
-        wrapper
-          .find('mock-Editor')
-          .simulate('change', { target: { value: '{"class":"MyClass"}' } });
-        wrapper.find('button[type="submit"]').simulate('submit');
+    const simulateFormSubmit = async () => {
+      userEvent.type(
+        screen.getByPlaceholderText('Connector Name'),
+        'my-connector'
+      );
+      userEvent.type(
+        screen.getByPlaceholderText('json'),
+        '{"class":"MyClass"}'.replace(/[{[]/g, '$&$&')
+      );
+      expect(screen.getByPlaceholderText('json')).toHaveValue(
+        '{"class":"MyClass"}'
+      );
+      await waitFor(() => {
+        fireEvent.submit(screen.getByRole('form'));
       });
+    };
 
-    const setupWrapper = (props: Partial<NewProps> = {}) => (
-      <ThemeProvider theme={theme}>
-        <TestRouterWrapper pathname={pathname} urlParams={{ clusterName }}>
+    const renderComponent = (props: Partial<NewProps> = {}) =>
+      render(
+        <Route path={clusterConnectorNewPath(':clusterName')}>
           <New
             fetchConnects={jest.fn()}
             areConnectsFetching={false}
@@ -52,30 +61,14 @@ describe('New', () => {
             createConnector={jest.fn()}
             {...props}
           />
-        </TestRouterWrapper>
-      </ThemeProvider>
-    );
-
-    it('matches snapshot', async () => {
-      let wrapper = create(<div />);
-      await rendererAct(async () => {
-        wrapper = create(setupWrapper());
-      });
-      expect(wrapper.toJSON()).toMatchSnapshot();
-    });
-
-    it('matches snapshot when fetching connects', async () => {
-      let wrapper = create(<div />);
-      await rendererAct(async () => {
-        wrapper = create(setupWrapper({ areConnectsFetching: true }));
-      });
-      expect(wrapper.toJSON()).toMatchSnapshot();
-    });
+        </Route>,
+        { pathname: clusterConnectorNewPath(clusterName) }
+      );
 
     it('fetches connects on mount', async () => {
       const fetchConnects = jest.fn();
       await act(async () => {
-        mount(setupWrapper({ fetchConnects }));
+        renderComponent({ fetchConnects });
       });
       expect(fetchConnects).toHaveBeenCalledTimes(1);
       expect(fetchConnects).toHaveBeenCalledWith(clusterName);
@@ -83,8 +76,8 @@ describe('New', () => {
 
     it('calls createConnector on form submit', async () => {
       const createConnector = jest.fn();
-      const wrapper = mount(setupWrapper({ createConnector }));
-      await simulateFormSubmit(wrapper);
+      renderComponent({ createConnector });
+      await simulateFormSubmit();
       expect(createConnector).toHaveBeenCalledTimes(1);
       expect(createConnector).toHaveBeenCalledWith(
         clusterName,
@@ -98,8 +91,8 @@ describe('New', () => {
 
     it('redirects to connector details view on successful submit', async () => {
       const createConnector = jest.fn().mockResolvedValue(connector);
-      const wrapper = mount(setupWrapper({ createConnector }));
-      await simulateFormSubmit(wrapper);
+      renderComponent({ createConnector });
+      await simulateFormSubmit();
       expect(mockHistoryPush).toHaveBeenCalledTimes(1);
       expect(mockHistoryPush).toHaveBeenCalledWith(
         clusterConnectConnectorPath(
@@ -112,8 +105,8 @@ describe('New', () => {
 
     it('does not redirect to connector details view on unsuccessful submit', async () => {
       const createConnector = jest.fn().mockResolvedValueOnce(undefined);
-      const wrapper = mount(setupWrapper({ createConnector }));
-      await simulateFormSubmit(wrapper);
+      renderComponent({ createConnector });
+      await simulateFormSubmit();
       expect(mockHistoryPush).not.toHaveBeenCalled();
     });
   });
diff --git a/kafka-ui-react-app/src/components/Connect/New/__tests__/__snapshots__/New.spec.tsx.snap b/kafka-ui-react-app/src/components/Connect/New/__tests__/__snapshots__/New.spec.tsx.snap
deleted file mode 100644
index 6030c7d78e2..00000000000
--- a/kafka-ui-react-app/src/components/Connect/New/__tests__/__snapshots__/New.spec.tsx.snap
+++ /dev/null
@@ -1,336 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`New view matches snapshot 1`] = `
-Array [
-  .c0 {
-  height: 56px;
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: -ms-flexbox;
-  display: flex;
-  -webkit-box-pack: justify;
-  -webkit-justify-content: space-between;
-  -ms-flex-pack: justify;
-  justify-content: space-between;
-  -webkit-align-items: center;
-  -webkit-box-align: center;
-  -ms-flex-align: center;
-  align-items: center;
-  padding: 0px 16px;
-}
-
-.c0 h1 {
-  font-size: 24px;
-  font-weight: 500;
-  line-height: 32px;
-  color: #000;
-}
-
-.c0 > div {
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: -ms-flexbox;
-  display: flex;
-  gap: 16px;
-}
-
-<div
-    className="c0"
-  >
-    <h1>
-      Create new connector
-    </h1>
-    <div />
-  </div>,
-  .c1 {
-  font-weight: 500;
-  font-size: 12px;
-  line-height: 20px;
-  color: #454F54;
-}
-
-.c3 {
-  height: 32px;
-  border: 1px #ABB5BA solid;
-  border-radius: 4px;
-  font-size: 14px;
-  width: 100%;
-  padding-left: 12px;
-  padding-right: 16px;
-  color: #171A1C;
-  min-width: auto;
-  background-image: url('data:image/svg+xml,%3Csvg width="10" height="6" viewBox="0 0 10 6" fill="none" xmlns="http://www.w3.org/2000/svg"%3E%3Cpath d="M1 1L5 5L9 1" stroke="%23454F54"/%3E%3C/svg%3E%0A') !important;
-  background-repeat: no-repeat !important;
-  background-position-x: calc(100% - 8px) !important;
-  background-position-y: 55% !important;
-  -webkit-appearance: none !important;
-  -moz-appearance: none !important;
-  appearance: none !important;
-}
-
-.c3:hover {
-  color: #171A1C;
-  border-color: #73848C;
-}
-
-.c3:focus {
-  outline: none;
-  color: #171A1C;
-  border-color: #454F54;
-}
-
-.c3:disabled {
-  color: #ABB5BA;
-  border-color: #E3E6E8;
-  cursor: not-allowed;
-}
-
-.c2 {
-  position: relative;
-}
-
-.c6 {
-  border: 1px #ABB5BA solid;
-  border-radius: 4px;
-  height: 32px;
-  width: 100%;
-  padding-left: 12px;
-  font-size: 14px;
-}
-
-.c6::-webkit-input-placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c6::-moz-placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c6:-ms-input-placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c6::placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c6:hover {
-  border-color: #73848C;
-}
-
-.c6:focus {
-  outline: none;
-  border-color: #454F54;
-}
-
-.c6:focus::-webkit-input-placeholder {
-  color: transparent;
-}
-
-.c6:focus::-moz-placeholder {
-  color: transparent;
-}
-
-.c6:focus:-ms-input-placeholder {
-  color: transparent;
-}
-
-.c6:focus::placeholder {
-  color: transparent;
-}
-
-.c6:disabled {
-  color: #ABB5BA;
-  border-color: #E3E6E8;
-  cursor: not-allowed;
-}
-
-.c6:read-only {
-  color: #171A1C;
-  border: none;
-  background-color: #F1F2F3;
-  cursor: not-allowed;
-}
-
-.c6:-moz-read-only:focus::placeholder {
-  color: #ABB5BA;
-}
-
-.c6:read-only:focus::placeholder {
-  color: #ABB5BA;
-}
-
-.c4 {
-  color: #E51A1A;
-  font-size: 12px;
-}
-
-.c5 {
-  position: relative;
-}
-
-.c7 {
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: -ms-flexbox;
-  display: flex;
-  -webkit-flex-direction: row;
-  -ms-flex-direction: row;
-  flex-direction: row;
-  -webkit-align-items: center;
-  -webkit-box-align: center;
-  -ms-flex-align: center;
-  align-items: center;
-  -webkit-box-pack: center;
-  -webkit-justify-content: center;
-  -ms-flex-pack: center;
-  justify-content: center;
-  padding: 0px 12px;
-  border: none;
-  border-radius: 4px;
-  white-space: nowrap;
-  background: #4F4FFF;
-  color: #FFFFFF;
-  font-size: 14px;
-  font-weight: 500;
-  height: 32px;
-}
-
-.c7:hover:enabled {
-  background: #1717CF;
-  color: #FFFFFF;
-  cursor: pointer;
-}
-
-.c7:active:enabled {
-  background: #1414B8;
-  color: #FFFFFF;
-}
-
-.c7:disabled {
-  opacity: 0.5;
-  cursor: not-allowed;
-}
-
-.c7 a {
-  color: white;
-}
-
-.c7 i {
-  margin-right: 7px;
-}
-
-.c0 {
-  padding: 16px;
-  padding-top: 0;
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: -ms-flexbox;
-  display: flex;
-  -webkit-flex-direction: column;
-  -ms-flex-direction: column;
-  flex-direction: column;
-  gap: 16px;
-}
-
-.c0 > button:last-child {
-  -webkit-align-self: flex-start;
-  -ms-flex-item-align: start;
-  align-self: flex-start;
-}
-
-<form
-    className="c0"
-    onSubmit={[Function]}
-  >
-    <div
-      className="field "
-    >
-      <label
-        className="c1"
-      >
-        Connect *
-      </label>
-      <div
-        className="select-wrapper c2"
-      >
-        <select
-          className="c3"
-          disabled={false}
-          name="connectName"
-          onBlur={[Function]}
-          onChange={[Function]}
-          role="listbox"
-        >
-          <option
-            value="first"
-          >
-            first
-          </option>
-          <option
-            value="second"
-          >
-            second
-          </option>
-        </select>
-      </div>
-      <p
-        className="c4"
-      />
-    </div>
-    <div>
-      <label
-        className="c1"
-      >
-        Name *
-      </label>
-      <div
-        className="c5"
-      >
-        <input
-          autoComplete="off"
-          className="c6 c5"
-          disabled={false}
-          name="name"
-          onBlur={[Function]}
-          onChange={[Function]}
-          placeholder="Connector Name"
-        />
-      </div>
-      <p
-        className="c4"
-      />
-    </div>
-    <div>
-      <label
-        className="c1"
-      >
-        Config *
-      </label>
-      <mock-Editor
-        name="config"
-        onBlur={[Function]}
-        onChange={[Function]}
-        readOnly={false}
-        value=""
-      />
-      <p
-        className="c4"
-      />
-    </div>
-    <button
-      className="c7"
-      disabled={true}
-      type="submit"
-    >
-      Submit
-    </button>
-  </form>,
-]
-`;
-
-exports[`New view matches snapshot when fetching connects 1`] = `<mock-PageLoader />`;
diff --git a/kafka-ui-react-app/src/lib/yupExtended.ts b/kafka-ui-react-app/src/lib/yupExtended.ts
index fab2a290018..91f88c3c279 100644
--- a/kafka-ui-react-app/src/lib/yupExtended.ts
+++ b/kafka-ui-react-app/src/lib/yupExtended.ts
@@ -16,11 +16,13 @@ declare module 'yup' {
 export const isValidJsonObject = (value?: string) => {
   try {
     if (!value) return false;
+
+    const trimmedValue = value.trim();
     if (
-      value.indexOf('{') === 0 &&
-      value.lastIndexOf('}') === value.length - 1
+      trimmedValue.indexOf('{') === 0 &&
+      trimmedValue.lastIndexOf('}') === trimmedValue.length - 1
     ) {
-      JSON.parse(value);
+      JSON.parse(trimmedValue);
       return true;
     }
   } catch {


Test patch: