Browse Source

Topics sorted alphabetically (#1999)

* Added topics sorting and tests

* Sorting topics logic moved to redux, test improved

* LocaleCompare replaced with default sort function

* Shadow fixed on overview topic page

* Code samples fixed in InfoModal

* String type removed

* Unused import removed

* Default JS sort method removed with lodash library sorting method
Kirill Morozov 3 years ago
parent
commit
ad2966f31b

+ 2 - 0
kafka-ui-react-app/src/components/Connect/List/ListContainer.ts

@@ -12,6 +12,7 @@ import {
   getAreConnectorsFetching,
   getConnectorSearch,
   getFailedConnectors,
+  getSortedTopics,
   getFailedTasks,
 } from 'redux/reducers/connect/selectors';
 import List from 'components/Connect/List/List';
@@ -21,6 +22,7 @@ const mapStateToProps = (state: RootState) => ({
   areConnectorsFetching: getAreConnectorsFetching(state),
   connects: getConnects(state),
   failedConnectors: getFailedConnectors(state),
+  sortedTopics: getSortedTopics(state),
   failedTasks: getFailedTasks(state),
   connectors: getConnectors(state),
   search: getConnectorSearch(state),

+ 8 - 0
kafka-ui-react-app/src/components/Connect/List/__tests__/ListItem.spec.tsx

@@ -52,6 +52,14 @@ describe('Connectors ListItem', () => {
     expect(screen.getAllByRole('cell')[6]).toHaveTextContent('2 of 2');
   });
 
+  it('topics tags are sorted', () => {
+    render(setupWrapper());
+    const getLink = screen.getAllByRole('link');
+    expect(getLink[1]).toHaveTextContent('a');
+    expect(getLink[2]).toHaveTextContent('b');
+    expect(getLink[3]).toHaveTextContent('c');
+  });
+
   it('renders item with failed tasks', () => {
     render(
       setupWrapper({

+ 10 - 0
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/Filters.styled.ts

@@ -125,6 +125,16 @@ export const InfoParagraph = styled.p`
   color: ${({ theme }) => theme.table.td.color.normal};
 `;
 
+export const InfoCodeSample = styled.pre`
+  background: #f5f5f5;
+  padding: 5px;
+  border: 1px solid #e1e1e1;
+  border-radius: 5px;
+  width: fit-content;
+  margin: 5px 20px;
+  color: #cc0f35;
+`;
+
 export const MessageFilterModal = styled.div`
   height: auto;
   width: 560px;

+ 22 - 17
kafka-ui-react-app/src/components/Topics/Topic/Details/Messages/Filters/InfoModal.tsx

@@ -26,32 +26,37 @@ const InfoModal: React.FC<InfoModalProps> = ({ toggleIsOpen }) => {
       </S.InfoParagraph>
       <ol aria-label="info-list">
         <S.ListItem>
-          `keyAsText != null && keyAsText ~&quot;([Gg])roovy&quot;` - regex for
-          key as a string
+          <code>keyAsText != null && keyAsText ~&quot;([Gg])roovy&quot;</code> -
+          regex for key as a string
         </S.ListItem>
         <S.ListItem>
-          `value.name == &quot;iS.ListItemax&quot; && value.age &gt; 30` - in
-          case value is json
+          <code>
+            value.name == &quot;iS.ListItemax&quot; && value.age &gt; 30
+          </code>{' '}
+          - in case value is json
         </S.ListItem>
         <S.ListItem>
-          `value == null && valueAsText != null` - search for values that are
-          not nulls and are not json
+          <code>value == null && valueAsText != null</code> - search for values
+          that are not nulls and are not json
         </S.ListItem>
         <S.ListItem>
-          `headers.sentBy == &quot;some system&quot; &&
-          headers[&quot;sentAt&quot;] == &quot;2020-01-01&quot;`
+          <code>
+            headers.sentBy == &quot;some system&quot; &&
+            headers[&quot;sentAt&quot;] == &quot;2020-01-01&quot;
+          </code>
         </S.ListItem>
         <S.ListItem>multiline filters are also allowed:</S.ListItem>
         <S.InfoParagraph>
-          ```
-          <br />
-          def name = value.name
-          <br />
-          def age = value.age
-          <br />
-          name == &quot;iliax&quot; && age == 30
-          <br />
-          ```
+          <S.InfoCodeSample>
+            <code>
+              def name = value.name
+              <br />
+              def age = value.age
+              <br />
+              name == &quot;iliax&quot; && age == 30
+              <br />
+            </code>
+          </S.InfoCodeSample>
         </S.InfoParagraph>
       </ol>
       <S.ButtonContainer>

+ 2 - 2
kafka-ui-react-app/src/components/common/Metrics/Metrics.styled.tsx

@@ -21,7 +21,6 @@ export const IndicatorWrapper = styled.div`
   align-items: flex-start;
   padding: 12px 16px;
   box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.08);
-  margin: 0 0 3px 0;
   flex-grow: 1;
 `;
 
@@ -36,10 +35,11 @@ export const IndicatorTitle = styled.div`
 
 export const IndicatorsWrapper = styled.div`
   display: flex;
-  gap: 1px;
+  gap: 2px;
   flex-wrap: wrap;
   border-radius: 8px;
   overflow: auto;
+  box-shadow: 3px 3px 3px rgba(0, 0, 0, 0.08);
 `;
 
 export const SectionTitle = styled.h5`

+ 4 - 4
kafka-ui-react-app/src/redux/reducers/connect/__test__/fixtures.ts

@@ -19,7 +19,7 @@ export const connectorsServerPayload = [
     name: 'hdfs-source-connector',
     connector_class: 'FileStreamSource',
     type: ConnectorType.SOURCE,
-    topics: ['test-topic'],
+    topics: ['a', 'b', 'c'],
     status: {
       state: ConnectorTaskStatus.RUNNING,
       workerId: 1,
@@ -48,7 +48,7 @@ export const connectors: FullConnectorInfo[] = [
     name: 'hdfs-source-connector',
     connectorClass: 'FileStreamSource',
     type: ConnectorType.SOURCE,
-    topics: ['test-topic'],
+    topics: ['a', 'b', 'c'],
     status: {
       state: ConnectorState.RUNNING,
     },
@@ -75,7 +75,7 @@ export const failedConnectors: FullConnectorInfo[] = [
     name: 'hdfs-source-connector',
     connectorClass: 'FileStreamSource',
     type: ConnectorType.SOURCE,
-    topics: ['test-topic'],
+    topics: ['a', 'b', 'c'],
     status: {
       state: ConnectorState.FAILED,
     },
@@ -87,7 +87,7 @@ export const failedConnectors: FullConnectorInfo[] = [
     name: 'hdfs2-source-connector',
     connectorClass: 'FileStreamSource',
     type: ConnectorType.SINK,
-    topics: ['test-topic'],
+    topics: ['a', 'b', 'c'],
     status: {
       state: ConnectorState.FAILED,
     },

+ 11 - 0
kafka-ui-react-app/src/redux/reducers/connect/__test__/selectors.spec.ts

@@ -83,6 +83,17 @@ describe('Connect selectors', () => {
       expect(selectors.getFailedTasks(store.getState())).toEqual(1);
     });
 
+    it('returns sorted topics', () => {
+      store.dispatch({
+        type: fetchConnectors.fulfilled.type,
+        payload: { connectors },
+      });
+      const sortedTopics = selectors.getSortedTopics(store.getState());
+      if (sortedTopics[0] && sortedTopics[0].length > 1) {
+        expect(sortedTopics[0]).toEqual(['a', 'b', 'c']);
+      }
+    });
+
     it('returns connector', () => {
       store.dispatch({
         type: fetchConnector.fulfilled.type,

+ 5 - 0
kafka-ui-react-app/src/redux/reducers/connect/selectors.ts

@@ -6,6 +6,7 @@ import {
   ConnectorState,
   FullConnectorInfo,
 } from 'generated-sources';
+import { sortBy } from 'lodash';
 
 import {
   deleteConnector,
@@ -63,6 +64,10 @@ export const getFailedTasks = createSelector(connectState, ({ connectors }) => {
     .reduce((acc: number, value: number) => acc + value, 0);
 });
 
+export const getSortedTopics = createSelector(connectState, ({ connectors }) =>
+  connectors.map(({ topics }) => sortBy(topics || []))
+);
+
 const getConnectorFetchingStatus = createFetchingSelector(
   fetchConnector.typePrefix
 );