Commit 5b91bb91 by Torkel Ödegaard

tech: minor progress on mobx state tree & react containers, working on unit testing

parent 8aff969f
import React from 'react';
import renderer from 'react-test-renderer';
import { ServerStats } from './ServerStats';
import { RootStore } from 'app/stores/RootStore';
describe('ServerStats', () => {
it('Should render table with stats', done => {
let backendSrvMock = {
get: jest.fn().mockReturnValue(
Promise.resolve({
dashboards: 10,
})
),
};
const store = RootStore.create({}, { backendSrv: backendSrvMock });
const page = renderer.create(<ServerStats store={store} />);
setTimeout(() => {
expect(page.toJSON()).toMatchSnapshot();
done();
});
});
});
...@@ -9,20 +9,19 @@ export interface IProps { ...@@ -9,20 +9,19 @@ export interface IProps {
@inject('store') @inject('store')
@observer @observer
export default class ServerStats extends React.Component<IProps, any> { export class ServerStats extends React.Component<IProps, any> {
navModel: NavModel;
constructor(props) { constructor(props) {
super(props); super(props);
this.navModel = new NavModelSrv().getNav('cfg', 'admin', 'server-stats', 1); // this.navModel = new NavModelSrv().getNav('cfg', 'admin', 'server-stats', 1);
this.props.store.nav.load('cfg', 'admin', 'server-stats');
this.props.store.serverStats.load(); this.props.store.serverStats.load();
} }
render() { render() {
return ( return (
<div> <div>
<PageHeader model={this.navModel} /> <PageHeader model={this.props.store.nav} />
<div className="page-container page-body"> <div className="page-container page-body">
<table className="filter-table form-inline"> <table className="filter-table form-inline">
<thead> <thead>
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ServerStats Should render table with stats 1`] = `
<div>
//
<div
className="page-container page-body"
>
<table
className="filter-table form-inline"
>
<thead>
<tr>
<th>
Name
</th>
<th>
Value
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
Total dashboards
</td>
<td>
10
</td>
</tr>
<tr>
<td>
Total users
</td>
<td>
0
</td>
</tr>
<tr>
<td>
Active users (seen last 30 days)
</td>
<td>
0
</td>
</tr>
<tr>
<td>
Total orgs
</td>
<td>
0
</td>
</tr>
<tr>
<td>
Total playlists
</td>
<td>
0
</td>
</tr>
<tr>
<td>
Total snapshots
</td>
<td>
0
</td>
</tr>
<tr>
<td>
Total dashboard tags
</td>
<td>
0
</td>
</tr>
<tr>
<td>
Total starred dashboards
</td>
<td>
0
</td>
</tr>
<tr>
<td>
Total alerts
</td>
<td>
0
</td>
</tr>
</tbody>
</table>
</div>
</div>
`;
...@@ -16,7 +16,7 @@ function WrapInProvider(store, Component, props) { ...@@ -16,7 +16,7 @@ function WrapInProvider(store, Component, props) {
export function reactContainer($route) { export function reactContainer($route) {
return { return {
restrict: 'E', restrict: 'E',
template: '<h2>hasad</h2>', template: '',
link(scope, elem) { link(scope, elem) {
let component = $route.current.locals.component; let component = $route.current.locals.component;
let props = {}; let props = {};
......
export class BundleLoader {
lazy: any;
constructor(bundleName) {
var defer = null;
this.lazy = [
'$q',
'$route',
'$rootScope',
($q, $route, $rootScope) => {
if (defer) {
return defer.promise;
}
defer = $q.defer();
System.import(bundleName).then(() => {
defer.resolve();
});
return defer.promise;
},
];
}
}
import './dashboard_loaders'; import './dashboard_loaders';
import './ReactContainer'; import './ReactContainer';
import ServerStats from 'app/containers/ServerStats'; import { ServerStats } from 'app/containers/ServerStats/ServerStats';
/** @ngInject **/ /** @ngInject **/
export function setupAngularRoutes($routeProvider, $locationProvider) { export function setupAngularRoutes($routeProvider, $locationProvider) {
......
import { types } from 'mobx-state-tree';
import config from 'app/core/config';
import _ from 'lodash';
export const NavItem = types.model('NavItem', {
id: types.identifier(types.string),
text: types.string,
url: types.optional(types.string, ''),
description: types.optional(types.string, ''),
icon: types.optional(types.string, ''),
img: types.optional(types.string, ''),
active: types.optional(types.boolean, false),
children: types.optional(types.array(types.late(() => NavItem)), []),
});
export const NavStore = types
.model('NavStore', {
main: types.maybe(NavItem),
node: types.maybe(NavItem),
breadcrumbs: types.optional(types.array(NavItem), []),
})
.actions(self => ({
load(...args) {
var children = config.bootData.navTree;
let main, node;
let breadcrumbs = [];
for (let id of args) {
// if its a number then it's the index to use for main
if (_.isNumber(id)) {
main = breadcrumbs[id];
break;
}
let current = _.find(children, { id: id });
breadcrumbs.push(current);
main = node;
node = current;
children = node.children;
}
if (main.children) {
for (let item of main.children) {
item.active = false;
if (item.url === node.url) {
item.active = true;
}
}
}
self.main = NavItem.create(main);
self.node = NavItem.create(node);
for (let item of breadcrumbs) {
self.breadcrumbs.push(NavItem.create(item));
}
// self.main = NavItem.create({
// id: 'test',
// text: 'test',
// url: '/test';
// children: [
// {
// id: 'test',
// text: 'text',
// url: '/test',
// active: true,
// children: []
// }
// ]
// });
},
}));
import { types } from 'mobx-state-tree'; import { types } from 'mobx-state-tree';
import { SearchStore } from './SearchStore'; import { SearchStore } from './SearchStore';
import { ServerStatsStore } from './ServerStatsStore'; import { ServerStatsStore } from './ServerStatsStore';
import { NavStore } from './NavStore/NavStore';
export const RootStore = types.model({ export const RootStore = types.model({
search: types.optional(SearchStore, { search: types.optional(SearchStore, {
...@@ -9,6 +10,7 @@ export const RootStore = types.model({ ...@@ -9,6 +10,7 @@ export const RootStore = types.model({
serverStats: types.optional(ServerStatsStore, { serverStats: types.optional(ServerStatsStore, {
stats: [], stats: [],
}), }),
nav: types.optional(NavStore, {}),
}); });
type IRootStoreType = typeof RootStore.Type; type IRootStoreType = typeof RootStore.Type;
......
...@@ -2,19 +2,20 @@ import { types, getEnv, flow } from 'mobx-state-tree'; ...@@ -2,19 +2,20 @@ import { types, getEnv, flow } from 'mobx-state-tree';
export const ServerStat = types.model('ServerStat', { export const ServerStat = types.model('ServerStat', {
name: types.string, name: types.string,
value: types.number, value: types.optional(types.number, 0),
}); });
export const ServerStatsStore = types export const ServerStatsStore = types
.model('ServerStatsStore', { .model('ServerStatsStore', {
stats: types.array(ServerStat), stats: types.array(ServerStat),
error: types.optional(types.string, ''),
}) })
.actions(self => ({ .actions(self => ({
load: flow(function* load() { load: flow(function* load() {
let backendSrv = getEnv(self).backendSrv; let backendSrv = getEnv(self).backendSrv;
try {
let res = yield backendSrv.get('/api/admin/stats'); let res = yield backendSrv.get('/api/admin/stats');
self.stats.clear(); self.stats.clear();
self.stats.push(ServerStat.create({ name: 'Total dashboards', value: res.dashboards })); self.stats.push(ServerStat.create({ name: 'Total dashboards', value: res.dashboards }));
self.stats.push(ServerStat.create({ name: 'Total users', value: res.users })); self.stats.push(ServerStat.create({ name: 'Total users', value: res.users }));
...@@ -25,5 +26,9 @@ export const ServerStatsStore = types ...@@ -25,5 +26,9 @@ export const ServerStatsStore = types
self.stats.push(ServerStat.create({ name: 'Total dashboard tags', value: res.tags })); self.stats.push(ServerStat.create({ name: 'Total dashboard tags', value: res.tags }));
self.stats.push(ServerStat.create({ name: 'Total starred dashboards', value: res.stars })); self.stats.push(ServerStat.create({ name: 'Total starred dashboards', value: res.stars }));
self.stats.push(ServerStat.create({ name: 'Total alerts', value: res.alerts })); self.stats.push(ServerStat.create({ name: 'Total alerts', value: res.alerts }));
} catch (err) {
console.log('ServerStats.load error', err);
self.error = err.toString();
}
}), }),
})); }));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment