[ISSUE-191] Cluster Widget improvements

This commit is contained in:
Oleg Shuralev 2021-02-17 17:59:33 +03:00
parent dd866f78ef
commit c66e1ab671
No known key found for this signature in database
GPG key ID: 99C6BDC0A1C2E647
6 changed files with 279 additions and 26 deletions

View file

@ -1,8 +1,8 @@
import React from 'react';
import formatBytes from 'lib/utils/formatBytes';
import { NavLink } from 'react-router-dom';
import { clusterBrokersPath } from 'lib/paths';
import { clusterBrokersPath, clusterTopicsPath } from 'lib/paths';
import { Cluster, ServerStatus } from 'generated-sources';
import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
interface ClusterWidgetProps {
cluster: Cluster;
@ -47,16 +47,20 @@ const ClusterWidget: React.FC<ClusterWidgetProps> = ({
<tr>
<th>Topics</th>
<td>
<NavLink to={clusterBrokersPath(name)}>{topicCount}</NavLink>
<NavLink to={clusterTopicsPath(name)}>{topicCount}</NavLink>
</td>
</tr>
<tr>
<th>Production</th>
<td>{formatBytes(bytesInPerSec || 0)}</td>
<td>
<BytesFormatted value={bytesInPerSec} />
</td>
</tr>
<tr>
<th>Consumption</th>
<td>{formatBytes(bytesOutPerSec || 0)}</td>
<td>
<BytesFormatted value={bytesOutPerSec} />
</td>
</tr>
</tbody>
</table>

View file

@ -0,0 +1,73 @@
import React from 'react';
import { shallow } from 'enzyme';
import { ServerStatus } from 'generated-sources';
import { clusterBrokersPath, clusterTopicsPath } from 'lib/paths';
import ClusterWidget from '../ClusterWidget';
import { offlineCluster, onlineCluster } from './fixtures';
describe('ClusterWidget', () => {
describe('when cluster is online', () => {
it('renders with correct tag', () => {
const tag = shallow(<ClusterWidget cluster={onlineCluster} />).find(
'.tag'
);
expect(tag.hasClass('is-primary')).toBeTruthy();
expect(tag.text()).toEqual(ServerStatus.Online);
});
it('renders table', () => {
const table = shallow(<ClusterWidget cluster={onlineCluster} />).find(
'table'
);
expect(table.hasClass('is-fullwidth')).toBeTruthy();
expect(
table.find(`NavLink[to="${clusterBrokersPath(onlineCluster.name)}"]`)
.exists
).toBeTruthy();
expect(
table.find(`NavLink[to="${clusterTopicsPath(onlineCluster.name)}"]`)
.exists
).toBeTruthy();
});
it('matches snapshot', () => {
expect(
shallow(<ClusterWidget cluster={onlineCluster} />)
).toMatchSnapshot();
});
});
describe('when cluster is offline', () => {
it('renders with correct tag', () => {
const tag = shallow(<ClusterWidget cluster={offlineCluster} />).find(
'.tag'
);
expect(tag.hasClass('is-danger')).toBeTruthy();
expect(tag.text()).toEqual(ServerStatus.Offline);
});
it('renders table', () => {
const table = shallow(<ClusterWidget cluster={offlineCluster} />).find(
'table'
);
expect(table.hasClass('is-fullwidth')).toBeTruthy();
expect(
table.find(`NavLink[to="${clusterBrokersPath(onlineCluster.name)}"]`)
.exists
).toBeTruthy();
expect(
table.find(`NavLink[to="${clusterTopicsPath(onlineCluster.name)}"]`)
.exists
).toBeTruthy();
});
it('matches snapshot', () => {
expect(
shallow(<ClusterWidget cluster={offlineCluster} />)
).toMatchSnapshot();
});
});
});

View file

@ -0,0 +1,159 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ClusterWidget when cluster is offline matches snapshot 1`] = `
<div
className="column is-full-modile is-6"
>
<div
className="box"
>
<div
className="title is-6 has-text-overflow-ellipsis"
>
<div
className="tag has-margin-right is-danger"
>
offline
</div>
local
</div>
<table
className="table is-fullwidth"
>
<tbody>
<tr>
<th>
Brokers
</th>
<td>
<NavLink
to="/ui/clusters/local/brokers"
>
1
</NavLink>
</td>
</tr>
<tr>
<th>
Partitions
</th>
<td>
2
</td>
</tr>
<tr>
<th>
Topics
</th>
<td>
<NavLink
to="/ui/clusters/local/topics"
>
2
</NavLink>
</td>
</tr>
<tr>
<th>
Production
</th>
<td>
<BytesFormatted
value={8000.00000673768}
/>
</td>
</tr>
<tr>
<th>
Consumption
</th>
<td>
<BytesFormatted
value={0.815306356729712}
/>
</td>
</tr>
</tbody>
</table>
</div>
</div>
`;
exports[`ClusterWidget when cluster is online matches snapshot 1`] = `
<div
className="column is-full-modile is-6"
>
<div
className="box"
>
<div
className="title is-6 has-text-overflow-ellipsis"
>
<div
className="tag has-margin-right is-primary"
>
online
</div>
secondLocal
</div>
<table
className="table is-fullwidth"
>
<tbody>
<tr>
<th>
Brokers
</th>
<td>
<NavLink
to="/ui/clusters/secondLocal/brokers"
>
1
</NavLink>
</td>
</tr>
<tr>
<th>
Partitions
</th>
<td>
6
</td>
</tr>
<tr>
<th>
Topics
</th>
<td>
<NavLink
to="/ui/clusters/secondLocal/topics"
>
3
</NavLink>
</td>
</tr>
<tr>
<th>
Production
</th>
<td>
<BytesFormatted
value={0.00003061819685376472}
/>
</td>
</tr>
<tr>
<th>
Consumption
</th>
<td>
<BytesFormatted
value={5.737800890036267}
/>
</td>
</tr>
</tbody>
</table>
</div>
</div>
`;

View file

@ -0,0 +1,25 @@
import { Cluster, ServerStatus } from 'generated-sources';
export const onlineCluster: Cluster = {
name: 'secondLocal',
defaultCluster: false,
status: ServerStatus.Online,
brokerCount: 1,
onlinePartitionCount: 6,
topicCount: 3,
bytesInPerSec: 0.000030618196853764715,
bytesOutPerSec: 5.737800890036267075817,
};
export const offlineCluster: Cluster = {
name: 'local',
defaultCluster: true,
status: ServerStatus.Offline,
brokerCount: 1,
onlinePartitionCount: 2,
topicCount: 2,
bytesInPerSec: 8000.0000067376808542600021,
bytesOutPerSec: 0.8153063567297119490871,
};
export const clusters: Cluster[] = [onlineCluster, offlineCluster];

View file

@ -5,18 +5,23 @@ interface Props {
precision?: number;
}
const BytesFormatted: React.FC<Props> = ({ value, precision }) => {
const formatBytes = React.useCallback(() => {
const numVal = typeof value === 'string' ? parseInt(value, 10) : value;
if (!numVal) return 0;
const pow = Math.floor(Math.log2(numVal) / 10);
const BytesFormatted: React.FC<Props> = ({ value, precision = 0 }) => {
const formatedValue = React.useMemo(() => {
const bytes = typeof value === 'string' ? parseInt(value, 10) : value;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
if (!bytes || bytes === 0) return [0, sizes[0]];
if (bytes < 1024) return [Math.ceil(bytes), sizes[0]];
const pow = Math.floor(Math.log2(bytes) / 10);
const multiplier = 10 ** (precision || 2);
return (
Math.round((numVal * multiplier) / 1024 ** pow) / multiplier +
['Bytes', 'KB', 'MB', 'GB', 'TB'][pow]
Math.round((bytes * multiplier) / 1024 ** pow) / multiplier + sizes[pow]
);
}, [value]);
return <span>{formatBytes()}</span>;
return <span>{formatedValue}</span>;
};
export default BytesFormatted;

View file

@ -1,13 +0,0 @@
function formatBytes(bytes: number, decimals = 0) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
if (bytes === 0) return [0, sizes[0]];
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const i = Math.floor(Math.log(bytes) / Math.log(k));
return [parseFloat((bytes / k ** i).toFixed(dm)), sizes[i]];
}
export default formatBytes;