Cleanup styling (#365)
This commit is contained in:
parent
6e8226298f
commit
d471759b79
27 changed files with 1033 additions and 504 deletions
|
@ -17,7 +17,7 @@ $navbar-width: 250px;
|
|||
z-index: 20;
|
||||
}
|
||||
|
||||
&__navbar {
|
||||
&__sidebar{
|
||||
width: $navbar-width;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -28,6 +28,19 @@ $navbar-width: 250px;
|
|||
bottom: 0;
|
||||
padding: 20px 20px;
|
||||
overflow-y: scroll;
|
||||
transition: width .25s,opacity .25s,transform .25s,-webkit-transform .25s;
|
||||
|
||||
&Overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
height: 120vh;
|
||||
z-index: 99;
|
||||
display: block;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
-webkit-transition: all .5s ease;
|
||||
transition: all .5s ease;
|
||||
}
|
||||
}
|
||||
|
||||
&__alerts {
|
||||
|
@ -47,3 +60,35 @@ $navbar-width: 250px;
|
|||
.react-datepicker-popper {
|
||||
z-index: 30 !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1023px) {
|
||||
.Layout {
|
||||
&__container {
|
||||
margin-left: initial;
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
&__sidebar {
|
||||
left: -$navbar-width;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
&__alerts {
|
||||
max-width: initial;
|
||||
}
|
||||
|
||||
&--sidebarVisible {
|
||||
.Layout__sidebar {
|
||||
transform: translate3d($navbar-width,0,0);
|
||||
|
||||
&Overlay {
|
||||
background-color: rgba(34,41,47,.5);
|
||||
left: 0;
|
||||
right: 0;
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +1,97 @@
|
|||
import './App.scss';
|
||||
import React from 'react';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
import cx from 'classnames';
|
||||
import { Cluster } from 'generated-sources';
|
||||
import { Switch, Route, useLocation } from 'react-router-dom';
|
||||
import { GIT_TAG, GIT_COMMIT } from 'lib/constants';
|
||||
import { Alerts } from 'redux/interfaces';
|
||||
import NavContainer from './Nav/NavContainer';
|
||||
import Nav from './Nav/Nav';
|
||||
import PageLoader from './common/PageLoader/PageLoader';
|
||||
import Dashboard from './Dashboard/Dashboard';
|
||||
import Cluster from './Cluster/Cluster';
|
||||
import ClusterPage from './Cluster/Cluster';
|
||||
import Version from './Version/Version';
|
||||
import Alert from './Alert/Alert';
|
||||
|
||||
export interface AppProps {
|
||||
isClusterListFetched?: boolean;
|
||||
alerts: Alerts;
|
||||
clusters: Cluster[];
|
||||
fetchClustersList: () => void;
|
||||
}
|
||||
|
||||
const App: React.FC<AppProps> = ({
|
||||
isClusterListFetched,
|
||||
alerts,
|
||||
clusters,
|
||||
fetchClustersList,
|
||||
}) => {
|
||||
const [isSidebarVisible, setIsSidebarVisible] = React.useState(false);
|
||||
|
||||
const onBurgerClick = React.useCallback(
|
||||
() => setIsSidebarVisible(!isSidebarVisible),
|
||||
[isSidebarVisible]
|
||||
);
|
||||
|
||||
const closeSidebar = React.useCallback(() => setIsSidebarVisible(false), []);
|
||||
|
||||
const location = useLocation();
|
||||
|
||||
React.useEffect(() => {
|
||||
closeSidebar();
|
||||
}, [location]);
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchClustersList();
|
||||
}, [fetchClustersList]);
|
||||
|
||||
return (
|
||||
<div className="Layout">
|
||||
<div
|
||||
className={cx('Layout', { 'Layout--sidebarVisible': isSidebarVisible })}
|
||||
>
|
||||
<nav
|
||||
className="navbar is-fixed-top is-white Layout__header"
|
||||
role="navigation"
|
||||
aria-label="main navigation"
|
||||
>
|
||||
<div className="navbar-brand">
|
||||
<div
|
||||
className={cx('navbar-burger', 'ml-0', {
|
||||
'is-active': isSidebarVisible,
|
||||
})}
|
||||
onClick={onBurgerClick}
|
||||
onKeyDown={onBurgerClick}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<span />
|
||||
<span />
|
||||
<span />
|
||||
</div>
|
||||
|
||||
<a className="navbar-item title is-5 is-marginless" href="/ui">
|
||||
Kafka UI
|
||||
</a>
|
||||
</div>
|
||||
<div className="navbar-end">
|
||||
<div className="navbar-item mr-2">
|
||||
|
||||
<div className="navbar-item">
|
||||
<Version tag={GIT_TAG} commit={GIT_COMMIT} />
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main className="Layout__container">
|
||||
<NavContainer className="Layout__navbar" />
|
||||
<div className="Layout__sidebar has-shadow has-background-white">
|
||||
<Nav
|
||||
clusters={clusters}
|
||||
isClusterListFetched={isClusterListFetched}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className="Layout__sidebarOverlay is-overlay"
|
||||
onClick={closeSidebar}
|
||||
onKeyDown={closeSidebar}
|
||||
tabIndex={-1}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{isClusterListFetched ? (
|
||||
<Switch>
|
||||
<Route
|
||||
|
@ -53,7 +99,7 @@ const App: React.FC<AppProps> = ({
|
|||
path={['/', '/ui', '/ui/clusters']}
|
||||
component={Dashboard}
|
||||
/>
|
||||
<Route path="/ui/clusters/:clusterName" component={Cluster} />
|
||||
<Route path="/ui/clusters/:clusterName" component={ClusterPage} />
|
||||
</Switch>
|
||||
) : (
|
||||
<PageLoader fullHeight />
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { fetchClustersList } from 'redux/actions';
|
||||
import { getIsClusterListFetched } from 'redux/reducers/clusters/selectors';
|
||||
import {
|
||||
getClusterList,
|
||||
getIsClusterListFetched,
|
||||
} from 'redux/reducers/clusters/selectors';
|
||||
import { getAlerts } from 'redux/reducers/alerts/selectors';
|
||||
import { RootState } from 'redux/interfaces';
|
||||
import App from './App';
|
||||
|
@ -8,6 +11,7 @@ import App from './App';
|
|||
const mapStateToProps = (state: RootState) => ({
|
||||
isClusterListFetched: getIsClusterListFetched(state),
|
||||
alerts: getAlerts(state),
|
||||
clusters: getClusterList(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
|
|
|
@ -7,16 +7,15 @@ import MetricsWrapper from 'components/common/Dashboard/MetricsWrapper';
|
|||
import Indicator from 'components/common/Dashboard/Indicator';
|
||||
import Breadcrumb from 'components/common/Breadcrumb/Breadcrumb';
|
||||
import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
|
||||
import { useParams } from 'react-router';
|
||||
|
||||
interface Props extends ClusterStats {
|
||||
clusterName: ClusterName;
|
||||
isFetched: boolean;
|
||||
fetchClusterStats: (clusterName: ClusterName) => void;
|
||||
fetchBrokers: (clusterName: ClusterName) => void;
|
||||
}
|
||||
|
||||
const Brokers: React.FC<Props> = ({
|
||||
clusterName,
|
||||
brokerCount,
|
||||
activeControllers,
|
||||
zooKeeperStatus,
|
||||
|
@ -29,6 +28,8 @@ const Brokers: React.FC<Props> = ({
|
|||
fetchClusterStats,
|
||||
fetchBrokers,
|
||||
}) => {
|
||||
const { clusterName } = useParams<{ clusterName: ClusterName }>();
|
||||
|
||||
React.useEffect(() => {
|
||||
fetchClusterStats(clusterName);
|
||||
fetchBrokers(clusterName);
|
||||
|
@ -44,9 +45,13 @@ const Brokers: React.FC<Props> = ({
|
|||
<div className="section">
|
||||
<Breadcrumb>Brokers overview</Breadcrumb>
|
||||
<MetricsWrapper title="Uptime">
|
||||
<Indicator label="Total Brokers">{brokerCount}</Indicator>
|
||||
<Indicator label="Active Controllers">{activeControllers}</Indicator>
|
||||
<Indicator label="Zookeeper Status">
|
||||
<Indicator className="is-one-third" label="Total Brokers">
|
||||
{brokerCount}
|
||||
</Indicator>
|
||||
<Indicator className="is-one-third" label="Active Controllers">
|
||||
{activeControllers}
|
||||
</Indicator>
|
||||
<Indicator className="is-one-third" label="Zookeeper Status">
|
||||
<span className={cx('tag', zkOnline ? 'is-primary' : 'is-danger')}>
|
||||
{zkOnline ? 'Online' : 'Offline'}
|
||||
</span>
|
||||
|
|
|
@ -1,43 +1,36 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { fetchClusterStats, fetchBrokers } from 'redux/actions';
|
||||
import * as brokerSelectors from 'redux/reducers/brokers/selectors';
|
||||
import { RootState, ClusterName } from 'redux/interfaces';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
import { RootState } from 'redux/interfaces';
|
||||
import {
|
||||
getIsBrokerListFetched,
|
||||
getBrokerCount,
|
||||
getZooKeeperStatus,
|
||||
getActiveControllers,
|
||||
getOnlinePartitionCount,
|
||||
getOfflinePartitionCount,
|
||||
getInSyncReplicasCount,
|
||||
getOutOfSyncReplicasCount,
|
||||
getUnderReplicatedPartitionCount,
|
||||
getDiskUsage,
|
||||
} from 'redux/reducers/brokers/selectors';
|
||||
import Brokers from './Brokers';
|
||||
|
||||
interface RouteProps {
|
||||
clusterName: ClusterName;
|
||||
}
|
||||
|
||||
type OwnProps = RouteComponentProps<RouteProps>;
|
||||
|
||||
const mapStateToProps = (
|
||||
state: RootState,
|
||||
{
|
||||
match: {
|
||||
params: { clusterName },
|
||||
},
|
||||
}: OwnProps
|
||||
) => ({
|
||||
isFetched: brokerSelectors.getIsBrokerListFetched(state),
|
||||
clusterName,
|
||||
brokerCount: brokerSelectors.getBrokerCount(state),
|
||||
zooKeeperStatus: brokerSelectors.getZooKeeperStatus(state),
|
||||
activeControllers: brokerSelectors.getActiveControllers(state),
|
||||
onlinePartitionCount: brokerSelectors.getOnlinePartitionCount(state),
|
||||
offlinePartitionCount: brokerSelectors.getOfflinePartitionCount(state),
|
||||
inSyncReplicasCount: brokerSelectors.getInSyncReplicasCount(state),
|
||||
outOfSyncReplicasCount: brokerSelectors.getOutOfSyncReplicasCount(state),
|
||||
underReplicatedPartitionCount: brokerSelectors.getUnderReplicatedPartitionCount(
|
||||
state
|
||||
),
|
||||
diskUsage: brokerSelectors.getDiskUsage(state),
|
||||
const mapStateToProps = (state: RootState) => ({
|
||||
isFetched: getIsBrokerListFetched(state),
|
||||
brokerCount: getBrokerCount(state),
|
||||
zooKeeperStatus: getZooKeeperStatus(state),
|
||||
activeControllers: getActiveControllers(state),
|
||||
onlinePartitionCount: getOnlinePartitionCount(state),
|
||||
offlinePartitionCount: getOfflinePartitionCount(state),
|
||||
inSyncReplicasCount: getInSyncReplicasCount(state),
|
||||
outOfSyncReplicasCount: getOutOfSyncReplicasCount(state),
|
||||
underReplicatedPartitionCount: getUnderReplicatedPartitionCount(state),
|
||||
diskUsage: getDiskUsage(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
fetchClusterStats: (clusterName: ClusterName) =>
|
||||
fetchClusterStats(clusterName),
|
||||
fetchBrokers: (clusterName: ClusterName) => fetchBrokers(clusterName),
|
||||
fetchClusterStats,
|
||||
fetchBrokers,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Brokers);
|
||||
|
|
|
@ -57,6 +57,7 @@ const List: React.FC<ListProps> = ({
|
|||
<PageLoader />
|
||||
) : (
|
||||
<div className="box">
|
||||
<div className="table-container">
|
||||
<table className="table is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -79,6 +80,7 @@ const List: React.FC<ListProps> = ({
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -54,6 +54,7 @@ const Details: React.FC<Props> = ({
|
|||
|
||||
{isFetched ? (
|
||||
<div className="box">
|
||||
<div className="table-container">
|
||||
<table className="table is-striped is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -77,6 +78,7 @@ const Details: React.FC<Props> = ({
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<PageLoader />
|
||||
)}
|
||||
|
|
|
@ -36,6 +36,7 @@ const List: React.FC<Props> = ({ consumerGroups }) => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="table-container">
|
||||
<table className="table is-striped is-fullwidth is-hoverable">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -60,6 +61,7 @@ const List: React.FC<Props> = ({ consumerGroups }) => {
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
'No active consumer groups'
|
||||
)}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Cluster } from 'generated-sources';
|
|||
import ClusterMenu from './ClusterMenu';
|
||||
|
||||
interface Props {
|
||||
isClusterListFetched: boolean;
|
||||
isClusterListFetched?: boolean;
|
||||
clusters: Cluster[];
|
||||
className?: string;
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
getIsClusterListFetched,
|
||||
getClusterList,
|
||||
} from 'redux/reducers/clusters/selectors';
|
||||
import { RootState } from 'redux/interfaces';
|
||||
import Nav from './Nav';
|
||||
|
||||
const mapStateToProps = (state: RootState) => ({
|
||||
isClusterListFetched: getIsClusterListFetched(state),
|
||||
clusters: getClusterList(state),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(Nav);
|
21
kafka-ui-react-app/src/components/Nav/__tests__/Nav.spec.tsx
Normal file
21
kafka-ui-react-app/src/components/Nav/__tests__/Nav.spec.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { onlineClusterPayload } from 'redux/reducers/clusters/__test__/fixtures';
|
||||
import Nav from '../Nav';
|
||||
|
||||
describe('Nav', () => {
|
||||
it('renders loader', () => {
|
||||
const wrapper = shallow(<Nav clusters={[]} />);
|
||||
expect(wrapper.find('.loader')).toBeTruthy();
|
||||
expect(wrapper.exists('ClusterMenu')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('renders ClusterMenu', () => {
|
||||
const wrapper = shallow(
|
||||
<Nav clusters={[onlineClusterPayload]} isClusterListFetched />
|
||||
);
|
||||
expect(wrapper.exists('.loader')).toBeFalsy();
|
||||
expect(wrapper.exists('ClusterMenu')).toBeTruthy();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Nav renders ClusterMenu 1`] = `
|
||||
<aside
|
||||
className="menu has-shadow has-background-white"
|
||||
>
|
||||
<p
|
||||
className="menu-label"
|
||||
>
|
||||
General
|
||||
</p>
|
||||
<ul
|
||||
className="menu-list"
|
||||
>
|
||||
<li>
|
||||
<NavLink
|
||||
activeClassName="is-active"
|
||||
exact={true}
|
||||
title="Dashboard"
|
||||
to="/ui"
|
||||
>
|
||||
Dashboard
|
||||
</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
<p
|
||||
className="menu-label"
|
||||
>
|
||||
Clusters
|
||||
</p>
|
||||
<ClusterMenu
|
||||
cluster={
|
||||
Object {
|
||||
"brokerCount": 1,
|
||||
"bytesInPerSec": 1.55,
|
||||
"bytesOutPerSec": 9.314,
|
||||
"defaultCluster": true,
|
||||
"features": Array [],
|
||||
"name": "secondLocal",
|
||||
"onlinePartitionCount": 6,
|
||||
"status": "online",
|
||||
"topicCount": 3,
|
||||
}
|
||||
}
|
||||
key="secondLocal"
|
||||
/>
|
||||
</aside>
|
||||
`;
|
|
@ -85,6 +85,7 @@ const Details: React.FC<DetailsProps> = ({
|
|||
<LatestVersionItem schema={schema} />
|
||||
</div>
|
||||
<div className="box">
|
||||
<div className="table-container">
|
||||
<table className="table is-striped is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -100,6 +101,7 @@ const Details: React.FC<DetailsProps> = ({
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<PageLoader />
|
||||
|
|
|
@ -12,6 +12,7 @@ const LatestVersionItem: React.FC<LatestVersionProps> = ({
|
|||
<div className="tile is-ancestor mt-1">
|
||||
<div className="tile is-4 is-parent">
|
||||
<div className="tile is-child">
|
||||
<div className="table-container">
|
||||
<table className="table is-fullwidth">
|
||||
<tbody>
|
||||
<tr>
|
||||
|
@ -30,6 +31,7 @@ const LatestVersionItem: React.FC<LatestVersionProps> = ({
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="tile is-parent">
|
||||
<div className="tile is-child box py-1">
|
||||
<JSONViewer data={JSON.parse(schema)} />
|
||||
|
|
|
@ -84,6 +84,9 @@ exports[`Details View Initial state matches snapshot 1`] = `
|
|||
</div>
|
||||
<div
|
||||
className="box"
|
||||
>
|
||||
<div
|
||||
className="table-container"
|
||||
>
|
||||
<table
|
||||
className="table is-striped is-fullwidth"
|
||||
|
@ -105,6 +108,7 @@ exports[`Details View Initial state matches snapshot 1`] = `
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Details View when page with schema versions is loading matches snapshot 1`] = `
|
||||
|
@ -215,6 +219,9 @@ exports[`Details View when page with schema versions loaded when schema has vers
|
|||
</div>
|
||||
<div
|
||||
className="box"
|
||||
>
|
||||
<div
|
||||
className="table-container"
|
||||
>
|
||||
<table
|
||||
className="table is-striped is-fullwidth"
|
||||
|
@ -263,6 +270,7 @@ exports[`Details View when page with schema versions loaded when schema has vers
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Details View when page with schema versions loaded when versions are empty matches snapshot 1`] = `
|
||||
|
@ -349,6 +357,9 @@ exports[`Details View when page with schema versions loaded when versions are em
|
|||
</div>
|
||||
<div
|
||||
className="box"
|
||||
>
|
||||
<div
|
||||
className="table-container"
|
||||
>
|
||||
<table
|
||||
className="table is-striped is-fullwidth"
|
||||
|
@ -370,4 +381,5 @@ exports[`Details View when page with schema versions loaded when versions are em
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -9,6 +9,9 @@ exports[`LatestVersionItem matches snapshot 1`] = `
|
|||
>
|
||||
<div
|
||||
className="tile is-child"
|
||||
>
|
||||
<div
|
||||
className="table-container"
|
||||
>
|
||||
<table
|
||||
className="table is-fullwidth"
|
||||
|
@ -42,6 +45,7 @@ exports[`LatestVersionItem matches snapshot 1`] = `
|
|||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="tile is-parent"
|
||||
>
|
||||
|
|
|
@ -48,6 +48,7 @@ const List: React.FC<ListProps> = ({
|
|||
<PageLoader />
|
||||
) : (
|
||||
<div className="box">
|
||||
<div className="table-container">
|
||||
<table className="table is-striped is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -68,6 +69,7 @@ const List: React.FC<ListProps> = ({
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -81,6 +81,7 @@ const List: React.FC<Props> = ({
|
|||
<PageLoader />
|
||||
) : (
|
||||
<div className="box">
|
||||
<div className="table-container">
|
||||
<table className="table is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -109,6 +110,7 @@ const List: React.FC<Props> = ({
|
|||
</table>
|
||||
<Pagination totalPages={totalPages} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -37,7 +37,7 @@ const ListItem: React.FC<ListItemProps> = ({
|
|||
|
||||
return (
|
||||
<tr>
|
||||
<td>
|
||||
<td className="has-text-overflow-ellipsis">
|
||||
<NavLink
|
||||
exact
|
||||
to={`topics/${name}`}
|
||||
|
|
|
@ -15,6 +15,7 @@ const MessagesTable: React.FC<MessagesTableProp> = ({ messages, onNext }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="table-container">
|
||||
<table className="table is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -39,6 +40,7 @@ const MessagesTable: React.FC<MessagesTableProp> = ({ messages, onNext }) => {
|
|||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="columns">
|
||||
<div className="column is-full">
|
||||
<CustomParamButton
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
exports[`MessagesTable when topic contains messages matches snapshot 1`] = `
|
||||
<Fragment>
|
||||
<div
|
||||
className="table-container"
|
||||
>
|
||||
<table
|
||||
className="table is-fullwidth"
|
||||
>
|
||||
|
@ -45,6 +48,7 @@ exports[`MessagesTable when topic contains messages matches snapshot 1`] = `
|
|||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div
|
||||
className="columns"
|
||||
>
|
||||
|
|
|
@ -43,6 +43,7 @@ const Overview: React.FC<Props> = ({
|
|||
<Indicator label="Segment count">{segmentCount}</Indicator>
|
||||
</MetricsWrapper>
|
||||
<div className="box">
|
||||
<div className="table-container">
|
||||
<table className="table is-striped is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -64,6 +65,7 @@ const Overview: React.FC<Props> = ({
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ const Settings: React.FC<Props> = ({
|
|||
|
||||
return (
|
||||
<div className="box">
|
||||
<div className="table-container">
|
||||
<table className="table is-striped is-fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -62,6 +63,7 @@ const Settings: React.FC<Props> = ({
|
|||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,61 +1,79 @@
|
|||
import React from 'react';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import { mount } from 'enzyme';
|
||||
import { Provider } from 'react-redux';
|
||||
import { StaticRouter } from 'react-router-dom';
|
||||
import { Alert } from 'redux/interfaces';
|
||||
import configureStore from 'redux/store/configureStore';
|
||||
import App, { AppProps } from '../App';
|
||||
import App, { AppProps } from 'components/App';
|
||||
import AppContainer from 'components/AppContainer';
|
||||
|
||||
const fetchClustersList = jest.fn();
|
||||
const store = configureStore();
|
||||
|
||||
describe('App', () => {
|
||||
describe('container', () => {
|
||||
it('renders view', () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<StaticRouter>
|
||||
<AppContainer />
|
||||
</StaticRouter>
|
||||
</Provider>
|
||||
);
|
||||
expect(wrapper.exists('App')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
describe('view', () => {
|
||||
const setupComponent = (props: Partial<AppProps> = {}) => (
|
||||
<Provider store={store}>
|
||||
<StaticRouter>
|
||||
<App
|
||||
isClusterListFetched
|
||||
alerts={[]}
|
||||
clusters={[]}
|
||||
fetchClustersList={fetchClustersList}
|
||||
{...props}
|
||||
/>
|
||||
</StaticRouter>
|
||||
</Provider>
|
||||
);
|
||||
|
||||
it('handles fetchClustersList', () => {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<StaticRouter>{setupComponent()}</StaticRouter>
|
||||
</Provider>
|
||||
);
|
||||
const wrapper = mount(setupComponent());
|
||||
expect(wrapper.exists()).toBeTruthy();
|
||||
expect(fetchClustersList).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('shows PageLoader until cluster list is fetched', () => {
|
||||
const component = shallow(setupComponent({ isClusterListFetched: false }));
|
||||
let component = mount(setupComponent({ isClusterListFetched: false }));
|
||||
expect(component.exists('.Layout__container PageLoader')).toBeTruthy();
|
||||
expect(component.exists('.Layout__container Switch')).toBeFalsy();
|
||||
component.setProps({ isClusterListFetched: true });
|
||||
|
||||
component = mount(setupComponent({ isClusterListFetched: true }));
|
||||
expect(component.exists('.Layout__container PageLoader')).toBeFalsy();
|
||||
expect(component.exists('.Layout__container Switch')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('correctly renders alerts', () => {
|
||||
const alert = {
|
||||
const alert: Alert = {
|
||||
id: 'alert-id',
|
||||
type: 'success',
|
||||
title: 'My Custom Title',
|
||||
message: 'My Custom Message',
|
||||
createdAt: 1234567890,
|
||||
};
|
||||
const wrapper = shallow(setupComponent());
|
||||
let wrapper = mount(setupComponent());
|
||||
expect(wrapper.exists('.Layout__alerts')).toBeTruthy();
|
||||
expect(wrapper.exists('Alert')).toBeFalsy();
|
||||
|
||||
wrapper.setProps({ alerts: [alert] });
|
||||
wrapper = mount(setupComponent({ alerts: [alert] }));
|
||||
expect(wrapper.exists('Alert')).toBeTruthy();
|
||||
expect(wrapper.find('Alert').length).toEqual(1);
|
||||
});
|
||||
|
||||
it('matches snapshot', () => {
|
||||
const component = shallow(setupComponent());
|
||||
expect(component).toMatchSnapshot();
|
||||
const wrapper = mount(setupComponent());
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,58 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`App matches snapshot 1`] = `
|
||||
exports[`App view matches snapshot 1`] = `
|
||||
<Provider
|
||||
store={
|
||||
Object {
|
||||
"dispatch": [Function],
|
||||
"getState": [Function],
|
||||
"replaceReducer": [Function],
|
||||
"subscribe": [Function],
|
||||
Symbol(observable): [Function],
|
||||
}
|
||||
}
|
||||
>
|
||||
<StaticRouter>
|
||||
<Router
|
||||
history={
|
||||
Object {
|
||||
"action": "POP",
|
||||
"block": [Function],
|
||||
"createHref": [Function],
|
||||
"go": [Function],
|
||||
"goBack": [Function],
|
||||
"goForward": [Function],
|
||||
"listen": [Function],
|
||||
"location": Object {
|
||||
"hash": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"state": undefined,
|
||||
},
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
}
|
||||
}
|
||||
staticContext={Object {}}
|
||||
>
|
||||
<App
|
||||
alerts={Array []}
|
||||
clusters={Array []}
|
||||
fetchClustersList={
|
||||
[MockFunction] {
|
||||
"calls": Array [
|
||||
Array [],
|
||||
],
|
||||
"results": Array [
|
||||
Object {
|
||||
"type": "return",
|
||||
"value": undefined,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
isClusterListFetched={true}
|
||||
>
|
||||
<div
|
||||
className="Layout"
|
||||
>
|
||||
|
@ -12,18 +64,25 @@ exports[`App matches snapshot 1`] = `
|
|||
<div
|
||||
className="navbar-brand"
|
||||
>
|
||||
<div
|
||||
className="navbar-burger ml-0"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<span />
|
||||
<span />
|
||||
<span />
|
||||
</div>
|
||||
<a
|
||||
className="navbar-item title is-5 is-marginless"
|
||||
href="/ui"
|
||||
>
|
||||
Kafka UI
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
className="navbar-end"
|
||||
>
|
||||
<div
|
||||
className="navbar-item mr-2"
|
||||
className="navbar-item"
|
||||
>
|
||||
<Version />
|
||||
</div>
|
||||
|
@ -32,13 +91,97 @@ exports[`App matches snapshot 1`] = `
|
|||
<main
|
||||
className="Layout__container"
|
||||
>
|
||||
<Connect(Nav)
|
||||
className="Layout__navbar"
|
||||
<div
|
||||
className="Layout__sidebar has-shadow has-background-white"
|
||||
>
|
||||
<Nav
|
||||
clusters={Array []}
|
||||
isClusterListFetched={true}
|
||||
>
|
||||
<aside
|
||||
className="menu has-shadow has-background-white"
|
||||
>
|
||||
<p
|
||||
className="menu-label"
|
||||
>
|
||||
General
|
||||
</p>
|
||||
<ul
|
||||
className="menu-list"
|
||||
>
|
||||
<li>
|
||||
<NavLink
|
||||
activeClassName="is-active"
|
||||
exact={true}
|
||||
title="Dashboard"
|
||||
to="/ui"
|
||||
>
|
||||
<Link
|
||||
aria-current={null}
|
||||
title="Dashboard"
|
||||
to={
|
||||
Object {
|
||||
"hash": "",
|
||||
"pathname": "/ui",
|
||||
"search": "",
|
||||
"state": null,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LinkAnchor
|
||||
aria-current={null}
|
||||
href="/ui"
|
||||
navigate={[Function]}
|
||||
title="Dashboard"
|
||||
>
|
||||
<a
|
||||
aria-current={null}
|
||||
href="/ui"
|
||||
onClick={[Function]}
|
||||
title="Dashboard"
|
||||
>
|
||||
Dashboard
|
||||
</a>
|
||||
</LinkAnchor>
|
||||
</Link>
|
||||
</NavLink>
|
||||
</li>
|
||||
</ul>
|
||||
<p
|
||||
className="menu-label"
|
||||
>
|
||||
Clusters
|
||||
</p>
|
||||
</aside>
|
||||
</Nav>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="Layout__sidebarOverlay is-overlay"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<Switch>
|
||||
<Route
|
||||
component={[Function]}
|
||||
computedMatch={
|
||||
Object {
|
||||
"isExact": true,
|
||||
"params": Object {},
|
||||
"path": "/",
|
||||
"url": "/",
|
||||
}
|
||||
}
|
||||
exact={true}
|
||||
location={
|
||||
Object {
|
||||
"hash": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"state": undefined,
|
||||
}
|
||||
}
|
||||
path={
|
||||
Array [
|
||||
"/",
|
||||
|
@ -46,15 +189,195 @@ exports[`App matches snapshot 1`] = `
|
|||
"/ui/clusters",
|
||||
]
|
||||
}
|
||||
>
|
||||
<Dashboard
|
||||
history={
|
||||
Object {
|
||||
"action": "POP",
|
||||
"block": [Function],
|
||||
"createHref": [Function],
|
||||
"go": [Function],
|
||||
"goBack": [Function],
|
||||
"goForward": [Function],
|
||||
"listen": [Function],
|
||||
"location": Object {
|
||||
"hash": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"state": undefined,
|
||||
},
|
||||
"push": [Function],
|
||||
"replace": [Function],
|
||||
}
|
||||
}
|
||||
location={
|
||||
Object {
|
||||
"hash": "",
|
||||
"pathname": "/",
|
||||
"search": "",
|
||||
"state": undefined,
|
||||
}
|
||||
}
|
||||
match={
|
||||
Object {
|
||||
"isExact": true,
|
||||
"params": Object {},
|
||||
"path": "/",
|
||||
"url": "/",
|
||||
}
|
||||
}
|
||||
staticContext={Object {}}
|
||||
>
|
||||
<div
|
||||
className="section"
|
||||
>
|
||||
<div
|
||||
className="level"
|
||||
>
|
||||
<div
|
||||
className="level-item level-left"
|
||||
>
|
||||
<Breadcrumb>
|
||||
<nav
|
||||
aria-label="breadcrumbs"
|
||||
className="breadcrumb"
|
||||
>
|
||||
<ul>
|
||||
<li
|
||||
className="is-active"
|
||||
>
|
||||
<span
|
||||
className=""
|
||||
>
|
||||
Dashboard
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</Breadcrumb>
|
||||
</div>
|
||||
</div>
|
||||
<Connect(ClustersWidget)>
|
||||
<ClustersWidget
|
||||
clusters={Array []}
|
||||
dispatch={[Function]}
|
||||
offlineClusters={Array []}
|
||||
onlineClusters={Array []}
|
||||
>
|
||||
<div>
|
||||
<h5
|
||||
className="title is-5"
|
||||
>
|
||||
Clusters
|
||||
</h5>
|
||||
<MetricsWrapper>
|
||||
<div
|
||||
className="box"
|
||||
>
|
||||
<div
|
||||
className="level"
|
||||
>
|
||||
<Indicator
|
||||
label="Online Clusters"
|
||||
>
|
||||
<div
|
||||
className="level-item"
|
||||
>
|
||||
<div
|
||||
title="Online Clusters"
|
||||
>
|
||||
<p
|
||||
className="heading"
|
||||
>
|
||||
Online Clusters
|
||||
</p>
|
||||
<p
|
||||
className="title has-text-centered"
|
||||
>
|
||||
<span
|
||||
className="tag is-primary"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Indicator>
|
||||
<Indicator
|
||||
label="Offline Clusters"
|
||||
>
|
||||
<div
|
||||
className="level-item"
|
||||
>
|
||||
<div
|
||||
title="Offline Clusters"
|
||||
>
|
||||
<p
|
||||
className="heading"
|
||||
>
|
||||
Offline Clusters
|
||||
</p>
|
||||
<p
|
||||
className="title has-text-centered"
|
||||
>
|
||||
<span
|
||||
className="tag is-danger"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Indicator>
|
||||
<Indicator
|
||||
label="Hide online clusters"
|
||||
>
|
||||
<div
|
||||
className="level-item"
|
||||
>
|
||||
<div
|
||||
title="Hide online clusters"
|
||||
>
|
||||
<p
|
||||
className="heading"
|
||||
>
|
||||
Hide online clusters
|
||||
</p>
|
||||
<p
|
||||
className="title has-text-centered"
|
||||
>
|
||||
<input
|
||||
checked={false}
|
||||
className="switch is-rounded"
|
||||
id="switchRoundedDefault"
|
||||
name="switchRoundedDefault"
|
||||
onChange={[Function]}
|
||||
type="checkbox"
|
||||
/>
|
||||
<Route
|
||||
component={[Function]}
|
||||
path="/ui/clusters/:clusterName"
|
||||
<label
|
||||
htmlFor="switchRoundedDefault"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Indicator>
|
||||
</div>
|
||||
</div>
|
||||
</MetricsWrapper>
|
||||
</div>
|
||||
</ClustersWidget>
|
||||
</Connect(ClustersWidget)>
|
||||
</div>
|
||||
</Dashboard>
|
||||
</Route>
|
||||
</Switch>
|
||||
</main>
|
||||
<div
|
||||
className="Layout__alerts"
|
||||
/>
|
||||
</div>
|
||||
</App>
|
||||
</Router>
|
||||
</StaticRouter>
|
||||
</Provider>
|
||||
`;
|
||||
|
|
|
@ -16,7 +16,7 @@ const Indicator: React.FC<Props> = ({
|
|||
children,
|
||||
}) => {
|
||||
return (
|
||||
<div className={cx('level-item', 'level-left', className)}>
|
||||
<div className={cx('level-item', className)}>
|
||||
<div title={title || label}>
|
||||
<p className="heading">{label}</p>
|
||||
<p className="title has-text-centered">
|
||||
|
|
|
@ -6,7 +6,7 @@ exports[`Indicator matches the snapshot 1`] = `
|
|||
title="title"
|
||||
>
|
||||
<div
|
||||
className="level-item level-left"
|
||||
className="level-item"
|
||||
>
|
||||
<div
|
||||
title="title"
|
||||
|
|
Loading…
Add table
Reference in a new issue