Quellcode durchsuchen

[CHORE] Schema Registry UI Improvements. Cleanup (#237)

* createSchema thunk throws an error for unsuccesfull response

* refactor PageLoader common component

* fix error on details page of schema reg section load

* refactor index page for schema reg

* refactoring

* Add cluster wrapper + cleanup
Oleg Shur vor 4 Jahren
Ursprung
Commit
1769a7a0fc
21 geänderte Dateien mit 273 neuen und 355 gelöschten Zeilen
  1. 4 26
      kafka-ui-react-app/src/components/App.tsx
  2. 39 0
      kafka-ui-react-app/src/components/Cluster/Cluster.tsx
  3. 56 51
      kafka-ui-react-app/src/components/Schemas/Details/Details.tsx
  4. 1 0
      kafka-ui-react-app/src/components/Schemas/Details/DetailsContainer.ts
  5. 1 0
      kafka-ui-react-app/src/components/Schemas/Details/__test__/Details.spec.tsx
  6. 0 62
      kafka-ui-react-app/src/components/Schemas/Details/__test__/__snapshots__/Details.spec.tsx.snap
  7. 37 21
      kafka-ui-react-app/src/components/Schemas/List/List.tsx
  8. 11 2
      kafka-ui-react-app/src/components/Schemas/List/ListContainer.tsx
  9. 40 3
      kafka-ui-react-app/src/components/Schemas/List/__test__/List.spec.tsx
  10. 20 47
      kafka-ui-react-app/src/components/Schemas/Schemas.tsx
  11. 0 33
      kafka-ui-react-app/src/components/Schemas/SchemasContainer.tsx
  12. 9 63
      kafka-ui-react-app/src/components/Schemas/__test__/Schemas.spec.tsx
  13. 1 1
      kafka-ui-react-app/src/components/Topics/Details/Messages/Messages.tsx
  14. 21 27
      kafka-ui-react-app/src/components/Topics/Topics.tsx
  15. 0 2
      kafka-ui-react-app/src/components/Topics/TopicsContainer.ts
  16. 4 4
      kafka-ui-react-app/src/components/common/Dashboard/__tests__/MetricsWrapper.spec.tsx
  17. 3 3
      kafka-ui-react-app/src/components/common/PageLoader/PageLoader.tsx
  18. 13 2
      kafka-ui-react-app/src/components/common/PageLoader/__tests__/PageLoader.spec.tsx
  19. 1 1
      kafka-ui-react-app/src/components/common/PageLoader/__tests__/__snapshots__/PageLoader.spec.tsx.snap
  20. 11 7
      kafka-ui-react-app/src/redux/actions/__test__/thunks.spec.ts
  21. 1 0
      kafka-ui-react-app/src/redux/actions/thunks.ts

+ 4 - 26
kafka-ui-react-app/src/components/App.tsx

@@ -1,13 +1,10 @@
 import React from 'react';
-import { Switch, Route, Redirect } from 'react-router-dom';
+import { Switch, Route } from 'react-router-dom';
 import './App.scss';
-import BrokersContainer from './Brokers/BrokersContainer';
-import TopicsContainer from './Topics/TopicsContainer';
 import NavContainer from './Nav/NavContainer';
 import PageLoader from './common/PageLoader/PageLoader';
 import Dashboard from './Dashboard/Dashboard';
-import ConsumersGroupsContainer from './ConsumerGroups/ConsumersGroupsContainer';
-import SchemasContainer from './Schemas/SchemasContainer';
+import Cluster from './Cluster/Cluster';
 
 interface AppProps {
   isClusterListFetched: boolean;
@@ -44,29 +41,10 @@ const App: React.FC<AppProps> = ({
               path={['/', '/ui', '/ui/clusters']}
               component={Dashboard}
             />
-            <Route
-              path="/ui/clusters/:clusterName/brokers"
-              component={BrokersContainer}
-            />
-            <Route
-              path="/ui/clusters/:clusterName/topics"
-              component={TopicsContainer}
-            />
-            <Route
-              path="/ui/clusters/:clusterName/consumer-groups"
-              component={ConsumersGroupsContainer}
-            />
-            <Route
-              path="/ui/clusters/:clusterName/schemas"
-              component={SchemasContainer}
-            />
-            <Redirect
-              from="/ui/clusters/:clusterName"
-              to="/ui/clusters/:clusterName/brokers"
-            />
+            <Route path="/ui/clusters/:clusterName" component={Cluster} />
           </Switch>
         ) : (
-          <PageLoader />
+          <PageLoader fullHeight />
         )}
       </main>
     </div>

+ 39 - 0
kafka-ui-react-app/src/components/Cluster/Cluster.tsx

@@ -0,0 +1,39 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import { Switch, Route, Redirect, useParams } from 'react-router-dom';
+import BrokersContainer from 'components/Brokers/BrokersContainer';
+import TopicsContainer from 'components/Topics/TopicsContainer';
+import ConsumersGroupsContainer from 'components/ConsumerGroups/ConsumersGroupsContainer';
+import Schemas from 'components/Schemas/Schemas';
+import { getClustersReadonlyStatus } from 'redux/reducers/clusters/selectors';
+import ClusterContext from 'components/contexts/ClusterContext';
+
+const Cluster: React.FC = () => {
+  const { clusterName } = useParams<{ clusterName: string }>();
+  const isReadOnly = useSelector(getClustersReadonlyStatus(clusterName));
+  return (
+    <ClusterContext.Provider value={{ isReadOnly }}>
+      <Switch>
+        <Route
+          path="/ui/clusters/:clusterName/brokers"
+          component={BrokersContainer}
+        />
+        <Route
+          path="/ui/clusters/:clusterName/topics"
+          component={TopicsContainer}
+        />
+        <Route
+          path="/ui/clusters/:clusterName/consumer-groups"
+          component={ConsumersGroupsContainer}
+        />
+        <Route path="/ui/clusters/:clusterName/schemas" component={Schemas} />
+        <Redirect
+          from="/ui/clusters/:clusterName"
+          to="/ui/clusters/:clusterName/brokers"
+        />
+      </Switch>
+    </ClusterContext.Provider>
+  );
+};
+
+export default Cluster;

+ 56 - 51
kafka-ui-react-app/src/components/Schemas/Details/Details.tsx

@@ -9,6 +9,7 @@ import LatestVersionItem from './LatestVersionItem';
 import PageLoader from '../../common/PageLoader/PageLoader';
 
 export interface DetailsProps {
+  subject: SchemaName;
   schema: SchemaSubject;
   clusterName: ClusterName;
   versions: SchemaSubject[];
@@ -20,6 +21,7 @@ export interface DetailsProps {
 }
 
 const Details: React.FC<DetailsProps> = ({
+  subject,
   schema,
   clusterName,
   fetchSchemaVersions,
@@ -28,8 +30,9 @@ const Details: React.FC<DetailsProps> = ({
 }) => {
   const { isReadOnly } = React.useContext(ClusterContext);
   React.useEffect(() => {
-    fetchSchemaVersions(clusterName, schema.subject as SchemaName);
+    fetchSchemaVersions(clusterName, subject);
   }, [fetchSchemaVersions, clusterName]);
+
   return (
     <div className="section">
       <div className="level">
@@ -41,61 +44,63 @@ const Details: React.FC<DetailsProps> = ({
             },
           ]}
         >
-          {schema.subject}
+          {subject}
         </Breadcrumb>
       </div>
-      <div className="box">
-        <div className="level">
-          <div className="level-left">
-            <div className="level-item">
-              <div className="mr-1">
-                <b>Latest Version</b>
-              </div>
-              <div className="tag is-info is-light" title="Version">
-                #{schema.version}
+      {isFetched ? (
+        <>
+          <div className="box">
+            <div className="level">
+              <div className="level-left">
+                <div className="level-item">
+                  <div className="mr-1">
+                    <b>Latest Version</b>
+                  </div>
+                  <div className="tag is-info is-light" title="Version">
+                    #{schema.version}
+                  </div>
+                </div>
               </div>
+              {!isReadOnly && (
+                <div className="level-right">
+                  <button
+                    className="button is-warning is-small level-item"
+                    type="button"
+                    title="in development"
+                    disabled
+                  >
+                    Update Schema
+                  </button>
+                  <button
+                    className="button is-danger is-small level-item"
+                    type="button"
+                    title="in development"
+                    disabled
+                  >
+                    Delete
+                  </button>
+                </div>
+              )}
             </div>
+            <LatestVersionItem schema={schema} />
           </div>
-          {!isReadOnly && (
-            <div className="level-right">
-              <button
-                className="button is-warning is-small level-item"
-                type="button"
-                title="in development"
-                disabled
-              >
-                Update Schema
-              </button>
-              <button
-                className="button is-danger is-small level-item"
-                type="button"
-                title="in development"
-                disabled
-              >
-                Delete
-              </button>
-            </div>
-          )}
-        </div>
-        <LatestVersionItem schema={schema} />
-      </div>
-      {isFetched ? (
-        <div className="box">
-          <table className="table is-striped is-fullwidth">
-            <thead>
-              <tr>
-                <th>Version</th>
-                <th>ID</th>
-                <th>Schema</th>
-              </tr>
-            </thead>
-            <tbody>
-              {versions.map((version) => (
-                <SchemaVersion key={version.id} version={version} />
-              ))}
-            </tbody>
-          </table>
-        </div>
+          <div className="box">
+            <table className="table is-striped is-fullwidth">
+              <thead>
+                <tr>
+                  <th>Version</th>
+                  <th>ID</th>
+                  <th>Schema</th>
+                </tr>
+              </thead>
+              <tbody>
+                {versions.map((version) => (
+                  <SchemaVersion key={version.id} version={version} />
+                ))}
+              </tbody>
+            </table>
+          </div>
+        </>
       ) : (
         <PageLoader />
       )}

+ 1 - 0
kafka-ui-react-app/src/components/Schemas/Details/DetailsContainer.ts

@@ -24,6 +24,7 @@ const mapStateToProps = (
     },
   }: OwnProps
 ) => ({
+  subject,
   schema: getSchema(state, subject),
   versions: getSortedSchemaVersions(state),
   isFetched: getIsSchemaVersionFetched(state),

+ 1 - 0
kafka-ui-react-app/src/components/Schemas/Details/__test__/Details.spec.tsx

@@ -26,6 +26,7 @@ describe('Details', () => {
   describe('View', () => {
     const setupWrapper = (props: Partial<DetailsProps> = {}) => (
       <Details
+        subject={schema.subject}
         schema={schema}
         clusterName="Test cluster"
         fetchSchemaVersions={jest.fn()}

+ 0 - 62
kafka-ui-react-app/src/components/Schemas/Details/__test__/__snapshots__/Details.spec.tsx.snap

@@ -127,68 +127,6 @@ exports[`Details View when page with schema versions is loading matches snapshot
       test
     </Breadcrumb>
   </div>
-  <div
-    className="box"
-  >
-    <div
-      className="level"
-    >
-      <div
-        className="level-left"
-      >
-        <div
-          className="level-item"
-        >
-          <div
-            className="mr-1"
-          >
-            <b>
-              Latest Version
-            </b>
-          </div>
-          <div
-            className="tag is-info is-light"
-            title="Version"
-          >
-            #
-            1
-          </div>
-        </div>
-      </div>
-      <div
-        className="level-right"
-      >
-        <button
-          className="button is-warning is-small level-item"
-          disabled={true}
-          title="in development"
-          type="button"
-        >
-          Update Schema
-        </button>
-        <button
-          className="button is-danger is-small level-item"
-          disabled={true}
-          title="in development"
-          type="button"
-        >
-          Delete
-        </button>
-      </div>
-    </div>
-    <LatestVersionItem
-      schema={
-        Object {
-          "compatibilityLevel": "BACKWARD",
-          "id": 1,
-          "schema": "{\\"type\\":\\"record\\",\\"name\\":\\"MyRecord1\\",\\"namespace\\":\\"com.mycompany\\",\\"fields\\":[{\\"name\\":\\"id\\",\\"type\\":\\"long\\"}]}",
-          "schemaType": "JSON",
-          "subject": "test",
-          "version": "1",
-        }
-      }
-    />
-  </div>
   <PageLoader />
 </div>
 `;

+ 37 - 21
kafka-ui-react-app/src/components/Schemas/List/List.tsx

@@ -2,18 +2,30 @@ import React from 'react';
 import { SchemaSubject } from 'generated-sources';
 import { NavLink, useParams } from 'react-router-dom';
 import { clusterSchemaNewPath } from 'lib/paths';
+import { ClusterName } from 'redux/interfaces';
+import PageLoader from 'components/common/PageLoader/PageLoader';
 import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
 import ClusterContext from 'components/contexts/ClusterContext';
 import ListItem from './ListItem';
 
 export interface ListProps {
   schemas: SchemaSubject[];
+  isFetching: boolean;
+  fetchSchemasByClusterName: (clusterName: ClusterName) => void;
 }
 
-const List: React.FC<ListProps> = ({ schemas }) => {
+const List: React.FC<ListProps> = ({
+  schemas,
+  isFetching,
+  fetchSchemasByClusterName,
+}) => {
   const { isReadOnly } = React.useContext(ClusterContext);
   const { clusterName } = useParams<{ clusterName: string }>();
 
+  React.useEffect(() => {
+    fetchSchemasByClusterName(clusterName);
+  }, [fetchSchemasByClusterName, clusterName]);
+
   return (
     <div className="section">
       <Breadcrumb>Schema Registry</Breadcrumb>
@@ -32,28 +44,32 @@ const List: React.FC<ListProps> = ({ schemas }) => {
         </div>
       </div>
 
-      <div className="box">
-        <table className="table is-striped is-fullwidth">
-          <thead>
-            <tr>
-              <th>Schema Name</th>
-              <th>Version</th>
-              <th>Compatibility</th>
-            </tr>
-          </thead>
-          <tbody>
-            {schemas.length > 0 ? (
-              schemas.map((subject) => (
-                <ListItem key={subject.id} subject={subject} />
-              ))
-            ) : (
+      {isFetching ? (
+        <PageLoader />
+      ) : (
+        <div className="box">
+          <table className="table is-striped is-fullwidth">
+            <thead>
               <tr>
-                <td colSpan={10}>No schemas found</td>
+                <th>Schema Name</th>
+                <th>Version</th>
+                <th>Compatibility</th>
               </tr>
-            )}
-          </tbody>
-        </table>
-      </div>
+            </thead>
+            <tbody>
+              {schemas.length > 0 ? (
+                schemas.map((subject) => (
+                  <ListItem key={subject.id} subject={subject} />
+                ))
+              ) : (
+                <tr>
+                  <td colSpan={10}>No schemas found</td>
+                </tr>
+              )}
+            </tbody>
+          </table>
+        </div>
+      )}
     </div>
   );
 };

+ 11 - 2
kafka-ui-react-app/src/components/Schemas/List/ListContainer.tsx

@@ -1,10 +1,19 @@
 import { connect } from 'react-redux';
 import { RootState } from 'redux/interfaces';
-import { getSchemaList } from 'redux/reducers/schemas/selectors';
+import { fetchSchemasByClusterName } from 'redux/actions';
+import {
+  getIsSchemaListFetching,
+  getSchemaList,
+} from 'redux/reducers/schemas/selectors';
 import List from './List';
 
 const mapStateToProps = (state: RootState) => ({
+  isFetching: getIsSchemaListFetching(state),
   schemas: getSchemaList(state),
 });
 
-export default connect(mapStateToProps)(List);
+const mapDispatchToProps = {
+  fetchSchemasByClusterName,
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(List);

+ 40 - 3
kafka-ui-react-app/src/components/Schemas/List/__test__/List.spec.tsx

@@ -28,13 +28,50 @@ describe('List', () => {
 
     const setupWrapper = (props: Partial<ListProps> = {}) => (
       <StaticRouter location={{ pathname }} context={{}}>
-        <List schemas={[]} {...props} />
+        <List
+          isFetching
+          fetchSchemasByClusterName={jest.fn()}
+          schemas={[]}
+          {...props}
+        />
       </StaticRouter>
     );
 
+    describe('Initial state', () => {
+      let useEffect: jest.SpyInstance<
+        void,
+        [effect: React.EffectCallback, deps?: React.DependencyList | undefined]
+      >;
+      const mockedFn = jest.fn();
+
+      const mockedUseEffect = () => {
+        useEffect.mockImplementationOnce(mockedFn);
+      };
+
+      beforeEach(() => {
+        useEffect = jest.spyOn(React, 'useEffect');
+        mockedUseEffect();
+      });
+
+      it('should call fetchSchemasByClusterName every render', () => {
+        mount(setupWrapper({ fetchSchemasByClusterName: mockedFn }));
+        expect(mockedFn).toHaveBeenCalled();
+      });
+    });
+
+    describe('when fetching', () => {
+      it('renders PageLoader', () => {
+        const wrapper = mount(setupWrapper({ isFetching: true }));
+        expect(wrapper.exists('Breadcrumb')).toBeTruthy();
+        expect(wrapper.exists('thead')).toBeFalsy();
+        expect(wrapper.exists('ListItem')).toBeFalsy();
+        expect(wrapper.exists('PageLoader')).toBeTruthy();
+      });
+    });
+
     describe('without schemas', () => {
       it('renders table heading without ListItem', () => {
-        const wrapper = mount(setupWrapper());
+        const wrapper = mount(setupWrapper({ isFetching: false }));
         expect(wrapper.exists('Breadcrumb')).toBeTruthy();
         expect(wrapper.exists('thead')).toBeTruthy();
         expect(wrapper.exists('ListItem')).toBeFalsy();
@@ -42,7 +79,7 @@ describe('List', () => {
     });
 
     describe('with schemas', () => {
-      const wrapper = mount(setupWrapper({ schemas }));
+      const wrapper = mount(setupWrapper({ isFetching: false, schemas }));
 
       it('renders table heading with ListItem', () => {
         expect(wrapper.exists('Breadcrumb')).toBeTruthy();

+ 20 - 47
kafka-ui-react-app/src/components/Schemas/Schemas.tsx

@@ -1,54 +1,27 @@
 import React from 'react';
-import { ClusterName } from 'redux/interfaces';
-import { Switch, Route, useParams } from 'react-router-dom';
-import PageLoader from 'components/common/PageLoader/PageLoader';
+import { Switch, Route } from 'react-router-dom';
 import ListContainer from './List/ListContainer';
 import DetailsContainer from './Details/DetailsContainer';
 import NewContainer from './New/NewContainer';
-import ClusterContext from '../contexts/ClusterContext';
 
-export interface SchemasProps {
-  isFetching: boolean;
-  fetchSchemasByClusterName: (clusterName: ClusterName) => void;
-  isReadOnly: boolean;
-}
-
-const Schemas: React.FC<SchemasProps> = ({
-  isFetching,
-  fetchSchemasByClusterName,
-  isReadOnly,
-}) => {
-  const { clusterName } = useParams<{ clusterName: string }>();
-
-  React.useEffect(() => {
-    fetchSchemasByClusterName(clusterName);
-  }, [fetchSchemasByClusterName, clusterName]);
-
-  if (isFetching) {
-    return <PageLoader />;
-  }
-
-  return (
-    <ClusterContext.Provider value={{ isReadOnly }}>
-      <Switch>
-        <Route
-          exact
-          path="/ui/clusters/:clusterName/schemas"
-          component={ListContainer}
-        />
-        <Route
-          exact
-          path="/ui/clusters/:clusterName/schemas/new"
-          component={NewContainer}
-        />
-        <Route
-          exact
-          path="/ui/clusters/:clusterName/schemas/:subject/latest"
-          component={DetailsContainer}
-        />
-      </Switch>
-    </ClusterContext.Provider>
-  );
-};
+const Schemas: React.FC = () => (
+  <Switch>
+    <Route
+      exact
+      path="/ui/clusters/:clusterName/schemas"
+      component={ListContainer}
+    />
+    <Route
+      exact
+      path="/ui/clusters/:clusterName/schemas/new"
+      component={NewContainer}
+    />
+    <Route
+      exact
+      path="/ui/clusters/:clusterName/schemas/:subject/latest"
+      component={DetailsContainer}
+    />
+  </Switch>
+);
 
 export default Schemas;

+ 0 - 33
kafka-ui-react-app/src/components/Schemas/SchemasContainer.tsx

@@ -1,33 +0,0 @@
-import { connect } from 'react-redux';
-import { RootState, ClusterName } from 'redux/interfaces';
-import { fetchSchemasByClusterName } from 'redux/actions';
-import { getIsSchemaListFetching } from 'redux/reducers/schemas/selectors';
-import { getClustersReadonlyStatus } from 'redux/reducers/clusters/selectors';
-import { RouteComponentProps, withRouter } from 'react-router-dom';
-import Schemas from './Schemas';
-
-interface RouteProps {
-  clusterName: ClusterName;
-}
-
-type OwnProps = RouteComponentProps<RouteProps>;
-
-const mapStateToProps = (
-  state: RootState,
-  {
-    match: {
-      params: { clusterName },
-    },
-  }: OwnProps
-) => ({
-  isFetching: getIsSchemaListFetching(state),
-  isReadOnly: getClustersReadonlyStatus(clusterName)(state),
-});
-
-const mapDispatchToProps = {
-  fetchSchemasByClusterName,
-};
-
-export default withRouter(
-  connect(mapStateToProps, mapDispatchToProps)(Schemas)
-);

+ 9 - 63
kafka-ui-react-app/src/components/Schemas/__test__/Schemas.spec.tsx

@@ -1,72 +1,18 @@
 import React from 'react';
-import { Provider } from 'react-redux';
-import { mount } from 'enzyme';
-import configureStore from 'redux/store/configureStore';
+import { shallow } from 'enzyme';
 import { StaticRouter } from 'react-router-dom';
-import Schemas, { SchemasProps } from '../Schemas';
-import SchemasContainer from '../SchemasContainer';
+import Schemas from '../Schemas';
 
 describe('Schemas', () => {
   const pathname = `/ui/clusters/clusterName/schemas`;
 
-  describe('Container', () => {
-    const store = configureStore();
+  it('renders', () => {
+    const wrapper = shallow(
+      <StaticRouter location={{ pathname }} context={{}}>
+        <Schemas />
+      </StaticRouter>
+    );
 
-    it('renders view', () => {
-      const component = mount(
-        <Provider store={store}>
-          <StaticRouter location={{ pathname }} context={{}}>
-            <SchemasContainer />
-          </StaticRouter>
-        </Provider>
-      );
-
-      expect(component.exists()).toBeTruthy();
-    });
-
-    describe('View', () => {
-      const setupWrapper = (props: Partial<SchemasProps> = {}) => (
-        <StaticRouter location={{ pathname }} context={{}}>
-          <Schemas
-            isFetching
-            fetchSchemasByClusterName={jest.fn()}
-            isReadOnly={false}
-            {...props}
-          />
-        </StaticRouter>
-      );
-      describe('Initial state', () => {
-        let useEffect: jest.SpyInstance<
-          void,
-          [
-            effect: React.EffectCallback,
-            deps?: React.DependencyList | undefined
-          ]
-        >;
-        const mockedFn = jest.fn();
-
-        const mockedUseEffect = () => {
-          useEffect.mockImplementationOnce(mockedFn);
-        };
-
-        beforeEach(() => {
-          useEffect = jest.spyOn(React, 'useEffect');
-          mockedUseEffect();
-        });
-
-        it('should call fetchSchemasByClusterName every render', () => {
-          mount(setupWrapper({ fetchSchemasByClusterName: mockedFn }));
-          expect(mockedFn).toHaveBeenCalled();
-        });
-      });
-
-      describe('when page is loading', () => {
-        const wrapper = mount(setupWrapper({ isFetching: true }));
-
-        it('renders PageLoader', () => {
-          expect(wrapper.exists('PageLoader')).toBeTruthy();
-        });
-      });
-    });
+    expect(wrapper.exists('Schemas')).toBeTruthy();
   });
 });

+ 1 - 1
kafka-ui-react-app/src/components/Topics/Details/Messages/Messages.tsx

@@ -195,7 +195,7 @@ const Messages: React.FC<Props> = ({
   };
 
   if (!isFetched) {
-    return <PageLoader isFullHeight={false} />;
+    return <PageLoader />;
   }
 
   return (

+ 21 - 27
kafka-ui-react-app/src/components/Topics/Topics.tsx

@@ -6,21 +6,17 @@ import EditContainer from 'components/Topics/Edit/EditContainer';
 import ListContainer from './List/ListContainer';
 import DetailsContainer from './Details/DetailsContainer';
 import NewContainer from './New/NewContainer';
-import ClusterContext from '../contexts/ClusterContext';
 
 interface Props {
   clusterName: ClusterName;
   isFetched: boolean;
-  fetchBrokers: (clusterName: ClusterName) => void;
   fetchTopicsList: (clusterName: ClusterName) => void;
-  isReadOnly: boolean;
 }
 
 const Topics: React.FC<Props> = ({
   clusterName,
   isFetched,
   fetchTopicsList,
-  isReadOnly,
 }) => {
   React.useEffect(() => {
     fetchTopicsList(clusterName);
@@ -28,29 +24,27 @@ const Topics: React.FC<Props> = ({
 
   if (isFetched) {
     return (
-      <ClusterContext.Provider value={{ isReadOnly }}>
-        <Switch>
-          <Route
-            exact
-            path="/ui/clusters/:clusterName/topics"
-            component={ListContainer}
-          />
-          <Route
-            exact
-            path="/ui/clusters/:clusterName/topics/new"
-            component={NewContainer}
-          />
-          <Route
-            exact
-            path="/ui/clusters/:clusterName/topics/:topicName/edit"
-            component={EditContainer}
-          />
-          <Route
-            path="/ui/clusters/:clusterName/topics/:topicName"
-            component={DetailsContainer}
-          />
-        </Switch>
-      </ClusterContext.Provider>
+      <Switch>
+        <Route
+          exact
+          path="/ui/clusters/:clusterName/topics"
+          component={ListContainer}
+        />
+        <Route
+          exact
+          path="/ui/clusters/:clusterName/topics/new"
+          component={NewContainer}
+        />
+        <Route
+          exact
+          path="/ui/clusters/:clusterName/topics/:topicName/edit"
+          component={EditContainer}
+        />
+        <Route
+          path="/ui/clusters/:clusterName/topics/:topicName"
+          component={DetailsContainer}
+        />
+      </Switch>
     );
   }
 

+ 0 - 2
kafka-ui-react-app/src/components/Topics/TopicsContainer.ts

@@ -3,7 +3,6 @@ import { fetchTopicsList } from 'redux/actions';
 import { getIsTopicListFetched } from 'redux/reducers/topics/selectors';
 import { RootState, ClusterName } from 'redux/interfaces';
 import { RouteComponentProps } from 'react-router-dom';
-import { getClustersReadonlyStatus } from 'redux/reducers/clusters/selectors';
 import Topics from './Topics';
 
 interface RouteProps {
@@ -22,7 +21,6 @@ const mapStateToProps = (
 ) => ({
   isFetched: getIsTopicListFetched(state),
   clusterName,
-  isReadOnly: getClustersReadonlyStatus(clusterName)(state),
 });
 
 const mapDispatchToProps = {

+ 4 - 4
kafka-ui-react-app/src/components/common/Dashboard/__tests__/MetricsWrapper.spec.tsx

@@ -8,17 +8,17 @@ describe('MetricsWrapper', () => {
     const component = shallow(
       <MetricsWrapper wrapperClassName={className} multiline />
     );
-    expect(component.find(`.${className}`).exists()).toBeTruthy();
-    expect(component.find('.level-multiline').exists()).toBeTruthy();
+    expect(component.exists(`.${className}`)).toBeTruthy();
+    expect(component.exists('.level-multiline')).toBeTruthy();
   });
 
   it('correctly renders children', () => {
     let component = shallow(<MetricsWrapper />);
-    expect(component.find('.subtitle').exists()).toBeFalsy();
+    expect(component.exists('.subtitle')).toBeFalsy();
 
     const title = 'title';
     component = shallow(<MetricsWrapper title={title} />);
-    expect(component.find('.subtitle').exists()).toBeTruthy();
+    expect(component.exists('.subtitle')).toBeTruthy();
     expect(component.text()).toEqual(title);
   });
 });

+ 3 - 3
kafka-ui-react-app/src/components/common/PageLoader/PageLoader.tsx

@@ -2,14 +2,14 @@ import React from 'react';
 import cx from 'classnames';
 
 interface Props {
-  isFullHeight: boolean;
+  fullHeight: boolean;
 }
 
-const PageLoader: React.FC<Partial<Props>> = ({ isFullHeight = true }) => (
+const PageLoader: React.FC<Partial<Props>> = ({ fullHeight }) => (
   <section
     className={cx(
       'hero',
-      isFullHeight ? 'is-fullheight-with-navbar' : 'is-halfheight'
+      fullHeight ? 'is-fullheight-with-navbar' : 'is-halfheight'
     )}
   >
     <div

+ 13 - 2
kafka-ui-react-app/src/components/common/PageLoader/__tests__/PageLoader.spec.tsx

@@ -4,7 +4,18 @@ import PageLoader from '../PageLoader';
 
 describe('PageLoader', () => {
   it('matches the snapshot', () => {
-    const component = mount(<PageLoader />);
-    expect(component).toMatchSnapshot();
+    expect(mount(<PageLoader />)).toMatchSnapshot();
+  });
+
+  it('renders half-height page loader by default', () => {
+    const wrapper = mount(<PageLoader />);
+    expect(wrapper.exists('.hero.is-halfheight')).toBeTruthy();
+    expect(wrapper.exists('.hero.is-fullheight-with-navbar')).toBeFalsy();
+  });
+
+  it('renders fullheight page loader', () => {
+    const wrapper = mount(<PageLoader fullHeight />);
+    expect(wrapper.exists('.hero.is-halfheight')).toBeFalsy();
+    expect(wrapper.exists('.hero.is-fullheight-with-navbar')).toBeTruthy();
   });
 });

+ 1 - 1
kafka-ui-react-app/src/components/common/PageLoader/__tests__/__snapshots__/PageLoader.spec.tsx.snap

@@ -3,7 +3,7 @@
 exports[`PageLoader matches the snapshot 1`] = `
 <PageLoader>
   <section
-    className="hero is-fullheight-with-navbar"
+    className="hero is-halfheight"
   >
     <div
       className="hero-body has-text-centered"

+ 11 - 7
kafka-ui-react-app/src/redux/actions/__test__/thunks.spec.ts

@@ -124,13 +124,17 @@ describe('Thunks', () => {
 
     it('creates POST_SCHEMA__FAILURE when posting new schema', async () => {
       fetchMock.postOnce(`/api/clusters/${clusterName}/schemas`, 404);
-      await store.dispatch(
-        thunks.createSchema(clusterName, fixtures.schemaPayload)
-      );
-      expect(store.getActions()).toEqual([
-        actions.createSchemaAction.request(),
-        actions.createSchemaAction.failure(),
-      ]);
+      try {
+        await store.dispatch(
+          thunks.createSchema(clusterName, fixtures.schemaPayload)
+        );
+      } catch (error) {
+        expect(error.status).toEqual(404);
+        expect(store.getActions()).toEqual([
+          actions.createSchemaAction.request(),
+          actions.createSchemaAction.failure(),
+        ]);
+      }
     });
   });
 });

+ 1 - 0
kafka-ui-react-app/src/redux/actions/thunks.ts

@@ -296,5 +296,6 @@ export const createSchema = (
     dispatch(actions.createSchemaAction.success(schema));
   } catch (e) {
     dispatch(actions.createSchemaAction.failure());
+    throw e;
   }
 };