Commit ec41d760 by Torkel Ödegaard

mobx -> redux: major progress on folder migration

parent 19cbff65
import { NavStore } from './../stores/NavStore/NavStore'; import { NavStore } from './../stores/NavStore/NavStore';
import { PermissionsStore } from './../stores/PermissionsStore/PermissionsStore'; import { PermissionsStore } from './../stores/PermissionsStore/PermissionsStore';
import { ViewStore } from './../stores/ViewStore/ViewStore'; import { ViewStore } from './../stores/ViewStore/ViewStore';
import { FolderStore } from './../stores/FolderStore/FolderStore';
interface ContainerProps { interface ContainerProps {
nav: typeof NavStore.Type; nav: typeof NavStore.Type;
permissions: typeof PermissionsStore.Type; permissions: typeof PermissionsStore.Type;
view: typeof ViewStore.Type; view: typeof ViewStore.Type;
folder: typeof FolderStore.Type;
backendSrv: any; backendSrv: any;
} }
......
...@@ -2,24 +2,34 @@ import React, { Component } from 'react'; ...@@ -2,24 +2,34 @@ import React, { Component } from 'react';
import { hot } from 'react-hot-loader'; import { hot } from 'react-hot-loader';
import { inject, observer } from 'mobx-react'; import { inject, observer } from 'mobx-react';
import { toJS } from 'mobx'; import { toJS } from 'mobx';
import ContainerProps from 'app/containers/ContainerProps'; import { connect } from 'react-redux';
import PageHeader from 'app/core/components/PageHeader/PageHeader'; import PageHeader from 'app/core/components/PageHeader/PageHeader';
import Permissions from 'app/core/components/Permissions/Permissions'; import Permissions from 'app/core/components/Permissions/Permissions';
import Tooltip from 'app/core/components/Tooltip/Tooltip'; import Tooltip from 'app/core/components/Tooltip/Tooltip';
import PermissionsInfo from 'app/core/components/Permissions/PermissionsInfo'; import PermissionsInfo from 'app/core/components/Permissions/PermissionsInfo';
import AddPermissions from 'app/core/components/Permissions/AddPermissions'; import AddPermissions from 'app/core/components/Permissions/AddPermissions';
import SlideDown from 'app/core/components/Animations/SlideDown'; import SlideDown from 'app/core/components/Animations/SlideDown';
import { getNavModel } from 'app/core/selectors/navModel';
import { NavModel, StoreState, FolderState } from 'app/types';
import { getFolderByUid, setFolderTitle, saveFolder, deleteFolder } from './state/actions';
@inject('nav', 'folder', 'view', 'permissions') export interface Props {
navModel: NavModel;
getFolderByUid: typeof getFolderByUid;
folderUid: string;
folder: FolderState;
}
@inject('permissions')
@observer @observer
export class FolderPermissions extends Component<ContainerProps, any> { export class FolderPermissions extends Component<Props> {
constructor(props) { constructor(props) {
super(props); super(props);
this.handleAddPermission = this.handleAddPermission.bind(this); this.handleAddPermission = this.handleAddPermission.bind(this);
} }
componentDidMount() { componentDidMount() {
this.loadStore(); this.props.getFolderByUid(this.props.folderUid);
} }
componentWillUnmount() { componentWillUnmount() {
...@@ -27,31 +37,23 @@ export class FolderPermissions extends Component<ContainerProps, any> { ...@@ -27,31 +37,23 @@ export class FolderPermissions extends Component<ContainerProps, any> {
permissions.hideAddPermissions(); permissions.hideAddPermissions();
} }
loadStore() {
const { nav, folder, view } = this.props;
return folder.load(view.routeParams.get('uid') as string).then(res => {
view.updatePathAndQuery(`${res.url}/permissions`, {}, {});
return nav.initFolderNav(toJS(folder.folder), 'manage-folder-permissions');
});
}
handleAddPermission() { handleAddPermission() {
const { permissions } = this.props; const { permissions } = this.props;
permissions.toggleAddPermissions(); permissions.toggleAddPermissions();
} }
render() { render() {
const { nav, folder, permissions, backendSrv } = this.props; const { navModel, permissions, backendSrv, folder } = this.props;
if (!folder.folder || !nav.main) { if (folder.id === 0) {
return <h2>Loading</h2>; return <h2>Loading</h2>;
} }
const dashboardId = folder.folder.id; const dashboardId = folder.id;
return ( return (
<div> <div>
<PageHeader model={nav as any} /> <PageHeader model={navModel} />
<div className="page-container page-body"> <div className="page-container page-body">
<div className="page-action-bar"> <div className="page-action-bar">
<h3 className="page-sub-heading">Folder Permissions</h3> <h3 className="page-sub-heading">Folder Permissions</h3>
...@@ -77,4 +79,17 @@ export class FolderPermissions extends Component<ContainerProps, any> { ...@@ -77,4 +79,17 @@ export class FolderPermissions extends Component<ContainerProps, any> {
} }
} }
export default hot(module)(FolderPermissions); const mapStateToProps = (state: StoreState) => {
const uid = state.location.routeParams.uid;
return {
navModel: getNavModel(state.navIndex, `folder-permissions-${uid}`),
folderUid: uid,
folder: state.folder,
};
};
const mapDispatchToProps = {
getFolderByUid,
};
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(FolderPermissions));
import React from 'react'; import React from 'react';
import { FolderSettings } from './FolderSettings'; import { FolderSettingsPage, Props } from './FolderSettingsPage';
import { RootStore } from 'app/stores/RootStore/RootStore'; import { NavModel, FolderState } from '../../types';
import { backendSrv } from 'test/mocks/common';
import { shallow } from 'enzyme'; import { shallow } from 'enzyme';
describe('FolderSettings', () => { const setup = (propOverrides?: object) => {
let wrapper; const props: Props = {
let page; navModel: {} as NavModel,
folderUid: '1234',
folder: {
id: 0,
uid: '1234',
title: 'loading',
canSave: true,
hasChanged: false,
version: 1,
},
getFolderByUid: jest.fn(),
setFolderTitle: jest.fn(),
saveFolder: jest.fn(),
deleteFolder: jest.fn(),
};
Object.assign(props, propOverrides);
const wrapper = shallow(<FolderSettingsPage {...props} />);
const instance = wrapper.instance() as FolderSettingsPage;
return {
wrapper,
instance,
};
};
describe('Render', () => {
it('should render component', () => {
const { wrapper } = setup();
expect(wrapper).toMatchSnapshot();
});
beforeAll(() => { it('should enable save button', () => {
backendSrv.getFolderByUid.mockReturnValue( const { wrapper } = setup({
Promise.resolve({ folder: {
id: 1, id: 1,
uid: 'uid', uid: '1234',
title: 'Folder Name', title: 'loading',
url: '/dashboards/f/uid/folder-name',
canSave: true, canSave: true,
hasChanged: true,
version: 1, version: 1,
})
);
const store = RootStore.create(
{
view: {
path: 'asd',
query: {},
routeParams: {
uid: 'uid-str',
},
},
}, },
{ });
backendSrv: backendSrv, expect(wrapper).toMatchSnapshot();
}
);
wrapper = shallow(<FolderSettings backendSrv={backendSrv} {...store} />);
page = wrapper.dive();
return page
.instance()
.loadStore()
.then(() => {
page.update();
});
});
it('should set the title input field', () => {
const titleInput = page.find('.gf-form-input');
expect(titleInput).toHaveLength(1);
expect(titleInput.prop('value')).toBe('Folder Name');
});
it('should update title and enable save button when changed', () => {
const titleInput = page.find('.gf-form-input');
const disabledSubmitButton = page.find('button[type="submit"]');
expect(disabledSubmitButton.prop('disabled')).toBe(true);
titleInput.simulate('change', { target: { value: 'New Title' } });
const updatedTitleInput = page.find('.gf-form-input');
expect(updatedTitleInput.prop('value')).toBe('New Title');
const enabledSubmitButton = page.find('button[type="submit"]');
expect(enabledSubmitButton.prop('disabled')).toBe(false);
});
it('should disable save button if title is changed back to old title', () => {
const titleInput = page.find('.gf-form-input');
titleInput.simulate('change', { target: { value: 'Folder Name' } });
const enabledSubmitButton = page.find('button[type="submit"]');
expect(enabledSubmitButton.prop('disabled')).toBe(true);
});
it('should disable save button if title is changed to empty string', () => {
const titleInput = page.find('.gf-form-input');
titleInput.simulate('change', { target: { value: '' } });
const enabledSubmitButton = page.find('button[type="submit"]');
expect(enabledSubmitButton.prop('disabled')).toBe(true);
}); });
}); });
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render should enable save button 1`] = `
<div>
<PageHeader
model={Object {}}
/>
<div
className="page-container page-body"
>
<h2
className="page-sub-heading"
>
Folder Settings
</h2>
<div
className="section gf-form-group"
>
<form
name="folderSettingsForm"
onSubmit={[Function]}
>
<div
className="gf-form"
>
<label
className="gf-form-label width-7"
>
Name
</label>
<input
className="gf-form-input width-30"
onChange={[Function]}
type="text"
value="loading"
/>
</div>
<div
className="gf-form-button-row"
>
<button
className="btn btn-success"
disabled={false}
type="submit"
>
<i
className="fa fa-save"
/>
Save
</button>
<button
className="btn btn-danger"
disabled={false}
onClick={[Function]}
>
<i
className="fa fa-trash"
/>
Delete
</button>
</div>
</form>
</div>
</div>
</div>
`;
exports[`Render should render component 1`] = `
<div>
<PageHeader
model={Object {}}
/>
<div
className="page-container page-body"
>
<h2
className="page-sub-heading"
>
Folder Settings
</h2>
<div
className="section gf-form-group"
>
<form
name="folderSettingsForm"
onSubmit={[Function]}
>
<div
className="gf-form"
>
<label
className="gf-form-label width-7"
>
Name
</label>
<input
className="gf-form-input width-30"
onChange={[Function]}
type="text"
value="loading"
/>
</div>
<div
className="gf-form-button-row"
>
<button
className="btn btn-success"
disabled={true}
type="submit"
>
<i
className="fa fa-save"
/>
Save
</button>
<button
className="btn btn-danger"
disabled={false}
onClick={[Function]}
>
<i
className="fa fa-trash"
/>
Delete
</button>
</div>
</form>
</div>
</div>
</div>
`;
...@@ -2,8 +2,8 @@ import { FolderState } from 'app/types'; ...@@ -2,8 +2,8 @@ import { FolderState } from 'app/types';
import { Action, ActionTypes } from './actions'; import { Action, ActionTypes } from './actions';
export const inititalState: FolderState = { export const inititalState: FolderState = {
id: 0,
uid: 'loading', uid: 'loading',
id: -1,
title: 'loading', title: 'loading',
url: '', url: '',
canSave: false, canSave: false,
...@@ -22,7 +22,7 @@ export const folderReducer = (state = inititalState, action: Action): FolderStat ...@@ -22,7 +22,7 @@ export const folderReducer = (state = inititalState, action: Action): FolderStat
return { return {
...state, ...state,
title: action.payload, title: action.payload,
hasChanged: true, hasChanged: action.payload.trim().length > 0,
}; };
} }
return state; return state;
......
...@@ -3,10 +3,10 @@ import './ReactContainer'; ...@@ -3,10 +3,10 @@ import './ReactContainer';
import ServerStats from 'app/features/admin/ServerStats'; import ServerStats from 'app/features/admin/ServerStats';
import AlertRuleList from 'app/features/alerting/AlertRuleList'; import AlertRuleList from 'app/features/alerting/AlertRuleList';
import FolderPermissions from 'app/containers/ManageDashboards/FolderPermissions';
import TeamPages from 'app/features/teams/TeamPages'; import TeamPages from 'app/features/teams/TeamPages';
import TeamList from 'app/features/teams/TeamList'; import TeamList from 'app/features/teams/TeamList';
import FolderSettingsPage from 'app/features/manage-dashboards/FolderSettingsPage'; import FolderSettingsPage from 'app/features/manage-dashboards/FolderSettingsPage';
import FolderPermissions from 'app/features/manage-dashboards/FolderPermissions';
/** @ngInject */ /** @ngInject */
export function setupAngularRoutes($routeProvider, $locationProvider) { export function setupAngularRoutes($routeProvider, $locationProvider) {
......
import { types } from 'mobx-state-tree'; import { types } from 'mobx-state-tree';
import { NavStore } from './../NavStore/NavStore'; import { NavStore } from './../NavStore/NavStore';
import { ViewStore } from './../ViewStore/ViewStore'; import { ViewStore } from './../ViewStore/ViewStore';
import { FolderStore } from './../FolderStore/FolderStore';
import { PermissionsStore } from './../PermissionsStore/PermissionsStore'; import { PermissionsStore } from './../PermissionsStore/PermissionsStore';
export const RootStore = types.model({ export const RootStore = types.model({
...@@ -15,7 +14,6 @@ export const RootStore = types.model({ ...@@ -15,7 +14,6 @@ export const RootStore = types.model({
query: {}, query: {},
routeParams: {}, routeParams: {},
}), }),
folder: types.optional(FolderStore, {}),
}); });
type RootStoreType = typeof RootStore.Type; type RootStoreType = typeof RootStore.Type;
......
...@@ -2,7 +2,7 @@ import { Team, TeamsState, TeamState, TeamGroup, TeamMember } from './teams'; ...@@ -2,7 +2,7 @@ import { Team, TeamsState, TeamState, TeamGroup, TeamMember } from './teams';
import { AlertRuleDTO, AlertRule, AlertRulesState } from './alerting'; import { AlertRuleDTO, AlertRule, AlertRulesState } from './alerting';
import { LocationState, LocationUpdate, UrlQueryMap, UrlQueryValue } from './location'; import { LocationState, LocationUpdate, UrlQueryMap, UrlQueryValue } from './location';
import { NavModel, NavModelItem, NavIndex } from './navModel'; import { NavModel, NavModelItem, NavIndex } from './navModel';
import { FolderDTO, FolderState } from './dashboard'; import { FolderDTO, FolderState } from './folder';
export { export {
Team, Team,
......
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