From 8abef88b943b71bc0fba78a4f774ccc7f3ac424d Mon Sep 17 00:00:00 2001 From: Johannes Schill Date: Wed, 3 Jan 2018 20:11:07 +0100 Subject: [PATCH] mobx: poc in using each store as individual prop on the react containers (#10414) * mobx: poc in using each store as individual prop on the react containers * prettier test * fix: end the war between prettier vs tslint. * mobx: Move stores into their own folders * mobx: Refactor the AlertRule into its own file and add a helper-file * mobx: Move NavItem out of NavStore and remove lodash dependancy * mobx: Move ResultItem and SearchResultSection models out of the SearchStore * mobx: ServerStatsStore rename .tsx => .ts. And move ServerStat-model to its own file. * mobx: Remove lodash and jquery dependancy from ViewStore * mobx: Remove issue with double question mark --- .../AlertRuleList/AlertRuleList.jest.tsx | 4 +- .../AlertRuleList/AlertRuleList.tsx | 22 ++--- public/app/containers/IContainerProps.ts | 15 ++++ .../ServerStats/ServerStats.jest.tsx | 4 +- .../containers/ServerStats/ServerStats.tsx | 19 ++--- public/app/routes/ReactContainer.tsx | 2 +- public/app/stores/AlertListStore.ts | 82 ------------------- .../stores/AlertListStore/AlertListStore.ts | 34 ++++++++ public/app/stores/AlertListStore/AlertRule.ts | 34 ++++++++ public/app/stores/AlertListStore/helpers.ts | 13 +++ public/app/stores/NavStore/NavItem.ts | 12 +++ public/app/stores/{ => NavStore}/NavStore.ts | 18 +--- .../app/stores/{ => RootStore}/RootStore.ts | 10 +-- public/app/stores/SearchStore.ts | 55 ------------- public/app/stores/SearchStore/ResultItem.ts | 10 +++ .../stores/SearchStore/SearchResultSection.ts | 27 ++++++ public/app/stores/SearchStore/SearchStore.ts | 22 +++++ .../app/stores/ServerStatsStore/ServerStat.ts | 6 ++ .../ServerStatsStore.ts} | 13 +-- public/app/stores/ViewStore.ts | 37 --------- public/app/stores/ViewStore/ViewStore.ts | 46 +++++++++++ public/app/stores/store.ts | 2 +- 22 files changed, 256 insertions(+), 231 deletions(-) create mode 100644 public/app/containers/IContainerProps.ts delete mode 100644 public/app/stores/AlertListStore.ts create mode 100644 public/app/stores/AlertListStore/AlertListStore.ts create mode 100644 public/app/stores/AlertListStore/AlertRule.ts create mode 100644 public/app/stores/AlertListStore/helpers.ts create mode 100644 public/app/stores/NavStore/NavItem.ts rename public/app/stores/{ => NavStore}/NavStore.ts (60%) rename public/app/stores/{ => RootStore}/RootStore.ts (61%) delete mode 100644 public/app/stores/SearchStore.ts create mode 100644 public/app/stores/SearchStore/ResultItem.ts create mode 100644 public/app/stores/SearchStore/SearchResultSection.ts create mode 100644 public/app/stores/SearchStore/SearchStore.ts create mode 100644 public/app/stores/ServerStatsStore/ServerStat.ts rename public/app/stores/{ServerStatsStore.tsx => ServerStatsStore/ServerStatsStore.ts} (79%) delete mode 100644 public/app/stores/ViewStore.ts create mode 100644 public/app/stores/ViewStore/ViewStore.ts diff --git a/public/app/containers/AlertRuleList/AlertRuleList.jest.tsx b/public/app/containers/AlertRuleList/AlertRuleList.jest.tsx index c49343697f7..1025a7991f6 100644 --- a/public/app/containers/AlertRuleList/AlertRuleList.jest.tsx +++ b/public/app/containers/AlertRuleList/AlertRuleList.jest.tsx @@ -1,7 +1,7 @@ import React from 'react'; import moment from 'moment'; import { AlertRuleList } from './AlertRuleList'; -import { RootStore } from 'app/stores/RootStore'; +import { RootStore } from 'app/stores/RootStore/RootStore'; import { backendSrv, createNavTree } from 'test/mocks/common'; import { mount } from 'enzyme'; import toJson from 'enzyme-to-json'; @@ -36,7 +36,7 @@ describe('AlertRuleList', () => { } ); - page = mount(); + page = mount(); }); it('should call api to get rules', () => { diff --git a/public/app/containers/AlertRuleList/AlertRuleList.tsx b/public/app/containers/AlertRuleList/AlertRuleList.tsx index 19a23e43c33..b12a9f234d0 100644 --- a/public/app/containers/AlertRuleList/AlertRuleList.tsx +++ b/public/app/containers/AlertRuleList/AlertRuleList.tsx @@ -2,17 +2,13 @@ import React from 'react'; import classNames from 'classnames'; import { inject, observer } from 'mobx-react'; import PageHeader from 'app/core/components/PageHeader/PageHeader'; -import { IRootStore } from 'app/stores/RootStore'; -import { IAlertRule } from 'app/stores/AlertListStore'; +import { IAlertRule } from 'app/stores/AlertListStore/AlertListStore'; import appEvents from 'app/core/app_events'; +import IContainerProps from 'app/containers/IContainerProps'; -export interface AlertRuleListProps { - store: IRootStore; -} - -@inject('store') +@inject('view', 'nav', 'alertList') @observer -export class AlertRuleList extends React.Component { +export class AlertRuleList extends React.Component { stateFilters = [ { text: 'All', value: 'all' }, { text: 'OK', value: 'ok' }, @@ -25,18 +21,18 @@ export class AlertRuleList extends React.Component { constructor(props) { super(props); - this.props.store.nav.load('alerting', 'alert-list'); + this.props.nav.load('alerting', 'alert-list'); this.fetchRules(); } onStateFilterChanged = evt => { - this.props.store.view.updateQuery({ state: evt.target.value }); + this.props.view.updateQuery({ state: evt.target.value }); this.fetchRules(); }; fetchRules() { - this.props.store.alertList.loadRules({ - state: this.props.store.view.query.get('state') || 'all', + this.props.alertList.loadRules({ + state: this.props.view.query.get('state') || 'all', }); } @@ -49,7 +45,7 @@ export class AlertRuleList extends React.Component { }; render() { - const { nav, alertList } = this.props.store; + const { nav, alertList } = this.props; return (
diff --git a/public/app/containers/IContainerProps.ts b/public/app/containers/IContainerProps.ts new file mode 100644 index 00000000000..aecba4e8ad3 --- /dev/null +++ b/public/app/containers/IContainerProps.ts @@ -0,0 +1,15 @@ +import { SearchStore } from './../stores/SearchStore/SearchStore'; +import { ServerStatsStore } from './../stores/ServerStatsStore/ServerStatsStore'; +import { NavStore } from './../stores/NavStore/NavStore'; +import { AlertListStore } from './../stores/AlertListStore/AlertListStore'; +import { ViewStore } from './../stores/ViewStore/ViewStore'; + +interface IContainerProps { + search: typeof SearchStore.Type; + serverStats: typeof ServerStatsStore.Type; + nav: typeof NavStore.Type; + alertList: typeof AlertListStore.Type; + view: typeof ViewStore.Type; +} + +export default IContainerProps; diff --git a/public/app/containers/ServerStats/ServerStats.jest.tsx b/public/app/containers/ServerStats/ServerStats.jest.tsx index 07b938c4d0f..ae5cb736ad6 100644 --- a/public/app/containers/ServerStats/ServerStats.jest.tsx +++ b/public/app/containers/ServerStats/ServerStats.jest.tsx @@ -1,7 +1,7 @@ import React from 'react'; import renderer from 'react-test-renderer'; import { ServerStats } from './ServerStats'; -import { RootStore } from 'app/stores/RootStore'; +import { RootStore } from 'app/stores/RootStore/RootStore'; import { backendSrv, createNavTree } from 'test/mocks/common'; describe('ServerStats', () => { @@ -20,7 +20,7 @@ describe('ServerStats', () => { } ); - const page = renderer.create(); + const page = renderer.create(); setTimeout(() => { expect(page.toJSON()).toMatchSnapshot(); diff --git a/public/app/containers/ServerStats/ServerStats.tsx b/public/app/containers/ServerStats/ServerStats.tsx index 60db299ab5a..e40b441d967 100644 --- a/public/app/containers/ServerStats/ServerStats.tsx +++ b/public/app/containers/ServerStats/ServerStats.tsx @@ -1,25 +1,24 @@ import React from 'react'; import { inject, observer } from 'mobx-react'; import PageHeader from 'app/core/components/PageHeader/PageHeader'; +import IContainerProps from 'app/containers/IContainerProps'; -export interface IProps { - store: any; -} - -@inject('store') +@inject('nav', 'serverStats') @observer -export class ServerStats extends React.Component { +export class ServerStats extends React.Component { constructor(props) { super(props); + const { nav, serverStats } = this.props; - this.props.store.nav.load('cfg', 'admin', 'server-stats'); - this.props.store.serverStats.load(); + nav.load('cfg', 'admin', 'server-stats'); + serverStats.load(); } render() { + const { nav, serverStats } = this.props; return (
- +
@@ -28,7 +27,7 @@ export class ServerStats extends React.Component { - {this.props.store.serverStats.stats.map(StatItem)} + {serverStats.stats.map(StatItem)}
Value
diff --git a/public/app/routes/ReactContainer.tsx b/public/app/routes/ReactContainer.tsx index b0f4e6b6ee5..72993661ec4 100644 --- a/public/app/routes/ReactContainer.tsx +++ b/public/app/routes/ReactContainer.tsx @@ -6,7 +6,7 @@ import { Provider } from 'mobx-react'; function WrapInProvider(store, Component, props) { return ( - + ); diff --git a/public/app/stores/AlertListStore.ts b/public/app/stores/AlertListStore.ts deleted file mode 100644 index a581ec0fedc..00000000000 --- a/public/app/stores/AlertListStore.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { types, getEnv, flow } from 'mobx-state-tree'; -import moment from 'moment'; -import alertDef from 'app/features/alerting/alert_def'; - -function setStateFields(rule, state) { - let stateModel = alertDef.getStateDisplayModel(state); - rule.state = state; - rule.stateText = stateModel.text; - rule.stateIcon = stateModel.iconClass; - rule.stateClass = stateModel.stateClass; - rule.stateAge = moment(rule.newStateDate) - .fromNow() - .replace(' ago', ''); -} - -export const AlertRule = types - .model('AlertRule', { - id: types.identifier(types.number), - dashboardId: types.number, - panelId: types.number, - name: types.string, - state: types.string, - stateText: types.string, - stateIcon: types.string, - stateClass: types.string, - stateAge: types.string, - info: types.optional(types.string, ''), - dashboardUri: types.string, - }) - .views(self => ({ - get isPaused() { - return self.state === 'paused'; - }, - })) - .actions(self => ({ - /** - * will toggle alert rule paused state - */ - togglePaused: flow(function* togglePaused() { - let backendSrv = getEnv(self).backendSrv; - - var payload = { paused: self.isPaused }; - let res = yield backendSrv.post(`/api/alerts/${self.id}/pause`, payload); - setStateFields(self, res.state); - self.info = ''; - }), - })); - -type IAlertRuleType = typeof AlertRule.Type; -export interface IAlertRule extends IAlertRuleType {} - -export const AlertListStore = types - .model('AlertListStore', { - rules: types.array(AlertRule), - stateFilter: types.optional(types.string, 'all'), - }) - .actions(self => ({ - loadRules: flow(function* load(filters) { - let backendSrv = getEnv(self).backendSrv; - - // store state filter used in api query - self.stateFilter = filters.state; - - let apiRules = yield backendSrv.get('/api/alerts', filters); - - self.rules.clear(); - - for (let rule of apiRules) { - setStateFields(rule, rule.state); - - if (rule.executionError) { - rule.info = 'Execution Error: ' + rule.executionError; - } - - if (rule.evalData && rule.evalData.noData) { - rule.info = 'Query returned no data'; - } - - self.rules.push(AlertRule.create(rule)); - } - }), - })); diff --git a/public/app/stores/AlertListStore/AlertListStore.ts b/public/app/stores/AlertListStore/AlertListStore.ts new file mode 100644 index 00000000000..78799f6dfd8 --- /dev/null +++ b/public/app/stores/AlertListStore/AlertListStore.ts @@ -0,0 +1,34 @@ +import { types, getEnv, flow } from 'mobx-state-tree'; +import { AlertRule } from './AlertRule'; +import { setStateFields } from './helpers'; + +type IAlertRuleType = typeof AlertRule.Type; +export interface IAlertRule extends IAlertRuleType {} + +export const AlertListStore = types + .model('AlertListStore', { + rules: types.array(AlertRule), + stateFilter: types.optional(types.string, 'all'), + }) + .actions(self => ({ + loadRules: flow(function* load(filters) { + const backendSrv = getEnv(self).backendSrv; + self.stateFilter = filters.state; // store state filter used in api query + const apiRules = yield backendSrv.get('/api/alerts', filters); + self.rules.clear(); + + for (let rule of apiRules) { + setStateFields(rule, rule.state); + + if (rule.executionError) { + rule.info = 'Execution Error: ' + rule.executionError; + } + + if (rule.evalData && rule.evalData.noData) { + rule.info = 'Query returned no data'; + } + + self.rules.push(AlertRule.create(rule)); + } + }), + })); diff --git a/public/app/stores/AlertListStore/AlertRule.ts b/public/app/stores/AlertListStore/AlertRule.ts new file mode 100644 index 00000000000..63b572555cb --- /dev/null +++ b/public/app/stores/AlertListStore/AlertRule.ts @@ -0,0 +1,34 @@ +import { types, getEnv, flow } from 'mobx-state-tree'; +import { setStateFields } from './helpers'; + +export const AlertRule = types + .model('AlertRule', { + id: types.identifier(types.number), + dashboardId: types.number, + panelId: types.number, + name: types.string, + state: types.string, + stateText: types.string, + stateIcon: types.string, + stateClass: types.string, + stateAge: types.string, + info: types.optional(types.string, ''), + dashboardUri: types.string, + }) + .views(self => ({ + get isPaused() { + return self.state === 'paused'; + }, + })) + .actions(self => ({ + /** + * will toggle alert rule paused state + */ + togglePaused: flow(function* togglePaused() { + const backendSrv = getEnv(self).backendSrv; + const payload = { paused: self.isPaused }; + const res = yield backendSrv.post(`/api/alerts/${self.id}/pause`, payload); + setStateFields(self, res.state); + self.info = ''; + }), + })); diff --git a/public/app/stores/AlertListStore/helpers.ts b/public/app/stores/AlertListStore/helpers.ts new file mode 100644 index 00000000000..bf3c81ca11c --- /dev/null +++ b/public/app/stores/AlertListStore/helpers.ts @@ -0,0 +1,13 @@ +import moment from 'moment'; +import alertDef from 'app/features/alerting/alert_def'; + +export function setStateFields(rule, state) { + const stateModel = alertDef.getStateDisplayModel(state); + rule.state = state; + rule.stateText = stateModel.text; + rule.stateIcon = stateModel.iconClass; + rule.stateClass = stateModel.stateClass; + rule.stateAge = moment(rule.newStateDate) + .fromNow() + .replace(' ago', ''); +} diff --git a/public/app/stores/NavStore/NavItem.ts b/public/app/stores/NavStore/NavItem.ts new file mode 100644 index 00000000000..cbd0f269f01 --- /dev/null +++ b/public/app/stores/NavStore/NavItem.ts @@ -0,0 +1,12 @@ +import { types } from 'mobx-state-tree'; + +export const NavItem = types.model('NavItem', { + id: types.identifier(types.string), + text: types.string, + url: types.optional(types.string, ''), + subTitle: 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)), []), +}); diff --git a/public/app/stores/NavStore.ts b/public/app/stores/NavStore/NavStore.ts similarity index 60% rename from public/app/stores/NavStore.ts rename to public/app/stores/NavStore/NavStore.ts index edd25d965ee..1cf622fc3df 100644 --- a/public/app/stores/NavStore.ts +++ b/public/app/stores/NavStore/NavStore.ts @@ -1,16 +1,5 @@ import { types, getEnv } from 'mobx-state-tree'; -import _ from 'lodash'; - -export const NavItem = types.model('NavItem', { - id: types.identifier(types.string), - text: types.string, - url: types.optional(types.string, ''), - subTitle: 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)), []), -}); +import { NavItem } from './NavItem'; export const NavStore = types .model('NavStore', { @@ -19,12 +8,13 @@ export const NavStore = types }) .actions(self => ({ load(...args) { - var children = getEnv(self).navTree; + let children = getEnv(self).navTree; let main, node; let parents = []; for (let id of args) { - node = _.find(children, { id: id }); + node = children.find(el => el.id === id); + if (!node) { throw new Error(`NavItem with id ${id} not found`); } diff --git a/public/app/stores/RootStore.ts b/public/app/stores/RootStore/RootStore.ts similarity index 61% rename from public/app/stores/RootStore.ts rename to public/app/stores/RootStore/RootStore.ts index 1b0640d4c71..88221c0ebd5 100644 --- a/public/app/stores/RootStore.ts +++ b/public/app/stores/RootStore/RootStore.ts @@ -1,9 +1,9 @@ import { types } from 'mobx-state-tree'; -import { SearchStore } from './SearchStore'; -import { ServerStatsStore } from './ServerStatsStore'; -import { NavStore } from './NavStore'; -import { AlertListStore } from './AlertListStore'; -import { ViewStore } from './ViewStore'; +import { SearchStore } from './../SearchStore/SearchStore'; +import { ServerStatsStore } from './../ServerStatsStore/ServerStatsStore'; +import { NavStore } from './../NavStore/NavStore'; +import { AlertListStore } from './../AlertListStore/AlertListStore'; +import { ViewStore } from './../ViewStore/ViewStore'; export const RootStore = types.model({ search: types.optional(SearchStore, { diff --git a/public/app/stores/SearchStore.ts b/public/app/stores/SearchStore.ts deleted file mode 100644 index 1779462a36d..00000000000 --- a/public/app/stores/SearchStore.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { types } from "mobx-state-tree"; - -export const ResultItem = types.model("ResultItem", { - id: types.identifier(types.number), - folderId: types.optional(types.number, 0), - title: types.string, - url: types.string, - icon: types.string, - folderTitle: types.optional(types.string, "") -}); - -export const SearchResultSection = types - .model("SearchResultSection", { - id: types.identifier(), - title: types.string, - icon: types.string, - expanded: types.boolean, - items: types.array(ResultItem) - }) - .actions(self => ({ - toggle() { - self.expanded = !self.expanded; - - for (let i = 0; i < 100; i++) { - self.items.push( - ResultItem.create({ - id: i, - title: "Dashboard " + self.items.length, - icon: "gicon gicon-dashboard", - url: "asd" - }) - ); - } - } - })); - -export const SearchStore = types - .model("SearchStore", { - sections: types.array(SearchResultSection) - }) - .actions(self => ({ - query() { - for (let i = 0; i < 100; i++) { - self.sections.push( - SearchResultSection.create({ - id: "starred" + i, - title: "starred", - icon: "fa fa-fw fa-star-o", - expanded: false, - items: [] - }) - ); - } - } - })); diff --git a/public/app/stores/SearchStore/ResultItem.ts b/public/app/stores/SearchStore/ResultItem.ts new file mode 100644 index 00000000000..eb0ff021526 --- /dev/null +++ b/public/app/stores/SearchStore/ResultItem.ts @@ -0,0 +1,10 @@ +import { types } from 'mobx-state-tree'; + +export const ResultItem = types.model('ResultItem', { + id: types.identifier(types.number), + folderId: types.optional(types.number, 0), + title: types.string, + url: types.string, + icon: types.string, + folderTitle: types.optional(types.string, ''), +}); diff --git a/public/app/stores/SearchStore/SearchResultSection.ts b/public/app/stores/SearchStore/SearchResultSection.ts new file mode 100644 index 00000000000..70b3ad48e96 --- /dev/null +++ b/public/app/stores/SearchStore/SearchResultSection.ts @@ -0,0 +1,27 @@ +import { types } from 'mobx-state-tree'; +import { ResultItem } from './ResultItem'; + +export const SearchResultSection = types + .model('SearchResultSection', { + id: types.identifier(), + title: types.string, + icon: types.string, + expanded: types.boolean, + items: types.array(ResultItem), + }) + .actions(self => ({ + toggle() { + self.expanded = !self.expanded; + + for (let i = 0; i < 100; i++) { + self.items.push( + ResultItem.create({ + id: i, + title: 'Dashboard ' + self.items.length, + icon: 'gicon gicon-dashboard', + url: 'asd', + }) + ); + } + }, + })); diff --git a/public/app/stores/SearchStore/SearchStore.ts b/public/app/stores/SearchStore/SearchStore.ts new file mode 100644 index 00000000000..36897f05f38 --- /dev/null +++ b/public/app/stores/SearchStore/SearchStore.ts @@ -0,0 +1,22 @@ +import { types } from 'mobx-state-tree'; +import { SearchResultSection } from './SearchResultSection'; + +export const SearchStore = types + .model('SearchStore', { + sections: types.array(SearchResultSection), + }) + .actions(self => ({ + query() { + for (let i = 0; i < 100; i++) { + self.sections.push( + SearchResultSection.create({ + id: 'starred' + i, + title: 'starred', + icon: 'fa fa-fw fa-star-o', + expanded: false, + items: [], + }) + ); + } + }, + })); diff --git a/public/app/stores/ServerStatsStore/ServerStat.ts b/public/app/stores/ServerStatsStore/ServerStat.ts new file mode 100644 index 00000000000..bd819a51e76 --- /dev/null +++ b/public/app/stores/ServerStatsStore/ServerStat.ts @@ -0,0 +1,6 @@ +import { types } from 'mobx-state-tree'; + +export const ServerStat = types.model('ServerStat', { + name: types.string, + value: types.optional(types.number, 0), +}); diff --git a/public/app/stores/ServerStatsStore.tsx b/public/app/stores/ServerStatsStore/ServerStatsStore.ts similarity index 79% rename from public/app/stores/ServerStatsStore.tsx rename to public/app/stores/ServerStatsStore/ServerStatsStore.ts index 4f67eed0d86..d27285d7a3b 100644 --- a/public/app/stores/ServerStatsStore.tsx +++ b/public/app/stores/ServerStatsStore/ServerStatsStore.ts @@ -1,9 +1,5 @@ -import { types, getEnv, flow } from 'mobx-state-tree'; - -export const ServerStat = types.model('ServerStat', { - name: types.string, - value: types.optional(types.number, 0), -}); +import { types, getEnv, flow } from 'mobx-state-tree'; +import { ServerStat } from './ServerStat'; export const ServerStatsStore = types .model('ServerStatsStore', { @@ -12,9 +8,8 @@ export const ServerStatsStore = types }) .actions(self => ({ load: flow(function* load() { - let backendSrv = getEnv(self).backendSrv; - - let res = yield backendSrv.get('/api/admin/stats'); + const backendSrv = getEnv(self).backendSrv; + const res = yield backendSrv.get('/api/admin/stats'); self.stats.clear(); self.stats.push(ServerStat.create({ name: 'Total dashboards', value: res.dashboards })); self.stats.push(ServerStat.create({ name: 'Total users', value: res.users })); diff --git a/public/app/stores/ViewStore.ts b/public/app/stores/ViewStore.ts deleted file mode 100644 index 6095e45fc9d..00000000000 --- a/public/app/stores/ViewStore.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { types } from 'mobx-state-tree'; -import _ from 'lodash'; -import $ from 'jquery'; - -let QueryValueType = types.union(types.string, types.boolean, types.number); - -export const ViewStore = types - .model({ - path: types.string, - query: types.map(QueryValueType), - }) - .views(self => ({ - get currentUrl() { - let path = self.path; - if (self.query.size) { - path += '?' + $.param(self.query.toJS()); - } - return path; - }, - })) - .actions(self => ({ - updatePathAndQuery(path: string, query: any) { - self.path = path; - self.query.clear(); - - for (let key of _.keys(query)) { - self.query.set(key, query[key]); - } - }, - - updateQuery(query: any) { - self.query.clear(); - for (let key of _.keys(query)) { - self.query.set(key, query[key]); - } - }, - })); diff --git a/public/app/stores/ViewStore/ViewStore.ts b/public/app/stores/ViewStore/ViewStore.ts new file mode 100644 index 00000000000..fb1111cf108 --- /dev/null +++ b/public/app/stores/ViewStore/ViewStore.ts @@ -0,0 +1,46 @@ +import { types } from 'mobx-state-tree'; + +const QueryValueType = types.union(types.string, types.boolean, types.number); +const urlParameterize = queryObj => { + const keys = Object.keys(queryObj); + const newQuery = keys.reduce((acc: string, key: string, idx: number) => { + const preChar = idx === 0 ? '?' : '&'; + return acc + preChar + key + '=' + queryObj[key]; + }, ''); + + return newQuery; +}; + +export const ViewStore = types + .model({ + path: types.string, + query: types.map(QueryValueType), + }) + .views(self => ({ + get currentUrl() { + let path = self.path; + + if (self.query.size) { + path += urlParameterize(self.query.toJS()); + } + return path; + }, + })) + .actions(self => { + function updateQuery(query: any) { + self.query.clear(); + for (let key of Object.keys(query)) { + self.query.set(key, query[key]); + } + } + + function updatePathAndQuery(path: string, query: any) { + self.path = path; + updateQuery(query); + } + + return { + updateQuery, + updatePathAndQuery, + }; + }); diff --git a/public/app/stores/store.ts b/public/app/stores/store.ts index 3405dc38157..8ad53607ac2 100644 --- a/public/app/stores/store.ts +++ b/public/app/stores/store.ts @@ -1,4 +1,4 @@ -import { RootStore, IRootStore } from './RootStore'; +import { RootStore, IRootStore } from './RootStore/RootStore'; import config from 'app/core/config'; export let store: IRootStore;