updated view to use angular loader

This commit is contained in:
Peter Holmberg 2018-10-30 16:40:08 +01:00
parent 596a83407e
commit a7bd944098
9 changed files with 127 additions and 953 deletions

View File

@ -1,331 +0,0 @@
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { DataSource } from 'app/types';
import FormSwitch from '../../core/components/FormSwitch/FormSwitch';
interface Props {
dataSource: DataSource;
showAccessOption: any;
tlsAuth: any;
tlsAuthWithCACert: any;
tlsCACert: any;
tlsClientCert: any;
tlsClientKey: any;
}
interface State {
basicAuthUser: string;
basicAuthPassword: string;
showAccessHelp: boolean;
url: string;
access: string;
}
export class DataSourceHttpSettings extends PureComponent<Props, State> {
constructor(props) {
super(props);
this.state = {
url: '',
basicAuthUser: props.dataSource.basicAuthUser,
basicAuthPassword: props.dataSource.basicAuthPassword,
showAccessHelp: false,
access: '',
};
}
onToggleAccessHelp = () => {
this.setState(prevState => ({
showAccessHelp: !prevState.showAccessHelp,
}));
};
onUrlChange = event => {
this.setState({
url: event.target.value,
});
};
onAccessChange = event => {
this.setState({
access: event.target.value,
});
};
onBasicAuthChange = event => {
console.log(event);
};
onWithCredentialsChange = event => {
console.log(event);
};
onTlsAuthChange = event => {
console.log(event);
};
onTlsAuthWithCACertChange = event => {
console.log(event);
};
onTlsSkipVerifyChange = event => {
console.log(event);
};
render() {
const {
dataSource,
showAccessOption,
tlsAuth,
tlsAuthWithCACert,
tlsCACert,
tlsClientCert,
tlsClientKey,
} = this.props;
const { access, showAccessHelp, basicAuthUser, basicAuthPassword, url } = this.state;
const accessOptions = [{ key: 'proxy', value: 'Server (Default)' }, { key: 'direct', value: 'Browser' }];
return (
<div className="gf-form-group">
<h3 className="page-heading">HTTP</h3>
<div className="gf-form-group">
<div className="gf-form-inline">
<div className="gf-form max-width-30">
<span className="gf-form-label width-10">URL</span>
<input
className="gf-form-input"
type="text"
value={url}
onChange={this.onUrlChange}
placeholder="https://localhost:9090"
/>
</div>
</div>
{showAccessOption && (
<div className="gf-form-inline">
<div className="gf-form max-width-30">
<span className="gf-form-label width-10">Access</span>
<select className="width-20" value={access} onChange={this.onAccessChange}>
{accessOptions.map(option => {
return (
<option key={option.key} value={option.key}>
{option.value}
</option>
);
})}
</select>
</div>
<div className="gf-form">
<label className="gf-form-label query-keyword pointer" onClick={this.onToggleAccessHelp}>
Help&nbsp;
{showAccessHelp ? <i className="fa fa-caret-down" /> : <i className="fa fa-caret-right">&nbsp;</i>}
</label>
</div>
</div>
)}
</div>
{showAccessHelp && (
<div className="grafana-info-box m-t-2">
<p>
Access mode controls how requests to the data source will be handled.
<strong>
<i>Server</i>
</strong>{' '}
should be the preferred way if nothing else stated.
</p>
<div className="alert-title">Server access mode (Default):</div>
<p>
All requests will be made from the browser to Grafana backend/server which in turn will forward the
requests to the data source and by that circumvent possible Cross-Origin Resource Sharing (CORS)
requirements. The URL needs to be accessible from the grafana backend/server if you select this access
mode.
</p>
<div className="alert-title">Browser access mode:</div>
<p>
All requests will be made from the browser directly to the data source and may be subject to Cross-Origin
Resource Sharing (CORS) requirements. The URL needs to be accessible from the browser if you select this
access mode.
</p>
</div>
)}
{access === 'proxy' && (
<div className="gf-form-inline">
<div className="gf-form">
<span className="gf-form-label width-10">Whitelisted Cookies</span>
</div>
</div>
)}
<h3 className="page-heading">Auth</h3>
<div className="gf-form-group">
<div className="gf-form-inline">
<FormSwitch
onChange={this.onBasicAuthChange}
label="Basic auth"
checked={dataSource.basicAuth}
labelClass="width-10"
switchClass="max-width-6"
/>
<FormSwitch
label="With credentials"
checked={dataSource.withCredentials}
onChange={this.onWithCredentialsChange}
labelClass="width-10"
switchClass="max-width-6"
/>
</div>
<div className="gf-form-inline">
{dataSource.jsonData && [
<FormSwitch
key="TLS CLient Auth"
label="TLS CLient Auth"
checked={dataSource.jsonData.authType === 'tlsAuth'}
onChange={this.onTlsAuthChange}
labelClass="width-10"
switchClass="max-width-6"
/>,
<FormSwitch
key="With CA Cert"
label="With CA Cert"
checked={dataSource.jsonData.authType === 'tlsAuthWithCACert'}
onChange={this.onTlsAuthWithCACertChange}
labelClass="width-10"
switchClass="max-width-6"
/>,
]}
</div>
</div>
<div className="gf-form-inline">
{dataSource.jsonData && (
<FormSwitch
label="Skip TLS Verify"
checked={dataSource.jsonData.authType === 'tlsSkipVerify'}
onChange={this.onTlsSkipVerifyChange}
labelClass="width-10"
switchClass="max-width-6"
/>
)}
</div>
{dataSource.basicAuth && (
<div className="gf-form-group">
<h6>Basic Auth Details</h6>
<div className="gf-form">
<span className="gf-form-label width-10">User</span>
<input className="gf-form-input max-width-21" type="text" value={basicAuthUser} placeholder="User" />
</div>
<div className="gf-form">
<span className="gf-form-label width-10">Password</span>
<input
className="gf-form-input max-width-21"
type="password"
value={basicAuthPassword}
placeholder="Password"
/>
</div>
</div>
)}
{(tlsAuth || tlsAuthWithCACert) &&
access === 'proxy' && (
<div className="gf-form-group">
<div className="gf-form">
<h6>TLS Auth Details</h6>
</div>
{tlsAuthWithCACert && (
<div>
<div className="gf-form-inline">
<div className="gf-form gf-form--v-stretch">
<label className="gf-form-label width-7">CA Cert</label>
</div>
{!tlsCACert && (
<div className="gf-form gf-form--grow">
<textarea
rows={7}
className="gf-form-input gf-form-textarea"
value={tlsCACert}
placeholder="Begins with -----BEGIN CERTIFICATE-----"
/>
</div>
)}
{tlsCACert && (
<div className="gf-form">
<input type="text" className="gf-form-input max-width-12" value="configured" />
<a className="btn btn-secondary gf-form-btn" href="#" onClick={() => {}}>
reset
</a>
</div>
)}
</div>
</div>
)}
{tlsAuth && (
<div>
<div className="gf-form-inline">
<div className="gf-form gf-form--v-stretch">
<label className="gf-form-label width-7">Client Cert</label>
</div>
{!tlsClientCert && (
<div className="gf-form gf-form--grow">
<textarea
rows={7}
className="gf-form-input gf-form-textarea"
value={tlsClientCert}
placeholder="Begins with -----BEGIN CERTIFICATE-----"
required
/>
</div>
)}
{tlsClientCert && (
<div className="gf-form">
<input type="text" className="gf-form-input max-width-12" value="configured" />
<a className="btn btn-secondary gf-form-btn" href="#" onClick={() => {}}>
reset
</a>
</div>
)}
</div>
<div className="gf-form-inline">
<div className="gf-form gf-form--v-stretch">
<label className="gf-form-label width-7">Client Key</label>
</div>
{tlsClientKey && (
<div className="gf-form gf-form--grow">
<textarea
rows={7}
className="gf-form-input gf-form-textarea"
value={tlsClientKey}
placeholder="Begins with -----BEGIN RSA PRIVATE KEY-----"
/>
</div>
)}
{tlsClientKey && (
<div className="gf-form">
<input type="text" className="gf-form-input max-width-12" value="configured" />
<a className="btn btn-secondary gf-form-btn" href="#" onClick={() => {}}>
reset
</a>
</div>
)}
</div>
</div>
)}
</div>
)}
</div>
);
}
}
function mapStateToProps(state) {
return {
dataSource: state.dataSources.dataSource,
};
}
export default connect(mapStateToProps)(DataSourceHttpSettings);

View File

@ -0,0 +1,40 @@
import React, { PureComponent } from 'react';
import { DataSource, Plugin } from 'app/types';
import { getAngularLoader, AngularComponent } from 'app/core/services/AngularLoader';
import { importPluginModule } from '../plugins/plugin_loader';
interface Props {
dataSource: DataSource;
dataSourceMeta: Plugin;
}
export class DataSourcePluginSettings extends PureComponent<Props> {
element: any;
component: AngularComponent;
componentDidMount() {
if (!this.element) {
return;
}
importPluginModule(this.props.dataSourceMeta.module).then(pluginExports => {
console.log(pluginExports);
});
const loader = getAngularLoader();
const template = '<plugin-component type="datasource-config-ctrl" />';
const scopeProps = {
ctrl: {
dataSourceMeta: this.props.dataSourceMeta,
current: this.props.dataSource,
},
};
this.component = loader.load(this.element, scopeProps, template);
}
render() {
return <div ref={element => (this.element = element)} />;
}
}
export default DataSourcePluginSettings;

View File

@ -1,11 +1,15 @@
import React, { createRef, PureComponent } from 'react';
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import { DataSource, NavModel, Plugin } from 'app/types';
import PageHeader from '../../core/components/PageHeader/PageHeader';
import { importPluginModule } from '../plugins/plugin_loader';
import { loadDataSource } from './state/actions';
import DataSourcePluginSettings from './DataSourcePluginSettings';
import { loadDataSource, setDataSourceName } from './state/actions';
import { getNavModel } from '../../core/selectors/navModel';
import { getRouteParamsId } from '../../core/selectors/location';
import { Label } from '../../core/components/Forms/Forms';
import PageLoader from '../../core/components/PageLoader/PageLoader';
import { getDataSource } from './state/selectors';
export interface Props {
navModel: NavModel;
@ -13,6 +17,7 @@ export interface Props {
dataSourceMeta: Plugin;
pageId: number;
loadDataSource: typeof loadDataSource;
setDataSourceName: typeof setDataSourceName;
}
interface State {
name: string;
@ -25,28 +30,12 @@ enum DataSourceStates {
}
export class DataSourceSettings extends PureComponent<Props, State> {
settingsElement = createRef<HTMLDivElement>();
state = {
name: this.props.dataSource.name,
showNamePopover: false,
};
async componentDidMount() {
const { loadDataSource, pageId } = this.props;
await loadDataSource(pageId);
importPluginModule(this.props.dataSourceMeta.module).then(pluginExports => {
console.log(pluginExports);
});
}
onNameChange = event => {
this.setState({
name: event.target.value,
});
};
onSubmit = event => {
event.preventDefault();
console.log(event);
@ -56,12 +45,6 @@ export class DataSourceSettings extends PureComponent<Props, State> {
console.log(event);
};
onTogglePopover = () => {
this.setState(prevState => ({
showNamePopover: !prevState.showNamePopover,
}));
};
isReadyOnly() {
return this.props.dataSource.readOnly === true;
}
@ -93,74 +76,74 @@ export class DataSourceSettings extends PureComponent<Props, State> {
}
render() {
const { name, showNamePopover } = this.state;
console.log(this.props);
const { dataSource, dataSourceMeta, navModel } = this.props;
return (
<div>
<PageHeader model={this.props.navModel} />
<div className="page-container page-body">
<div>
<form onSubmit={this.onSubmit}>
<div className="gf-form-group">
<div className="gf-form-inline">
<PageHeader model={navModel} />
{Object.keys(dataSource).length === 0 && Object.keys(dataSourceMeta).length === 0 ? (
<PageLoader pageName="Data source settings" />
) : (
<div className="page-container page-body">
<div>
<form onSubmit={this.onSubmit}>
<div className="gf-form-group">
<div className="gf-form max-width-30">
<span className="gf-form-label width-10">Name</span>
<Label
tooltip={
'The name is used when you select the data source in panels. The Default data source is' +
'preselected in new panels.'
}
>
Name
</Label>
<input
className="gf-form-input max-width-23"
type="text"
value={name}
value={this.props.dataSource.name}
placeholder="Name"
onChange={this.onNameChange}
onChange={event => this.props.setDataSourceName(event.target.value)}
required
/>
<div onClick={this.onTogglePopover}>
<i className="fa fa-info-circle" />
</div>
{showNamePopover && (
<div
style={{
position: 'absolute',
left: '450px',
top: '-20px',
padding: '10px',
backgroundColor: 'black',
zIndex: 2,
width: '300px',
border: '1px solid gray',
borderRadius: '3px',
}}
>
The name is used when you select the data source in panels. The <em>Default</em> data source is
preselected in new panels.
</div>
)}
</div>
</div>
</div>
{this.shouldRenderInfoBox() && <div className="grafana-info-box">{this.getInfoText()}</div>}
{this.isReadyOnly() && (
<div className="grafana-info-box span8">
This datasource was added by config and cannot be modified using the UI. Please contact your server
admin to update this datasource.
{this.shouldRenderInfoBox() && <div className="grafana-info-box">{this.getInfoText()}</div>}
{this.isReadyOnly() ? (
<div className="grafana-info-box span8">
This datasource was added by config and cannot be modified using the UI. Please contact your server
admin to update this datasource.
</div>
) : (
<DataSourcePluginSettings dataSource={dataSource} dataSourceMeta={dataSourceMeta} />
)}
<div className="gf-form-button-row">
<button
type="submit"
className="btn btn-success"
disabled={this.isReadyOnly()}
onClick={this.onSubmit}
>
Save &amp; Test
</button>
<button
type="submit"
className="btn btn-danger"
disabled={this.isReadyOnly()}
onClick={this.onDelete}
>
Delete
</button>
<a className="btn btn-inverse" href="/datasources">
Back
</a>
</div>
)}
<div ref={this.settingsElement} />
<div className="gf-form-button-row">
<button type="submit" className="btn btn-success" disabled={this.isReadyOnly()} onClick={this.onSubmit}>
Save &amp; Test
</button>
<button type="submit" className="btn btn-danger" disabled={this.isReadyOnly()} onClick={this.onDelete}>
Delete
</button>
<a className="btn btn-inverse" href="datasources">
Back
</a>
</div>
</form>
</form>
</div>
</div>
</div>
)}
</div>
);
}
@ -171,7 +154,7 @@ function mapStateToProps(state) {
return {
navModel: getNavModel(state.navIndex, `datasource-settings-${pageId}`),
dataSource: state.dataSources.dataSource,
dataSource: getDataSource(state.dataSources, pageId),
dataSourceMeta: state.dataSources.dataSourceMeta,
pageId: pageId,
};
@ -179,6 +162,7 @@ function mapStateToProps(state) {
const mapDispatchToProps = {
loadDataSource,
setDataSourceName,
};
export default connect(mapStateToProps, mapDispatchToProps)(DataSourceSettings);
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DataSourceSettings));

View File

@ -1,177 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render should render component 1`] = `
<div
className="gf-form-inline cta-form"
>
<button
className="cta-form__close btn btn-transparent"
onClick={[MockFunction]}
>
<i
className="fa fa-close"
/>
</button>
<form
name="addPermission"
onSubmit={[Function]}
>
<h5>
Add Permission For
</h5>
<div
className="gf-form-inline"
>
<div
className="gf-form"
>
<select
className="gf-form-input gf-size-auto"
onChange={[Function]}
value="Team"
>
<option
key="0"
value="Team"
>
Team
</option>
<option
key="1"
value="User"
>
User
</option>
</select>
</div>
<div
className="gf-form"
>
<TeamPicker
className="width-20"
onSelected={[Function]}
/>
</div>
<div
className="gf-form"
>
<DescriptionPicker
className="gf-form-input--form-dropdown-right"
disabled={false}
onSelected={[Function]}
optionsWithDesc={
Array [
Object {
"description": "Can query data source.",
"label": "Query",
"value": 1,
},
]
}
value={1}
/>
</div>
<div
className="gf-form"
>
<button
className="btn btn-success"
data-save-permission={true}
disabled={true}
type="submit"
>
Save
</button>
</div>
</div>
</form>
</div>
`;
exports[`Render should render user picker 1`] = `
<div
className="gf-form-inline cta-form"
>
<button
className="cta-form__close btn btn-transparent"
onClick={[MockFunction]}
>
<i
className="fa fa-close"
/>
</button>
<form
name="addPermission"
onSubmit={[Function]}
>
<h5>
Add Permission For
</h5>
<div
className="gf-form-inline"
>
<div
className="gf-form"
>
<select
className="gf-form-input gf-size-auto"
onChange={[Function]}
value="User"
>
<option
key="0"
value="Team"
>
Team
</option>
<option
key="1"
value="User"
>
User
</option>
</select>
</div>
<div
className="gf-form"
>
<UserPicker
className="width-20"
onSelected={[Function]}
/>
</div>
<div
className="gf-form"
>
<DescriptionPicker
className="gf-form-input--form-dropdown-right"
disabled={false}
onSelected={[Function]}
optionsWithDesc={
Array [
Object {
"description": "Can query data source.",
"label": "Query",
"value": 1,
},
]
}
value={1}
/>
</div>
<div
className="gf-form"
>
<button
className="btn btn-success"
data-save-permission={true}
disabled={true}
type="submit"
>
Save
</button>
</div>
</div>
</form>
</div>
`;

View File

@ -1,327 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render should render component 1`] = `
<table
className="filter-table gf-form-group"
>
<tbody>
<tr
className="gf-form-disabled"
>
<td
style={
Object {
"width": "1%",
}
}
>
<i
className="gicon gicon-shield"
style={
Object {
"height": "25px",
"width": "25px",
}
}
/>
</td>
<td
style={
Object {
"width": "90%",
}
}
>
Admin
<span
className="filter-table__weak-italic"
>
(Role)
</span>
</td>
<td />
<td
className="query-keyword"
>
Can
</td>
<td>
<div
className="gf-form"
>
<DescriptionPicker
className="gf-form-input--form-dropdown-right"
disabled={true}
onSelected={[Function]}
optionsWithDesc={
Array [
Object {
"description": "Can query data source.",
"label": "Query",
"value": 1,
},
Object {
"description": "",
"label": "Admin",
"value": 2,
},
]
}
value={2}
/>
</div>
</td>
<td>
<button
className="btn btn-inverse btn-small"
>
<i
className="fa fa-lock"
/>
</button>
</td>
</tr>
</tbody>
</table>
`;
exports[`Render should render items 1`] = `
<table
className="filter-table gf-form-group"
>
<tbody>
<tr
className="gf-form-disabled"
>
<td
style={
Object {
"width": "1%",
}
}
>
<i
className="gicon gicon-shield"
style={
Object {
"height": "25px",
"width": "25px",
}
}
/>
</td>
<td
style={
Object {
"width": "90%",
}
}
>
Admin
<span
className="filter-table__weak-italic"
>
(Role)
</span>
</td>
<td />
<td
className="query-keyword"
>
Can
</td>
<td>
<div
className="gf-form"
>
<DescriptionPicker
className="gf-form-input--form-dropdown-right"
disabled={true}
onSelected={[Function]}
optionsWithDesc={
Array [
Object {
"description": "Can query data source.",
"label": "Query",
"value": 1,
},
Object {
"description": "",
"label": "Admin",
"value": 2,
},
]
}
value={2}
/>
</div>
</td>
<td>
<button
className="btn btn-inverse btn-small"
>
<i
className="fa fa-lock"
/>
</button>
</td>
</tr>
<tr
key="2-0"
>
<td
style={
Object {
"width": "1%",
}
}
>
<img
className="filter-table__avatar"
src="/avatar/926aa85c6bcefa0b4deca3223f337ae1"
/>
</td>
<td
style={
Object {
"width": "90%",
}
}
>
<span
key="name"
>
testUser
</span>
<span
className="filter-table__weak-italic"
key="description"
>
(User)
</span>
</td>
<td />
<td
className="query-keyword"
>
Can
</td>
<td>
<div
className="gf-form"
>
<DescriptionPicker
className="gf-form-input--form-dropdown-right"
disabled={true}
onSelected={[Function]}
optionsWithDesc={
Array [
Object {
"description": "Can query data source.",
"label": "Query",
"value": 1,
},
Object {
"description": "",
"label": "Admin",
"value": 2,
},
]
}
value={1}
/>
</div>
</td>
<td>
<button
className="btn btn-danger btn-small"
onClick={[Function]}
>
<i
className="fa fa-remove"
/>
</button>
</td>
</tr>
<tr
key="6-1"
>
<td
style={
Object {
"width": "1%",
}
}
>
<img
className="filter-table__avatar"
src="/avatar/93c0801b955cbd443a8cfa91a401d7bc"
/>
</td>
<td
style={
Object {
"width": "90%",
}
}
>
<span
key="name"
>
A-team
</span>
<span
className="filter-table__weak-italic"
key="description"
>
(Team)
</span>
</td>
<td />
<td
className="query-keyword"
>
Can
</td>
<td>
<div
className="gf-form"
>
<DescriptionPicker
className="gf-form-input--form-dropdown-right"
disabled={true}
onSelected={[Function]}
optionsWithDesc={
Array [
Object {
"description": "Can query data source.",
"label": "Query",
"value": 1,
},
Object {
"description": "",
"label": "Admin",
"value": 2,
},
]
}
value={1}
/>
</div>
</td>
<td>
<button
className="btn btn-danger btn-small"
onClick={[Function]}
>
<i
className="fa fa-remove"
/>
</button>
</td>
</tr>
</tbody>
</table>
`;

View File

@ -1,27 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render should render component 1`] = `
<div>
<PageHeader
model={Object {}}
/>
<div
className="page-container page-body"
>
<Connect(DataSourceSettings) />
</div>
</div>
`;
exports[`Render should render permissions page 1`] = `
<div>
<PageHeader
model={Object {}}
/>
<div
className="page-container page-body"
>
<Connect(DataSourcePermissions) />
</div>
</div>
`;

View File

@ -14,43 +14,49 @@ export enum ActionTypes {
SetDataSourcesSearchQuery = 'SET_DATA_SOURCES_SEARCH_QUERY',
SetDataSourcesLayoutMode = 'SET_DATA_SOURCES_LAYOUT_MODE',
SetDataSourceTypeSearchQuery = 'SET_DATA_SOURCE_TYPE_SEARCH_QUERY',
SetDataSourceName = 'SET_DATA_SOURCE_NAME',
}
export interface LoadDataSourcesAction {
interface LoadDataSourcesAction {
type: ActionTypes.LoadDataSources;
payload: DataSource[];
}
export interface SetDataSourcesSearchQueryAction {
interface SetDataSourcesSearchQueryAction {
type: ActionTypes.SetDataSourcesSearchQuery;
payload: string;
}
export interface SetDataSourcesLayoutModeAction {
interface SetDataSourcesLayoutModeAction {
type: ActionTypes.SetDataSourcesLayoutMode;
payload: LayoutMode;
}
export interface LoadDataSourceTypesAction {
interface LoadDataSourceTypesAction {
type: ActionTypes.LoadDataSourceTypes;
payload: Plugin[];
}
export interface SetDataSourceTypeSearchQueryAction {
interface SetDataSourceTypeSearchQueryAction {
type: ActionTypes.SetDataSourceTypeSearchQuery;
payload: string;
}
export interface LoadDataSourceAction {
interface LoadDataSourceAction {
type: ActionTypes.LoadDataSource;
payload: DataSource;
}
export interface LoadDataSourceMetaAction {
interface LoadDataSourceMetaAction {
type: ActionTypes.LoadDataSourceMeta;
payload: Plugin;
}
interface SetDataSourceNameAction {
type: ActionTypes.SetDataSourceName;
payload: string;
}
const dataSourcesLoaded = (dataSources: DataSource[]): LoadDataSourcesAction => ({
type: ActionTypes.LoadDataSources,
payload: dataSources,
@ -86,6 +92,11 @@ export const setDataSourceTypeSearchQuery = (query: string): SetDataSourceTypeSe
payload: query,
});
export const setDataSourceName = (name: string) => ({
type: ActionTypes.SetDataSourceName,
payload: name,
});
export type Action =
| LoadDataSourcesAction
| SetDataSourcesSearchQueryAction
@ -95,7 +106,8 @@ export type Action =
| SetDataSourceTypeSearchQueryAction
| LoadDataSourceAction
| UpdateNavIndexAction
| LoadDataSourceMetaAction;
| LoadDataSourceMetaAction
| SetDataSourceNameAction;
type ThunkResult<R> = ThunkAction<R, StoreState, undefined, Action>;

View File

@ -20,7 +20,7 @@ export const getDataSource = (state, dataSourceId): DataSource | null => {
if (state.dataSource.id === parseInt(dataSourceId, 10)) {
return state.dataSource;
}
return null;
return {} as DataSource;
};
export const getDataSourcesSearchQuery = state => state.searchQuery;

View File

@ -47,7 +47,7 @@ module.exports = merge(common, {
module: {
rules: [
{
test: /\.tsx?$/,
test: /(?!.*\.test)\.tsx$/,
exclude: /node_modules/,
use: [{
loader: 'babel-loader',