mirror of
https://github.com/grafana/grafana.git
synced 2026-07-30 00:08:10 -05:00
Merge pull request #10719 from grafana/add_permissions_10676
Grafana 5.0: Add permissions modal for the permissions pages
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import Transition from 'react-transition-group/Transition';
|
||||
|
||||
const defaultMaxHeight = '200px'; // When animating using max-height we need to use a static value.
|
||||
// If this is not enough, pass in <SlideDown maxHeight="....
|
||||
const defaultDuration = 200;
|
||||
const defaultStyle = {
|
||||
transition: `max-height ${defaultDuration}ms ease-in-out`,
|
||||
overflow: 'hidden',
|
||||
};
|
||||
|
||||
export default ({ children, in: inProp, maxHeight = defaultMaxHeight }) => {
|
||||
// There are 4 main states a Transition can be in:
|
||||
// ENTERING, ENTERED, EXITING, EXITED
|
||||
// https://reactcommunity.org/react-transition-group/
|
||||
const transitionStyles = {
|
||||
exited: { maxHeight: 0 },
|
||||
entering: { maxHeight: maxHeight },
|
||||
entered: { maxHeight: maxHeight, overflow: 'visible' },
|
||||
exiting: { maxHeight: 0 },
|
||||
};
|
||||
|
||||
return (
|
||||
<Transition in={inProp} timeout={defaultDuration}>
|
||||
{state => (
|
||||
<div
|
||||
style={{
|
||||
...defaultStyle,
|
||||
...transitionStyles[state],
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
import React from 'react';
|
||||
import AddPermissions from './AddPermissions';
|
||||
import { RootStore } from 'app/stores/RootStore/RootStore';
|
||||
import { backendSrv } from 'test/mocks/common';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
describe('AddPermissions', () => {
|
||||
let wrapper;
|
||||
let store;
|
||||
let instance;
|
||||
|
||||
beforeAll(() => {
|
||||
backendSrv.get.mockReturnValue(
|
||||
Promise.resolve([
|
||||
{ id: 2, dashboardId: 1, role: 'Viewer', permission: 1, permissionName: 'View' },
|
||||
{ id: 3, dashboardId: 1, role: 'Editor', permission: 1, permissionName: 'Edit' },
|
||||
])
|
||||
);
|
||||
|
||||
backendSrv.post = jest.fn();
|
||||
|
||||
store = RootStore.create(
|
||||
{},
|
||||
{
|
||||
backendSrv: backendSrv,
|
||||
}
|
||||
);
|
||||
|
||||
wrapper = shallow(<AddPermissions permissions={store.permissions} backendSrv={backendSrv} dashboardId={1} />);
|
||||
instance = wrapper.instance();
|
||||
return store.permissions.load(1, true, false);
|
||||
});
|
||||
|
||||
describe('when permission for a user is added', () => {
|
||||
it('should save permission to db', () => {
|
||||
const evt = {
|
||||
target: {
|
||||
value: 'User',
|
||||
},
|
||||
};
|
||||
const userItem = {
|
||||
id: 2,
|
||||
login: 'user2',
|
||||
};
|
||||
|
||||
instance.typeChanged(evt);
|
||||
instance.userPicked(userItem);
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('[data-save-permission]').prop('disabled')).toBe(false);
|
||||
|
||||
wrapper.find('form').simulate('submit', { preventDefault() {} });
|
||||
|
||||
expect(backendSrv.post.mock.calls.length).toBe(1);
|
||||
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/acl');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when permission for team is added', () => {
|
||||
it('should save permission to db', () => {
|
||||
const evt = {
|
||||
target: {
|
||||
value: 'Group',
|
||||
},
|
||||
};
|
||||
|
||||
const teamItem = {
|
||||
id: 2,
|
||||
name: 'ug1',
|
||||
};
|
||||
|
||||
instance.typeChanged(evt);
|
||||
instance.teamPicked(teamItem);
|
||||
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('[data-save-permission]').prop('disabled')).toBe(false);
|
||||
|
||||
wrapper.find('form').simulate('submit', { preventDefault() {} });
|
||||
|
||||
expect(backendSrv.post.mock.calls.length).toBe(1);
|
||||
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/acl');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
backendSrv.post.mockClear();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,158 @@
|
||||
import React, { Component } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { aclTypes } from 'app/stores/PermissionsStore/PermissionsStore';
|
||||
import UserPicker, { User } from 'app/core/components/Picker/UserPicker';
|
||||
import TeamPicker, { Team } from 'app/core/components/Picker/TeamPicker';
|
||||
import DescriptionPicker, { OptionWithDescription } from 'app/core/components/Picker/DescriptionPicker';
|
||||
import { permissionOptions } from 'app/stores/PermissionsStore/PermissionsStore';
|
||||
|
||||
export interface IProps {
|
||||
permissions: any;
|
||||
backendSrv: any;
|
||||
dashboardId: any;
|
||||
}
|
||||
@observer
|
||||
class AddPermissions extends Component<IProps, any> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.userPicked = this.userPicked.bind(this);
|
||||
this.teamPicked = this.teamPicked.bind(this);
|
||||
this.permissionPicked = this.permissionPicked.bind(this);
|
||||
this.typeChanged = this.typeChanged.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const { permissions } = this.props;
|
||||
permissions.resetNewType();
|
||||
}
|
||||
|
||||
typeChanged(evt) {
|
||||
const { value } = evt.target;
|
||||
const { permissions } = this.props;
|
||||
|
||||
// if (value === 'Viewer' || value === 'Editor') {
|
||||
// // permissions.addStoreItem({ permission: 1, role: value, dashboardId: dashboardId }, dashboardId);
|
||||
// // this.resetNewType();
|
||||
// return;
|
||||
// }
|
||||
|
||||
permissions.setNewType(value);
|
||||
}
|
||||
|
||||
userPicked(user: User) {
|
||||
const { permissions } = this.props;
|
||||
if (!user) {
|
||||
permissions.newItem.setUser(null, null);
|
||||
return;
|
||||
}
|
||||
return permissions.newItem.setUser(user.id, user.login);
|
||||
}
|
||||
|
||||
teamPicked(team: Team) {
|
||||
const { permissions } = this.props;
|
||||
if (!team) {
|
||||
permissions.newItem.setTeam(null, null);
|
||||
return;
|
||||
}
|
||||
return permissions.newItem.setTeam(team.id, team.name);
|
||||
}
|
||||
|
||||
permissionPicked(permission: OptionWithDescription) {
|
||||
const { permissions } = this.props;
|
||||
return permissions.newItem.setPermission(permission.value);
|
||||
}
|
||||
|
||||
resetNewType() {
|
||||
const { permissions } = this.props;
|
||||
return permissions.resetNewType();
|
||||
}
|
||||
|
||||
handleSubmit(evt) {
|
||||
evt.preventDefault();
|
||||
const { permissions } = this.props;
|
||||
permissions.addStoreItem();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { permissions, backendSrv } = this.props;
|
||||
const newItem = permissions.newItem;
|
||||
const pickerClassName = 'width-20';
|
||||
|
||||
const isValid = newItem.isValid();
|
||||
|
||||
return (
|
||||
<div className="gf-form-inline cta-form">
|
||||
<button className="cta-form__close btn btn-transparent" onClick={permissions.hideAddPermissions}>
|
||||
<i className="fa fa-close" />
|
||||
</button>
|
||||
<form name="addPermission" onSubmit={this.handleSubmit}>
|
||||
<h6>Add Permission For</h6>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-select-wrapper">
|
||||
<select className="gf-form-input gf-size-auto" value={newItem.type} onChange={this.typeChanged}>
|
||||
{aclTypes.map((option, idx) => {
|
||||
return (
|
||||
<option key={idx} value={option.value}>
|
||||
{option.text}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{newItem.type === 'User' ? (
|
||||
<div className="gf-form">
|
||||
<UserPicker
|
||||
backendSrv={backendSrv}
|
||||
handlePicked={this.userPicked}
|
||||
value={newItem.userId}
|
||||
className={pickerClassName}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{newItem.type === 'Group' ? (
|
||||
<div className="gf-form">
|
||||
<TeamPicker
|
||||
backendSrv={backendSrv}
|
||||
handlePicked={this.teamPicked}
|
||||
value={newItem.teamId}
|
||||
className={pickerClassName}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="gf-form">
|
||||
<DescriptionPicker
|
||||
optionsWithDesc={permissionOptions}
|
||||
handlePicked={this.permissionPicked}
|
||||
value={newItem.permission}
|
||||
disabled={false}
|
||||
className={'gf-form-input--form-dropdown-right'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="gf-form">
|
||||
<button data-save-permission className="btn btn-success" type="submit" disabled={!isValid}>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{permissions.error ? (
|
||||
<div className="gf-form width-17">
|
||||
<span ng-if="ctrl.error" className="text-error p-l-1">
|
||||
<i className="fa fa-warning" />
|
||||
{permissions.error}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AddPermissions;
|
||||
@@ -1,8 +1,11 @@
|
||||
import React, { Component } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { store } from 'app/stores/store';
|
||||
import Permissions from 'app/core/components/Permissions/Permissions';
|
||||
import Tooltip from 'app/core/components/Tooltip/Tooltip';
|
||||
import PermissionsInfo from 'app/core/components/Permissions/PermissionsInfo';
|
||||
import AddPermissions from 'app/core/components/Permissions/AddPermissions';
|
||||
import SlideDown from 'app/core/components/Animations/SlideDown';
|
||||
|
||||
export interface IProps {
|
||||
dashboardId: number;
|
||||
@@ -11,26 +14,44 @@ export interface IProps {
|
||||
folderSlug: string;
|
||||
backendSrv: any;
|
||||
}
|
||||
|
||||
@observer
|
||||
class DashboardPermissions extends Component<IProps, any> {
|
||||
permissions: any;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.handleAddPermission = this.handleAddPermission.bind(this);
|
||||
this.permissions = store.permissions;
|
||||
}
|
||||
|
||||
handleAddPermission() {
|
||||
this.permissions.toggleAddPermissions();
|
||||
}
|
||||
|
||||
render() {
|
||||
const { dashboardId, folderTitle, folderSlug, folderId, backendSrv } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="dashboard-settings__header">
|
||||
<h3 className="d-inline-block">Permissions</h3>
|
||||
<Tooltip className="page-sub-heading-icon" placement="auto" content={PermissionsInfo}>
|
||||
<i className="gicon gicon-question gicon--has-hover" />
|
||||
</Tooltip>
|
||||
<div className="page-action-bar">
|
||||
<h3 className="d-inline-block">Permissions</h3>
|
||||
<Tooltip className="page-sub-heading-icon" placement="auto" content={PermissionsInfo}>
|
||||
<i className="gicon gicon-question gicon--has-hover" />
|
||||
</Tooltip>
|
||||
<div className="page-action-bar__spacer" />
|
||||
<button
|
||||
className="btn btn-success pull-right"
|
||||
onClick={this.handleAddPermission}
|
||||
disabled={this.permissions.isAddPermissionsVisible}
|
||||
>
|
||||
<i className="fa fa-plus" /> Add Permission
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<SlideDown in={this.permissions.isAddPermissionsVisible}>
|
||||
<AddPermissions permissions={this.permissions} backendSrv={backendSrv} dashboardId={dashboardId} />
|
||||
</SlideDown>
|
||||
<Permissions
|
||||
permissions={this.permissions}
|
||||
isFolder={false}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import React from 'react';
|
||||
import Permissions from './Permissions';
|
||||
import { RootStore } from 'app/stores/RootStore/RootStore';
|
||||
import { backendSrv } from 'test/mocks/common';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
describe('Permissions', () => {
|
||||
let wrapper;
|
||||
|
||||
beforeAll(() => {
|
||||
backendSrv.get.mockReturnValue(
|
||||
Promise.resolve([
|
||||
{ id: 2, dashboardId: 1, role: 'Viewer', permission: 1, permissionName: 'View' },
|
||||
{ id: 3, dashboardId: 1, role: 'Editor', permission: 1, permissionName: 'Edit' },
|
||||
{
|
||||
id: 4,
|
||||
dashboardId: 1,
|
||||
userId: 2,
|
||||
userLogin: 'danlimerick',
|
||||
userEmail: 'dan.limerick@gmail.com',
|
||||
permission: 4,
|
||||
permissionName: 'Admin',
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
backendSrv.post = jest.fn();
|
||||
|
||||
const store = RootStore.create(
|
||||
{},
|
||||
{
|
||||
backendSrv: backendSrv,
|
||||
}
|
||||
);
|
||||
|
||||
wrapper = shallow(<Permissions backendSrv={backendSrv} isFolder={true} dashboardId={1} {...store} />);
|
||||
return wrapper.instance().loadStore(1, true);
|
||||
});
|
||||
|
||||
describe('when permission for a user is added', () => {
|
||||
it('should save permission to db', () => {
|
||||
const userItem = {
|
||||
id: 2,
|
||||
login: 'user2',
|
||||
};
|
||||
|
||||
wrapper
|
||||
.instance()
|
||||
.userPicked(userItem)
|
||||
.then(() => {
|
||||
expect(backendSrv.post.mock.calls.length).toBe(1);
|
||||
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/acl');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when permission for team is added', () => {
|
||||
it('should save permission to db', () => {
|
||||
const teamItem = {
|
||||
id: 2,
|
||||
name: 'ug1',
|
||||
};
|
||||
|
||||
wrapper
|
||||
.instance()
|
||||
.teamPicked(teamItem)
|
||||
.then(() => {
|
||||
expect(backendSrv.post.mock.calls.length).toBe(1);
|
||||
expect(backendSrv.post.mock.calls[0][0]).toBe('/api/dashboards/id/1/acl');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,6 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PermissionsList from './PermissionsList';
|
||||
import { observer } from 'mobx-react';
|
||||
import UserPicker, { User } from 'app/core/components/Picker/UserPicker';
|
||||
import TeamPicker, { Team } from 'app/core/components/Picker/TeamPicker';
|
||||
import { aclTypes } from 'app/stores/PermissionsStore/PermissionsStore';
|
||||
import { FolderInfo } from './FolderInfo';
|
||||
|
||||
export interface DashboardAcl {
|
||||
@@ -40,8 +37,6 @@ class Permissions extends Component<IProps, any> {
|
||||
this.permissionChanged = this.permissionChanged.bind(this);
|
||||
this.typeChanged = this.typeChanged.bind(this);
|
||||
this.removeItem = this.removeItem.bind(this);
|
||||
this.userPicked = this.userPicked.bind(this);
|
||||
this.teamPicked = this.teamPicked.bind(this);
|
||||
this.loadStore(dashboardId, isFolder, folderInfo && folderInfo.id === 0);
|
||||
}
|
||||
|
||||
@@ -77,28 +72,8 @@ class Permissions extends Component<IProps, any> {
|
||||
permissions.setNewType(value);
|
||||
}
|
||||
|
||||
userPicked(user: User) {
|
||||
const { permissions, dashboardId } = this.props;
|
||||
return permissions.addStoreItem({
|
||||
userId: user.id,
|
||||
userLogin: user.login,
|
||||
permission: 1,
|
||||
dashboardId: dashboardId,
|
||||
});
|
||||
}
|
||||
|
||||
teamPicked(team: Team) {
|
||||
const { permissions, dashboardId } = this.props;
|
||||
return permissions.addStoreItem({
|
||||
teamId: team.id,
|
||||
team: team.name,
|
||||
permission: 1,
|
||||
dashboardId: dashboardId,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { permissions, folderInfo, backendSrv } = this.props;
|
||||
const { permissions, folderInfo } = this.props;
|
||||
|
||||
return (
|
||||
<div className="gf-form-group">
|
||||
@@ -109,50 +84,6 @@ class Permissions extends Component<IProps, any> {
|
||||
fetching={permissions.fetching}
|
||||
folderInfo={folderInfo}
|
||||
/>
|
||||
<div className="gf-form-inline">
|
||||
<form name="addPermission" className="gf-form-group">
|
||||
<h6 className="muted">Add Permission For</h6>
|
||||
<div className="gf-form-inline">
|
||||
<div className="gf-form">
|
||||
<div className="gf-form-select-wrapper">
|
||||
<select
|
||||
className="gf-form-input gf-size-auto"
|
||||
value={permissions.newType}
|
||||
onChange={this.typeChanged}
|
||||
>
|
||||
{aclTypes.map((option, idx) => {
|
||||
return (
|
||||
<option key={idx} value={option.value}>
|
||||
{option.text}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{permissions.newType === 'User' ? (
|
||||
<div className="gf-form">
|
||||
<UserPicker backendSrv={backendSrv} handlePicked={this.userPicked} />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{permissions.newType === 'Group' ? (
|
||||
<div className="gf-form">
|
||||
<TeamPicker backendSrv={backendSrv} handlePicked={this.teamPicked} />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</form>
|
||||
{permissions.error ? (
|
||||
<div className="gf-form width-17">
|
||||
<span ng-if="ctrl.error" className="text-error p-l-1">
|
||||
<i className="fa fa-warning" />
|
||||
{permissions.error}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import TeamPicker from './TeamPicker';
|
||||
|
||||
const model = {
|
||||
backendSrv: {
|
||||
get: () => {
|
||||
return new Promise((resolve, reject) => {});
|
||||
},
|
||||
},
|
||||
handlePicked: () => {},
|
||||
};
|
||||
|
||||
describe('TeamPicker', () => {
|
||||
it('renders correctly', () => {
|
||||
const tree = renderer.create(<TeamPicker {...model} />).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -9,6 +9,8 @@ export interface IProps {
|
||||
isLoading: boolean;
|
||||
toggleLoading: any;
|
||||
handlePicked: (user) => void;
|
||||
value?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export interface Team {
|
||||
@@ -54,7 +56,7 @@ class TeamPicker extends Component<IProps, any> {
|
||||
|
||||
render() {
|
||||
const AsyncComponent = this.state.creatable ? Select.AsyncCreatable : Select.Async;
|
||||
const { isLoading, handlePicked } = this.props;
|
||||
const { isLoading, handlePicked, value, className } = this.props;
|
||||
|
||||
return (
|
||||
<div className="user-picker">
|
||||
@@ -66,10 +68,13 @@ class TeamPicker extends Component<IProps, any> {
|
||||
isLoading={isLoading}
|
||||
loadOptions={this.debouncedSearch}
|
||||
loadingPlaceholder="Loading..."
|
||||
noResultsText="No teams found"
|
||||
onChange={handlePicked}
|
||||
className="width-8 gf-form-input gf-form-input--form-dropdown"
|
||||
className={`gf-form-input gf-form-input--form-dropdown ${className || ''}`}
|
||||
optionComponent={PickerOption}
|
||||
placeholder="Choose"
|
||||
value={value}
|
||||
autosize={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -9,6 +9,8 @@ export interface IProps {
|
||||
isLoading: boolean;
|
||||
toggleLoading: any;
|
||||
handlePicked: (user) => void;
|
||||
value?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
@@ -53,8 +55,7 @@ class UserPicker extends Component<IProps, any> {
|
||||
|
||||
render() {
|
||||
const AsyncComponent = this.state.creatable ? Select.AsyncCreatable : Select.Async;
|
||||
const { isLoading, handlePicked } = this.props;
|
||||
|
||||
const { isLoading, handlePicked, value, className } = this.props;
|
||||
return (
|
||||
<div className="user-picker">
|
||||
<AsyncComponent
|
||||
@@ -67,9 +68,11 @@ class UserPicker extends Component<IProps, any> {
|
||||
loadingPlaceholder="Loading..."
|
||||
noResultsText="No users found"
|
||||
onChange={handlePicked}
|
||||
className="width-8 gf-form-input gf-form-input--form-dropdown"
|
||||
className={`gf-form-input gf-form-input--form-dropdown ${className || ''}`}
|
||||
optionComponent={PickerOption}
|
||||
placeholder="Choose"
|
||||
value={value}
|
||||
autosize={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TeamPicker renders correctly 1`] = `
|
||||
<div
|
||||
className="user-picker"
|
||||
>
|
||||
<div
|
||||
className="Select gf-form-input gf-form-input--form-dropdown is-clearable is-loading is-searchable Select--single"
|
||||
style={undefined}
|
||||
>
|
||||
<div
|
||||
className="Select-control"
|
||||
onKeyDown={[Function]}
|
||||
onMouseDown={[Function]}
|
||||
onTouchEnd={[Function]}
|
||||
onTouchMove={[Function]}
|
||||
onTouchStart={[Function]}
|
||||
style={undefined}
|
||||
>
|
||||
<span
|
||||
className="Select-multi-value-wrapper"
|
||||
id="react-select-2--value"
|
||||
>
|
||||
<div
|
||||
className="Select-placeholder"
|
||||
>
|
||||
Loading...
|
||||
</div>
|
||||
<div
|
||||
className="Select-input"
|
||||
style={
|
||||
Object {
|
||||
"display": "inline-block",
|
||||
}
|
||||
}
|
||||
>
|
||||
<input
|
||||
aria-activedescendant="react-select-2--value"
|
||||
aria-describedby={undefined}
|
||||
aria-expanded="false"
|
||||
aria-haspopup="false"
|
||||
aria-label={undefined}
|
||||
aria-labelledby={undefined}
|
||||
aria-owns=""
|
||||
className={undefined}
|
||||
id={undefined}
|
||||
onBlur={[Function]}
|
||||
onChange={[Function]}
|
||||
onFocus={[Function]}
|
||||
required={false}
|
||||
role="combobox"
|
||||
style={
|
||||
Object {
|
||||
"boxSizing": "content-box",
|
||||
"width": "5px",
|
||||
}
|
||||
}
|
||||
tabIndex={undefined}
|
||||
value=""
|
||||
/>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"height": 0,
|
||||
"left": 0,
|
||||
"overflow": "scroll",
|
||||
"position": "absolute",
|
||||
"top": 0,
|
||||
"visibility": "hidden",
|
||||
"whiteSpace": "pre",
|
||||
}
|
||||
}
|
||||
>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="Select-loading-zone"
|
||||
>
|
||||
<span
|
||||
className="Select-loading"
|
||||
/>
|
||||
</span>
|
||||
<span
|
||||
className="Select-arrow-zone"
|
||||
onMouseDown={[Function]}
|
||||
>
|
||||
<span
|
||||
className="Select-arrow"
|
||||
onMouseDown={[Function]}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -5,7 +5,7 @@ exports[`UserPicker renders correctly 1`] = `
|
||||
className="user-picker"
|
||||
>
|
||||
<div
|
||||
className="Select width-8 gf-form-input gf-form-input--form-dropdown is-clearable is-loading is-searchable Select--single"
|
||||
className="Select gf-form-input gf-form-input--form-dropdown is-clearable is-loading is-searchable Select--single"
|
||||
style={undefined}
|
||||
>
|
||||
<div
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
export interface IProps {
|
||||
backendSrv: any;
|
||||
handlePicked: (data) => void;
|
||||
value?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function withPicker(WrappedComponent) {
|
||||
|
||||
Reference in New Issue
Block a user