Selaa lähdekoodia

[Fixes #1534] No originally selected custom parameters in topic's edit tab (#1538)

- Fixes No originally selected custom parameters in topic's edit tab #1534
- Adds test case for CustomParams to verify fix
- Updates CustomParamsField to use predefined value
- Renames INDEX_PREFIX to TOPIC_CUSTOM_PARAMS_PREFIX and moves it to constants file
- Removes unused configs from Topic/Edit component
- Rewrites DangerZone styled's
- Rewrites DangerZone tests
- Adds margin to DangerZone to match TopicForm width
- Adds simple Topic/Edit test
- Changes sonar-project.properties file's sonar.exclusions to correctly ignore paths

Signed-off-by: Roman Zabaluev <rzabaluev@provectus.com>

Co-authored-by: Roman Zabaluev <rzabaluev@provectus.com>
Damir Abdulganiev 3 vuotta sitten
vanhempi
commit
ced74ac550
18 muutettua tiedostoa jossa 789 lisäystä ja 499 poistoa
  1. 1 1
      .github/workflows/backend.yml
  2. 1 1
      .github/workflows/e2e-checks.yaml
  3. 1 1
      kafka-ui-react-app/sonar-project.properties
  4. 4 5
      kafka-ui-react-app/src/components/Topics/Topic/Edit/DangerZone/DangerZone.styled.tsx
  5. 10 14
      kafka-ui-react-app/src/components/Topics/Topic/Edit/DangerZone/DangerZone.tsx
  6. 15 27
      kafka-ui-react-app/src/components/Topics/Topic/Edit/Edit.tsx
  7. 116 0
      kafka-ui-react-app/src/components/Topics/Topic/Edit/__test__/DangerZone.spec.tsx
  8. 34 0
      kafka-ui-react-app/src/components/Topics/Topic/Edit/__test__/Edit.spec.tsx
  9. 553 0
      kafka-ui-react-app/src/components/Topics/Topic/Edit/__test__/fixtures.ts
  10. 0 64
      kafka-ui-react-app/src/components/Topics/Topic/Edit/__tests__/DangerZone.spec.tsx
  11. 0 360
      kafka-ui-react-app/src/components/Topics/Topic/Edit/__tests__/__snapshots__/DangerZone.spec.tsx.snap
  12. 2 1
      kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamField.tsx
  13. 4 6
      kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParams.tsx
  14. 0 0
      kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__test__/CustomParamField.spec.tsx
  15. 30 15
      kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__test__/CustomParams.spec.tsx
  16. 15 0
      kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__test__/fixtures.ts
  17. 2 4
      kafka-ui-react-app/src/components/Topics/shared/Form/TopicForm.tsx
  18. 1 0
      kafka-ui-react-app/src/lib/constants.ts

+ 1 - 1
.github/workflows/backend.yml

@@ -19,7 +19,7 @@ jobs:
       - name: compose app
         id: step_five
         run: |
-          docker-compose -f ./docker/kafka-ui.yaml up -d
+          docker-compose -f ./documentation/compose/kafka-ui.yaml up -d
       - name: Set up JDK 1.13
         uses: actions/setup-java@v1
         with:

+ 1 - 1
.github/workflows/e2e-checks.yaml

@@ -39,7 +39,7 @@ jobs:
         id: compose_app
         # use the following command until #819 will be fixed
         run: |
-          docker-compose -f ./docker/kafka-ui-connectors.yaml up -d
+          docker-compose -f ./documentation/compose/kafka-ui-connectors.yaml up -d
       - name: e2e run
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}  # Needed to get PR information, if any

+ 1 - 1
kafka-ui-react-app/sonar-project.properties

@@ -2,7 +2,7 @@ sonar.projectKey=provectus_kafka-ui_frontend
 sonar.organization=provectus
 
 sonar.sources=.
-sonar.exclusions="**/__test?__/**,src/setupWorker.ts,src/setupTests.ts,**/fixtures.ts,src/lib/testHelpers.tsx"
+sonar.exclusions=**/__test?__/**,src/setupWorker.ts,src/setupTests.ts,**/fixtures.ts,src/lib/testHelpers.tsx
 
 sonar.typescript.lcov.reportPaths=./coverage/lcov.info
 sonar.testExecutionReportPaths=./test-report.xml

+ 4 - 5
kafka-ui-react-app/src/components/Topics/Topic/Edit/DangerZone/DangerZone.styled.tsx

@@ -1,12 +1,11 @@
 import styled from 'styled-components';
 
-export const DangerZoneWrapperStyled = styled.div`
-  margin-top: 16px;
+export const Wrapper = styled.div`
+  margin: 16px;
   padding: 8px 16px;
   border: 1px solid ${({ theme }) => theme.dangerZone.borderColor};
   box-sizing: border-box;
   border-radius: 8px;
-  margin-bottom: 16px;
 
   & > div {
     display: flex;
@@ -15,13 +14,13 @@ export const DangerZoneWrapperStyled = styled.div`
   }
 `;
 
-export const DangerZoneTitleStyled = styled.h1`
+export const Title = styled.h1`
   color: ${({ theme }) => theme.dangerZone.color};
   font-size: 20px;
   padding-bottom: 16px;
 `;
 
-export const DagerZoneFormStyled = styled.form`
+export const Form = styled.form`
   display: flex;
   align-items: flex-end;
   gap: 16px;

+ 10 - 14
kafka-ui-react-app/src/components/Topics/Topic/Edit/DangerZone/DangerZone.tsx

@@ -7,11 +7,7 @@ import { InputLabel } from 'components/common/Input/InputLabel.styled';
 import React from 'react';
 import { FormProvider, useForm } from 'react-hook-form';
 
-import {
-  DagerZoneFormStyled,
-  DangerZoneTitleStyled,
-  DangerZoneWrapperStyled,
-} from './DangerZone.styled';
+import * as S from './DangerZone.styled';
 
 export interface Props {
   clusterName: string;
@@ -109,12 +105,13 @@ const DangerZone: React.FC<Props> = ({
     );
   };
   return (
-    <DangerZoneWrapperStyled>
-      <DangerZoneTitleStyled>Danger Zone</DangerZoneTitleStyled>
+    <S.Wrapper>
+      <S.Title>Danger Zone</S.Title>
       <div>
         <FormProvider {...partitionsMethods}>
-          <DagerZoneFormStyled
+          <S.Form
             onSubmit={partitionsMethods.handleSubmit(validatePartitions)}
+            aria-label="Edit number of partitions"
           >
             <div>
               <InputLabel htmlFor="partitions">
@@ -137,12 +134,11 @@ const DangerZone: React.FC<Props> = ({
                 buttonSize="M"
                 type="submit"
                 disabled={!partitionsMethods.formState.isDirty}
-                data-testid="partitionsSubmit"
               >
                 Submit
               </Button>
             </div>
-          </DagerZoneFormStyled>
+          </S.Form>
         </FormProvider>
         <FormError>
           <ErrorMessage
@@ -160,10 +156,11 @@ const DangerZone: React.FC<Props> = ({
         </ConfirmationModal>
 
         <FormProvider {...replicationFactorMethods}>
-          <DagerZoneFormStyled
+          <S.Form
             onSubmit={replicationFactorMethods.handleSubmit(
               validateReplicationFactor
             )}
+            aria-label="Edit replication factor"
           >
             <div>
               <InputLabel htmlFor="replicationFactor">
@@ -185,12 +182,11 @@ const DangerZone: React.FC<Props> = ({
                 buttonSize="M"
                 type="submit"
                 disabled={!replicationFactorMethods.formState.isDirty}
-                data-testid="replicationFactorSubmit"
               >
                 Submit
               </Button>
             </div>
-          </DagerZoneFormStyled>
+          </S.Form>
         </FormProvider>
 
         <FormError>
@@ -207,7 +203,7 @@ const DangerZone: React.FC<Props> = ({
           Are you sure you want to update the replication factor?
         </ConfirmationModal>
       </div>
-    </DangerZoneWrapperStyled>
+    </S.Wrapper>
   );
 };
 

+ 15 - 27
kafka-ui-react-app/src/components/Topics/Topic/Edit/Edit.tsx

@@ -7,21 +7,19 @@ import {
   TopicWithDetailedInfo,
   TopicFormData,
 } from 'redux/interfaces';
-import { TopicConfig } from 'generated-sources';
 import { useForm, FormProvider } from 'react-hook-form';
-import { camelCase } from 'lodash';
 import TopicForm from 'components/Topics/shared/Form/TopicForm';
 import { clusterTopicPath } from 'lib/paths';
 import { useHistory } from 'react-router';
 import { yupResolver } from '@hookform/resolvers/yup';
 import { topicFormValidationSchema } from 'lib/yupExtended';
-import { TOPIC_CUSTOM_PARAMS } from 'lib/constants';
+import { TOPIC_CUSTOM_PARAMS_PREFIX, TOPIC_CUSTOM_PARAMS } from 'lib/constants';
 import styled from 'styled-components';
 import PageHeading from 'components/common/PageHeading/PageHeading';
 
 import DangerZoneContainer from './DangerZone/DangerZoneContainer';
 
-interface Props {
+export interface Props {
   clusterName: ClusterName;
   topicName: TopicName;
   topic?: TopicWithDetailedInfo;
@@ -64,27 +62,18 @@ const topicParams = (topic: TopicWithDetailedInfo | undefined) => {
 
   const { name, replicationFactor } = topic;
 
-  const configs = topic.config?.reduce(
-    (result: { [key: string]: TopicConfig['value'] }, param) => ({
-      ...result,
-      [camelCase(param.name)]: param.value || param.defaultValue,
-    }),
-    {}
-  );
-
   return {
     ...DEFAULTS,
     name,
     partitions: topic.partitionCount || DEFAULTS.partitions,
     replicationFactor,
-    customParams: topic.config
+    [TOPIC_CUSTOM_PARAMS_PREFIX]: topic.config
       ?.filter(
         (el) =>
           el.value !== el.defaultValue &&
           Object.keys(TOPIC_CUSTOM_PARAMS).includes(el.name)
       )
       .map((el) => ({ name: el.name, value: el.value })),
-    ...configs,
   };
 };
 
@@ -99,8 +88,10 @@ const Edit: React.FC<Props> = ({
   fetchTopicConfig,
   updateTopic,
 }) => {
-  const defaultValues = topicParams(topic);
-
+  const defaultValues = React.useMemo(
+    () => topicParams(topic),
+    [topicParams, topic]
+  );
   const methods = useForm<TopicFormData>({
     defaultValues,
     resolver: yupResolver(topicFormValidationSchema),
@@ -147,17 +138,14 @@ const Edit: React.FC<Props> = ({
       <PageHeading text={`Edit ${topicName}`} />
       <EditWrapperStyled>
         <div>
-          <div>
-            <FormProvider {...methods}>
-              <TopicForm
-                topicName={topicName}
-                config={config}
-                isSubmitting={isSubmitting}
-                isEditing
-                onSubmit={methods.handleSubmit(onSubmit)}
-              />
-            </FormProvider>
-          </div>
+          <FormProvider {...methods}>
+            <TopicForm
+              topicName={topicName}
+              isSubmitting={isSubmitting}
+              isEditing
+              onSubmit={methods.handleSubmit(onSubmit)}
+            />
+          </FormProvider>
           {topic && (
             <DangerZoneContainer
               defaultPartitions={defaultValues.partitions}

+ 116 - 0
kafka-ui-react-app/src/components/Topics/Topic/Edit/__test__/DangerZone.spec.tsx

@@ -0,0 +1,116 @@
+import React from 'react';
+import DangerZone, {
+  Props,
+} from 'components/Topics/Topic/Edit/DangerZone/DangerZone';
+import { screen, waitFor, within } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { render } from 'lib/testHelpers';
+
+import { topicName, clusterName } from './fixtures';
+
+const renderComponent = (props?: Partial<Props>) =>
+  render(
+    <DangerZone
+      clusterName={clusterName}
+      topicName={topicName}
+      defaultPartitions={3}
+      defaultReplicationFactor={3}
+      partitionsCountIncreased={false}
+      replicationFactorUpdated={false}
+      updateTopicPartitionsCount={jest.fn()}
+      updateTopicReplicationFactor={jest.fn()}
+      {...props}
+    />
+  );
+
+const clickOnDialogSubmitButton = () => {
+  userEvent.click(
+    within(screen.getByRole('dialog')).getByRole('button', {
+      name: 'Submit',
+    })
+  );
+};
+describe('DangerZone', () => {
+  it('renders', () => {
+    renderComponent();
+
+    const numberOfPartitionsEditForm = screen.getByRole('form', {
+      name: 'Edit number of partitions',
+    });
+    expect(numberOfPartitionsEditForm).toBeInTheDocument();
+    expect(
+      within(numberOfPartitionsEditForm).getByRole('spinbutton', {
+        name: 'Number of partitions *',
+      })
+    ).toBeInTheDocument();
+    expect(
+      within(numberOfPartitionsEditForm).getByRole('button', { name: 'Submit' })
+    ).toBeInTheDocument();
+
+    const replicationFactorEditForm = screen.getByRole('form', {
+      name: 'Edit replication factor',
+    });
+    expect(replicationFactorEditForm).toBeInTheDocument();
+    expect(
+      within(replicationFactorEditForm).getByRole('spinbutton', {
+        name: 'Replication Factor *',
+      })
+    ).toBeInTheDocument();
+    expect(
+      within(replicationFactorEditForm).getByRole('button', { name: 'Submit' })
+    ).toBeInTheDocument();
+  });
+
+  it('calls updateTopicPartitionsCount', async () => {
+    const mockUpdateTopicPartitionsCount = jest.fn();
+    renderComponent({
+      updateTopicPartitionsCount: mockUpdateTopicPartitionsCount,
+    });
+    const numberOfPartitionsEditForm = screen.getByRole('form', {
+      name: 'Edit number of partitions',
+    });
+
+    userEvent.type(
+      within(numberOfPartitionsEditForm).getByRole('spinbutton'),
+      '4'
+    );
+    userEvent.click(within(numberOfPartitionsEditForm).getByRole('button'));
+
+    await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument());
+    await waitFor(() => clickOnDialogSubmitButton());
+
+    expect(mockUpdateTopicPartitionsCount).toHaveBeenCalledTimes(1);
+  });
+
+  it('calls updateTopicReplicationFactor', async () => {
+    const mockUpdateTopicReplicationFactor = jest.fn();
+    renderComponent({
+      updateTopicReplicationFactor: mockUpdateTopicReplicationFactor,
+    });
+
+    const replicationFactorEditForm = screen.getByRole('form', {
+      name: 'Edit replication factor',
+    });
+    expect(
+      within(replicationFactorEditForm).getByRole('spinbutton', {
+        name: 'Replication Factor *',
+      })
+    ).toBeInTheDocument();
+    expect(
+      within(replicationFactorEditForm).getByRole('button', { name: 'Submit' })
+    ).toBeInTheDocument();
+
+    userEvent.type(
+      within(replicationFactorEditForm).getByRole('spinbutton'),
+      '4'
+    );
+    userEvent.click(within(replicationFactorEditForm).getByRole('button'));
+
+    await waitFor(() => expect(screen.getByRole('dialog')).toBeInTheDocument());
+    await waitFor(() => clickOnDialogSubmitButton());
+
+    await waitFor(() => {
+      expect(mockUpdateTopicReplicationFactor).toHaveBeenCalledTimes(1);
+    });
+  });
+});

+ 34 - 0
kafka-ui-react-app/src/components/Topics/Topic/Edit/__test__/Edit.spec.tsx

@@ -0,0 +1,34 @@
+import React from 'react';
+import Edit, { Props } from 'components/Topics/Topic/Edit/Edit';
+import { screen } from '@testing-library/react';
+import { render } from 'lib/testHelpers';
+
+import { topicName, clusterName, topicWithInfo } from './fixtures';
+
+const renderComponent = (props?: Partial<Props>) =>
+  render(
+    <Edit
+      clusterName={clusterName}
+      topicName={topicName}
+      topic={topicWithInfo}
+      isFetched
+      isTopicUpdated={false}
+      fetchTopicConfig={jest.fn()}
+      updateTopic={jest.fn()}
+      updateTopicPartitionsCount={jest.fn()}
+      {...props}
+    />
+  );
+
+describe('DangerZone', () => {
+  it('renders', () => {
+    renderComponent();
+
+    expect(
+      screen.getByRole('heading', { name: `Edit ${topicName}` })
+    ).toBeInTheDocument();
+    expect(
+      screen.getByRole('heading', { name: `Danger Zone` })
+    ).toBeInTheDocument();
+  });
+});

+ 553 - 0
kafka-ui-react-app/src/components/Topics/Topic/Edit/__test__/fixtures.ts

@@ -0,0 +1,553 @@
+import { CleanUpPolicy, ConfigSource, TopicConfig } from 'generated-sources';
+import { TopicWithDetailedInfo } from 'redux/interfaces/topic';
+
+export const clusterName = 'testCluster';
+export const topicName = 'testTopic';
+
+export const config: TopicConfig[] = [
+  {
+    name: 'compression.type',
+    value: 'producer',
+    defaultValue: 'producer',
+    source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'compression.type',
+        value: 'producer',
+        source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+      },
+      {
+        name: 'compression.type',
+        value: 'producer',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'confluent.value.schema.validation',
+    value: 'false',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [],
+  },
+  {
+    name: 'leader.replication.throttled.replicas',
+    value: '',
+    defaultValue: '',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [],
+  },
+  {
+    name: 'confluent.key.subject.name.strategy',
+    value: 'io.confluent.kafka.serializers.subject.TopicNameStrategy',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [],
+  },
+  {
+    name: 'message.downconversion.enable',
+    value: 'true',
+    defaultValue: 'true',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.message.downconversion.enable',
+        value: 'true',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'min.insync.replicas',
+    value: '1',
+    defaultValue: '1',
+    source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'min.insync.replicas',
+        value: '1',
+        source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+      },
+      {
+        name: 'min.insync.replicas',
+        value: '1',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'segment.jitter.ms',
+    value: '0',
+    defaultValue: '0',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [],
+  },
+  {
+    name: 'cleanup.policy',
+    value: 'delete',
+    defaultValue: 'delete',
+    source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'cleanup.policy',
+        value: 'delete',
+        source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+      },
+      {
+        name: 'log.cleanup.policy',
+        value: 'delete',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'flush.ms',
+    value: '9223372036854775807',
+    defaultValue: '9223372036854775807',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [],
+  },
+  {
+    name: 'confluent.tier.local.hotset.ms',
+    value: '86400000',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'confluent.tier.local.hotset.ms',
+        value: '86400000',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'follower.replication.throttled.replicas',
+    value: '',
+    defaultValue: '',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [],
+  },
+  {
+    name: 'confluent.tier.local.hotset.bytes',
+    value: '-1',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'confluent.tier.local.hotset.bytes',
+        value: '-1',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'confluent.value.subject.name.strategy',
+    value: 'io.confluent.kafka.serializers.subject.TopicNameStrategy',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [],
+  },
+  {
+    name: 'segment.bytes',
+    value: '1073741824',
+    defaultValue: '1073741824',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.segment.bytes',
+        value: '1073741824',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'retention.ms',
+    value: '604800000',
+    defaultValue: '604800000',
+    source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'retention.ms',
+        value: '604800000',
+        source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'flush.messages',
+    value: '9223372036854775807',
+    defaultValue: '9223372036854775807',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.flush.interval.messages',
+        value: '9223372036854775807',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'confluent.tier.enable',
+    value: 'false',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'confluent.tier.enable',
+        value: 'false',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'confluent.tier.segment.hotset.roll.min.bytes',
+    value: '104857600',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'confluent.tier.segment.hotset.roll.min.bytes',
+        value: '104857600',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'confluent.segment.speculative.prefetch.enable',
+    value: 'false',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'confluent.segment.speculative.prefetch.enable',
+        value: 'false',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'message.format.version',
+    value: '2.7-IV2',
+    defaultValue: '2.7-IV2',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.message.format.version',
+        value: '2.7-IV2',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'max.compaction.lag.ms',
+    value: '9223372036854775807',
+    defaultValue: '9223372036854775807',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.cleaner.max.compaction.lag.ms',
+        value: '9223372036854775807',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'file.delete.delay.ms',
+    value: '60000',
+    defaultValue: '60000',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.segment.delete.delay.ms',
+        value: '60000',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'max.message.bytes',
+    value: '1000012',
+    defaultValue: '1000012',
+    source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'max.message.bytes',
+        value: '1000012',
+        source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+      },
+      {
+        name: 'message.max.bytes',
+        value: '1048588',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'min.compaction.lag.ms',
+    value: '0',
+    defaultValue: '0',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.cleaner.min.compaction.lag.ms',
+        value: '0',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'message.timestamp.type',
+    value: 'CreateTime',
+    defaultValue: 'CreateTime',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.message.timestamp.type',
+        value: 'CreateTime',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'preallocate',
+    value: 'false',
+    defaultValue: 'false',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.preallocate',
+        value: 'false',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'confluent.placement.constraints',
+    value: '',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [],
+  },
+  {
+    name: 'min.cleanable.dirty.ratio',
+    value: '0.5',
+    defaultValue: '0.5',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.cleaner.min.cleanable.ratio',
+        value: '0.5',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'index.interval.bytes',
+    value: '4096',
+    defaultValue: '4096',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.index.interval.bytes',
+        value: '4096',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'unclean.leader.election.enable',
+    value: 'false',
+    defaultValue: 'false',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'unclean.leader.election.enable',
+        value: 'false',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'retention.bytes',
+    value: '-1',
+    defaultValue: '-1',
+    source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'retention.bytes',
+        value: '-1',
+        source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+      },
+      {
+        name: 'log.retention.bytes',
+        value: '-1',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'delete.retention.ms',
+    value: '86400001',
+    defaultValue: '86400000',
+    source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'delete.retention.ms',
+        value: '86400001',
+        source: ConfigSource.DYNAMIC_TOPIC_CONFIG,
+      },
+      {
+        name: 'log.cleaner.delete.retention.ms',
+        value: '86400000',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'confluent.prefer.tier.fetch.ms',
+    value: '-1',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'confluent.prefer.tier.fetch.ms',
+        value: '-1',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'confluent.key.schema.validation',
+    value: 'false',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [],
+  },
+  {
+    name: 'segment.ms',
+    value: '604800000',
+    defaultValue: '604800000',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [],
+  },
+  {
+    name: 'message.timestamp.difference.max.ms',
+    value: '9223372036854775807',
+    defaultValue: '9223372036854775807',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.message.timestamp.difference.max.ms',
+        value: '9223372036854775807',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+  {
+    name: 'segment.index.bytes',
+    value: '10485760',
+    defaultValue: '10485760',
+    source: ConfigSource.DEFAULT_CONFIG,
+    isSensitive: false,
+    isReadOnly: false,
+    synonyms: [
+      {
+        name: 'log.index.size.max.bytes',
+        value: '10485760',
+        source: ConfigSource.DEFAULT_CONFIG,
+      },
+    ],
+  },
+];
+
+export const partitions = [
+  {
+    partition: 0,
+    leader: 2,
+    replicas: [
+      {
+        broker: 2,
+        leader: false,
+        inSync: true,
+      },
+    ],
+    offsetMax: 0,
+    offsetMin: 0,
+  },
+];
+
+export const topicWithInfo: TopicWithDetailedInfo = {
+  name: topicName,
+  internal: false,
+  partitionCount: 1,
+  replicationFactor: 1,
+  replicas: 1,
+  inSyncReplicas: 1,
+  segmentSize: 0,
+  segmentCount: 1,
+  underReplicatedPartitions: 0,
+  cleanUpPolicy: CleanUpPolicy.DELETE,
+  partitions,
+  config,
+};

+ 0 - 64
kafka-ui-react-app/src/components/Topics/Topic/Edit/__tests__/DangerZone.spec.tsx

@@ -1,64 +0,0 @@
-import React from 'react';
-import DangerZone, {
-  Props,
-} from 'components/Topics/Topic/Edit/DangerZone/DangerZone';
-import { screen, waitFor } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import { render } from 'lib/testHelpers';
-
-const setupWrapper = (props?: Partial<Props>) => (
-  <DangerZone
-    clusterName="testCluster"
-    topicName="testTopic"
-    defaultPartitions={3}
-    defaultReplicationFactor={3}
-    partitionsCountIncreased={false}
-    replicationFactorUpdated={false}
-    updateTopicPartitionsCount={jest.fn()}
-    updateTopicReplicationFactor={jest.fn()}
-    {...props}
-  />
-);
-
-describe('DangerZone', () => {
-  it('is rendered properly', () => {
-    const component = render(setupWrapper());
-    expect(component.baseElement).toMatchSnapshot();
-  });
-
-  it('calls updateTopicPartitionsCount', async () => {
-    const mockUpdateTopicPartitionsCount = jest.fn();
-    render(
-      setupWrapper({
-        updateTopicPartitionsCount: mockUpdateTopicPartitionsCount,
-      })
-    );
-
-    userEvent.type(screen.getByLabelText('Number of partitions *'), '4');
-    userEvent.click(screen.getByTestId('partitionsSubmit'));
-
-    await waitFor(() => {
-      userEvent.click(screen.getAllByText('Submit')[1]);
-      expect(mockUpdateTopicPartitionsCount).toHaveBeenCalledTimes(1);
-    });
-  });
-
-  it('calls updateTopicReplicationFactor', async () => {
-    const mockUpdateTopicReplicationFactor = jest.fn();
-    render(
-      setupWrapper({
-        updateTopicReplicationFactor: mockUpdateTopicReplicationFactor,
-      })
-    );
-
-    userEvent.type(screen.getByLabelText('Replication Factor *'), '4');
-    userEvent.click(screen.getByTestId('replicationFactorSubmit'));
-    await waitFor(() => {
-      userEvent.click(screen.getAllByText('Submit')[2]);
-    });
-
-    await waitFor(() => {
-      expect(mockUpdateTopicReplicationFactor).toHaveBeenCalledTimes(1);
-    });
-  });
-});

+ 0 - 360
kafka-ui-react-app/src/components/Topics/Topic/Edit/__tests__/__snapshots__/DangerZone.spec.tsx.snap

@@ -1,360 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`DangerZone is rendered properly 1`] = `
-.c6 {
-  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;
-}
-
-.c6:hover:enabled {
-  background: #1717CF;
-  color: #FFFFFF;
-  cursor: pointer;
-}
-
-.c6:active:enabled {
-  background: #1414B8;
-  color: #FFFFFF;
-}
-
-.c6:disabled {
-  opacity: 0.5;
-  cursor: not-allowed;
-}
-
-.c6 a {
-  color: #FFFFFF;
-}
-
-.c6 i {
-  margin-right: 7px;
-}
-
-.c5 {
-  border: 1px #ABB5BA solid;
-  border-radius: 4px;
-  height: 32px;
-  width: 100%;
-  padding-left: 12px;
-  font-size: 14px;
-}
-
-.c5::-webkit-input-placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c5::-moz-placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c5:-ms-input-placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c5::placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c5:hover {
-  border-color: #73848C;
-}
-
-.c5:focus {
-  outline: none;
-  border-color: #454F54;
-}
-
-.c5:focus::-webkit-input-placeholder {
-  color: transparent;
-}
-
-.c5:focus::-moz-placeholder {
-  color: transparent;
-}
-
-.c5:focus:-ms-input-placeholder {
-  color: transparent;
-}
-
-.c5:focus::placeholder {
-  color: transparent;
-}
-
-.c5:disabled {
-  color: #ABB5BA;
-  border-color: #E3E6E8;
-  cursor: not-allowed;
-}
-
-.c5:read-only {
-  color: #171A1C;
-  border: none;
-  background-color: #F1F2F3;
-  cursor: not-allowed;
-}
-
-.c5:-moz-read-only:focus::placeholder {
-  color: #ABB5BA;
-}
-
-.c5:read-only:focus::placeholder {
-  color: #ABB5BA;
-}
-
-.c8 {
-  border: 1px #ABB5BA solid;
-  border-radius: 4px;
-  height: 40px;
-  width: 100%;
-  padding-left: 12px;
-  font-size: 14px;
-}
-
-.c8::-webkit-input-placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c8::-moz-placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c8:-ms-input-placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c8::placeholder {
-  color: #ABB5BA;
-  font-size: 14px;
-}
-
-.c8:hover {
-  border-color: #73848C;
-}
-
-.c8:focus {
-  outline: none;
-  border-color: #454F54;
-}
-
-.c8:focus::-webkit-input-placeholder {
-  color: transparent;
-}
-
-.c8:focus::-moz-placeholder {
-  color: transparent;
-}
-
-.c8:focus:-ms-input-placeholder {
-  color: transparent;
-}
-
-.c8:focus::placeholder {
-  color: transparent;
-}
-
-.c8:disabled {
-  color: #ABB5BA;
-  border-color: #E3E6E8;
-  cursor: not-allowed;
-}
-
-.c8:read-only {
-  color: #171A1C;
-  border: none;
-  background-color: #F1F2F3;
-  cursor: not-allowed;
-}
-
-.c8:-moz-read-only:focus::placeholder {
-  color: #ABB5BA;
-}
-
-.c8:read-only:focus::placeholder {
-  color: #ABB5BA;
-}
-
-.c7 {
-  color: #E51A1A;
-  font-size: 12px;
-}
-
-.c4 {
-  position: relative;
-}
-
-.c3 {
-  font-weight: 500;
-  font-size: 12px;
-  line-height: 20px;
-  color: #454F54;
-}
-
-.c0 {
-  margin-top: 16px;
-  padding: 8px 16px;
-  border: 1px solid #E3E6E8;
-  box-sizing: border-box;
-  border-radius: 8px;
-  margin-bottom: 16px;
-}
-
-.c0 > div {
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: -ms-flexbox;
-  display: flex;
-  -webkit-flex-direction: column;
-  -ms-flex-direction: column;
-  flex-direction: column;
-  gap: 8px;
-}
-
-.c1 {
-  color: #E51A1A;
-  font-size: 20px;
-  padding-bottom: 16px;
-}
-
-.c2 {
-  display: -webkit-box;
-  display: -webkit-flex;
-  display: -ms-flexbox;
-  display: flex;
-  -webkit-align-items: flex-end;
-  -webkit-box-align: flex-end;
-  -ms-flex-align: flex-end;
-  align-items: flex-end;
-  gap: 16px;
-}
-
-.c2 > *:first-child {
-  -webkit-box-flex: 4;
-  -webkit-flex-grow: 4;
-  -ms-flex-positive: 4;
-  flex-grow: 4;
-}
-
-.c2 > *:last-child {
-  -webkit-box-flex: 1;
-  -webkit-flex-grow: 1;
-  -ms-flex-positive: 1;
-  flex-grow: 1;
-}
-
-<body>
-  <div>
-    <div
-      class="c0"
-    >
-      <h1
-        class="c1"
-      >
-        Danger Zone
-      </h1>
-      <div>
-        <form
-          class="c2"
-        >
-          <div>
-            <label
-              class="c3"
-              for="partitions"
-            >
-              Number of partitions *
-            </label>
-            <div
-              class="c4"
-            >
-              <input
-                class="c5 c4"
-                id="partitions"
-                name="partitions"
-                placeholder="Number of partitions"
-                type="number"
-              />
-            </div>
-          </div>
-          <div>
-            <button
-              class="c6"
-              data-testid="partitionsSubmit"
-              disabled=""
-              type="submit"
-            >
-              Submit
-            </button>
-          </div>
-        </form>
-        <p
-          class="c7"
-        />
-        <form
-          class="c2"
-        >
-          <div>
-            <label
-              class="c3"
-              for="replicationFactor"
-            >
-              Replication Factor *
-            </label>
-            <div
-              class="c4"
-            >
-              <input
-                class="c8 c4"
-                id="replicationFactor"
-                name="replicationFactor"
-                placeholder="Replication Factor"
-                type="number"
-              />
-            </div>
-          </div>
-          <div>
-            <button
-              class="c6"
-              data-testid="replicationFactorSubmit"
-              disabled=""
-              type="submit"
-            >
-              Submit
-            </button>
-          </div>
-        </form>
-        <p
-          class="c7"
-        />
-      </div>
-    </div>
-  </div>
-</body>
-`;

+ 2 - 1
kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParamField.tsx

@@ -64,13 +64,14 @@ const CustomParamField: React.FC<Props> = ({
           control={control}
           rules={{ required: 'Custom Parameter is required.' }}
           name={`customParams.${index}.name`}
-          render={({ field: { name, onChange } }) => (
+          render={({ field: { value, name, onChange } }) => (
             <Select
               name={name}
               placeholder="Select"
               disabled={isDisabled}
               minWidth="270px"
               onChange={onChange}
+              value={value}
               options={Object.keys(TOPIC_CUSTOM_PARAMS)
                 .sort()
                 .map((opt) => ({

+ 4 - 6
kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/CustomParams.tsx

@@ -1,27 +1,25 @@
 import React from 'react';
-import { TopicConfigByName, TopicFormData } from 'redux/interfaces';
+import { TopicFormData } from 'redux/interfaces';
 import { useFieldArray, useFormContext, useWatch } from 'react-hook-form';
 import { Button } from 'components/common/Button/Button';
+import { TOPIC_CUSTOM_PARAMS_PREFIX } from 'lib/constants';
 
 import CustomParamField from './CustomParamField';
 import * as S from './CustomParams.styled';
 
-export const INDEX_PREFIX = 'customParams';
-
 export interface CustomParamsProps {
   isSubmitting: boolean;
-  config?: TopicConfigByName;
 }
 
 const CustomParams: React.FC<CustomParamsProps> = ({ isSubmitting }) => {
   const { control } = useFormContext<TopicFormData>();
   const { fields, append, remove } = useFieldArray({
     control,
-    name: INDEX_PREFIX,
+    name: TOPIC_CUSTOM_PARAMS_PREFIX,
   });
   const watchFieldArray = useWatch({
     control,
-    name: INDEX_PREFIX,
+    name: TOPIC_CUSTOM_PARAMS_PREFIX,
     defaultValue: fields,
   });
   const controlledFields = fields.map((field, index) => {

+ 0 - 0
kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__tests__/CustomParamField.spec.tsx → kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__test__/CustomParamField.spec.tsx


+ 30 - 15
kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__tests__/CustomParams.spec.tsx → kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__test__/CustomParams.spec.tsx

@@ -8,6 +8,8 @@ import { FormProvider, useForm } from 'react-hook-form';
 import userEvent from '@testing-library/user-event';
 import { TOPIC_CUSTOM_PARAMS } from 'lib/constants';
 
+import { defaultValues } from './fixtures';
+
 const selectOption = async (listbox: HTMLElement, option: string) => {
   await waitFor(() => {
     userEvent.click(listbox);
@@ -44,31 +46,44 @@ const expectOptionAvailability = async (
   await waitFor(() => userEvent.click(listbox));
 };
 
-describe('CustomParams', () => {
-  const setupComponent = (props: CustomParamsProps) => {
-    const Wrapper: React.FC = ({ children }) => {
-      const methods = useForm();
-      return <FormProvider {...methods}>{children}</FormProvider>;
-    };
-
-    return render(
-      <Wrapper>
-        <CustomParams {...props} />
-      </Wrapper>
-    );
+const renderComponent = (props: CustomParamsProps, defaults = {}) => {
+  const Wrapper: React.FC = ({ children }) => {
+    const methods = useForm({ defaultValues: defaults });
+    return <FormProvider {...methods}>{children}</FormProvider>;
   };
 
-  beforeEach(() => {
-    setupComponent({ isSubmitting: false });
-  });
+  return render(
+    <Wrapper>
+      <CustomParams {...props} />
+    </Wrapper>
+  );
+};
 
+describe('CustomParams', () => {
   it('renders with props', () => {
+    renderComponent({ isSubmitting: false });
+
     const button = screen.getByRole('button');
     expect(button).toBeInTheDocument();
     expect(button).toHaveTextContent('Add Custom Parameter');
   });
 
+  it('has defaultValues when they are set', () => {
+    renderComponent({ isSubmitting: false }, defaultValues);
+
+    expect(
+      screen.getByRole('option', { name: defaultValues.customParams[0].name })
+    ).toBeInTheDocument();
+    expect(screen.getByRole('textbox')).toHaveValue(
+      defaultValues.customParams[0].value
+    );
+  });
+
   describe('works with user inputs correctly', () => {
+    beforeEach(() => {
+      renderComponent({ isSubmitting: false });
+    });
+
     it('button click creates custom param fieldset', async () => {
       const button = screen.getByRole('button');
       await waitFor(() => userEvent.click(button));

+ 15 - 0
kafka-ui-react-app/src/components/Topics/shared/Form/CustomParams/__test__/fixtures.ts

@@ -0,0 +1,15 @@
+export const defaultValues = {
+  partitions: 1,
+  replicationFactor: 1,
+  minInSyncReplicas: 1,
+  cleanupPolicy: 'delete',
+  retentionBytes: -1,
+  maxMessageBytes: 1000012,
+  name: 'TestCustomParamEdit',
+  customParams: [
+    {
+      name: 'delete.retention.ms',
+      value: '86400001',
+    },
+  ],
+};

+ 2 - 4
kafka-ui-react-app/src/components/Topics/shared/Form/TopicForm.tsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import { useFormContext, Controller } from 'react-hook-form';
 import { NOT_SET, BYTES_IN_GB } from 'lib/constants';
-import { TopicName, TopicConfigByName } from 'redux/interfaces';
+import { TopicName } from 'redux/interfaces';
 import { ErrorMessage } from '@hookform/error-message';
 import Select, { SelectOption } from 'components/common/Select/Select';
 import Input from 'components/common/Input/Input';
@@ -16,7 +16,6 @@ import * as S from './TopicForm.styled';
 
 export interface Props {
   topicName?: TopicName;
-  config?: TopicConfigByName;
   isEditing?: boolean;
   isSubmitting: boolean;
   onSubmit: (e: React.BaseSyntheticEvent) => Promise<void>;
@@ -38,7 +37,6 @@ const RetentionBytesOptions: Array<SelectOption> = [
 
 const TopicForm: React.FC<Props> = ({
   topicName,
-  config,
   isEditing,
   isSubmitting,
   onSubmit,
@@ -197,7 +195,7 @@ const TopicForm: React.FC<Props> = ({
         </S.Column>
 
         <S.CustomParamsHeading>Custom parameters</S.CustomParamsHeading>
-        <CustomParamsContainer isSubmitting={isSubmitting} config={config} />
+        <CustomParamsContainer isSubmitting={isSubmitting} />
 
         <Button type="submit" buttonType="primary" buttonSize="L">
           Send

+ 1 - 0
kafka-ui-react-app/src/lib/constants.ts

@@ -17,6 +17,7 @@ export const BASE_PARAMS: ConfigurationParameters = {
 export const TOPIC_NAME_VALIDATION_PATTERN = /^[.,A-Za-z0-9_-]+$/;
 export const SCHEMA_NAME_VALIDATION_PATTERN = /^[.,A-Za-z0-9_-]+$/;
 
+export const TOPIC_CUSTOM_PARAMS_PREFIX = 'customParams';
 export const TOPIC_CUSTOM_PARAMS: Record<string, string> = {
   'compression.type': 'producer',
   'leader.replication.throttled.replicas': '',