From 166f93cf5445dabb67534ce1e6b72504093fa1ab Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Fri, 28 Sep 2018 11:05:34 +0200 Subject: [PATCH 1/3] components, test, removed old not used files --- .../datasources/DataSourceList.test.tsx | 22 +++ .../datasources/DataSourcesActionBar.test.tsx | 23 +++ .../datasources/DataSourcesActionBar.tsx | 62 +++++++ .../features/datasources/DataSourcesList.tsx | 32 ++++ .../datasources/DataSourcesListItem.test.tsx | 20 +++ .../datasources/DataSourcesListItem.tsx | 34 ++++ .../datasources/DataSourcesListPage.test.tsx | 35 ++++ .../datasources/DataSourcesListPage.tsx | 72 +++++++++ .../datasources/__mocks__/dataSourcesMocks.ts | 45 ++++++ .../DataSourceList.test.tsx.snap | 108 +++++++++++++ .../DataSourcesActionBar.test.tsx.snap | 42 +++++ .../DataSourcesListItem.test.tsx.snap | 45 ++++++ .../DataSourcesListPage.test.tsx.snap | 152 ++++++++++++++++++ .../app/features/datasources/state/actions.ts | 51 ++++++ .../features/datasources/state/reducers.ts | 28 ++++ .../features/datasources/state/selectors.ts | 10 ++ public/app/features/plugins/all.ts | 1 - public/app/features/plugins/ds_list_ctrl.ts | 61 ------- .../features/plugins/partials/ds_list.html | 63 -------- public/app/routes/routes.ts | 8 +- public/app/store/configureStore.ts | 2 + public/app/types/datasources.ts | 17 ++ public/app/types/index.ts | 3 +- 23 files changed, 807 insertions(+), 129 deletions(-) create mode 100644 public/app/features/datasources/DataSourceList.test.tsx create mode 100644 public/app/features/datasources/DataSourcesActionBar.test.tsx create mode 100644 public/app/features/datasources/DataSourcesActionBar.tsx create mode 100644 public/app/features/datasources/DataSourcesList.tsx create mode 100644 public/app/features/datasources/DataSourcesListItem.test.tsx create mode 100644 public/app/features/datasources/DataSourcesListItem.tsx create mode 100644 public/app/features/datasources/DataSourcesListPage.test.tsx create mode 100644 public/app/features/datasources/DataSourcesListPage.tsx create mode 100644 public/app/features/datasources/__mocks__/dataSourcesMocks.ts create mode 100644 public/app/features/datasources/__snapshots__/DataSourceList.test.tsx.snap create mode 100644 public/app/features/datasources/__snapshots__/DataSourcesActionBar.test.tsx.snap create mode 100644 public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap create mode 100644 public/app/features/datasources/__snapshots__/DataSourcesListPage.test.tsx.snap create mode 100644 public/app/features/datasources/state/actions.ts create mode 100644 public/app/features/datasources/state/reducers.ts create mode 100644 public/app/features/datasources/state/selectors.ts delete mode 100644 public/app/features/plugins/ds_list_ctrl.ts delete mode 100644 public/app/features/plugins/partials/ds_list.html diff --git a/public/app/features/datasources/DataSourceList.test.tsx b/public/app/features/datasources/DataSourceList.test.tsx new file mode 100644 index 00000000000..6e097da2c53 --- /dev/null +++ b/public/app/features/datasources/DataSourceList.test.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import DataSourcesList from './DataSourcesList'; +import { getMockDataSources } from './__mocks__/dataSourcesMocks'; +import { LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector'; + +const setup = () => { + const props = { + dataSources: getMockDataSources(3), + layoutMode: LayoutModes.Grid, + }; + + return shallow(); +}; + +describe('Render', () => { + it('should render component', () => { + const wrapper = setup(); + + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/public/app/features/datasources/DataSourcesActionBar.test.tsx b/public/app/features/datasources/DataSourcesActionBar.test.tsx new file mode 100644 index 00000000000..8337271271e --- /dev/null +++ b/public/app/features/datasources/DataSourcesActionBar.test.tsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { DataSourcesActionBar, Props } from './DataSourcesActionBar'; +import { LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector'; + +const setup = (propOverrides?: object) => { + const props: Props = { + layoutMode: LayoutModes.Grid, + searchQuery: '', + setDataSourcesLayoutMode: jest.fn(), + setDataSourcesSearchQuery: jest.fn(), + }; + + return shallow(); +}; + +describe('Render', () => { + it('should render component', () => { + const wrapper = setup(); + + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/public/app/features/datasources/DataSourcesActionBar.tsx b/public/app/features/datasources/DataSourcesActionBar.tsx new file mode 100644 index 00000000000..d28089b1f21 --- /dev/null +++ b/public/app/features/datasources/DataSourcesActionBar.tsx @@ -0,0 +1,62 @@ +import React, { PureComponent } from 'react'; +import { connect } from 'react-redux'; +import LayoutSelector, { LayoutMode } from '../../core/components/LayoutSelector/LayoutSelector'; +import { setDataSourcesLayoutMode, setDataSourcesSearchQuery } from './state/actions'; +import { getDataSourcesLayoutMode, getDataSourcesSearchQuery } from './state/selectors'; + +export interface Props { + searchQuery: string; + layoutMode: LayoutMode; + setDataSourcesLayoutMode: typeof setDataSourcesLayoutMode; + setDataSourcesSearchQuery: typeof setDataSourcesSearchQuery; +} + +export class DataSourcesActionBar extends PureComponent { + onSearchQueryChange = event => { + this.props.setDataSourcesSearchQuery(event.target.value); + }; + + render() { + const { searchQuery, layoutMode, setDataSourcesLayoutMode } = this.props; + + return ( +
+
+ + setDataSourcesLayoutMode(mode)} + /> +
+ + ); + } +} + +function mapStateToProps(state) { + return { + searchQuery: getDataSourcesSearchQuery(state.dataSources), + layoutMode: getDataSourcesLayoutMode(state.dataSources), + }; +} + +const mapDispatchToProps = { + setDataSourcesLayoutMode, + setDataSourcesSearchQuery, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(DataSourcesActionBar); diff --git a/public/app/features/datasources/DataSourcesList.tsx b/public/app/features/datasources/DataSourcesList.tsx new file mode 100644 index 00000000000..4ed2203bf29 --- /dev/null +++ b/public/app/features/datasources/DataSourcesList.tsx @@ -0,0 +1,32 @@ +import React, { SFC } from 'react'; +import classNames from 'classnames/bind'; +import DataSourcesListItem from './DataSourcesListItem'; +import { DataSource } from 'app/types'; +import { LayoutMode, LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector'; + +export interface Props { + dataSources: DataSource[]; + layoutMode: LayoutMode; +} + +const DataSourcesList: SFC = props => { + const { dataSources, layoutMode } = props; + + const listStyle = classNames({ + 'card-section': true, + 'card-list-layout-grid': layoutMode === LayoutModes.Grid, + 'card-list-layout-list': layoutMode === LayoutModes.List, + }); + + return ( +
+
    + {dataSources.map((dataSource, index) => { + return ; + })} +
+
+ ); +}; + +export default DataSourcesList; diff --git a/public/app/features/datasources/DataSourcesListItem.test.tsx b/public/app/features/datasources/DataSourcesListItem.test.tsx new file mode 100644 index 00000000000..138c71cb46a --- /dev/null +++ b/public/app/features/datasources/DataSourcesListItem.test.tsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import DataSourcesListItem from './DataSourcesListItem'; +import { getMockDataSource } from './__mocks__/dataSourcesMocks'; + +const setup = () => { + const props = { + dataSource: getMockDataSource(), + }; + + return shallow(); +}; + +describe('Render', () => { + it('should render component', () => { + const wrapper = setup(); + + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/public/app/features/datasources/DataSourcesListItem.tsx b/public/app/features/datasources/DataSourcesListItem.tsx new file mode 100644 index 00000000000..1bb612a299a --- /dev/null +++ b/public/app/features/datasources/DataSourcesListItem.tsx @@ -0,0 +1,34 @@ +import React, { SFC } from 'react'; +import { DataSource } from 'app/types'; + +export interface Props { + dataSource: DataSource; +} + +const DataSourcesListItem: SFC = props => { + const { dataSource } = props; + + return ( +
  • + +
    +
    {dataSource.type}
    +
    +
    +
    + +
    +
    +
    + {dataSource.name} + {dataSource.isDefault && default} +
    +
    {dataSource.url}
    +
    +
    +
    +
  • + ); +}; + +export default DataSourcesListItem; diff --git a/public/app/features/datasources/DataSourcesListPage.test.tsx b/public/app/features/datasources/DataSourcesListPage.test.tsx new file mode 100644 index 00000000000..2cb6652ee13 --- /dev/null +++ b/public/app/features/datasources/DataSourcesListPage.test.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import { DataSourcesListPage, Props } from './DataSourcesListPage'; +import { DataSource, NavModel } from 'app/types'; +import { LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector'; +import { getMockDataSources } from './__mocks__/dataSourcesMocks'; + +const setup = (propOverrides?: object) => { + const props: Props = { + dataSources: [] as DataSource[], + layoutMode: LayoutModes.Grid, + loadDataSources: jest.fn(), + navModel: {} as NavModel, + }; + + Object.assign(props, propOverrides); + + return shallow(); +}; + +describe('Render', () => { + it('should render component', () => { + const wrapper = setup(); + + expect(wrapper).toMatchSnapshot(); + }); + + it('should render action bar and datasources', () => { + const wrapper = setup({ + dataSources: getMockDataSources(5), + }); + + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/public/app/features/datasources/DataSourcesListPage.tsx b/public/app/features/datasources/DataSourcesListPage.tsx new file mode 100644 index 00000000000..a6ce1be7be9 --- /dev/null +++ b/public/app/features/datasources/DataSourcesListPage.tsx @@ -0,0 +1,72 @@ +import React, { PureComponent } from 'react'; +import { connect } from 'react-redux'; +import { hot } from 'react-hot-loader'; +import PageHeader from '../../core/components/PageHeader/PageHeader'; +import DataSourcesActionBar from './DataSourcesActionBar'; +import DataSourcesList from './DataSourcesList'; +import { loadDataSources } from './state/actions'; +import { getDataSources, getDataSourcesLayoutMode } from './state/selectors'; +import { getNavModel } from '../../core/selectors/navModel'; +import { DataSource, NavModel } from 'app/types'; +import { LayoutMode } from '../../core/components/LayoutSelector/LayoutSelector'; +import EmptyListCTA from '../../core/components/EmptyListCTA/EmptyListCTA'; + +export interface Props { + navModel: NavModel; + dataSources: DataSource[]; + layoutMode: LayoutMode; + loadDataSources: typeof loadDataSources; +} + +const emptyListModel = { + title: 'There are no data sources defined yet', + buttonIcon: 'gicon gicon-add-datasources', + buttonLink: 'datasources/new', + buttonTitle: 'Add data source', + proTip: 'You can also define data sources through configuration files.', + proTipLink: 'http://docs.grafana.org/administration/provisioning/#datasources?utm_source=grafana_ds_list', + proTipLinkTitle: 'Learn more', + proTipTarget: '_blank', +}; + +export class DataSourcesListPage extends PureComponent { + componentDidMount() { + this.fetchDataSources(); + } + + async fetchDataSources() { + return await this.props.loadDataSources(); + } + + render() { + const { navModel, dataSources, layoutMode } = this.props; + + if (dataSources.length === 0) { + return ; + } + + return ( +
    + +
    + + +
    +
    + ); + } +} + +function mapStateToProps(state) { + return { + navModel: getNavModel(state.navIndex, 'datasources'), + dataSources: getDataSources(state.dataSources), + layoutMode: getDataSourcesLayoutMode(state.dataSources), + }; +} + +const mapDispatchToProps = { + loadDataSources, +}; + +export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DataSourcesListPage)); diff --git a/public/app/features/datasources/__mocks__/dataSourcesMocks.ts b/public/app/features/datasources/__mocks__/dataSourcesMocks.ts new file mode 100644 index 00000000000..97819a18c82 --- /dev/null +++ b/public/app/features/datasources/__mocks__/dataSourcesMocks.ts @@ -0,0 +1,45 @@ +import { DataSource } from 'app/types'; + +export const getMockDataSources = (amount: number): DataSource[] => { + const dataSources = []; + + for (let i = 0; i <= amount; i++) { + dataSources.push({ + access: '', + basicAuth: false, + database: `database-${i}`, + id: i, + isDefault: false, + jsonData: { authType: 'credentials', defaultRegion: 'eu-west-2' }, + name: `dataSource-${i}`, + orgId: 1, + password: '', + readOnly: false, + type: 'cloudwatch', + typeLogoUrl: 'public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png', + url: '', + user: '', + }); + } + + return dataSources; +}; + +export const getMockDataSource = (): DataSource => { + return { + access: '', + basicAuth: false, + database: '', + id: 13, + isDefault: false, + jsonData: { authType: 'credentials', defaultRegion: 'eu-west-2' }, + name: 'gdev-cloudwatch', + orgId: 1, + password: '', + readOnly: false, + type: 'cloudwatch', + typeLogoUrl: 'public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png', + url: '', + user: '', + }; +}; diff --git a/public/app/features/datasources/__snapshots__/DataSourceList.test.tsx.snap b/public/app/features/datasources/__snapshots__/DataSourceList.test.tsx.snap new file mode 100644 index 00000000000..7167f59b048 --- /dev/null +++ b/public/app/features/datasources/__snapshots__/DataSourceList.test.tsx.snap @@ -0,0 +1,108 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render should render component 1`] = ` +
    +
      + + + + +
    +
    +`; diff --git a/public/app/features/datasources/__snapshots__/DataSourcesActionBar.test.tsx.snap b/public/app/features/datasources/__snapshots__/DataSourcesActionBar.test.tsx.snap new file mode 100644 index 00000000000..24f9f2126d0 --- /dev/null +++ b/public/app/features/datasources/__snapshots__/DataSourcesActionBar.test.tsx.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render should render component 1`] = ` +
    +
    + + +
    + +`; diff --git a/public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap b/public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap new file mode 100644 index 00000000000..a424276cf32 --- /dev/null +++ b/public/app/features/datasources/__snapshots__/DataSourcesListItem.test.tsx.snap @@ -0,0 +1,45 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render should render component 1`] = ` +
  • + +
    +
    + cloudwatch +
    +
    +
  • +`; diff --git a/public/app/features/datasources/__snapshots__/DataSourcesListPage.test.tsx.snap b/public/app/features/datasources/__snapshots__/DataSourcesListPage.test.tsx.snap new file mode 100644 index 00000000000..837a8aceb24 --- /dev/null +++ b/public/app/features/datasources/__snapshots__/DataSourcesListPage.test.tsx.snap @@ -0,0 +1,152 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render should render action bar and datasources 1`] = ` +
    + +
    + + +
    +
    +`; + +exports[`Render should render component 1`] = ` + +`; diff --git a/public/app/features/datasources/state/actions.ts b/public/app/features/datasources/state/actions.ts new file mode 100644 index 00000000000..297797f2e59 --- /dev/null +++ b/public/app/features/datasources/state/actions.ts @@ -0,0 +1,51 @@ +import { ThunkAction } from 'redux-thunk'; +import { DataSource, StoreState } from 'app/types'; +import { getBackendSrv } from '../../../core/services/backend_srv'; +import { LayoutMode } from '../../../core/components/LayoutSelector/LayoutSelector'; + +export enum ActionTypes { + LoadDataSources = 'LOAD_DATA_SOURCES', + SetDataSourcesSearchQuery = 'SET_DATA_SOURCES_SEARCH_QUERY', + SetDataSourcesLayoutMode = 'SET_DATA_SOURCES_LAYOUT_MODE', +} + +export interface LoadDataSourcesAction { + type: ActionTypes.LoadDataSources; + payload: DataSource[]; +} + +export interface SetDataSourcesSearchQueryAction { + type: ActionTypes.SetDataSourcesSearchQuery; + payload: string; +} + +export interface SetDataSourcesLayoutModeAction { + type: ActionTypes.SetDataSourcesLayoutMode; + payload: LayoutMode; +} + +const dataSourcesLoaded = (dataSources: DataSource[]): LoadDataSourcesAction => ({ + type: ActionTypes.LoadDataSources, + payload: dataSources, +}); + +export const setDataSourcesSearchQuery = (searchQuery: string): SetDataSourcesSearchQueryAction => ({ + type: ActionTypes.SetDataSourcesSearchQuery, + payload: searchQuery, +}); + +export const setDataSourcesLayoutMode = (layoutMode: LayoutMode): SetDataSourcesLayoutModeAction => ({ + type: ActionTypes.SetDataSourcesLayoutMode, + payload: layoutMode, +}); + +export type Action = LoadDataSourcesAction | SetDataSourcesSearchQueryAction | SetDataSourcesLayoutModeAction; + +type ThunkResult = ThunkAction; + +export function loadDataSources(): ThunkResult { + return async dispatch => { + const response = await getBackendSrv().get('/api/datasources'); + dispatch(dataSourcesLoaded(response)); + }; +} diff --git a/public/app/features/datasources/state/reducers.ts b/public/app/features/datasources/state/reducers.ts new file mode 100644 index 00000000000..15604fa8b53 --- /dev/null +++ b/public/app/features/datasources/state/reducers.ts @@ -0,0 +1,28 @@ +import { DataSource, DataSourcesState } from 'app/types'; +import { Action, ActionTypes } from './actions'; +import { LayoutModes } from '../../../core/components/LayoutSelector/LayoutSelector'; + +const initialState: DataSourcesState = { + dataSources: [] as DataSource[], + layoutMode: LayoutModes.Grid, + searchQuery: '', +}; + +export const dataSourcesReducer = (state = initialState, action: Action): DataSourcesState => { + switch (action.type) { + case ActionTypes.LoadDataSources: + return { ...state, dataSources: action.payload }; + + case ActionTypes.SetDataSourcesSearchQuery: + return { ...state, searchQuery: action.payload }; + + case ActionTypes.SetDataSourcesLayoutMode: + return { ...state, layoutMode: action.payload }; + } + + return state; +}; + +export default { + dataSources: dataSourcesReducer, +}; diff --git a/public/app/features/datasources/state/selectors.ts b/public/app/features/datasources/state/selectors.ts new file mode 100644 index 00000000000..15ee88e715a --- /dev/null +++ b/public/app/features/datasources/state/selectors.ts @@ -0,0 +1,10 @@ +export const getDataSources = state => { + const regex = new RegExp(state.searchQuery, 'i'); + + return state.dataSources.filter(dataSource => { + return regex.test(dataSource.name) || regex.test(dataSource.database); + }); +}; + +export const getDataSourcesSearchQuery = state => state.searchQuery; +export const getDataSourcesLayoutMode = state => state.layoutMode; diff --git a/public/app/features/plugins/all.ts b/public/app/features/plugins/all.ts index 5be7593f68d..d164a6d4255 100644 --- a/public/app/features/plugins/all.ts +++ b/public/app/features/plugins/all.ts @@ -3,6 +3,5 @@ import './plugin_page_ctrl'; import './import_list/import_list'; import './ds_edit_ctrl'; import './ds_dashboards_ctrl'; -import './ds_list_ctrl'; import './datasource_srv'; import './plugin_component'; diff --git a/public/app/features/plugins/ds_list_ctrl.ts b/public/app/features/plugins/ds_list_ctrl.ts deleted file mode 100644 index 71c1a516842..00000000000 --- a/public/app/features/plugins/ds_list_ctrl.ts +++ /dev/null @@ -1,61 +0,0 @@ -import coreModule from '../../core/core_module'; -import _ from 'lodash'; - -export class DataSourcesCtrl { - datasources: any; - unfiltered: any; - navModel: any; - searchQuery: string; - - /** @ngInject */ - constructor(private $scope, private backendSrv, private datasourceSrv, private navModelSrv) { - this.navModel = this.navModelSrv.getNav('cfg', 'datasources', 0); - backendSrv.get('/api/datasources').then(result => { - this.datasources = result; - this.unfiltered = result; - }); - } - - onQueryUpdated() { - const regex = new RegExp(this.searchQuery, 'ig'); - this.datasources = _.filter(this.unfiltered, item => { - regex.lastIndex = 0; - return regex.test(item.name) || regex.test(item.type); - }); - } - - removeDataSourceConfirmed(ds) { - this.backendSrv - .delete('/api/datasources/' + ds.id) - .then( - () => { - this.$scope.appEvent('alert-success', ['Datasource deleted', '']); - }, - () => { - this.$scope.appEvent('alert-error', ['Unable to delete datasource', '']); - } - ) - .then(() => { - this.backendSrv.get('/api/datasources').then(result => { - this.datasources = result; - }); - this.backendSrv.get('/api/frontend/settings').then(settings => { - this.datasourceSrv.init(settings.datasources); - }); - }); - } - - removeDataSource(ds) { - this.$scope.appEvent('confirm-modal', { - title: 'Delete', - text: 'Are you sure you want to delete datasource ' + ds.name + '?', - yesText: 'Delete', - icon: 'fa-trash', - onConfirm: () => { - this.removeDataSourceConfirmed(ds); - }, - }); - } -} - -coreModule.controller('DataSourcesCtrl', DataSourcesCtrl); diff --git a/public/app/features/plugins/partials/ds_list.html b/public/app/features/plugins/partials/ds_list.html deleted file mode 100644 index fd537fc47d4..00000000000 --- a/public/app/features/plugins/partials/ds_list.html +++ /dev/null @@ -1,63 +0,0 @@ - - - diff --git a/public/app/routes/routes.ts b/public/app/routes/routes.ts index e4662c77367..8f17dce9757 100644 --- a/public/app/routes/routes.ts +++ b/public/app/routes/routes.ts @@ -8,6 +8,7 @@ import TeamList from 'app/features/teams/TeamList'; import PluginListPage from 'app/features/plugins/PluginListPage'; import FolderSettingsPage from 'app/features/folders/FolderSettingsPage'; import FolderPermissions from 'app/features/folders/FolderPermissions'; +import DataSourcesListPage from 'app/features/datasources/DataSourcesListPage'; /** @ngInject */ export function setupAngularRoutes($routeProvider, $locationProvider) { @@ -62,9 +63,10 @@ export function setupAngularRoutes($routeProvider, $locationProvider) { controllerAs: 'ctrl', }) .when('/datasources', { - templateUrl: 'public/app/features/plugins/partials/ds_list.html', - controller: 'DataSourcesCtrl', - controllerAs: 'ctrl', + template: '', + resolve: { + component: () => DataSourcesListPage, + }, }) .when('/datasources/edit/:id', { templateUrl: 'public/app/features/plugins/partials/ds_edit.html', diff --git a/public/app/store/configureStore.ts b/public/app/store/configureStore.ts index 08d3d5bede0..6313bddfb3a 100644 --- a/public/app/store/configureStore.ts +++ b/public/app/store/configureStore.ts @@ -7,6 +7,7 @@ import teamsReducers from 'app/features/teams/state/reducers'; import foldersReducers from 'app/features/folders/state/reducers'; import dashboardReducers from 'app/features/dashboard/state/reducers'; import pluginReducers from 'app/features/plugins/state/reducers'; +import dataSourcesReducers from 'app/features/datasources/state/reducers'; const rootReducer = combineReducers({ ...sharedReducers, @@ -15,6 +16,7 @@ const rootReducer = combineReducers({ ...foldersReducers, ...dashboardReducers, ...pluginReducers, + ...dataSourcesReducers, }); export let store; diff --git a/public/app/types/datasources.ts b/public/app/types/datasources.ts index 78ff7b0724c..40266fbbc5a 100644 --- a/public/app/types/datasources.ts +++ b/public/app/types/datasources.ts @@ -1,7 +1,24 @@ +import { LayoutMode } from '../core/components/LayoutSelector/LayoutSelector'; + export interface DataSource { id: number; orgId: number; name: string; typeLogoUrl: string; type: string; + access: string; + url: string; + password: string; + user: string; + database: string; + basicAuth: false; + isDefault: false; + jsonData: { authType: string; defaultRegion: string }; + readOnly: false; +} + +export interface DataSourcesState { + dataSources: DataSource[]; + searchQuery: string; + layoutMode: LayoutMode; } diff --git a/public/app/types/index.ts b/public/app/types/index.ts index 1dd11d73564..3dbef72ce17 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -5,7 +5,7 @@ import { NavModel, NavModelItem, NavIndex } from './navModel'; import { FolderDTO, FolderState, FolderInfo } from './folders'; import { DashboardState } from './dashboard'; import { DashboardAcl, OrgRole, PermissionLevel } from './acl'; -import { DataSource } from './datasources'; +import { DataSource, DataSourcesState } from './datasources'; import { PluginMeta, Plugin, PluginsState } from './plugins'; export { @@ -35,6 +35,7 @@ export { PluginMeta, Plugin, PluginsState, + DataSourcesState, }; export interface StoreState { From 7ae4076ddd3923e6b6463cef4e09c7db7f0e3090 Mon Sep 17 00:00:00 2001 From: Peter Holmberg Date: Fri, 28 Sep 2018 11:29:18 +0200 Subject: [PATCH 2/3] added no datasources added --- .../datasources/DataSourcesListPage.test.tsx | 2 + .../datasources/DataSourcesListPage.tsx | 20 +++++---- .../DataSourcesListPage.test.tsx.snap | 42 ++++++++++++------- .../features/datasources/state/reducers.ts | 3 +- .../features/datasources/state/selectors.ts | 1 + public/app/types/datasources.ts | 1 + 6 files changed, 45 insertions(+), 24 deletions(-) diff --git a/public/app/features/datasources/DataSourcesListPage.test.tsx b/public/app/features/datasources/DataSourcesListPage.test.tsx index 2cb6652ee13..fed7954d716 100644 --- a/public/app/features/datasources/DataSourcesListPage.test.tsx +++ b/public/app/features/datasources/DataSourcesListPage.test.tsx @@ -11,6 +11,7 @@ const setup = (propOverrides?: object) => { layoutMode: LayoutModes.Grid, loadDataSources: jest.fn(), navModel: {} as NavModel, + dataSourcesCount: 0, }; Object.assign(props, propOverrides); @@ -28,6 +29,7 @@ describe('Render', () => { it('should render action bar and datasources', () => { const wrapper = setup({ dataSources: getMockDataSources(5), + dataSourcesCount: 5, }); expect(wrapper).toMatchSnapshot(); diff --git a/public/app/features/datasources/DataSourcesListPage.tsx b/public/app/features/datasources/DataSourcesListPage.tsx index a6ce1be7be9..c6db6ee7889 100644 --- a/public/app/features/datasources/DataSourcesListPage.tsx +++ b/public/app/features/datasources/DataSourcesListPage.tsx @@ -5,7 +5,7 @@ import PageHeader from '../../core/components/PageHeader/PageHeader'; import DataSourcesActionBar from './DataSourcesActionBar'; import DataSourcesList from './DataSourcesList'; import { loadDataSources } from './state/actions'; -import { getDataSources, getDataSourcesLayoutMode } from './state/selectors'; +import { getDataSources, getDataSourcesCount, getDataSourcesLayoutMode } from './state/selectors'; import { getNavModel } from '../../core/selectors/navModel'; import { DataSource, NavModel } from 'app/types'; import { LayoutMode } from '../../core/components/LayoutSelector/LayoutSelector'; @@ -14,6 +14,7 @@ import EmptyListCTA from '../../core/components/EmptyListCTA/EmptyListCTA'; export interface Props { navModel: NavModel; dataSources: DataSource[]; + dataSourcesCount: number; layoutMode: LayoutMode; loadDataSources: typeof loadDataSources; } @@ -39,18 +40,20 @@ export class DataSourcesListPage extends PureComponent { } render() { - const { navModel, dataSources, layoutMode } = this.props; - - if (dataSources.length === 0) { - return ; - } + const { dataSources, dataSourcesCount, navModel, layoutMode } = this.props; return (
    - - + {dataSourcesCount === 0 ? ( + + ) : ( + [ + , + , + ] + )}
    ); @@ -62,6 +65,7 @@ function mapStateToProps(state) { navModel: getNavModel(state.navIndex, 'datasources'), dataSources: getDataSources(state.dataSources), layoutMode: getDataSourcesLayoutMode(state.dataSources), + dataSourcesCount: getDataSourcesCount(state.dataSources), }; } diff --git a/public/app/features/datasources/__snapshots__/DataSourcesListPage.test.tsx.snap b/public/app/features/datasources/__snapshots__/DataSourcesListPage.test.tsx.snap index 837a8aceb24..c19ee641e1b 100644 --- a/public/app/features/datasources/__snapshots__/DataSourcesListPage.test.tsx.snap +++ b/public/app/features/datasources/__snapshots__/DataSourcesListPage.test.tsx.snap @@ -8,7 +8,9 @@ exports[`Render should render action bar and datasources 1`] = `
    - +
    @@ -135,18 +138,27 @@ exports[`Render should render action bar and datasources 1`] = ` `; exports[`Render should render component 1`] = ` - +
    + +
    + +
    +
    `; diff --git a/public/app/features/datasources/state/reducers.ts b/public/app/features/datasources/state/reducers.ts index 15604fa8b53..d57b0ad523a 100644 --- a/public/app/features/datasources/state/reducers.ts +++ b/public/app/features/datasources/state/reducers.ts @@ -6,12 +6,13 @@ const initialState: DataSourcesState = { dataSources: [] as DataSource[], layoutMode: LayoutModes.Grid, searchQuery: '', + dataSourcesCount: 0, }; export const dataSourcesReducer = (state = initialState, action: Action): DataSourcesState => { switch (action.type) { case ActionTypes.LoadDataSources: - return { ...state, dataSources: action.payload }; + return { ...state, dataSources: action.payload, dataSourcesCount: action.payload.length }; case ActionTypes.SetDataSourcesSearchQuery: return { ...state, searchQuery: action.payload }; diff --git a/public/app/features/datasources/state/selectors.ts b/public/app/features/datasources/state/selectors.ts index 15ee88e715a..6df08f68037 100644 --- a/public/app/features/datasources/state/selectors.ts +++ b/public/app/features/datasources/state/selectors.ts @@ -8,3 +8,4 @@ export const getDataSources = state => { export const getDataSourcesSearchQuery = state => state.searchQuery; export const getDataSourcesLayoutMode = state => state.layoutMode; +export const getDataSourcesCount = state => state.dataSourcesCount; diff --git a/public/app/types/datasources.ts b/public/app/types/datasources.ts index 40266fbbc5a..b9936e7c01b 100644 --- a/public/app/types/datasources.ts +++ b/public/app/types/datasources.ts @@ -21,4 +21,5 @@ export interface DataSourcesState { dataSources: DataSource[]; searchQuery: string; layoutMode: LayoutMode; + dataSourcesCount: number; } From 8fd1d8a057619576b9b13cc963241b15debb92dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 2 Oct 2018 09:27:02 +0200 Subject: [PATCH 3/3] changed from RFC to PureComponent --- .../features/datasources/DataSourcesList.tsx | 38 +++++++------- .../datasources/DataSourcesListItem.tsx | 51 ++++++++++--------- 2 files changed, 46 insertions(+), 43 deletions(-) diff --git a/public/app/features/datasources/DataSourcesList.tsx b/public/app/features/datasources/DataSourcesList.tsx index 4ed2203bf29..904ed0cf679 100644 --- a/public/app/features/datasources/DataSourcesList.tsx +++ b/public/app/features/datasources/DataSourcesList.tsx @@ -1,4 +1,4 @@ -import React, { SFC } from 'react'; +import React, { PureComponent } from 'react'; import classNames from 'classnames/bind'; import DataSourcesListItem from './DataSourcesListItem'; import { DataSource } from 'app/types'; @@ -9,24 +9,26 @@ export interface Props { layoutMode: LayoutMode; } -const DataSourcesList: SFC = props => { - const { dataSources, layoutMode } = props; +export class DataSourcesList extends PureComponent { + render() { + const { dataSources, layoutMode } = this.props; - const listStyle = classNames({ - 'card-section': true, - 'card-list-layout-grid': layoutMode === LayoutModes.Grid, - 'card-list-layout-list': layoutMode === LayoutModes.List, - }); + const listStyle = classNames({ + 'card-section': true, + 'card-list-layout-grid': layoutMode === LayoutModes.Grid, + 'card-list-layout-list': layoutMode === LayoutModes.List, + }); - return ( -
    -
      - {dataSources.map((dataSource, index) => { - return ; - })} -
    -
    - ); -}; + return ( +
    +
      + {dataSources.map((dataSource, index) => { + return ; + })} +
    +
    + ); + } +} export default DataSourcesList; diff --git a/public/app/features/datasources/DataSourcesListItem.tsx b/public/app/features/datasources/DataSourcesListItem.tsx index 1bb612a299a..a4fedb893fb 100644 --- a/public/app/features/datasources/DataSourcesListItem.tsx +++ b/public/app/features/datasources/DataSourcesListItem.tsx @@ -1,34 +1,35 @@ -import React, { SFC } from 'react'; +import React, { PureComponent } from 'react'; import { DataSource } from 'app/types'; export interface Props { dataSource: DataSource; } -const DataSourcesListItem: SFC = props => { - const { dataSource } = props; - - return ( -
  • - -
    -
    {dataSource.type}
    -
    -
    -
    - -
    -
    -
    - {dataSource.name} - {dataSource.isDefault && default} -
    -
    {dataSource.url}
    +export class DataSourcesListItem extends PureComponent { + render() { + const { dataSource } = this.props; + return ( +
  • + +
    +
    {dataSource.type}
    -
  • - - - ); -}; +
    +
    + +
    +
    +
    + {dataSource.name} + {dataSource.isDefault && default} +
    +
    {dataSource.url}
    +
    +
    + + + ); + } +} export default DataSourcesListItem;