diff --git a/kafka-ui-react-app/src/components/Topics/List/ListItem.tsx b/kafka-ui-react-app/src/components/Topics/List/ListItem.tsx index 960b532597..ca92c22f5a 100644 --- a/kafka-ui-react-app/src/components/Topics/List/ListItem.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/ListItem.tsx @@ -64,33 +64,37 @@ const ListItem: React.FC = ({ {internal ? 'Internal' : 'External'} - -
- - - - } - right - > - - Clear Messages - - setDeleteTopicConfirmationVisible(true)} + + {!internal ? ( + <> +
+ + + + } + right + > + + Clear Messages + + setDeleteTopicConfirmationVisible(true)} + > + Remove Topic + + +
+ setDeleteTopicConfirmationVisible(false)} + onConfirm={deleteTopicHandler} > - Remove Topic -
-
-
- setDeleteTopicConfirmationVisible(false)} - onConfirm={deleteTopicHandler} - > - Are you sure want to remove {name} topic? - + Are you sure want to remove {name} topic? + + + ) : null} ); diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListItem.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListItem.spec.tsx index 6aa399efa6..f652155374 100644 --- a/kafka-ui-react-app/src/components/Topics/List/__tests__/ListItem.spec.tsx +++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/ListItem.spec.tsx @@ -28,28 +28,31 @@ describe('ListItem', () => { ); it('triggers the deleting messages when clicked on the delete messages button', () => { - const component = shallow(setupComponent()); + const component = shallow(setupComponent({ topic: externalTopicPayload })); + expect(component.exists('.topic-action-block')).toBeTruthy(); component.find('DropdownItem').at(0).simulate('click'); expect(mockDeleteMessages).toBeCalledTimes(1); expect(mockDeleteMessages).toBeCalledWith( clusterName, - internalTopicPayload.name + externalTopicPayload.name ); }); it('triggers the deleteTopic when clicked on the delete button', () => { - const wrapper = shallow(setupComponent()); + const wrapper = shallow(setupComponent({ topic: externalTopicPayload })); + expect(wrapper.exists('.topic-action-block')).toBeTruthy(); expect(wrapper.find('mock-ConfirmationModal').prop('isOpen')).toBeFalsy(); wrapper.find('DropdownItem').at(1).simulate('click'); const modal = wrapper.find('mock-ConfirmationModal'); expect(modal.prop('isOpen')).toBeTruthy(); modal.simulate('confirm'); expect(mockDelete).toBeCalledTimes(1); - expect(mockDelete).toBeCalledWith(clusterName, internalTopicPayload.name); + expect(mockDelete).toBeCalledWith(clusterName, externalTopicPayload.name); }); it('closes ConfirmationModal when clicked on the cancel button', () => { - const wrapper = shallow(setupComponent()); + const wrapper = shallow(setupComponent({ topic: externalTopicPayload })); + expect(wrapper.exists('.topic-action-block')).toBeTruthy(); expect(wrapper.find('mock-ConfirmationModal').prop('isOpen')).toBeFalsy(); wrapper.find('DropdownItem').last().simulate('click'); expect(wrapper.find('mock-ConfirmationModal').prop('isOpen')).toBeTruthy(); diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Details/Details.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Details/Details.tsx index 548ceea0a1..62fcca3882 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/Details/Details.tsx +++ b/kafka-ui-react-app/src/components/Topics/Topic/Details/Details.tsx @@ -19,6 +19,7 @@ import SettingsContainer from './Settings/SettingsContainer'; interface Props extends Topic, TopicDetails { clusterName: ClusterName; topicName: TopicName; + isInternal: boolean; deleteTopic: (clusterName: ClusterName, topicName: TopicName) => void; clearTopicMessages(clusterName: ClusterName, topicName: TopicName): void; } @@ -26,6 +27,7 @@ interface Props extends Topic, TopicDetails { const Details: React.FC = ({ clusterName, topicName, + isInternal, deleteTopic, clearTopicMessages, }) => { @@ -72,8 +74,8 @@ const Details: React.FC = ({
-
- {!isReadOnly && ( + {!isReadOnly && !isInternal ? ( +
<>
+
+ ) : null}

diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Details/DetailsContainer.ts b/kafka-ui-react-app/src/components/Topics/Topic/Details/DetailsContainer.ts index b704926fc8..475f138bd1 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/Details/DetailsContainer.ts +++ b/kafka-ui-react-app/src/components/Topics/Topic/Details/DetailsContainer.ts @@ -2,6 +2,7 @@ import { connect } from 'react-redux'; import { ClusterName, RootState, TopicName } from 'redux/interfaces'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import { deleteTopic, clearTopicMessages } from 'redux/actions'; +import { getIsTopicInternal } from 'redux/reducers/topics/selectors'; import Details from './Details'; @@ -22,6 +23,7 @@ const mapStateToProps = ( ) => ({ clusterName, topicName, + isInternal: getIsTopicInternal(state, topicName), }); const mapDispatchToProps = { diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Details/Overview/Overview.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Details/Overview/Overview.tsx index 02f235475b..52811ac353 100644 --- a/kafka-ui-react-app/src/components/Topics/Topic/Details/Overview/Overview.tsx +++ b/kafka-ui-react-app/src/components/Topics/Topic/Details/Overview/Overview.tsx @@ -75,22 +75,24 @@ const Overview: React.FC = ({ {offsetMin} {offsetMax} - - - - } - right - > - - clearTopicMessages(clusterName, topicName, [partition]) + {!internal ? ( + + + } + right > - Clear Messages - - + + clearTopicMessages(clusterName, topicName, [partition]) + } + > + Clear Messages + + + ) : null} ))} diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Details/Overview/__test__/Overview.spec.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Details/Overview/__test__/Overview.spec.tsx new file mode 100644 index 0000000000..824e327fb5 --- /dev/null +++ b/kafka-ui-react-app/src/components/Topics/Topic/Details/Overview/__test__/Overview.spec.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import Overview from 'components/Topics/Topic/Details/Overview/Overview'; + +describe('Overview', () => { + const mockInternal = false; + const mockClusterName = 'local'; + const mockTopicName = 'topic'; + const mockClearTopicMessages = jest.fn(); + const mockPartitions = [ + { + partition: 1, + leader: 1, + replicas: [ + { + broker: 1, + leader: false, + inSync: true, + }, + ], + offsetMax: 0, + offsetMin: 0, + }, + ]; + + describe('when it has internal flag', () => { + it('does not render the Action button a Topic', () => { + const component = shallow( + + ); + + expect(component.exists('Dropdown')).toBeTruthy(); + }); + }); +}); diff --git a/kafka-ui-react-app/src/components/Topics/Topic/Details/__test__/Details.spec.tsx b/kafka-ui-react-app/src/components/Topics/Topic/Details/__test__/Details.spec.tsx new file mode 100644 index 0000000000..a32b88a95e --- /dev/null +++ b/kafka-ui-react-app/src/components/Topics/Topic/Details/__test__/Details.spec.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import { StaticRouter } from 'react-router-dom'; +import ClusterContext from 'components/contexts/ClusterContext'; +import Details from 'components/Topics/Topic/Details/Details'; +import { + internalTopicPayload, + externalTopicPayload, +} from 'redux/reducers/topics/__test__/fixtures'; + +describe('Details', () => { + const mockDelete = jest.fn(); + const mockClusterName = 'local'; + const mockClearTopicMessages = jest.fn(); + const mockInternalTopicPayload = internalTopicPayload.internal; + const mockExternalTopicPayload = externalTopicPayload.internal; + + describe('when it has readonly flag', () => { + it('does not render the Action button a Topic', () => { + const component = mount( + + +
+ + + ); + + expect(component.exists('button')).toBeFalsy(); + }); + }); + + describe('when it does not have readonly flag', () => { + it('renders the Action button a Topic', () => { + const component = mount( + + +
+ + + ); + + expect(component.exists('button')).toBeTruthy(); + }); + }); +}); diff --git a/kafka-ui-react-app/src/redux/reducers/topics/selectors.ts b/kafka-ui-react-app/src/redux/reducers/topics/selectors.ts index 8de5390119..6aa5a6f228 100644 --- a/kafka-ui-react-app/src/redux/reducers/topics/selectors.ts +++ b/kafka-ui-react-app/src/redux/reducers/topics/selectors.ts @@ -121,3 +121,8 @@ export const getTopicConfigByParamName = createSelector( return byParamName; } ); + +export const getIsTopicInternal = createSelector( + getTopicByName, + ({ internal }) => !!internal +);