mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Data source list: Use Card component (#31326)
* Replace DataSourcesListItem with Card * Add tests * Remove unused styles * Make card heading semi bold * Make heading semi-bold * Show type name instead of type id * Fix key warning * Update Card * Fix tests * Make typeName optional * remove styling that was just a test * Make typeName non-optional and fix tests * Update list key Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import DataSourcesList from './DataSourcesList';
|
||||
import { getMockDataSources } from './__mocks__/dataSourcesMocks';
|
||||
import { LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector';
|
||||
@@ -10,13 +10,20 @@ const setup = () => {
|
||||
layoutMode: LayoutModes.Grid,
|
||||
};
|
||||
|
||||
return shallow(<DataSourcesList {...props} />);
|
||||
return render(<DataSourcesList {...props} />);
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = setup();
|
||||
describe('DataSourcesList', () => {
|
||||
it('should render list of datasources', () => {
|
||||
setup();
|
||||
expect(screen.getAllByRole('listitem')).toHaveLength(3);
|
||||
expect(screen.getAllByRole('heading')).toHaveLength(3);
|
||||
});
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
it('should render all elements in the list item', () => {
|
||||
setup();
|
||||
expect(screen.getByRole('heading', { name: 'dataSource-0' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: 'dataSource-0 dataSource-0' })).toBeInTheDocument();
|
||||
expect(screen.getByAltText('dataSource-0')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,39 +1,50 @@
|
||||
// Libraries
|
||||
import React, { PureComponent } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
// Components
|
||||
import DataSourcesListItem from './DataSourcesListItem';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
// Types
|
||||
import { DataSourceSettings } from '@grafana/data';
|
||||
import { LayoutMode, LayoutModes } from '../../core/components/LayoutSelector/LayoutSelector';
|
||||
import { LayoutMode } from '../../core/components/LayoutSelector/LayoutSelector';
|
||||
import { Card, Tag, useStyles } from '@grafana/ui';
|
||||
import { css } from 'emotion';
|
||||
|
||||
export interface Props {
|
||||
dataSources: DataSourceSettings[];
|
||||
layoutMode: LayoutMode;
|
||||
}
|
||||
|
||||
export class DataSourcesList extends PureComponent<Props> {
|
||||
render() {
|
||||
const { dataSources, layoutMode } = this.props;
|
||||
export const DataSourcesList: FC<Props> = ({ dataSources, layoutMode }) => {
|
||||
const styles = useStyles(getStyles);
|
||||
|
||||
const listStyle = classNames({
|
||||
'card-section': true,
|
||||
'card-list-layout-grid': layoutMode === LayoutModes.Grid,
|
||||
'card-list-layout-list': layoutMode === LayoutModes.List,
|
||||
});
|
||||
|
||||
return (
|
||||
<section className={listStyle}>
|
||||
<ol className="card-list">
|
||||
{dataSources.map((dataSource, index) => {
|
||||
return <DataSourcesListItem dataSource={dataSource} key={`${dataSource.id}-${index}`} />;
|
||||
})}
|
||||
</ol>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<ul className={styles.list}>
|
||||
{dataSources.map((dataSource, index) => {
|
||||
return (
|
||||
<li key={dataSource.id}>
|
||||
<Card heading={dataSource.name} href={`datasources/edit/${dataSource.id}`}>
|
||||
<Card.Figure>
|
||||
<img src={dataSource.typeLogoUrl} alt={dataSource.name} />
|
||||
</Card.Figure>
|
||||
<Card.Meta>
|
||||
{[
|
||||
dataSource.typeName,
|
||||
dataSource.url,
|
||||
dataSource.isDefault && <Tag key="default-tag" name={'default'} colorIndex={1} />,
|
||||
]}
|
||||
</Card.Meta>
|
||||
</Card>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export default DataSourcesList;
|
||||
|
||||
const getStyles = () => {
|
||||
return {
|
||||
list: css`
|
||||
list-style: none;
|
||||
`,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
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(<DataSourcesListItem {...props} />);
|
||||
};
|
||||
|
||||
describe('Render', () => {
|
||||
it('should render component', () => {
|
||||
const wrapper = setup();
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import { DataSourceSettings } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
|
||||
export interface Props {
|
||||
dataSource: DataSourceSettings;
|
||||
}
|
||||
|
||||
export class DataSourcesListItem extends PureComponent<Props> {
|
||||
render() {
|
||||
const { dataSource } = this.props;
|
||||
return (
|
||||
<li className="card-item-wrapper">
|
||||
<a className="card-item" href={`datasources/edit/${dataSource.id}`}>
|
||||
<div className="card-item-header">
|
||||
<div className="card-item-type">{dataSource.type}</div>
|
||||
</div>
|
||||
<div className="card-item-body">
|
||||
<figure className="card-item-figure">
|
||||
<img src={dataSource.typeLogoUrl} alt={dataSource.name} />
|
||||
</figure>
|
||||
<div className="card-item-details">
|
||||
<div className="card-item-name" aria-label={selectors.pages.DataSources.dataSources(dataSource.name)}>
|
||||
{dataSource.name}
|
||||
{dataSource.isDefault && <span className="btn btn-secondary btn-small card-item-label">default</span>}
|
||||
</div>
|
||||
<div className="card-item-sub-name">{dataSource.url}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default DataSourcesListItem;
|
||||
@@ -49,11 +49,7 @@ const emptyListModel = {
|
||||
|
||||
export class DataSourcesListPage extends PureComponent<Props> {
|
||||
componentDidMount() {
|
||||
this.fetchDataSources();
|
||||
}
|
||||
|
||||
async fetchDataSources() {
|
||||
return await this.props.loadDataSources();
|
||||
this.props.loadDataSources();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { DataSourceSettings } from '@grafana/data';
|
||||
export const getMockDataSources = (amount: number) => {
|
||||
const dataSources = [];
|
||||
|
||||
for (let i = 0; i <= amount; i++) {
|
||||
for (let i = 0; i < amount; i++) {
|
||||
dataSources.push({
|
||||
access: '',
|
||||
basicAuth: false,
|
||||
@@ -37,6 +37,7 @@ export const getMockDataSource = (): DataSourceSettings => {
|
||||
isDefault: false,
|
||||
jsonData: { authType: 'credentials', defaultRegion: 'eu-west-2' },
|
||||
name: 'gdev-cloudwatch',
|
||||
typeName: 'Cloudwatch',
|
||||
orgId: 1,
|
||||
password: '',
|
||||
readOnly: false,
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<section
|
||||
className="card-section card-list-layout-grid"
|
||||
>
|
||||
<ol
|
||||
className="card-list"
|
||||
>
|
||||
<DataSourcesListItem
|
||||
dataSource={
|
||||
Object {
|
||||
"access": "",
|
||||
"basicAuth": false,
|
||||
"database": "database-0",
|
||||
"id": 0,
|
||||
"isDefault": false,
|
||||
"jsonData": Object {
|
||||
"authType": "credentials",
|
||||
"defaultRegion": "eu-west-2",
|
||||
},
|
||||
"name": "dataSource-0",
|
||||
"orgId": 1,
|
||||
"password": "",
|
||||
"readOnly": false,
|
||||
"type": "cloudwatch",
|
||||
"typeLogoUrl": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"url": "",
|
||||
"user": "",
|
||||
}
|
||||
}
|
||||
key="0-0"
|
||||
/>
|
||||
<DataSourcesListItem
|
||||
dataSource={
|
||||
Object {
|
||||
"access": "",
|
||||
"basicAuth": false,
|
||||
"database": "database-1",
|
||||
"id": 1,
|
||||
"isDefault": false,
|
||||
"jsonData": Object {
|
||||
"authType": "credentials",
|
||||
"defaultRegion": "eu-west-2",
|
||||
},
|
||||
"name": "dataSource-1",
|
||||
"orgId": 1,
|
||||
"password": "",
|
||||
"readOnly": false,
|
||||
"type": "cloudwatch",
|
||||
"typeLogoUrl": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"url": "",
|
||||
"user": "",
|
||||
}
|
||||
}
|
||||
key="1-1"
|
||||
/>
|
||||
<DataSourcesListItem
|
||||
dataSource={
|
||||
Object {
|
||||
"access": "",
|
||||
"basicAuth": false,
|
||||
"database": "database-2",
|
||||
"id": 2,
|
||||
"isDefault": false,
|
||||
"jsonData": Object {
|
||||
"authType": "credentials",
|
||||
"defaultRegion": "eu-west-2",
|
||||
},
|
||||
"name": "dataSource-2",
|
||||
"orgId": 1,
|
||||
"password": "",
|
||||
"readOnly": false,
|
||||
"type": "cloudwatch",
|
||||
"typeLogoUrl": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"url": "",
|
||||
"user": "",
|
||||
}
|
||||
}
|
||||
key="2-2"
|
||||
/>
|
||||
<DataSourcesListItem
|
||||
dataSource={
|
||||
Object {
|
||||
"access": "",
|
||||
"basicAuth": false,
|
||||
"database": "database-3",
|
||||
"id": 3,
|
||||
"isDefault": false,
|
||||
"jsonData": Object {
|
||||
"authType": "credentials",
|
||||
"defaultRegion": "eu-west-2",
|
||||
},
|
||||
"name": "dataSource-3",
|
||||
"orgId": 1,
|
||||
"password": "",
|
||||
"readOnly": false,
|
||||
"type": "cloudwatch",
|
||||
"typeLogoUrl": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"url": "",
|
||||
"user": "",
|
||||
}
|
||||
}
|
||||
key="3-3"
|
||||
/>
|
||||
</ol>
|
||||
</section>
|
||||
`;
|
||||
@@ -1,47 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<li
|
||||
className="card-item-wrapper"
|
||||
>
|
||||
<a
|
||||
className="card-item"
|
||||
href="datasources/edit/13"
|
||||
>
|
||||
<div
|
||||
className="card-item-header"
|
||||
>
|
||||
<div
|
||||
className="card-item-type"
|
||||
>
|
||||
cloudwatch
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="card-item-body"
|
||||
>
|
||||
<figure
|
||||
className="card-item-figure"
|
||||
>
|
||||
<img
|
||||
alt="gdev-cloudwatch"
|
||||
src="public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png"
|
||||
/>
|
||||
</figure>
|
||||
<div
|
||||
className="card-item-details"
|
||||
>
|
||||
<div
|
||||
aria-label="Data source list item gdev-cloudwatch"
|
||||
className="card-item-name"
|
||||
>
|
||||
gdev-cloudwatch
|
||||
</div>
|
||||
<div
|
||||
className="card-item-sub-name"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
@@ -125,25 +125,6 @@ exports[`Render should render action bar and datasources 1`] = `
|
||||
"url": "",
|
||||
"user": "",
|
||||
},
|
||||
Object {
|
||||
"access": "",
|
||||
"basicAuth": false,
|
||||
"database": "database-5",
|
||||
"id": 5,
|
||||
"isDefault": false,
|
||||
"jsonData": Object {
|
||||
"authType": "credentials",
|
||||
"defaultRegion": "eu-west-2",
|
||||
},
|
||||
"name": "dataSource-5",
|
||||
"orgId": 1,
|
||||
"password": "",
|
||||
"readOnly": false,
|
||||
"type": "cloudwatch",
|
||||
"typeLogoUrl": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"url": "",
|
||||
"user": "",
|
||||
},
|
||||
]
|
||||
}
|
||||
key="list"
|
||||
|
||||
@@ -7,6 +7,7 @@ export function createDatasourceSettings<T>(jsonData: T): DataSourceSettings<T>
|
||||
name: 'datasource-test',
|
||||
typeLogoUrl: '',
|
||||
type: 'datasource',
|
||||
typeName: 'Datasource',
|
||||
access: 'server',
|
||||
url: 'http://localhost',
|
||||
password: '',
|
||||
|
||||
@@ -48,6 +48,7 @@ exports[`Render should render alpha info text 1`] = `
|
||||
"secureJsonFields": Object {},
|
||||
"type": "cloudwatch",
|
||||
"typeLogoUrl": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"typeName": "Cloudwatch",
|
||||
"url": "",
|
||||
"user": "",
|
||||
"withCredentials": false,
|
||||
@@ -81,6 +82,7 @@ exports[`Render should render alpha info text 1`] = `
|
||||
"secureJsonFields": Object {},
|
||||
"type": "cloudwatch",
|
||||
"typeLogoUrl": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"typeName": "Cloudwatch",
|
||||
"url": "",
|
||||
"user": "",
|
||||
"withCredentials": false,
|
||||
@@ -198,6 +200,7 @@ exports[`Render should render beta info text 1`] = `
|
||||
"secureJsonFields": Object {},
|
||||
"type": "cloudwatch",
|
||||
"typeLogoUrl": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"typeName": "Cloudwatch",
|
||||
"url": "",
|
||||
"user": "",
|
||||
"withCredentials": false,
|
||||
@@ -257,6 +260,7 @@ exports[`Render should render component 1`] = `
|
||||
"secureJsonFields": Object {},
|
||||
"type": "cloudwatch",
|
||||
"typeLogoUrl": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"typeName": "Cloudwatch",
|
||||
"url": "",
|
||||
"user": "",
|
||||
"withCredentials": false,
|
||||
@@ -321,6 +325,7 @@ exports[`Render should render is ready only message 1`] = `
|
||||
"secureJsonFields": Object {},
|
||||
"type": "cloudwatch",
|
||||
"typeLogoUrl": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"typeName": "Cloudwatch",
|
||||
"url": "",
|
||||
"user": "",
|
||||
"withCredentials": false,
|
||||
@@ -354,6 +359,7 @@ exports[`Render should render is ready only message 1`] = `
|
||||
"secureJsonFields": Object {},
|
||||
"type": "cloudwatch",
|
||||
"typeLogoUrl": "public/app/plugins/datasource/cloudwatch/img/amazon-web-services.png",
|
||||
"typeName": "Cloudwatch",
|
||||
"url": "",
|
||||
"user": "",
|
||||
"withCredentials": false,
|
||||
|
||||
@@ -83,6 +83,7 @@ export function getDataSourceLoadingNav(pageName: string): NavModel {
|
||||
password: '',
|
||||
readOnly: false,
|
||||
type: 'Loading',
|
||||
typeName: 'Loading',
|
||||
typeLogoUrl: 'public/img/icn-datasource.svg',
|
||||
url: '',
|
||||
user: '',
|
||||
|
||||
@@ -40,7 +40,7 @@ const mockPlugin = () =>
|
||||
describe('dataSourcesReducer', () => {
|
||||
describe('when dataSourcesLoaded is dispatched', () => {
|
||||
it('then state should be correct', () => {
|
||||
const dataSources = getMockDataSources(0);
|
||||
const dataSources = getMockDataSources(1);
|
||||
|
||||
reducerTester<DataSourcesState>()
|
||||
.givenReducer(dataSourcesReducer, initialState)
|
||||
|
||||
Reference in New Issue
Block a user