simple select

This commit is contained in:
Peter Holmberg 2018-10-25 16:56:49 +02:00
parent 1f8b61f9a6
commit a98f7e548f
11 changed files with 274 additions and 119 deletions

View File

@ -0,0 +1,33 @@
import React, { SFC } from 'react';
import Select from 'react-select';
import DescriptionOption from './DescriptionOption';
import ResetStyles from './ResetStyles';
interface Props {
options: any[];
className?: string;
onSelected: (item: any) => {} | void;
getOptionValue: (item: any) => string;
getOptionLabel: (item: any) => string;
}
const SimplePicker: SFC<Props> = ({ className, getOptionLabel, getOptionValue, onSelected, options }) => {
return (
<Select
isSearchable={false}
classNamePrefix={`gf-form-select-box`}
className={`width-7 gf-form-input gf-form-input--form-dropdown ${className || ''}`}
placeholder="Choose"
options={options}
onChange={onSelected}
components={{
Option: DescriptionOption,
}}
styles={ResetStyles}
getOptionValue={getOptionValue}
getOptionLabel={getOptionLabel}
/>
);
};
export default SimplePicker;

View File

@ -1,37 +1,28 @@
import React from 'react'; import React, { PureComponent } from 'react';
import withTooltip from './withTooltip'; import withTooltip from './withTooltip';
import { Target } from 'react-popper'; import { Target } from 'react-popper';
interface TooltipProps { interface Props {
tooltipSetState: (prevState: object) => void; tooltipSetState: (prevState: object) => void;
} }
class Tooltip extends React.Component<TooltipProps, any> { class Tooltip extends PureComponent<Props> {
constructor(props) { showTooltip = () => {
super(props);
this.showTooltip = this.showTooltip.bind(this);
this.hideTooltip = this.hideTooltip.bind(this);
}
showTooltip() {
const { tooltipSetState } = this.props; const { tooltipSetState } = this.props;
tooltipSetState(prevState => {
return {
...prevState,
show: true,
};
});
}
hideTooltip() { tooltipSetState(prevState => ({
...prevState,
show: true,
}));
};
hideTooltip = () => {
const { tooltipSetState } = this.props; const { tooltipSetState } = this.props;
tooltipSetState(prevState => { tooltipSetState(prevState => ({
return { ...prevState,
...prevState, show: false,
show: false, }));
}; };
});
}
render() { render() {
return ( return (

View File

@ -1,6 +1,6 @@
import { Action, ActionTypes } from './actions'; import { Action, ActionTypes } from './actions';
import { OrgRole, PermissionLevel, DashboardState } from 'app/types'; import { OrgRole, PermissionLevel, DashboardState } from 'app/types';
import { inititalState, dashboardReducer } from './reducers'; import { initialState, dashboardReducer } from './reducers';
describe('dashboard reducer', () => { describe('dashboard reducer', () => {
describe('loadDashboardPermissions', () => { describe('loadDashboardPermissions', () => {
@ -14,7 +14,7 @@ describe('dashboard reducer', () => {
{ id: 3, dashboardId: 1, role: OrgRole.Editor, permission: PermissionLevel.Edit }, { id: 3, dashboardId: 1, role: OrgRole.Editor, permission: PermissionLevel.Edit },
], ],
}; };
state = dashboardReducer(inititalState, action); state = dashboardReducer(initialState, action);
}); });
it('should add permissions to state', async () => { it('should add permissions to state', async () => {

View File

@ -2,11 +2,11 @@ import { DashboardState } from 'app/types';
import { Action, ActionTypes } from './actions'; import { Action, ActionTypes } from './actions';
import { processAclItems } from 'app/core/utils/acl'; import { processAclItems } from 'app/core/utils/acl';
export const inititalState: DashboardState = { export const initialState: DashboardState = {
permissions: [], permissions: [],
}; };
export const dashboardReducer = (state = inititalState, action: Action): DashboardState => { export const dashboardReducer = (state = initialState, action: Action): DashboardState => {
switch (action.type) { switch (action.type) {
case ActionTypes.LoadDashboardPermissions: case ActionTypes.LoadDashboardPermissions:
return { return {

View File

@ -2,30 +2,50 @@ import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader'; import { hot } from 'react-hot-loader';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import PageHeader from '../../core/components/PageHeader/PageHeader'; import PageHeader from '../../core/components/PageHeader/PageHeader';
import { loadOrganisation } from './state/actions'; import PageLoader from '../../core/components/PageLoader/PageLoader';
import { NavModel, Organisation, OrganisationPreferences, StoreState } from 'app/types'; import { loadOrganization, loadOrganizationPreferences } from './state/actions';
import { DashboardAcl, NavModel, Organization, OrganisationPreferences, StoreState } from 'app/types';
import { getNavModel } from '../../core/selectors/navModel'; import { getNavModel } from '../../core/selectors/navModel';
import OrgProfile from './OrgProfile';
import OrgPreferences from './OrgPreferences';
export interface Props { export interface Props {
navModel: NavModel; navModel: NavModel;
organisation: Organisation; organization: Organization;
preferences: OrganisationPreferences; preferences: OrganisationPreferences;
loadOrganisation: typeof loadOrganisation; starredDashboards: DashboardAcl[];
loadOrganization: typeof loadOrganization;
loadOrganizationPreferences: typeof loadOrganizationPreferences;
} }
interface State { interface State {
orgName: string; orgName: string;
hasSet: boolean; theme: string;
isReady: boolean;
selectedDashboard: DashboardAcl;
} }
export class OrgDetailsPage extends PureComponent<Props, State> { export class OrgDetailsPage extends PureComponent<Props, State> {
state = { state = {
orgName: '', orgName: '',
hasSet: false, theme: '',
isReady: false,
selectedDashboard: null,
}; };
async componentDidMount() { async componentDidMount() {
await this.props.loadOrganisation(); this.fetchOrganisation();
}
async fetchOrganisation() {
const organization = await this.props.loadOrganization();
// const preferences = await this.props.loadOrganizationPreferences();
this.setState({
orgName: organization.name,
// theme: preferences.theme,
isReady: true,
});
} }
onOrgNameChange = event => { onOrgNameChange = event => {
@ -34,82 +54,41 @@ export class OrgDetailsPage extends PureComponent<Props, State> {
}); });
}; };
onSubmitForm = event => {}; onSubmitForm = () => {};
onSubmitPreferences = () => {};
onDashboardSelected = dashboard => {
this.setState({
selectedDashboard: dashboard,
});
};
render() { render() {
const { navModel, preferences } = this.props; const { navModel, preferences, starredDashboards } = this.props;
const themes: any = [
{ value: '', text: 'Default' },
{ value: 'dark', text: 'Dark' },
{ value: 'light', text: 'Light' },
];
return ( return (
<div> <div>
<PageHeader model={navModel} /> <PageHeader model={navModel} />
<div className="page-container page-body"> <div className="page-container page-body">
<h3 className="page-sub-heading">Organisation profile</h3> {!this.state.isReady ? (
<form name="orgForm" className="gf-form-group" onSubmit={this.onSubmitForm}> <PageLoader pageName="Organisation" />
<div className="gf-form-inline"> ) : (
<div className="gf-form max-width-28"> <div>
<span className="gf-form-label">Organization name</span> <OrgProfile
<input onOrgNameChange={name => this.onOrgNameChange(name)}
className="gf-form-input" onSubmit={this.onSubmitForm}
type="text" orgName={this.state.orgName}
onChange={this.onOrgNameChange} />
value={this.state.orgName} <OrgPreferences
/> preferences={preferences}
</div> starredDashboards={starredDashboards}
onDashboardSelected={dashboard => this.onDashboardSelected(dashboard)}
onTimeZoneChange={() => {}}
onSubmit={this.onSubmitPreferences}
/>
</div> </div>
)}
<div className="gf-form-button-row">
<button type="submit" className="btn btn-success">
Save
</button>
</div>
</form>
<form name="ctrl.prefsForm" className="section gf-form-group">
<h3 className="page-heading">Preferences</h3>
<div className="gf-form">
<span className="gf-form-label width-11">UI Theme</span>
<div className="gf-form-select-wrapper max-width-20">
<select className="gf-form-input" value={preferences.theme}>
{themes.map((theme, index) => {
return (
<option key={`${theme.value}-${index}`} value={theme.value}>
{theme.text}
</option>
);
})}
</select>
</div>
</div>
<div className="gf-form">
<span className="gf-form-label width-11">
Home Dashboard
{/*<info-popover mode="right-normal">*/}
{/*Not finding dashboard you want? Star it first, then it should appear in this select box.*/}
{/*</info-popover>*/}
</span>
{/*<dashboard-selector className="gf-form-select-wrapper max-width-20" model="ctrl.prefs.homeDashboardId" />*/}
</div>
<div className="gf-form">
<label className="gf-form-label width-11">Timezone</label>
<div className="gf-form-select-wrapper max-width-20">
<select className="gf-form-input" ng-model="ctrl.prefs.timezone" />
</div>
</div>
<div className="gf-form-button-row">
<button type="submit" className="btn btn-success" ng-click="ctrl.updatePrefs()">
Save
</button>
</div>
</form>
</div> </div>
</div> </div>
); );
@ -121,11 +100,13 @@ function mapStateToProps(state: StoreState) {
navModel: getNavModel(state.navIndex, 'org-settings'), navModel: getNavModel(state.navIndex, 'org-settings'),
organisation: state.organisation.organisation, organisation: state.organisation.organisation,
preferences: state.organisation.preferences, preferences: state.organisation.preferences,
starredDashboards: state.organisation.starredDashboards,
}; };
} }
const mapDispatchToProps = { const mapDispatchToProps = {
loadOrganisation, loadOrganization,
loadOrganizationPreferences,
}; };
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(OrgDetailsPage)); export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(OrgDetailsPage));

View File

@ -0,0 +1,83 @@
import React, { SFC } from 'react';
import Tooltip from '../../core/components/Tooltip/Tooltip';
import { DashboardAcl, OrganisationPreferences } from 'app/types';
import SimplePicker from '../../core/components/Picker/SimplePicker';
interface Props {
preferences: OrganisationPreferences;
starredDashboards: DashboardAcl[];
onDashboardSelected: (dashboard: DashboardAcl) => void;
onTimeZoneChange: (timeZone: string) => void;
onSubmit: () => void;
}
const OrgPreferences: SFC<Props> = ({
preferences,
starredDashboards,
onDashboardSelected,
onSubmit,
onTimeZoneChange,
}) => {
const themes = [{ value: '', text: 'Default' }, { value: 'dark', text: 'Dark' }, { value: 'light', text: 'Light' }];
const timezones = [
{ value: '', text: 'Default' },
{ value: 'browser', text: 'Local browser time' },
{ value: 'utc', text: 'UTC' },
];
return (
<form className="section gf-form-group" onSubmit={onSubmit}>
<h3 className="page-heading">Preferences</h3>
<div className="gf-form">
<span className="gf-form-label width-11">UI Theme</span>
<SimplePicker
options={themes}
getOptionValue={i => i.value}
getOptionLabel={i => i.text}
onSelected={theme => {
console.log(theme);
}}
/>
</div>
<div className="gf-form">
<span className="gf-form-label width-11">
Home Dashboard
<Tooltip
className="gf-form-help-icon gf-form-help-icon--right-normal"
placement="right"
content="Not finding dashboard you want? Star it first, then it should appear in this select box."
>
<i className="fa fa-info-circle" />
</Tooltip>
</span>
<SimplePicker
getOptionLabel={i => i.title}
getOptionValue={i => i.id}
onSelected={dashboard => onDashboardSelected(dashboard)}
options={starredDashboards}
/>
</div>
<div className="gf-form">
<label className="gf-form-label width-11">Timezone</label>
<SimplePicker
className="gf-form-input"
onSelected={timezone => {
console.log(timezone);
}}
options={timezones}
getOptionLabel={i => i.text}
getOptionValue={i => i.value}
/>
</div>
<div className="gf-form-button-row">
<button type="submit" className="btn btn-success">
Save
</button>
</div>
</form>
);
};
export default OrgPreferences;

View File

@ -0,0 +1,37 @@
import React, { SFC } from 'react';
interface Props {
orgName: string;
onSubmit: () => void;
onOrgNameChange: (orgName: string) => void;
}
const OrgProfile: SFC<Props> = ({ onSubmit, onOrgNameChange, orgName }) => {
return (
<div>
<h3 className="page-sub-heading">Organization profile</h3>
<form name="orgForm" className="gf-form-group" onSubmit={onSubmit}>
<div className="gf-form-inline">
<div className="gf-form max-width-28">
<span className="gf-form-label">Organization name</span>
<input
className="gf-form-input"
type="text"
onChange={event => {
onOrgNameChange(event.target.value);
}}
value={orgName}
/>
</div>
</div>
<div className="gf-form-button-row">
<button type="submit" className="btn btn-success">
Save
</button>
</div>
</form>
</div>
);
};
export default OrgProfile;

View File

@ -1,15 +1,16 @@
import { ThunkAction } from 'redux-thunk'; import { ThunkAction } from 'redux-thunk';
import { Organisation, OrganisationPreferences, StoreState } from 'app/types'; import { DashboardAcl, Organization, OrganisationPreferences, StoreState } from 'app/types';
import { getBackendSrv } from '../../../core/services/backend_srv'; import { getBackendSrv } from '../../../core/services/backend_srv';
export enum ActionTypes { export enum ActionTypes {
LoadOrganisation = 'LOAD_ORGANISATION', LoadOrganisation = 'LOAD_ORGANISATION',
LoadPreferences = 'LOAD_PREFERENCES', LoadPreferences = 'LOAD_PREFERENCES',
LoadStarredDashboards = 'LOAD_STARRED_DASHBOARDS',
} }
interface LoadOrganisationAction { interface LoadOrganizationAction {
type: ActionTypes.LoadOrganisation; type: ActionTypes.LoadOrganisation;
payload: Organisation; payload: Organization;
} }
interface LoadPreferencesAction { interface LoadPreferencesAction {
@ -17,7 +18,12 @@ interface LoadPreferencesAction {
payload: OrganisationPreferences; payload: OrganisationPreferences;
} }
const organisationLoaded = (organisation: Organisation) => ({ interface LoadStarredDashboardsAction {
type: ActionTypes.LoadStarredDashboards;
payload: DashboardAcl[];
}
const organisationLoaded = (organisation: Organization) => ({
type: ActionTypes.LoadOrganisation, type: ActionTypes.LoadOrganisation,
payload: organisation, payload: organisation,
}); });
@ -27,14 +33,31 @@ const preferencesLoaded = (preferences: OrganisationPreferences) => ({
payload: preferences, payload: preferences,
}); });
export type Action = LoadOrganisationAction | LoadPreferencesAction; const starredDashboardsLoaded = (dashboards: DashboardAcl[]) => ({
type: ActionTypes.LoadStarredDashboards,
payload: dashboards,
});
export type Action = LoadOrganizationAction | LoadPreferencesAction | LoadStarredDashboardsAction;
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, any>; type ThunkResult<R> = ThunkAction<R, StoreState, undefined, any>;
export function loadOrganisation(): ThunkResult<void> { export function loadOrganization(): ThunkResult<void> {
return async dispatch => { return async dispatch => {
const organisationResponse = await getBackendSrv().get('/api/org'); const organisationResponse = await getBackendSrv().get('/api/org');
const preferencesResponse = await getBackendSrv().get('/api/org/preferences');
dispatch(organisationLoaded(organisationResponse)); dispatch(organisationLoaded(organisationResponse));
dispatch(preferencesLoaded(preferencesResponse));
return organisationResponse;
};
}
export function loadOrganizationPreferences(): ThunkResult<void> {
return async dispatch => {
const preferencesResponse = await getBackendSrv().get('/api/org/preferences');
dispatch(preferencesLoaded(preferencesResponse));
const starredDashboards = await getBackendSrv().search({ starred: true });
dispatch(starredDashboardsLoaded(starredDashboards));
return preferencesResponse;
}; };
} }

View File

@ -1,9 +1,10 @@
import { Organisation, OrganisationPreferences, OrganisationState } from 'app/types'; import { DashboardAcl, Organization, OrganisationPreferences, OrganisationState } from 'app/types';
import { Action, ActionTypes } from './actions'; import { Action, ActionTypes } from './actions';
const initialState: OrganisationState = { const initialState: OrganisationState = {
organisation: {} as Organisation, organisation: {} as Organization,
preferences: {} as OrganisationPreferences, preferences: {} as OrganisationPreferences,
starredDashboards: [] as DashboardAcl[],
}; };
const organisationReducer = (state = initialState, action: Action): OrganisationState => { const organisationReducer = (state = initialState, action: Action): OrganisationState => {
@ -13,6 +14,9 @@ const organisationReducer = (state = initialState, action: Action): Organisation
case ActionTypes.LoadPreferences: case ActionTypes.LoadPreferences:
return { ...state, preferences: action.payload }; return { ...state, preferences: action.payload };
case ActionTypes.LoadStarredDashboards:
return { ...state, starredDashboards: action.payload };
} }
return state; return state;

View File

@ -22,7 +22,7 @@ import {
} from './series'; } from './series';
import { PanelProps } from './panel'; import { PanelProps } from './panel';
import { PluginDashboard, PluginMeta, Plugin, PluginsState } from './plugins'; import { PluginDashboard, PluginMeta, Plugin, PluginsState } from './plugins';
import { Organisation, OrganisationPreferences, OrganisationState } from './organisation'; import { Organization, OrganisationPreferences, OrganisationState } from './organization';
export { export {
Team, Team,
@ -71,7 +71,7 @@ export {
DataQueryResponse, DataQueryResponse,
DataQueryOptions, DataQueryOptions,
PluginDashboard, PluginDashboard,
Organisation, Organization,
OrganisationState, OrganisationState,
OrganisationPreferences, OrganisationPreferences,
}; };

View File

@ -1,4 +1,6 @@
export interface Organisation { import { DashboardAcl } from './acl';
export interface Organization {
name: string; name: string;
id: number; id: number;
} }
@ -10,6 +12,7 @@ export interface OrganisationPreferences {
} }
export interface OrganisationState { export interface OrganisationState {
organisation: Organisation; organisation: Organization;
preferences: OrganisationPreferences; preferences: OrganisationPreferences;
starredDashboards: DashboardAcl[];
} }