mirror of https://github.com/grafana/grafana
Migration: Migrate org switcher to react (#19607)
* Migration: Migrate org switcher to react * Improve modal overflow behavior * Updated modal backdrop * Renamed type * Modal: Refactoring and reducing duplicationpull/20855/head
parent
5cd4ffffe3
commit
a093fbb51a
@ -0,0 +1,11 @@ |
||||
export interface UserOrgDTO { |
||||
orgId: number; |
||||
name: string; |
||||
role: OrgRole; |
||||
} |
||||
|
||||
export enum OrgRole { |
||||
Admin = 'Admin', |
||||
Editor = 'Editor', |
||||
Viewer = 'Viewer', |
||||
} |
@ -0,0 +1,81 @@ |
||||
import React from 'react'; |
||||
|
||||
import { getBackendSrv } from '@grafana/runtime'; |
||||
import { UserOrgDTO } from '@grafana/data'; |
||||
import { Modal, Button } from '@grafana/ui'; |
||||
|
||||
import { contextSrv } from 'app/core/services/context_srv'; |
||||
import config from 'app/core/config'; |
||||
|
||||
interface Props { |
||||
onDismiss: () => void; |
||||
isOpen: boolean; |
||||
} |
||||
|
||||
interface State { |
||||
orgs: UserOrgDTO[]; |
||||
} |
||||
|
||||
export class OrgSwitcher extends React.PureComponent<Props, State> { |
||||
state: State = { |
||||
orgs: [], |
||||
}; |
||||
|
||||
componentDidMount() { |
||||
this.getUserOrgs(); |
||||
} |
||||
|
||||
getUserOrgs = async () => { |
||||
const orgs: UserOrgDTO[] = await getBackendSrv().get('/api/user/orgs'); |
||||
this.setState({ |
||||
orgs: orgs.sort((a, b) => a.orgId - b.orgId), |
||||
}); |
||||
}; |
||||
|
||||
setCurrentOrg = async (org: UserOrgDTO) => { |
||||
await getBackendSrv().post(`/api/user/using/${org.orgId}`); |
||||
this.setWindowLocation(`${config.appSubUrl}${config.appSubUrl.endsWith('/') ? '' : '/'}?orgId=${org.orgId}`); |
||||
}; |
||||
|
||||
setWindowLocation(href: string) { |
||||
window.location.href = href; |
||||
} |
||||
|
||||
render() { |
||||
const { onDismiss, isOpen } = this.props; |
||||
const { orgs } = this.state; |
||||
|
||||
const currentOrgId = contextSrv.user.orgId; |
||||
|
||||
return ( |
||||
<Modal title="Switch Organization" icon="fa fa-random" onDismiss={onDismiss} isOpen={isOpen}> |
||||
<table className="filter-table form-inline"> |
||||
<thead> |
||||
<tr> |
||||
<th>Name</th> |
||||
<th>Role</th> |
||||
<th /> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
{orgs.map(org => ( |
||||
<tr key={org.orgId}> |
||||
<td>{org.name}</td> |
||||
<td>{org.role}</td> |
||||
<td className="text-right"> |
||||
{org.orgId === currentOrgId ? ( |
||||
<Button size="sm">Current</Button> |
||||
) : ( |
||||
<Button variant="inverse" size="sm" onClick={() => this.setCurrentOrg(org)}> |
||||
Switch to |
||||
</Button> |
||||
)} |
||||
</td> |
||||
</tr> |
||||
))} |
||||
</tbody> |
||||
</table> |
||||
</Modal> |
||||
); |
||||
} |
||||
} |
@ -1,84 +0,0 @@ |
||||
import coreModule from 'app/core/core_module'; |
||||
import { contextSrv } from 'app/core/services/context_srv'; |
||||
import config from 'app/core/config'; |
||||
import { BackendSrv } from '../services/backend_srv'; |
||||
|
||||
const template = ` |
||||
<div class="modal-body"> |
||||
<div class="modal-header"> |
||||
<h2 class="modal-header-title"> |
||||
<i class="fa fa-random"></i> |
||||
<span class="p-l-1">Switch Organization</span> |
||||
</h2> |
||||
|
||||
<a class="modal-header-close" ng-click="ctrl.dismiss();"> |
||||
<i class="fa fa-remove"></i> |
||||
</a> |
||||
</div> |
||||
|
||||
<div class="modal-content modal-content--has-scroll" grafana-scrollbar> |
||||
<table class="filter-table form-inline"> |
||||
<thead> |
||||
<tr> |
||||
<th>Name</th> |
||||
<th>Role</th> |
||||
<th></th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
<tr ng-repeat="org in ctrl.orgs"> |
||||
<td>{{org.name}}</td> |
||||
<td>{{org.role}}</td> |
||||
<td class="text-right"> |
||||
<span class="btn btn-primary btn-small" ng-show="org.orgId === ctrl.currentOrgId"> |
||||
Current |
||||
</span> |
||||
<a ng-click="ctrl.setUsingOrg(org)" class="btn btn-inverse btn-small" ng-show="org.orgId !== ctrl.currentOrgId"> |
||||
Switch to |
||||
</a> |
||||
</td> |
||||
</tr> |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
</div>`;
|
||||
|
||||
export class OrgSwitchCtrl { |
||||
orgs: any[]; |
||||
currentOrgId: any; |
||||
|
||||
/** @ngInject */ |
||||
constructor(private backendSrv: BackendSrv) { |
||||
this.currentOrgId = contextSrv.user.orgId; |
||||
this.getUserOrgs(); |
||||
} |
||||
|
||||
getUserOrgs() { |
||||
this.backendSrv.get('/api/user/orgs').then((orgs: any) => { |
||||
this.orgs = orgs; |
||||
}); |
||||
} |
||||
|
||||
setUsingOrg(org: any) { |
||||
return this.backendSrv.post('/api/user/using/' + org.orgId).then(() => { |
||||
this.setWindowLocation(config.appSubUrl + (config.appSubUrl.endsWith('/') ? '' : '/') + '?orgId=' + org.orgId); |
||||
}); |
||||
} |
||||
|
||||
setWindowLocation(href: string) { |
||||
window.location.href = href; |
||||
} |
||||
} |
||||
|
||||
export function orgSwitcher() { |
||||
return { |
||||
restrict: 'E', |
||||
template: template, |
||||
controller: OrgSwitchCtrl, |
||||
bindToController: true, |
||||
controllerAs: 'ctrl', |
||||
scope: { dismiss: '&' }, |
||||
}; |
||||
} |
||||
|
||||
coreModule.directive('orgSwitcher', orgSwitcher); |
@ -0,0 +1,51 @@ |
||||
import React from 'react'; |
||||
// @ts-ignore
|
||||
import { getBackendSrv } from '@grafana/runtime/src/services/backendSrv'; |
||||
import { OrgSwitcher } from '../components/OrgSwitcher'; |
||||
import { shallow } from 'enzyme'; |
||||
import { OrgRole } from '@grafana/data'; |
||||
|
||||
const getMock = jest.fn(() => Promise.resolve([])); |
||||
const postMock = jest.fn(); |
||||
|
||||
jest.mock('@grafana/runtime/src/services/backendSrv', () => ({ |
||||
getBackendSrv: () => ({ |
||||
get: getMock, |
||||
post: postMock, |
||||
}), |
||||
})); |
||||
|
||||
jest.mock('app/core/services/context_srv', () => ({ |
||||
contextSrv: { |
||||
user: { orgId: 1 }, |
||||
}, |
||||
})); |
||||
|
||||
jest.mock('app/core/config', () => { |
||||
return { |
||||
appSubUrl: '/subUrl', |
||||
}; |
||||
}); |
||||
|
||||
let wrapper; |
||||
let orgSwitcher: OrgSwitcher; |
||||
|
||||
describe('OrgSwitcher', () => { |
||||
describe('when switching org', () => { |
||||
beforeEach(async () => { |
||||
wrapper = shallow(<OrgSwitcher onDismiss={() => {}} isOpen={true} />); |
||||
orgSwitcher = wrapper.instance() as OrgSwitcher; |
||||
orgSwitcher.setWindowLocation = jest.fn(); |
||||
wrapper.update(); |
||||
await orgSwitcher.setCurrentOrg({ name: 'mock org', orgId: 2, role: OrgRole.Viewer }); |
||||
}); |
||||
|
||||
it('should switch orgId in call to backend', () => { |
||||
expect(postMock).toBeCalledWith('/api/user/using/2'); |
||||
}); |
||||
|
||||
it('should switch orgId in url and redirect to home page', () => { |
||||
expect(orgSwitcher.setWindowLocation).toBeCalledWith('/subUrl/?orgId=2'); |
||||
}); |
||||
}); |
||||
}); |
@ -1,48 +0,0 @@ |
||||
import { OrgSwitchCtrl } from '../components/org_switcher'; |
||||
// @ts-ignore
|
||||
import q from 'q'; |
||||
|
||||
jest.mock('app/core/services/context_srv', () => ({ |
||||
contextSrv: { |
||||
user: { orgId: 1 }, |
||||
}, |
||||
})); |
||||
|
||||
jest.mock('app/core/config', () => { |
||||
return { |
||||
appSubUrl: '/subUrl', |
||||
}; |
||||
}); |
||||
|
||||
describe('OrgSwitcher', () => { |
||||
describe('when switching org', () => { |
||||
let expectedHref: string; |
||||
let expectedUsingUrl: string; |
||||
|
||||
beforeEach(() => { |
||||
const backendSrvStub: any = { |
||||
get: (url: string) => { |
||||
return q.resolve([]); |
||||
}, |
||||
post: (url: string) => { |
||||
expectedUsingUrl = url; |
||||
return q.resolve({}); |
||||
}, |
||||
}; |
||||
|
||||
const orgSwitcherCtrl = new OrgSwitchCtrl(backendSrvStub); |
||||
|
||||
orgSwitcherCtrl.setWindowLocation = href => (expectedHref = href); |
||||
|
||||
return orgSwitcherCtrl.setUsingOrg({ orgId: 2 }); |
||||
}); |
||||
|
||||
it('should switch orgId in call to backend', () => { |
||||
expect(expectedUsingUrl).toBe('/api/user/using/2'); |
||||
}); |
||||
|
||||
it('should switch orgId in url and redirect to home page', () => { |
||||
expect(expectedHref).toBe('/subUrl/?orgId=2'); |
||||
}); |
||||
}); |
||||
}); |
Loading…
Reference in new issue