mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
mobx -> redux: major progress on folder migration
This commit is contained in:
parent
19cbff658b
commit
ec41d76080
@ -1,13 +1,11 @@
|
|||||||
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';
|
|||||||
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> {
|
|||||||
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> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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));
|
@ -1,84 +1,54 @@
|
|||||||
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(),
|
||||||
|
};
|
||||||
|
|
||||||
beforeAll(() => {
|
Object.assign(props, propOverrides);
|
||||||
backendSrv.getFolderByUid.mockReturnValue(
|
|
||||||
Promise.resolve({
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should enable save button', () => {
|
||||||
|
const { wrapper } = setup({
|
||||||
|
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);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,131 @@
|
|||||||
|
// 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';
|
|||||||
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
|
|||||||
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';
|
|||||||
|
|
||||||
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) {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
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({
|
|||||||
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';
|
|||||||
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,
|
||||||
|
Loading…
Reference in New Issue
Block a user