mirror of https://github.com/grafana/grafana
Merge pull request #13537 from grafana/new-data-source-as-separate-page
New data source as separate pagepull/13552/head
commit
07eba60e24
@ -0,0 +1,88 @@ |
||||
import React, { PureComponent } from 'react'; |
||||
import { connect } from 'react-redux'; |
||||
import { hot } from 'react-hot-loader'; |
||||
import PageHeader from 'app/core/components/PageHeader/PageHeader'; |
||||
import { NavModel, Plugin } from 'app/types'; |
||||
import { addDataSource, loadDataSourceTypes, setDataSourceTypeSearchQuery } from './state/actions'; |
||||
import { updateLocation } from '../../core/actions'; |
||||
import { getNavModel } from 'app/core/selectors/navModel'; |
||||
import { getDataSourceTypes } from './state/selectors'; |
||||
|
||||
export interface Props { |
||||
navModel: NavModel; |
||||
dataSourceTypes: Plugin[]; |
||||
addDataSource: typeof addDataSource; |
||||
loadDataSourceTypes: typeof loadDataSourceTypes; |
||||
updateLocation: typeof updateLocation; |
||||
dataSourceTypeSearchQuery: string; |
||||
setDataSourceTypeSearchQuery: typeof setDataSourceTypeSearchQuery; |
||||
} |
||||
|
||||
class NewDataSourcePage extends PureComponent<Props> { |
||||
componentDidMount() { |
||||
this.props.loadDataSourceTypes(); |
||||
} |
||||
|
||||
onDataSourceTypeClicked = type => { |
||||
this.props.addDataSource(type); |
||||
}; |
||||
|
||||
onSearchQueryChange = event => { |
||||
this.props.setDataSourceTypeSearchQuery(event.target.value); |
||||
}; |
||||
|
||||
render() { |
||||
const { navModel, dataSourceTypes, dataSourceTypeSearchQuery } = this.props; |
||||
|
||||
return ( |
||||
<div> |
||||
<PageHeader model={navModel} /> |
||||
<div className="page-container page-body"> |
||||
<h2 className="add-data-source-header">Choose data source type</h2> |
||||
<div className="add-data-source-search"> |
||||
<label className="gf-form--has-input-icon"> |
||||
<input |
||||
type="text" |
||||
className="gf-form-input width-20" |
||||
value={dataSourceTypeSearchQuery} |
||||
onChange={this.onSearchQueryChange} |
||||
placeholder="Filter by name or type" |
||||
/> |
||||
<i className="gf-form-input-icon fa fa-search" /> |
||||
</label> |
||||
</div> |
||||
<div className="add-data-source-grid"> |
||||
{dataSourceTypes.map((type, index) => { |
||||
return ( |
||||
<div |
||||
onClick={() => this.onDataSourceTypeClicked(type)} |
||||
className="add-data-source-grid-item" |
||||
key={`${type.id}-${index}`} |
||||
> |
||||
<img className="add-data-source-grid-item-logo" src={type.info.logos.small} /> |
||||
<span className="add-data-source-grid-item-text">{type.name}</span> |
||||
</div> |
||||
); |
||||
})} |
||||
</div> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
||||
} |
||||
|
||||
function mapStateToProps(state) { |
||||
return { |
||||
navModel: getNavModel(state.navIndex, 'datasources'), |
||||
dataSourceTypes: getDataSourceTypes(state.dataSources), |
||||
}; |
||||
} |
||||
|
||||
const mapDispatchToProps = { |
||||
addDataSource, |
||||
loadDataSourceTypes, |
||||
updateLocation, |
||||
setDataSourceTypeSearchQuery, |
||||
}; |
||||
|
||||
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(NewDataSourcePage)); |
@ -0,0 +1,44 @@ |
||||
import { findNewName, nameExits } from './actions'; |
||||
import { getMockPlugin, getMockPlugins } from '../../plugins/__mocks__/pluginMocks'; |
||||
|
||||
describe('Name exists', () => { |
||||
const plugins = getMockPlugins(5); |
||||
|
||||
it('should be true', () => { |
||||
const name = 'pretty cool plugin-1'; |
||||
|
||||
expect(nameExits(plugins, name)).toEqual(true); |
||||
}); |
||||
|
||||
it('should be false', () => { |
||||
const name = 'pretty cool plugin-6'; |
||||
|
||||
expect(nameExits(plugins, name)); |
||||
}); |
||||
}); |
||||
|
||||
describe('Find new name', () => { |
||||
it('should create a new name', () => { |
||||
const plugins = getMockPlugins(5); |
||||
const name = 'pretty cool plugin-1'; |
||||
|
||||
expect(findNewName(plugins, name)).toEqual('pretty cool plugin-6'); |
||||
}); |
||||
|
||||
it('should create new name without suffix', () => { |
||||
const plugin = getMockPlugin(); |
||||
plugin.name = 'prometheus'; |
||||
const plugins = [plugin]; |
||||
const name = 'prometheus'; |
||||
|
||||
expect(findNewName(plugins, name)).toEqual('prometheus-1'); |
||||
}); |
||||
|
||||
it('should handle names that end with -', () => { |
||||
const plugin = getMockPlugin(); |
||||
const plugins = [plugin]; |
||||
const name = 'pretty cool plugin-'; |
||||
|
||||
expect(findNewName(plugins, name)).toEqual('pretty cool plugin-'); |
||||
}); |
||||
}); |
@ -0,0 +1,46 @@ |
||||
.add-data-source-header { |
||||
margin-bottom: $spacer * 2; |
||||
padding-top: $spacer; |
||||
text-align: center; |
||||
} |
||||
|
||||
.add-data-source-search { |
||||
display: flex; |
||||
justify-content: center; |
||||
margin-bottom: $panel-margin * 2; |
||||
} |
||||
|
||||
.add-data-source-grid { |
||||
display: grid; |
||||
grid-template-columns: repeat(2, 1fr); |
||||
grid-row-gap: 10px; |
||||
grid-column-gap: 10px; |
||||
|
||||
@include media-breakpoint-up(md) { |
||||
grid-template-columns: repeat(3, 1fr); |
||||
} |
||||
} |
||||
|
||||
.add-data-source-grid-item { |
||||
padding: 15px; |
||||
display: flex; |
||||
align-items: center; |
||||
cursor: pointer; |
||||
background: $card-background; |
||||
box-shadow: $card-shadow; |
||||
color: $text-color; |
||||
|
||||
&:hover { |
||||
background: $card-background-hover; |
||||
color: $text-color-strong; |
||||
} |
||||
} |
||||
|
||||
.add-data-source-grid-item-text { |
||||
font-size: $font-size-h5; |
||||
} |
||||
|
||||
.add-data-source-grid-item-logo { |
||||
margin: 0 15px; |
||||
width: 55px; |
||||
} |
Loading…
Reference in new issue