Move WriteData to ts

pull/3431/head
Andrew Watkins 2018-05-11 17:14:58 -07:00
parent c1ae106c24
commit eb024b928e
6 changed files with 258 additions and 195 deletions

View File

@ -1,105 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import WriteDataFooter from 'src/data_explorer/components/WriteDataFooter'
const WriteDataBody = ({
handleKeyUp,
handleCancelFile,
handleFile,
handleEdit,
handleSubmit,
inputContent,
uploadContent,
fileName,
isManual,
fileInput,
handleFileOpen,
isUploading,
}) => (
<div className="write-data-form--body">
{isManual ? (
<textarea
className="form-control write-data-form--input"
autoComplete="off"
spellCheck="false"
placeholder="<measurement>,<tag_key>=<tag_value> <field_key>=<field_value>"
onKeyUp={handleKeyUp}
onChange={handleEdit}
autoFocus={true}
data-test="manual-entry-field"
/>
) : (
<div
className={
uploadContent
? 'write-data-form--file'
: 'write-data-form--file write-data-form--file_active'
}
onClick={handleFileOpen}
>
{uploadContent ? (
<h3 className="write-data-form--filepath_selected">{fileName}</h3>
) : (
<h3 className="write-data-form--filepath_empty">
Drop a file here or click to upload
</h3>
)}
<div
className={
uploadContent
? 'write-data-form--graphic write-data-form--graphic_success'
: 'write-data-form--graphic'
}
/>
<input
type="file"
onChange={handleFile(false)}
className="write-data-form--upload"
ref={fileInput}
accept="text/*, application/gzip"
/>
{uploadContent && (
<span className="write-data-form--file-submit">
<button className="btn btn-md btn-success" onClick={handleSubmit}>
Write this File
</button>
<button
className="btn btn-md btn-default"
onClick={handleCancelFile}
>
Cancel
</button>
</span>
)}
</div>
)}
{isManual && (
<WriteDataFooter
isUploading={isUploading}
isManual={isManual}
inputContent={inputContent}
handleSubmit={handleSubmit}
uploadContent={uploadContent}
/>
)}
</div>
)
const {func, string, bool} = PropTypes
WriteDataBody.propTypes = {
handleKeyUp: func.isRequired,
handleEdit: func.isRequired,
handleCancelFile: func.isRequired,
handleFile: func.isRequired,
handleSubmit: func.isRequired,
inputContent: string,
uploadContent: string,
fileName: string,
isManual: bool,
fileInput: func.isRequired,
handleFileOpen: func.isRequired,
isUploading: bool.isRequired,
}
export default WriteDataBody

View File

@ -0,0 +1,162 @@
import React, {
PureComponent,
ChangeEvent,
KeyboardEvent,
MouseEvent,
DragEvent,
ReactElement,
} from 'react'
import WriteDataFooter from 'src/data_explorer/components/WriteDataFooter'
interface Props {
handleCancelFile: (e: MouseEvent<HTMLButtonElement>) => void
handleEdit: (e: ChangeEvent<HTMLTextAreaElement>) => void
handleKeyUp: (e: KeyboardEvent<HTMLTextAreaElement>) => void
handleFile: (drop: boolean) => (e: DragEvent<HTMLInputElement>) => void
handleSubmit: (e: MouseEvent<HTMLButtonElement>) => void
inputContent: string
uploadContent: string
fileName: string
isManual: boolean
fileInput: (ref: any) => any
handleFileOpen: () => void
isUploading: boolean
}
class WriteDataBody extends PureComponent<Props> {
public render() {
return (
<div className="write-data-form--body">
{this.input}
{this.footer}
</div>
)
}
private handleFile = (e: any): void => {
this.props.handleFile(false)(e)
}
private get input(): JSX.Element {
const {isManual} = this.props
if (isManual) {
return this.textarea
}
return this.dragArea
}
private get textarea(): ReactElement<HTMLTextAreaElement> {
const {handleKeyUp, handleEdit} = this.props
return (
<textarea
spellCheck={false}
autoFocus={true}
autoComplete="off"
onKeyUp={handleKeyUp}
onChange={handleEdit}
data-test="manual-entry-field"
className="form-control write-data-form--input"
placeholder="<measurement>,<tag_key>=<tag_value> <field_key>=<field_value>"
/>
)
}
private get dragArea(): ReactElement<HTMLDivElement> {
const {fileInput, handleFileOpen} = this.props
return (
<div className={this.dragAreaClass} onClick={handleFileOpen}>
{this.dragAreaHeader}
<div className={this.infoClass} />
<input
type="file"
ref={fileInput}
className="write-data-form--upload"
accept="text/*, application/gzip"
onChange={this.handleFile}
/>
{this.buttons}
</div>
)
}
private get dragAreaHeader(): ReactElement<HTMLHeadElement> {
const {uploadContent, fileName} = this.props
if (uploadContent) {
return <h3 className="write-data-form--filepath_selected">{fileName}</h3>
}
return (
<h3 className="write-data-form--filepath_empty">
Drop a file here or click to upload
</h3>
)
}
private get infoClass(): string {
const {uploadContent} = this.props
if (uploadContent) {
return 'write-data-form--graphic write-data-form--graphic_success'
}
return 'write-data-form--graphic'
}
private get buttons(): ReactElement<HTMLSpanElement> | null {
const {uploadContent, handleSubmit, handleCancelFile} = this.props
if (!uploadContent) {
return null
}
return (
<span className="write-data-form--file-submit">
<button className="btn btn-md btn-success" onClick={handleSubmit}>
Write this File
</button>
<button className="btn btn-md btn-default" onClick={handleCancelFile}>
Cancel
</button>
</span>
)
}
private get dragAreaClass(): string {
const {uploadContent} = this.props
if (uploadContent) {
return 'write-data-form--file'
}
return 'write-data-form--file write-data-form--file_active'
}
private get footer(): JSX.Element | null {
const {
isUploading,
isManual,
inputContent,
handleSubmit,
uploadContent,
} = this.props
if (!isManual) {
return null
}
return (
<WriteDataFooter
isUploading={isUploading}
isManual={isManual}
inputContent={inputContent}
handleSubmit={handleSubmit}
uploadContent={uploadContent}
/>
)
}
}
export default WriteDataBody

View File

@ -1,17 +1,43 @@
import React, {Component} from 'react' import React, {
import PropTypes from 'prop-types' PureComponent,
DragEvent,
ChangeEvent,
KeyboardEvent,
} from 'react'
import classnames from 'classnames' import classnames from 'classnames'
import OnClickOutside from 'shared/components/OnClickOutside' import OnClickOutside from 'src/shared/components/OnClickOutside'
import WriteDataBody from 'src/data_explorer/components/WriteDataBody' import WriteDataBody from 'src/data_explorer/components/WriteDataBody'
import WriteDataHeader from 'src/data_explorer/components/WriteDataHeader' import WriteDataHeader from 'src/data_explorer/components/WriteDataHeader'
import {OVERLAY_TECHNOLOGY} from 'shared/constants/classNames' import {OVERLAY_TECHNOLOGY} from 'src/shared/constants/classNames'
import {ErrorHandling} from 'src/shared/decorators/errors' import {ErrorHandling} from 'src/shared/decorators/errors'
import {Source, DropdownItem} from 'src/types'
let dragCounter = 0 let dragCounter = 0
interface Props {
source: Source
selectedDatabase: string
onClose: () => void
errorThrown: () => void
writeLineProtocol: (source: Source, database: string, content: string) => void
}
interface State {
selectedDatabase: string
inputContent: string | null
uploadContent: string
fileName: string
progress: string
isManual: boolean
dragClass: string
isUploading: boolean
}
@ErrorHandling @ErrorHandling
class WriteDataForm extends Component { class WriteDataForm extends PureComponent<Props, State> {
private fileInput: HTMLInputElement
constructor(props) { constructor(props) {
super(props) super(props)
this.state = { this.state = {
@ -26,23 +52,52 @@ class WriteDataForm extends Component {
} }
} }
toggleWriteView = isManual => () => { public render() {
const {onClose, errorThrown, source} = this.props
const {dragClass} = this.state
return (
<div
onDrop={this.handleFile(true)}
onDragOver={this.handleDragOver}
onDragEnter={this.handleDragEnter}
onDragExit={this.handleDragLeave}
onDragLeave={this.handleDragLeave}
className={classnames(OVERLAY_TECHNOLOGY, dragClass)}
>
<div className="write-data-form">
<WriteDataHeader
{...this.state}
source={source}
onClose={onClose}
errorThrown={errorThrown}
toggleWriteView={this.toggleWriteView}
handleSelectDatabase={this.handleSelectDatabase}
/>
<WriteDataBody
{...this.state}
fileInput={this.handleFileInputRef}
handleEdit={this.handleEdit}
handleFile={this.handleFile}
handleKeyUp={this.handleKeyUp}
handleSubmit={this.handleSubmit}
handleFileOpen={this.handleFileOpen}
handleCancelFile={this.handleCancelFile}
/>
</div>
</div>
)
}
private toggleWriteView = (isManual: boolean) => {
this.setState({isManual}) this.setState({isManual})
} }
handleSelectDatabase = item => { private handleSelectDatabase = (item: DropdownItem): void => {
this.setState({selectedDatabase: item.text}) this.setState({selectedDatabase: item.text})
} }
handleClickOutside = e => { private handleKeyUp = (e: KeyboardEvent<HTMLTextAreaElement>) => {
// guard against clicking to close error notification
if (e.target.className === OVERLAY_TECHNOLOGY) {
const {onClose} = this.props
onClose()
}
}
handleKeyUp = e => {
e.stopPropagation() e.stopPropagation()
if (e.key === 'Escape') { if (e.key === 'Escape') {
const {onClose} = this.props const {onClose} = this.props
@ -50,7 +105,7 @@ class WriteDataForm extends Component {
} }
} }
handleSubmit = async () => { private handleSubmit = async () => {
const {onClose, source, writeLineProtocol} = this.props const {onClose, source, writeLineProtocol} = this.props
const {inputContent, uploadContent, selectedDatabase, isManual} = this.state const {inputContent, uploadContent, selectedDatabase, isManual} = this.state
const content = isManual ? inputContent : uploadContent const content = isManual ? inputContent : uploadContent
@ -67,11 +122,11 @@ class WriteDataForm extends Component {
} }
} }
handleEdit = e => { private handleEdit = (e: ChangeEvent<HTMLTextAreaElement>): void => {
this.setState({inputContent: e.target.value.trim()}) this.setState({inputContent: e.target.value.trim()})
} }
handleFile = drop => e => { private handleFile = (drop: boolean) => (e: any): void => {
let file let file
if (drop) { if (drop) {
file = e.dataTransfer.files[0] file = e.dataTransfer.files[0]
@ -79,7 +134,7 @@ class WriteDataForm extends Component {
dragClass: 'drag-none', dragClass: 'drag-none',
}) })
} else { } else {
file = e.target.files[0] file = e.currentTarget.files[0]
} }
if (!file) { if (!file) {
@ -99,23 +154,23 @@ class WriteDataForm extends Component {
} }
} }
handleCancelFile = () => { private handleCancelFile = (): void => {
this.setState({uploadContent: ''}) this.setState({uploadContent: ''})
this.fileInput.value = '' this.fileInput.value = ''
} }
handleDragOver = e => { private handleDragOver = e => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
} }
handleDragEnter = e => { private handleDragEnter = (e: DragEvent<HTMLDivElement>): void => {
dragCounter += 1 dragCounter += 1
e.preventDefault() e.preventDefault()
this.setState({dragClass: 'drag-over'}) this.setState({dragClass: 'drag-over'})
} }
handleDragLeave = e => { private handleDragLeave = (e: DragEvent<HTMLDivElement>): void => {
dragCounter -= 1 dragCounter -= 1
e.preventDefault() e.preventDefault()
if (dragCounter === 0) { if (dragCounter === 0) {
@ -123,67 +178,14 @@ class WriteDataForm extends Component {
} }
} }
handleFileOpen = () => { private handleFileOpen = (): void => {
const {uploadContent} = this.state const {uploadContent} = this.state
if (uploadContent === '') { if (uploadContent === '') {
this.fileInput.click() this.fileInput.click()
} }
} }
handleFileInputRef = el => (this.fileInput = el) private handleFileInputRef = r => (this.fileInput = r)
render() {
const {onClose, errorThrown, source} = this.props
const {dragClass} = this.state
return (
<div
onDrop={this.handleFile(true)}
onDragOver={this.handleDragOver}
onDragEnter={this.handleDragEnter}
onDragExit={this.handleDragLeave}
onDragLeave={this.handleDragLeave}
className={classnames(OVERLAY_TECHNOLOGY, dragClass)}
>
<div className="write-data-form">
<WriteDataHeader
{...this.state}
source={source}
handleSelectDatabase={this.handleSelectDatabase}
errorThrown={errorThrown}
toggleWriteView={this.toggleWriteView}
onClose={onClose}
/>
<WriteDataBody
{...this.state}
fileInput={this.handleFileInputRef}
handleEdit={this.handleEdit}
handleFile={this.handleFile}
handleKeyUp={this.handleKeyUp}
handleSubmit={this.handleSubmit}
handleFileOpen={this.handleFileOpen}
handleCancelFile={this.handleCancelFile}
/>
</div>
</div>
)
}
}
const {func, shape, string} = PropTypes
WriteDataForm.propTypes = {
source: shape({
links: shape({
proxy: string.isRequired,
self: string.isRequired,
queries: string.isRequired,
}).isRequired,
}).isRequired,
onClose: func.isRequired,
writeLineProtocol: func.isRequired,
errorThrown: func.isRequired,
selectedDatabase: string,
} }
export default OnClickOutside(WriteDataForm) export default OnClickOutside(WriteDataForm)

View File

@ -1,10 +1,10 @@
import React, {PureComponent} from 'react' import React, {PureComponent} from 'react'
import DatabaseDropdown from 'src/shared/components/DatabaseDropdown' import DatabaseDropdown from 'src/shared/components/DatabaseDropdown'
import {Source} from 'src/types' import {Source, DropdownItem} from 'src/types'
interface Props { interface Props {
handleSelectDatabase: () => void handleSelectDatabase: (item: DropdownItem) => void
selectedDatabase: () => void selectedDatabase: string
toggleWriteView: (isWriteViewToggled: boolean) => void toggleWriteView: (isWriteViewToggled: boolean) => void
errorThrown: () => void errorThrown: () => void
onClose: () => void onClose: () => void

View File

@ -30,7 +30,9 @@ class DatabaseDropdown extends Component {
return ( return (
<Dropdown <Dropdown
items={databases.map(text => ({text}))} items={databases.map(text => ({
text,
}))}
selected={database || 'Loading...'} selected={database || 'Loading...'}
onChoose={onSelectDatabase} onChoose={onSelectDatabase}
onClick={onStartEdit ? onStartEdit : null} onClick={onStartEdit ? onStartEdit : null}
@ -50,11 +52,15 @@ class DatabaseDropdown extends Component {
const nonSystemDatabases = databases.filter(name => name !== '_internal') const nonSystemDatabases = databases.filter(name => name !== '_internal')
this.setState({databases: nonSystemDatabases}) this.setState({
databases: nonSystemDatabases,
})
const selectedDatabaseText = nonSystemDatabases.includes(database) const selectedDatabaseText = nonSystemDatabases.includes(database)
? database ? database
: nonSystemDatabases[0] || 'No databases' : nonSystemDatabases[0] || 'No databases'
onSelectDatabase({text: selectedDatabaseText}) onSelectDatabase({
text: selectedDatabaseText,
})
} catch (error) { } catch (error) {
console.error(error) console.error(error)
onErrorThrown(error) onErrorThrown(error)

View File

@ -1,10 +1,8 @@
import {ReactNode} from 'react' import {ReactNode} from 'react'
export type DropdownItem = export interface DropdownItem {
| {
text: string text: string
} }
| string
export interface DropdownAction { export interface DropdownAction {
icon: string icon: string