Merge pull request #1786 from influxdata/feat/line-protocol-status

Create different loading, success and error state for line protocol u…
pull/10616/head
Deniz Kusefoglu 2018-12-07 17:12:12 -08:00 committed by GitHub
commit 4d7a675316
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 333 additions and 133 deletions

View File

@ -17,6 +17,7 @@
"clean": "rm -rf ./build && rm -rf ./.cache && rm -rf node_modules",
"test": "jest --maxWorkers=2",
"test:watch": "jest --watch --verbose false",
"test:u": "jest --updateSnapshot",
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand --watch --verbose false",
"lint": "npm run tslint && npm run tsc",
"tslint": "tslint -c ./tslint.json '{src,test}/**/*.ts?(x)'",

View File

@ -22,6 +22,8 @@ class SparkleSpinner extends PureComponent<Props> {
id="sparkle_spinner"
x="0px"
y="0px"
width="100%"
height="100%"
viewBox="0 0 250 250"
>
<path

View File

@ -28,7 +28,7 @@
border-radius: $radius;
flex: 1 0 100%;
transition: flex 0.4s ease;
transform: translate3d(0,0,0);
transform: translate3d(0, 0, 0);
}
.side-bar.show + .wizard-step--container {
@ -38,7 +38,7 @@
.wizard-button-bar {
display: inline-flex;
flex-shrink: 0;
margin: 32px auto;
margin: 10px auto;
position: relative;
min-width: 100%;
justify-content: center;
@ -69,13 +69,15 @@
background-repeat: no-repeat;
width: 812px;
height: 170px;
}
margin: 0 auto;
.splash-logo.primary {
background-image: url('../../assets/images/cubo_doodle.svg');
}
.splash-logo.secondary {
background-image: url('../../assets/images/cubo_doodle_green.svg');
&.primary {
background-image: url('../../assets/images/cubo_doodle.svg');
}
&.secondary {
background-image: url('../../assets/images/cubo_doodle_green.svg');
}
}
.wizard-step--title {
@ -96,15 +98,16 @@
@include no-user-select();
}
.wizard-step--body, .wizard-step--body-streaming{
background-color: $g5-pepper;
color: $g20-white;
padding: 25px;
margin-left: 60px;
margin-right: 60px;
margin-top: 30px;
text-align: left;
border-radius: $radius;
.wizard-step--body,
.wizard-step--body-streaming {
background-color: $g5-pepper;
color: $g20-white;
padding: 25px;
margin-left: 60px;
margin-right: 60px;
margin-top: 30px;
text-align: left;
border-radius: $radius;
> h6 {
color: $g15-platinum;
@ -118,21 +121,81 @@ border-radius: $radius;
.wizard-step--body-streaming {
text-align: center;
.loading{
color: $c-star
}
.wizard-step--text-state {
text-align: center;
&.loading {
color: $c-star;
}
.success{
color: $c-rainforest
&.success {
color: $c-rainforest;
}
.error{
color: $c-fire
&.error {
color: $c-curacao;
}
}
.wizard-step--body-snippet{
.wizard-step--body-snippet {
background-color: $g3-castle;
border-radius: $radius;
margin: $ix-marg-a 0;
padding: $ix-marg-b;
font-family: "RobotoMono", monospace;
}
font-family: 'RobotoMono', monospace;
}
.wizard-step--lp-body {
height: 280px;
width: 750px;
margin: 10px auto 0px auto;
}
.wizard-step--top-container {
width: 100%;
padding: 40px;
max-width: 750px;
margin: 10px auto 0 auto;
border-radius: $radius;
background-color: #202028;
transition: background-color 0.25s ease, border-color 0.25s ease;
}
.wizard-step--sparkle-container {
width: 100%;
height: 250px;
}
.wizard-step--retry-button {
margin-top: 20px;
}
.wizard-step--footer {
display: inline-flex;
justify-content: center;
align-items: center;
border-radius: $radius;
height: 50px;
width: 100%;
max-width: 750px;
margin-top: 10px;
background-color: #202028;
text-align: center;
}
.onboarding-step {
width: 100%;
text-align: center;
}
.wizard-step--grid-container-lg {
max-width: 750px;
display: block;
margin: 0 auto;
}
.wizard-step--grid-container-sm {
max-width: 500px;
display: block;
margin: 0 auto;
}

View File

@ -5,9 +5,8 @@ import {
LineProtocolTab,
} from 'src/types/v2/dataLoaders'
import {notify} from 'src/shared/actions/notifications'
import {writeLineProtocolFailed} from 'src/shared/copy/v2/notifications'
import {writeLineProtocol} from 'src/onboarding/apis/index'
import {RemoteDataState} from 'src/types'
export type Action =
| SetDataLoadersType
@ -16,6 +15,7 @@ export type Action =
| SetActiveTelegrafPlugin
| SetLineProtocolText
| SetActiveLPTab
| SetLPStatus
interface SetDataLoadersType {
type: 'SET_DATA_LOADERS_TYPE'
@ -89,14 +89,26 @@ export const setActiveLPTab = (
payload: {activeLPTab},
})
interface SetLPStatus {
type: 'SET_LP_STATUS'
payload: {lpStatus: RemoteDataState}
}
export const setLPStatus = (lpStatus: RemoteDataState): SetLPStatus => ({
type: 'SET_LP_STATUS',
payload: {lpStatus},
})
export const writeLineProtocolAction = (
org: string,
bucket: string,
body: string
) => async dispatch => {
try {
dispatch(setLPStatus(RemoteDataState.Loading))
await writeLineProtocol(org, bucket, body)
dispatch(setLPStatus(RemoteDataState.Done))
} catch (error) {
dispatch(notify(writeLineProtocolFailed(error)))
dispatch(setLPStatus(RemoteDataState.Error))
}
}

View File

@ -3,9 +3,7 @@ import React from 'react'
import {shallow} from 'enzyme'
// Components
import LineProtocol from 'src/onboarding/components/configureStep/lineProtocol/LineProtocol'
import {LineProtocolStatus} from 'src/types/v2/dataLoaders'
import {LineProtocol} from 'src/onboarding/components/configureStep/lineProtocol/LineProtocol'
const setup = (override?) => {
const props = {
@ -26,9 +24,5 @@ describe('LineProtocol', () => {
expect(wrapper).toMatchSnapshot()
})
it('defaults to selecting importdata tab if no props provided.', () => {
const {wrapper} = setup()
expect(wrapper.state('activeCard')).toBe(LineProtocolStatus.ImportData)
})
})
})

View File

@ -1,53 +1,50 @@
// Libraries
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import _ from 'lodash'
// Components
import LineProtocolTabs from 'src/onboarding/components/configureStep/lineProtocol/LineProtocolTabs'
import LoadingState from 'src/onboarding/components/configureStep/lineProtocol/LoadingState'
import LoadingStatusIndicator from 'src/onboarding/components/configureStep/lineProtocol/LoadingStatusIndicator'
// Decorator
import {ErrorHandling} from 'src/shared/decorators/errors'
// Types
import {LineProtocolTab, LineProtocolStatus} from 'src/types/v2/dataLoaders'
import {AppState} from 'src/types/v2/index'
import {RemoteDataState} from 'src/types'
interface Props {
interface OwnProps {
bucket: string
org: string
}
interface State {
activeCard: LineProtocolStatus
interface StateProps {
lpStatus: RemoteDataState
}
type Props = OwnProps & StateProps
@ErrorHandling
class LineProtocol extends PureComponent<Props, State> {
export class LineProtocol extends PureComponent<Props> {
constructor(props) {
super(props)
this.state = {activeCard: LineProtocolStatus.ImportData}
this.state = {status: LineProtocolStatus.ImportData}
}
public render() {
const {activeCard} = this.state
const {bucket, org} = this.props
return (
<>
<h3 className="wizard-step--title">Add Data via Line Protocol</h3>
<h5 className="wizard-step--sub-title">
Need help writing InfluxDB Line Protocol? See Documentation
</h5>
{activeCard === LineProtocolStatus.ImportData ? (
<LineProtocolTabs
tabs={this.LineProtocolTabs}
bucket={bucket}
org={org}
/>
) : (
<LoadingState activeCard={activeCard} />
)}
{this.Content}
</>
)
}
private get LineProtocolTabs(): LineProtocolTab[] {
return [
LineProtocolTab.UploadFile,
@ -55,6 +52,28 @@ class LineProtocol extends PureComponent<Props, State> {
LineProtocolTab.EnterURL,
]
}
private get Content(): JSX.Element {
const {bucket, org, lpStatus} = this.props
if (lpStatus === RemoteDataState.NotStarted) {
return (
<LineProtocolTabs
tabs={this.LineProtocolTabs}
bucket={bucket}
org={org}
/>
)
}
return <LoadingStatusIndicator status={lpStatus} />
}
}
export default LineProtocol
const mstp = ({
onboarding: {
dataLoaders: {lpStatus},
},
}: AppState): StateProps => {
return {lpStatus}
}
export default connect<StateProps, null, OwnProps>(mstp)(LineProtocol)

View File

@ -41,28 +41,16 @@ interface StateProps {
activeLPTab: LineProtocolTab
}
const lineProtocolTabsStyle = {
height: '280px',
width: '750px',
marginTop: '30px',
}
export class LineProtocolTabs extends PureComponent<Props> {
public render() {
return (
<>
{this.tabSelector}
<div style={lineProtocolTabsStyle}>{this.tabBody}</div>
<div className={'wizard-step--lp-body'}>{this.tabBody}</div>
</>
)
}
private handleTabClick = (tab: LineProtocolTab) => () => {
const {setActiveLPTab, setLineProtocolText} = this.props
setLineProtocolText('')
setActiveLPTab(tab)
}
private get tabSelector(): JSX.Element {
const {tabs, activeLPTab} = this.props
return (
@ -83,6 +71,12 @@ export class LineProtocolTabs extends PureComponent<Props> {
)
}
private handleTabClick = (tab: LineProtocolTab) => () => {
const {setActiveLPTab, setLineProtocolText} = this.props
setLineProtocolText('')
setActiveLPTab(tab)
}
private get tabBody(): JSX.Element {
const {setLineProtocolText, lineProtocolText, activeLPTab} = this.props

View File

@ -1,15 +0,0 @@
// Libraries
import React, {SFC} from 'react'
// Types
import {LineProtocolStatus} from 'src/types/v2/dataLoaders'
interface Props {
activeCard: LineProtocolStatus
}
const LoadingState: SFC<Props> = ({activeCard}) => {
return <>{activeCard}</>
}
export default LoadingState

View File

@ -0,0 +1,31 @@
// Libraries
import React from 'react'
import {shallow} from 'enzyme'
// Components
import LoadingStatusIndicator from 'src/onboarding/components/configureStep/lineProtocol/LoadingStatusIndicator'
// Types
import {RemoteDataState} from 'src/types'
const setup = (override?) => {
const props = {
status: RemoteDataState.NotStarted,
...override,
}
const wrapper = shallow(<LoadingStatusIndicator {...props} />)
return {wrapper}
}
describe('LoadingStatusIndicator', () => {
describe('rendering', () => {
it('renders!', () => {
const {wrapper} = setup()
expect(wrapper.exists()).toBe(true)
expect(wrapper).toMatchSnapshot()
})
})
})

View File

@ -0,0 +1,76 @@
// Libraries
import React, {PureComponent} from 'react'
import classnames from 'classnames'
import {
SparkleSpinner,
Button,
ComponentColor,
ComponentSize,
} from 'src/clockface'
// Types
import {RemoteDataState} from 'src/types'
interface Props {
status: RemoteDataState
}
class LoadingStatusIndicator extends PureComponent<Props> {
public render() {
const {status} = this.props
return (
<>
<div className={'wizard-step--top-container'}>
<div className={'wizard-step--sparkle-container'}>
<SparkleSpinner loading={status} />
</div>
{this.retryButton}
</div>
<div className={'wizard-step--footer'}>
<div className={this.footerClass}>{this.footerText}</div>
</div>
<br />
</>
)
}
private get retryButton(): JSX.Element {
const {status} = this.props
if (status === RemoteDataState.Error) {
return (
<Button
text={'Try Again'}
color={ComponentColor.Primary}
size={ComponentSize.Small}
customClass={'wizard-step--retry-button'}
/>
)
} else {
return null
}
}
private get footerClass(): string {
const {status} = this.props
return classnames(`wizard-step--text-state`, {
loading: status === RemoteDataState.Loading,
success: status === RemoteDataState.Done,
error: status === RemoteDataState.Error,
})
}
private get footerText(): string {
switch (this.props.status) {
case RemoteDataState.Loading:
return 'Loading...'
case RemoteDataState.Done:
return 'Data Written Successfully!'
case RemoteDataState.Error:
return 'Unable to Write Data'
}
}
}
export default LoadingStatusIndicator

View File

@ -12,16 +12,6 @@ exports[`LineProtocol rendering renders! 1`] = `
>
Need help writing InfluxDB Line Protocol? See Documentation
</h5>
<Connect(LineProtocolTabs)
bucket="a"
org="a"
tabs={
Array [
"uploadFile",
"enterManually",
"enterURL",
]
}
/>
<LoadingStatusIndicator />
</Fragment>
`;

View File

@ -45,13 +45,7 @@ exports[`LineProtocolTabs rendering renders! 1`] = `
</RadioButton>
</Radio>
<div
style={
Object {
"height": "280px",
"marginTop": "30px",
"width": "750px",
}
}
className="wizard-step--lp-body"
/>
</Fragment>
`;

View File

@ -0,0 +1,25 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LoadingStatusIndicator rendering renders! 1`] = `
<Fragment>
<div
className="wizard-step--top-container"
>
<div
className="wizard-step--sparkle-container"
>
<SparkleSpinner
loading="NotStarted"
/>
</div>
</div>
<div
className="wizard-step--footer"
>
<div
className="wizard-step--text-state"
/>
</div>
<br />
</Fragment>
`;

View File

@ -27,20 +27,22 @@ const STREAMING_DATA_SOURCES_OPTIONS = [
class StreamingDataSourcesSelector extends PureComponent<Props> {
public render() {
return (
<GridSizer>
{STREAMING_DATA_SOURCES_OPTIONS.map(ds => {
return (
<CardSelectCard
key={ds}
id={ds}
name={ds}
label={ds}
checked={this.isCardChecked(ds)}
onClick={this.handleToggle(ds)}
/>
)
})}
</GridSizer>
<div className="wizard-step--grid-container-lg">
<GridSizer>
{STREAMING_DATA_SOURCES_OPTIONS.map(ds => {
return (
<CardSelectCard
key={ds}
id={ds}
name={ds}
label={ds}
checked={this.isCardChecked(ds)}
onClick={this.handleToggle(ds)}
/>
)
})}
</GridSizer>
</div>
)
}

View File

@ -24,20 +24,22 @@ const DATA_SOURCES_OPTIONS = [
class DataSourceTypeSelector extends PureComponent<Props> {
public render() {
return (
<GridSizer>
{DATA_SOURCES_OPTIONS.map(ds => {
return (
<CardSelectCard
key={ds}
id={ds}
name={ds}
label={ds}
checked={this.isCardChecked(ds)}
onClick={this.handleClick(ds)}
/>
)
})}
</GridSizer>
<div className="wizard-step--grid-container-sm">
<GridSizer>
{DATA_SOURCES_OPTIONS.map(ds => {
return (
<CardSelectCard
key={ds}
id={ds}
name={ds}
label={ds}
checked={this.isCardChecked(ds)}
onClick={this.handleClick(ds)}
/>
)
})}
</GridSizer>
</div>
)
}

View File

@ -18,7 +18,9 @@ class ListeningResults extends PureComponent<Props> {
public render() {
return (
<>
<h4 className={this.className}>{this.header}</h4>
<h4 className={`wizard-step--text-state ${this.className}`}>
{this.header}
</h4>
<p>{this.additionalText}</p>
</>
)

View File

@ -3,7 +3,7 @@
exports[`Onboarding.Components.ConnectionInformation matches snapshot if error 1`] = `
<Fragment>
<h4
className="error"
className="wizard-step--text-state error"
>
Connection Not Found
</h4>
@ -16,7 +16,7 @@ exports[`Onboarding.Components.ConnectionInformation matches snapshot if error 1
exports[`Onboarding.Components.ConnectionInformation matches snapshot if loading 1`] = `
<Fragment>
<h4
className="loading"
className="wizard-step--text-state loading"
>
Awaiting Connection...
</h4>
@ -29,7 +29,7 @@ exports[`Onboarding.Components.ConnectionInformation matches snapshot if loading
exports[`Onboarding.Components.ConnectionInformation matches snapshot if success 1`] = `
<Fragment>
<h4
className="success"
className="wizard-step--text-state success"
>
Connection Found!
</h4>

View File

@ -5,12 +5,14 @@ import {
DataLoaderType,
LineProtocolTab,
} from 'src/types/v2/dataLoaders'
import {RemoteDataState} from 'src/types'
export interface DataLoadersState {
telegrafPlugins: TelegrafPlugin[]
type: DataLoaderType
lineProtocolText: string
activeLPTab: LineProtocolTab
lpStatus: RemoteDataState
}
export const INITIAL_STATE: DataLoadersState = {
@ -18,6 +20,7 @@ export const INITIAL_STATE: DataLoadersState = {
type: DataLoaderType.Empty,
lineProtocolText: '',
activeLPTab: LineProtocolTab.UploadFile,
lpStatus: RemoteDataState.NotStarted,
}
export default (state = INITIAL_STATE, action: Action): DataLoadersState => {
@ -62,6 +65,11 @@ export default (state = INITIAL_STATE, action: Action): DataLoadersState => {
...state,
activeLPTab: action.payload.activeLPTab,
}
case 'SET_LP_STATUS':
return {
...state,
lpStatus: action.payload.lpStatus,
}
default:
return state
}

View File

@ -99,7 +99,7 @@ export enum LineProtocolTab {
export enum LineProtocolStatus {
ImportData = 'importData',
Loading = 'enterManually',
Loading = 'loading',
Success = 'success',
Error = 'error',
}