Rewrite to context & update the tests

This commit is contained in:
GneyHabub 2021-03-10 15:26:27 +03:00
parent b9f6390b10
commit 0c3687d013
17 changed files with 125 additions and 177 deletions

View file

@ -6,13 +6,13 @@ import Breadcrumb from '../../common/Breadcrumb/Breadcrumb';
import SchemaVersion from './SchemaVersion'; import SchemaVersion from './SchemaVersion';
import LatestVersionItem from './LatestVersionItem'; import LatestVersionItem from './LatestVersionItem';
import PageLoader from '../../common/PageLoader/PageLoader'; import PageLoader from '../../common/PageLoader/PageLoader';
import _ReadOnlyContext from '../../contexts/ReadOnlyContext';
export interface DetailsProps { export interface DetailsProps {
schema: SchemaSubject; schema: SchemaSubject;
clusterName: ClusterName; clusterName: ClusterName;
versions: SchemaSubject[]; versions: SchemaSubject[];
isFetched: boolean; isFetched: boolean;
isReadOnly?: boolean | undefined;
fetchSchemaVersions: ( fetchSchemaVersions: (
clusterName: ClusterName, clusterName: ClusterName,
schemaName: SchemaName schemaName: SchemaName
@ -25,8 +25,8 @@ const Details: React.FC<DetailsProps> = ({
fetchSchemaVersions, fetchSchemaVersions,
versions, versions,
isFetched, isFetched,
isReadOnly,
}) => { }) => {
const ReadOnlyContext = React.useContext(_ReadOnlyContext);
React.useEffect(() => { React.useEffect(() => {
fetchSchemaVersions(clusterName, schema.subject as SchemaName); fetchSchemaVersions(clusterName, schema.subject as SchemaName);
}, [fetchSchemaVersions, clusterName]); }, [fetchSchemaVersions, clusterName]);
@ -56,7 +56,7 @@ const Details: React.FC<DetailsProps> = ({
</div> </div>
</div> </div>
</div> </div>
{!isReadOnly && ( {!ReadOnlyContext.isReadOnly && (
<div className="level-right"> <div className="level-right">
<button <button
className="button is-warning is-small level-item" className="button is-warning is-small level-item"

View file

@ -6,7 +6,6 @@ import {
getSchema, getSchema,
getSortedSchemaVersions, getSortedSchemaVersions,
} from 'redux/reducers/schemas/selectors'; } from 'redux/reducers/schemas/selectors';
import { getClustersReadonlyStatus } from 'redux/reducers/clusters/selectors';
import { fetchSchemaVersions } from 'redux/actions'; import { fetchSchemaVersions } from 'redux/actions';
import Details from './Details'; import Details from './Details';
@ -29,7 +28,6 @@ const mapStateToProps = (
versions: getSortedSchemaVersions(state), versions: getSortedSchemaVersions(state),
isFetched: getIsSchemaVersionFetched(state), isFetched: getIsSchemaVersionFetched(state),
clusterName, clusterName,
isReadOnly: getClustersReadonlyStatus(clusterName)(state),
}); });
const mapDispatchToProps = { const mapDispatchToProps = {

View file

@ -1,10 +1,12 @@
import React from 'react'; import React from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { shallow } from 'enzyme'; import { shallow, mount } from 'enzyme';
import configureStore from 'redux/store/configureStore'; import configureStore from 'redux/store/configureStore';
import { StaticRouter } from 'react-router';
import DetailsContainer from '../DetailsContainer'; import DetailsContainer from '../DetailsContainer';
import Details, { DetailsProps } from '../Details'; import Details, { DetailsProps } from '../Details';
import { schema, versions } from './fixtures'; import { schema, versions } from './fixtures';
import ReadOnlyContext from '../../../contexts/ReadOnlyContext';
describe('Details', () => { describe('Details', () => {
describe('Container', () => { describe('Container', () => {
@ -103,8 +105,16 @@ describe('Details', () => {
}); });
describe('when the readonly flag is set', () => { describe('when the readonly flag is set', () => {
it('mathces the snapshot', () => { it('does not render update & delete buttons', () => {
expect(shallow(setupWrapper({ isReadOnly: true }))).toMatchSnapshot(); expect(
mount(
<StaticRouter>
<ReadOnlyContext.Provider value={{ isReadOnly: true }}>
{setupWrapper({ versions })}
</ReadOnlyContext.Provider>
</StaticRouter>
).exists('.level-right')
).toBeFalsy();
}); });
}); });
}); });

View file

@ -322,92 +322,6 @@ exports[`Details View when page with schema versions loaded when schema has vers
</div> </div>
`; `;
exports[`Details View when page with schema versions loaded when the readonly flag is set mathces the snapshot 1`] = `
<div
className="section"
>
<div
className="level"
>
<Breadcrumb
links={
Array [
Object {
"href": "/ui/clusters/Test cluster/schemas",
"label": "Schema Registry",
},
]
}
>
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>
<LatestVersionItem
schema={
Object {
"compatibilityLevel": "BACKWARD",
"id": 1,
"schema": "{\\"type\\":\\"record\\",\\"name\\":\\"MyRecord1\\",\\"namespace\\":\\"com.mycompany\\",\\"fields\\":[{\\"name\\":\\"id\\",\\"type\\":\\"long\\"}]}",
"subject": "test",
"version": "1",
}
}
/>
</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 />
</table>
</div>
</div>
`;
exports[`Details View when page with schema versions loaded when versions are empty matches snapshot 1`] = ` exports[`Details View when page with schema versions loaded when versions are empty matches snapshot 1`] = `
<div <div
className="section" className="section"

View file

@ -4,13 +4,14 @@ import { NavLink, useParams } from 'react-router-dom';
import { clusterSchemaNewPath } from 'lib/paths'; import { clusterSchemaNewPath } from 'lib/paths';
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb'; import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
import ListItem from './ListItem'; import ListItem from './ListItem';
import _ReadOnlyContext from '../../contexts/ReadOnlyContext';
export interface ListProps { export interface ListProps {
schemas: SchemaSubject[]; schemas: SchemaSubject[];
isReadOnly?: boolean | undefined;
} }
const List: React.FC<ListProps> = ({ schemas, isReadOnly }) => { const List: React.FC<ListProps> = ({ schemas }) => {
const ReadOnlyContext = React.useContext(_ReadOnlyContext);
const { clusterName } = useParams<{ clusterName: string }>(); const { clusterName } = useParams<{ clusterName: string }>();
return ( return (
@ -18,7 +19,7 @@ const List: React.FC<ListProps> = ({ schemas, isReadOnly }) => {
<Breadcrumb>Schema Registry</Breadcrumb> <Breadcrumb>Schema Registry</Breadcrumb>
<div className="box"> <div className="box">
<div className="level"> <div className="level">
{!isReadOnly && ( {!ReadOnlyContext.isReadOnly && (
<div className="level-item level-right"> <div className="level-item level-right">
<NavLink <NavLink
className="button is-primary" className="button is-primary"

View file

@ -1,26 +1,10 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { RootState, ClusterName } from 'redux/interfaces'; import { RootState } from 'redux/interfaces';
import { getSchemaList } from 'redux/reducers/schemas/selectors'; import { getSchemaList } from 'redux/reducers/schemas/selectors';
import { getClustersReadonlyStatus } from 'redux/reducers/clusters/selectors';
import { RouteComponentProps, withRouter } from 'react-router-dom';
import List from './List'; import List from './List';
interface RouteProps { const mapStateToProps = (state: RootState) => ({
clusterName: ClusterName;
}
type OwnProps = RouteComponentProps<RouteProps>;
const mapStateToProps = (
state: RootState,
{
match: {
params: { clusterName },
},
}: OwnProps
) => ({
schemas: getSchemaList(state), schemas: getSchemaList(state),
isReadOnly: getClustersReadonlyStatus(clusterName)(state),
}); });
export default withRouter(connect(mapStateToProps)(List)); export default connect(mapStateToProps)(List);

View file

@ -3,6 +3,7 @@ import { mount, shallow } from 'enzyme';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { StaticRouter } from 'react-router'; import { StaticRouter } from 'react-router';
import configureStore from 'redux/store/configureStore'; import configureStore from 'redux/store/configureStore';
import ReadOnlyContext from 'components/contexts/ReadOnlyContext';
import ListContainer from '../ListContainer'; import ListContainer from '../ListContainer';
import List, { ListProps } from '../List'; import List, { ListProps } from '../List';
import { schemas } from './fixtures'; import { schemas } from './fixtures';
@ -51,7 +52,13 @@ describe('List', () => {
}); });
describe('with readonly cluster', () => { describe('with readonly cluster', () => {
const wrapper = shallow(setupWrapper({ schemas, isReadOnly: true })); const wrapper = mount(
<StaticRouter>
<ReadOnlyContext.Provider value={{ isReadOnly: true }}>
{setupWrapper({ schemas: [] })}
</ReadOnlyContext.Provider>
</StaticRouter>
);
it('does not render Create Schema button', () => { it('does not render Create Schema button', () => {
expect(wrapper.exists('NavLink')).toBeFalsy(); expect(wrapper.exists('NavLink')).toBeFalsy();
}); });

View file

@ -5,15 +5,18 @@ import PageLoader from 'components/common/PageLoader/PageLoader';
import ListContainer from './List/ListContainer'; import ListContainer from './List/ListContainer';
import DetailsContainer from './Details/DetailsContainer'; import DetailsContainer from './Details/DetailsContainer';
import NewContainer from './New/NewContainer'; import NewContainer from './New/NewContainer';
import ReadOnlyContext from '../contexts/ReadOnlyContext';
export interface SchemasProps { export interface SchemasProps {
isFetching: boolean; isFetching: boolean;
fetchSchemasByClusterName: (clusterName: ClusterName) => void; fetchSchemasByClusterName: (clusterName: ClusterName) => void;
isReadOnly?: boolean | undefined;
} }
const Schemas: React.FC<SchemasProps> = ({ const Schemas: React.FC<SchemasProps> = ({
isFetching, isFetching,
fetchSchemasByClusterName, fetchSchemasByClusterName,
isReadOnly,
}) => { }) => {
const { clusterName } = useParams<{ clusterName: string }>(); const { clusterName } = useParams<{ clusterName: string }>();
@ -26,23 +29,25 @@ const Schemas: React.FC<SchemasProps> = ({
} }
return ( return (
<Switch> <ReadOnlyContext.Provider value={{ isReadOnly }}>
<Route <Switch>
exact <Route
path="/ui/clusters/:clusterName/schemas" exact
component={ListContainer} path="/ui/clusters/:clusterName/schemas"
/> component={ListContainer}
<Route />
exact <Route
path="/ui/clusters/:clusterName/schemas/new" exact
component={NewContainer} path="/ui/clusters/:clusterName/schemas/new"
/> component={NewContainer}
<Route />
exact <Route
path="/ui/clusters/:clusterName/schemas/:subject/latest" exact
component={DetailsContainer} path="/ui/clusters/:clusterName/schemas/:subject/latest"
/> component={DetailsContainer}
</Switch> />
</Switch>
</ReadOnlyContext.Provider>
); );
}; };

View file

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

View file

@ -14,14 +14,15 @@ import OverviewContainer from './Overview/OverviewContainer';
import MessagesContainer from './Messages/MessagesContainer'; import MessagesContainer from './Messages/MessagesContainer';
import SettingsContainer from './Settings/SettingsContainer'; import SettingsContainer from './Settings/SettingsContainer';
import SettingsEditButton from './Settings/SettingsEditButton'; import SettingsEditButton from './Settings/SettingsEditButton';
import _ReadOnlyContext from '../../contexts/ReadOnlyContext';
interface Props extends Topic, TopicDetails { interface Props extends Topic, TopicDetails {
clusterName: ClusterName; clusterName: ClusterName;
topicName: TopicName; topicName: TopicName;
isReadOnly: boolean | undefined;
} }
const Details: React.FC<Props> = ({ clusterName, topicName, isReadOnly }) => { const Details: React.FC<Props> = ({ clusterName, topicName }) => {
const ReadOnlyContext = React.useContext(_ReadOnlyContext);
return ( return (
<div className="section"> <div className="section">
<div className="level"> <div className="level">
@ -34,7 +35,7 @@ const Details: React.FC<Props> = ({ clusterName, topicName, isReadOnly }) => {
{topicName} {topicName}
</Breadcrumb> </Breadcrumb>
</div> </div>
{!isReadOnly && ( {!ReadOnlyContext.isReadOnly && (
<SettingsEditButton <SettingsEditButton
to={clusterTopicsTopicEditPath(clusterName, topicName)} to={clusterTopicsTopicEditPath(clusterName, topicName)}
/> />

View file

@ -1,6 +1,5 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ClusterName, RootState, TopicName } from 'redux/interfaces'; import { ClusterName, RootState, TopicName } from 'redux/interfaces';
import { getClustersReadonlyStatus } from 'redux/reducers/clusters/selectors';
import { withRouter, RouteComponentProps } from 'react-router-dom'; import { withRouter, RouteComponentProps } from 'react-router-dom';
import Details from './Details'; import Details from './Details';
@ -21,7 +20,6 @@ const mapStateToProps = (
) => ({ ) => ({
clusterName, clusterName,
topicName, topicName,
isReadOnly: getClustersReadonlyStatus(clusterName)(state),
}); });
export default withRouter(connect(mapStateToProps)(Details)); export default withRouter(connect(mapStateToProps)(Details));

View file

@ -4,24 +4,19 @@ import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import { clusterTopicNewPath } from 'lib/paths'; import { clusterTopicNewPath } from 'lib/paths';
import ListItem from './ListItem'; import ListItem from './ListItem';
import _ReadOnlyContext from '../../contexts/ReadOnlyContext';
interface Props { interface Props {
clusterName: ClusterName; clusterName: ClusterName;
topics: TopicWithDetailedInfo[]; topics: TopicWithDetailedInfo[];
externalTopics: TopicWithDetailedInfo[]; externalTopics: TopicWithDetailedInfo[];
isReadOnly?: boolean | undefined;
} }
const List: React.FC<Props> = ({ const List: React.FC<Props> = ({ clusterName, topics, externalTopics }) => {
clusterName,
topics,
externalTopics,
isReadOnly,
}) => {
const [showInternal, setShowInternal] = React.useState<boolean>(true); const [showInternal, setShowInternal] = React.useState<boolean>(true);
const handleSwitch = () => setShowInternal(!showInternal); const handleSwitch = () => setShowInternal(!showInternal);
const ReadOnlyContext = React.useContext(_ReadOnlyContext);
const items = showInternal ? topics : externalTopics; const items = showInternal ? topics : externalTopics;
return ( return (
@ -44,7 +39,7 @@ const List: React.FC<Props> = ({
</div> </div>
</div> </div>
<div className="level-item level-right"> <div className="level-item level-right">
{!isReadOnly && ( {!ReadOnlyContext.isReadOnly && (
<NavLink <NavLink
className="button is-primary" className="button is-primary"
to={clusterTopicNewPath(clusterName)} to={clusterTopicNewPath(clusterName)}

View file

@ -4,7 +4,6 @@ import {
getTopicList, getTopicList,
getExternalTopicList, getExternalTopicList,
} from 'redux/reducers/topics/selectors'; } from 'redux/reducers/topics/selectors';
import { getClustersReadonlyStatus } from 'redux/reducers/clusters/selectors';
import { withRouter, RouteComponentProps } from 'react-router-dom'; import { withRouter, RouteComponentProps } from 'react-router-dom';
import List from './List'; import List from './List';
@ -25,7 +24,6 @@ const mapStateToProps = (
clusterName, clusterName,
topics: getTopicList(state), topics: getTopicList(state),
externalTopics: getExternalTopicList(state), externalTopics: getExternalTopicList(state),
isReadOnly: getClustersReadonlyStatus(clusterName)(state),
}); });
export default withRouter(connect(mapStateToProps)(List)); export default withRouter(connect(mapStateToProps)(List));

View file

@ -1,6 +1,7 @@
import { shallow } from 'enzyme'; import { mount } from 'enzyme';
import React from 'react'; import React from 'react';
import List from '../List'; import List from '../List';
import ReadOnlyContext from '../../../contexts/ReadOnlyContext';
describe('List', () => { describe('List', () => {
describe('when it has readonly flag', () => { describe('when it has readonly flag', () => {
@ -9,9 +10,12 @@ describe('List', () => {
clusterName: 'Cluster', clusterName: 'Cluster',
topics: [], topics: [],
externalTopics: [], externalTopics: [],
isReadOnly: true,
}; };
const component = shallow(<List {...props} />); const component = mount(
<ReadOnlyContext.Provider value={{ isReadOnly: true }}>
<List {...props} />
</ReadOnlyContext.Provider>
);
expect(component.exists('NavLink')).toBeFalsy(); expect(component.exists('NavLink')).toBeFalsy();
}); });
}); });

View file

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

View file

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

View file

@ -0,0 +1,8 @@
import React from 'react';
const initialValue: { isReadOnly: boolean | undefined } = {
isReadOnly: undefined,
};
const ReadOnlyContext = React.createContext(initialValue);
export default ReadOnlyContext;