Add tabs and descriptions to Templates Page (#14105)

* Add tabs and descriptions to Templates Page

* Fixes tests broken by axios upgrade

* Add type metadata

* add no description placeholder
pull/14130/head
Daniel Campbell 2019-06-13 09:31:18 -07:00 committed by Brandon Farmer
parent 5240d5d558
commit fd458cb33e
9 changed files with 128 additions and 54 deletions

View File

@ -123,7 +123,7 @@
"express": "^4.14.0",
"http-proxy-middleware": "^0.18.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^24.1.0",
"jest": "^24.8.0",
"jsdom": "^9.0.0",
"junit-viewer": "^4.11.1",
"mocha": "^5.2.0",

View File

@ -8,6 +8,7 @@
min-height: $form-xs-height;
}
.resource-description--static,
.resource-description--preview,
.input.resource-description--input > input {
font-size: $form-xs-font;
@ -15,12 +16,14 @@
font-family: $ix-text-font;
}
.resource-description--static,
.resource-description--preview,
.resource-description--input {
position: relative;
width: 100%;
}
.resource-description--static,
.resource-description--preview {
width: auto;
display: inline-block;
@ -29,9 +32,12 @@
overflow: hidden;
@include no-user-select();
color: $g13-mist;
line-height: $form-xs-font + $ix-border;
}
.resource-description--preview {
transition: color 0.25s ease, background-color 0.25s ease,
border-color 0.25s ease;
line-height: $form-xs-font + $ix-border;
.icon {
position: relative;

View File

@ -13,7 +13,7 @@ import {ComponentSize} from '@influxdata/clockface'
import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props {
onUpdate: (name: string) => void
onUpdate?: (name: string) => void
description: string
placeholder?: string
}
@ -35,9 +35,17 @@ class ResourceDescription extends Component<Props, State> {
}
public render() {
const {description} = this.props
const {description, onUpdate} = this.props
const {isEditing} = this.state
if (!onUpdate) {
return (
<div className="resource-description">
<div className="resource-description--static">{description}</div>
</div>
)
}
if (isEditing) {
return (
<div className="resource-description">

View File

@ -1,6 +1,6 @@
import {formatDownloadName} from './download'
describe('formatDownloadName', () => [
describe('formatDownloadName', () => {
it('formats name correctly', () => {
const name1 = 'My Dashboard '
const name2 = 'my_dash'
@ -19,5 +19,5 @@ describe('formatDownloadName', () => [
expect(actual1).toEqual(expected1)
expect(actual2).toEqual(expected2)
expect(actual3).toEqual(expected3)
}),
])
})
})

View File

@ -1,5 +1,6 @@
// Libraries
import React, {PureComponent, MouseEvent} from 'react'
import _ from 'lodash'
import {connect} from 'react-redux'
import {withRouter, WithRouterProps} from 'react-router'
import {
@ -50,6 +51,7 @@ class StaticTemplateCard extends PureComponent<Props & WithRouterProps> {
<ResourceList.Card
testID="template-card"
contextMenu={() => this.contextMenu}
description={() => this.description}
name={() => (
<ResourceList.Name
onClick={this.handleNameClick}
@ -59,6 +61,7 @@ class StaticTemplateCard extends PureComponent<Props & WithRouterProps> {
inputTestID="template-card--input"
/>
)}
metaData={() => [this.templateType]}
/>
)
}
@ -80,6 +83,25 @@ class StaticTemplateCard extends PureComponent<Props & WithRouterProps> {
)
}
private get description(): JSX.Element {
const {template} = this.props
const description = _.get(template, 'content.data.attributes.description')
return (
<ResourceList.Description description={description || 'No description'} />
)
}
private get templateType(): JSX.Element {
const {template} = this.props
return (
<div className="resource-list--meta-item">
{_.get(template, 'content.data.type')}
</div>
)
}
private handleCreate = () => {
const {onCreateFromTemplate, name} = this.props

View File

@ -26,7 +26,6 @@ interface Props {
sortKey: string
sortDirection: Sort
sortType: SortTypes
title?: string
onClickColumn: (nextSort: Sort, sortKey: SortKey) => void
}
@ -47,8 +46,6 @@ export default class StaticTemplatesList extends PureComponent<Props> {
const headerKeys: SortKey[] = ['meta.name']
return (
<>
<h1>Static Templates</h1>
<ResourceList>
<ResourceList.Header>
<ResourceList.Sorter
@ -66,7 +63,6 @@ export default class StaticTemplatesList extends PureComponent<Props> {
{this.rows}
</ResourceList.Body>
</ResourceList>
</>
)
}

View File

@ -26,7 +26,6 @@ interface Props {
sortKey: string
sortDirection: Sort
sortType: SortTypes
title?: string
onClickColumn: (nextSort: Sort, sortKey: SortKey) => void
}
@ -48,7 +47,6 @@ export default class TemplatesList extends PureComponent<Props> {
return (
<>
{this.header}
<ResourceList>
<ResourceList.Header>
<ResourceList.Sorter
@ -70,16 +68,6 @@ export default class TemplatesList extends PureComponent<Props> {
)
}
private get header(): JSX.Element {
const {title} = this.props
if (title) {
return <h1>{title}</h1>
} else {
return null
}
}
private get rows(): JSX.Element[] {
const {
templates,

View File

@ -15,7 +15,14 @@ import GetResources, {ResourceTypes} from 'src/shared/components/GetResources'
// Types
import {TemplateSummary, AppState} from 'src/types'
import {SortTypes} from 'src/shared/utils/sort'
import {Sort} from '@influxdata/clockface'
import {
Sort,
Radio,
ComponentSpacer,
AlignItems,
FlexDirection,
JustifyContent,
} from '@influxdata/clockface'
import {staticTemplates as statics} from 'src/templates/constants/defaultTemplates'
@ -44,6 +51,7 @@ interface State {
sortKey: SortKey
sortDirection: Sort
sortType: SortTypes
activeTab: string
}
type SortKey = 'meta.name'
@ -58,12 +66,13 @@ class TemplatesPage extends PureComponent<Props, State> {
sortKey: 'meta.name',
sortDirection: Sort.Ascending,
sortType: SortTypes.String,
activeTab: 'static-templates',
}
}
public render() {
const {templates, onImport} = this.props
const {searchTerm, sortKey, sortDirection, sortType} = this.state
const {onImport} = this.props
const {activeTab} = this.state
return (
<>
@ -73,6 +82,53 @@ class TemplatesPage extends PureComponent<Props, State> {
isFullPage={false}
filterComponent={() => this.filterComponent}
/>
<ComponentSpacer
direction={FlexDirection.Row}
alignItems={AlignItems.Center}
justifyContent={JustifyContent.Center}
stretchToFitWidth={true}
>
<Radio>
<Radio.Button
id="static-templates"
active={activeTab === 'static-templates'}
value="static-templates"
onClick={this.handleClickTab}
titleText="Static Templates"
>
Static Templates
</Radio.Button>
<Radio.Button
id="user-templates"
active={activeTab === 'user-templates'}
value="user-templates"
onClick={this.handleClickTab}
titleText="User Templates"
>
User Templates
</Radio.Button>
</Radio>
</ComponentSpacer>
{this.templatesList}
</>
)
}
private handleClickTab = val => {
this.setState({activeTab: val})
}
private handleClickColumn = (nextSort: Sort, sortKey: SortKey) => {
const sortType = SortTypes.String
this.setState({sortKey, sortDirection: nextSort, sortType})
}
private get templatesList(): JSX.Element {
const {templates, onImport} = this.props
const {searchTerm, sortKey, sortDirection, sortType, activeTab} = this.state
if (activeTab === 'static-templates') {
return (
<FilterList<StaticTemplate>
searchTerm={searchTerm}
searchKeys={['template.meta.name', 'labels[].name']}
@ -81,7 +137,6 @@ class TemplatesPage extends PureComponent<Props, State> {
{ts => {
return (
<StaticTemplatesList
title="Static Templates"
searchTerm={searchTerm}
templates={ts}
onFilterChange={this.setSearchTerm}
@ -94,6 +149,11 @@ class TemplatesPage extends PureComponent<Props, State> {
)
}}
</FilterList>
)
}
if (activeTab === 'user-templates') {
return (
<GetResources resource={ResourceTypes.Labels}>
<FilterList<TemplateSummary>
searchTerm={searchTerm}
@ -103,7 +163,6 @@ class TemplatesPage extends PureComponent<Props, State> {
{ts => {
return (
<TemplatesList
title="User Templates"
searchTerm={searchTerm}
templates={ts}
onFilterChange={this.setSearchTerm}
@ -117,13 +176,8 @@ class TemplatesPage extends PureComponent<Props, State> {
}}
</FilterList>
</GetResources>
</>
)
}
private handleClickColumn = (nextSort: Sort, sortKey: SortKey) => {
const sortType = SortTypes.String
this.setState({sortKey, sortDirection: nextSort, sortType})
}
private get filterComponent(): JSX.Element {

View File

@ -1,4 +1,4 @@
import axios, {AxiosResponse} from 'axios'
import axios, {AxiosResponse, Method} from 'axios'
// do not prefix route with basepath, ex. for external links
const addBasepath = (url, excludeBasepath): string => {
@ -11,7 +11,7 @@ interface RequestParams {
url?: string | string[]
resource?: string | null
id?: string | null
method?: string
method?: Method
data?: object | string
params?: object
headers?: object