feat(notebooks): display raw data within query pipes (#18341)
* refactor: add left indentation on all pipes to make titles easier to scan * refactor: store results in raw and parsed formats This intended is a bandaid fix until raw data table comes from Giraffe and accepts data in the standard way * refactor: remove unused scss variable * feat: create raw data components and display within query pipes * fix: remove console log * refactor: begone lodashpull/18357/head
parent
fd289883fe
commit
b4f4a11cff
|
@ -12,7 +12,7 @@ const Pipe: FC<PipeProp> = props => {
|
|||
|
||||
return useMemo(
|
||||
() => createElement(PIPE_DEFINITIONS[data.type].component, props),
|
||||
[props.data]
|
||||
[props.data, props.results]
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import EmptyPipeList from 'src/notebooks/components/EmptyPipeList'
|
|||
import {DapperScrollbars} from '@influxdata/clockface'
|
||||
|
||||
const PipeList: FC = () => {
|
||||
const {id, pipes, updatePipe} = useContext(NotebookContext)
|
||||
const {id, pipes, updatePipe, results} = useContext(NotebookContext)
|
||||
const {scrollPosition} = useContext(ScrollContext)
|
||||
const update = useCallback(updatePipe, [id])
|
||||
|
||||
|
@ -26,6 +26,7 @@ const PipeList: FC = () => {
|
|||
index={index}
|
||||
data={pipes[index]}
|
||||
onUpdate={update}
|
||||
results={results[index]}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, {FC, useState, useCallback, RefObject} from 'react'
|
||||
import {RemoteDataState} from 'src/types'
|
||||
import {PipeData} from 'src/notebooks'
|
||||
import {FromFluxResult} from '@influxdata/giraffe'
|
||||
import {BothResults} from 'src/notebooks/context/query'
|
||||
|
||||
export interface PipeMeta {
|
||||
title: string
|
||||
|
@ -16,11 +16,11 @@ export interface NotebookContextType {
|
|||
id: string
|
||||
pipes: PipeData[]
|
||||
meta: PipeMeta[] // data only used for the view layer for Notebooks
|
||||
results: FromFluxResult[]
|
||||
results: BothResults[]
|
||||
addPipe: (pipe: PipeData) => void
|
||||
updatePipe: (idx: number, pipe: PipeData) => void
|
||||
updateMeta: (idx: number, pipe: PipeMeta) => void
|
||||
updateResult: (idx: number, result: FromFluxResult) => void
|
||||
updateResult: (idx: number, result: BothResults) => void
|
||||
movePipe: (currentIdx: number, newIdx: number) => void
|
||||
removePipe: (idx: number) => void
|
||||
}
|
||||
|
@ -115,11 +115,11 @@ export const NotebookProvider: FC = ({children}) => {
|
|||
)
|
||||
|
||||
const updateResult = useCallback(
|
||||
(idx: number, results: FromFluxResult) => {
|
||||
(idx: number, results: BothResults) => {
|
||||
_setResults(pipes => {
|
||||
pipes[idx] = {
|
||||
...results,
|
||||
} as FromFluxResult
|
||||
} as BothResults
|
||||
return pipes.slice()
|
||||
})
|
||||
},
|
||||
|
|
|
@ -11,12 +11,17 @@ import {NotebookContext} from 'src/notebooks/context/notebook'
|
|||
import {TimeContext} from 'src/notebooks/context/time'
|
||||
import {fromFlux as parse, FromFluxResult} from '@influxdata/giraffe'
|
||||
|
||||
export interface BothResults {
|
||||
parsed: FromFluxResult
|
||||
raw: string
|
||||
}
|
||||
|
||||
export interface QueryContextType {
|
||||
query: (text: string) => Promise<FromFluxResult>
|
||||
query: (text: string) => Promise<BothResults>
|
||||
}
|
||||
|
||||
export const DEFAULT_CONTEXT: QueryContextType = {
|
||||
query: () => Promise.resolve({} as FromFluxResult),
|
||||
query: () => Promise.resolve({} as BothResults),
|
||||
}
|
||||
|
||||
export const QueryContext = React.createContext<QueryContextType>(
|
||||
|
@ -48,7 +53,10 @@ export const QueryProvider: FC<Props> = ({children, variables, org}) => {
|
|||
return raw
|
||||
})
|
||||
.then(raw => {
|
||||
return parse(raw.csv)
|
||||
return {
|
||||
raw: raw.csv,
|
||||
parsed: parse(raw.csv),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {FunctionComponent, ComponentClass, ReactNode} from 'react'
|
||||
import {FromFluxResult} from '@influxdata/giraffe'
|
||||
import {BothResults} from 'src/notebooks/context/query'
|
||||
|
||||
export interface PipeContextProps {
|
||||
children?: ReactNode
|
||||
|
@ -11,7 +11,7 @@ export type PipeData = any
|
|||
export interface PipeProp {
|
||||
data: PipeData
|
||||
onUpdate: (data: PipeData) => void
|
||||
results?: FromFluxResult
|
||||
results?: BothResults
|
||||
|
||||
Context:
|
||||
| FunctionComponent<PipeContextProps>
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import {BothResults} from 'src/notebooks/context/query'
|
||||
import {AutoSizer} from 'react-virtualized'
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Components
|
||||
import RawFluxDataTable from 'src/timeMachine/components/RawFluxDataTable'
|
||||
import ResultsHeader from 'src/notebooks/pipes/Query/ResultsHeader'
|
||||
|
||||
// Types
|
||||
import {RawDataSize} from 'src/notebooks/pipes/Query'
|
||||
|
||||
interface Props {
|
||||
results: BothResults
|
||||
size: RawDataSize
|
||||
onUpdateSize: (size: RawDataSize) => void
|
||||
}
|
||||
|
||||
const Results: FC<Props> = ({results, size, onUpdateSize}) => {
|
||||
const resultsExist = !!results.raw
|
||||
const className = classnames('notebook-raw-data', {
|
||||
[`notebook-raw-data__${size}`]: resultsExist && size,
|
||||
})
|
||||
|
||||
let resultsBody = (
|
||||
<div className="notebook-raw-data--empty">
|
||||
Run the Notebook to see results
|
||||
</div>
|
||||
)
|
||||
|
||||
if (resultsExist) {
|
||||
resultsBody = (
|
||||
<div className="notebook-raw-data--body">
|
||||
<AutoSizer>
|
||||
{({width, height}) =>
|
||||
width &&
|
||||
height && (
|
||||
<RawFluxDataTable
|
||||
files={[results.raw]}
|
||||
width={width}
|
||||
height={height}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<ResultsHeader
|
||||
resultsExist={resultsExist}
|
||||
size={size}
|
||||
onUpdateSize={onUpdateSize}
|
||||
/>
|
||||
{resultsBody}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Results
|
|
@ -0,0 +1,55 @@
|
|||
// Libraries
|
||||
import React, {FC} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
// Components
|
||||
import {Icon, IconFont} from '@influxdata/clockface'
|
||||
|
||||
// Types
|
||||
import {RawDataSize} from 'src/notebooks/pipes/Query'
|
||||
|
||||
interface Props {
|
||||
resultsExist: boolean
|
||||
size: RawDataSize
|
||||
onUpdateSize: (size: RawDataSize) => void
|
||||
}
|
||||
|
||||
const ResultsHeader: FC<Props> = ({resultsExist, size, onUpdateSize}) => {
|
||||
if (!resultsExist) {
|
||||
return (
|
||||
<div className="notebook-raw-data--header">
|
||||
<Icon glyph={IconFont.DashF} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const handleClick = (newSize: RawDataSize) => (): void => {
|
||||
onUpdateSize(newSize)
|
||||
}
|
||||
|
||||
const generateClassName = (buttonSize: RawDataSize): string => {
|
||||
return classnames('notebook-raw-data--size-toggle', {
|
||||
[`notebook-raw-data--size-toggle__${buttonSize}`]: buttonSize,
|
||||
'notebook-raw-data--size-toggle__active': buttonSize === size,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="notebook-raw-data--header">
|
||||
<div
|
||||
className={generateClassName('small')}
|
||||
onClick={handleClick('small')}
|
||||
/>
|
||||
<div
|
||||
className={generateClassName('medium')}
|
||||
onClick={handleClick('medium')}
|
||||
/>
|
||||
<div
|
||||
className={generateClassName('large')}
|
||||
onClick={handleClick('large')}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ResultsHeader
|
|
@ -2,11 +2,14 @@ import {register} from 'src/notebooks'
|
|||
import View from './view'
|
||||
import './style.scss'
|
||||
|
||||
export type RawDataSize = 'small' | 'medium' | 'large'
|
||||
|
||||
register({
|
||||
type: 'query',
|
||||
component: View,
|
||||
button: 'Custom Script',
|
||||
initial: {
|
||||
rawDataSize: 'small',
|
||||
activeQuery: 0,
|
||||
queries: [
|
||||
{
|
||||
|
|
|
@ -1,8 +1,114 @@
|
|||
@import '@influxdata/clockface/dist/variables.scss';
|
||||
|
||||
$notebook-results-header: 47px;
|
||||
$notebook-results-small: 200px;
|
||||
$notebook-results-medium: 400px;
|
||||
$notebook-results-large: 600px;
|
||||
|
||||
.notebook-panel--body .flux-editor--monaco {
|
||||
position: relative;
|
||||
|
||||
.react-monaco-editor-container {
|
||||
min-height: 100px;
|
||||
min-height: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
.notebook-raw-data {
|
||||
margin-top: $cf-marg-a;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
border: $cf-border solid $g2-kevlar;
|
||||
background-color: $g2-kevlar;
|
||||
border-radius: $cf-radius 0 0 $cf-radius;
|
||||
}
|
||||
|
||||
.notebook-raw-data__small {
|
||||
height: $notebook-results-small;
|
||||
}
|
||||
|
||||
.notebook-raw-data__medium {
|
||||
height: $notebook-results-medium;
|
||||
}
|
||||
|
||||
.notebook-raw-data__large {
|
||||
height: $notebook-results-large;
|
||||
}
|
||||
|
||||
.notebook-raw-data--header {
|
||||
color: $g8-storm;
|
||||
width: $notebook-results-header;
|
||||
flex: 0 0 $notebook-results-header;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: $cf-marg-b 0;
|
||||
}
|
||||
|
||||
.notebook-raw-data--body,
|
||||
.notebook-raw-data--empty {
|
||||
position: relative;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
|
||||
.notebook-raw-data--empty {
|
||||
color: $g8-storm;
|
||||
user-select: none;
|
||||
padding: $cf-marg-b 0;
|
||||
font-weight: $cf-font-weight--medium;
|
||||
}
|
||||
|
||||
.notebook-raw-data__success {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.notebook-raw-data--size-toggle {
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-bottom: $cf-marg-a;
|
||||
border: $cf-border solid $g3-castle;
|
||||
background-color: $g1-raven;
|
||||
transition: background-color 0.25s ease;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
border-radius: 50%;
|
||||
transition: width 0.25s ease, height 0.25s ease, background-color 0.25s ease;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: $g5-pepper;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
&:after {
|
||||
background-color: $g7-graphite;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notebook-raw-data--size-toggle__active:after,
|
||||
.notebook-raw-data--size-toggle__active:hover:after {
|
||||
background-color: $c-pool;
|
||||
}
|
||||
|
||||
.notebook-raw-data--size-toggle__small:after {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.notebook-raw-data--size-toggle__medium:after {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.notebook-raw-data--size-toggle__large:after {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,21 @@
|
|||
// Libraries
|
||||
import React, {FC, useMemo} from 'react'
|
||||
import {PipeProp} from 'src/notebooks'
|
||||
import FluxMonacoEditor from 'src/shared/components/FluxMonacoEditor'
|
||||
|
||||
const Query: FC<PipeProp> = ({data, onUpdate, Context}) => {
|
||||
// Types
|
||||
import {PipeProp} from 'src/notebooks'
|
||||
import {RawDataSize} from 'src/notebooks/pipes/Query'
|
||||
|
||||
// Components
|
||||
import FluxMonacoEditor from 'src/shared/components/FluxMonacoEditor'
|
||||
import Results from 'src/notebooks/pipes/Query/Results'
|
||||
|
||||
// Styles
|
||||
import 'src/notebooks/pipes/Query/style.scss'
|
||||
|
||||
const Query: FC<PipeProp> = ({data, onUpdate, Context, results}) => {
|
||||
const {queries, activeQuery} = data
|
||||
const query = queries[activeQuery]
|
||||
const size = data.rawDataSize || 'small'
|
||||
|
||||
function updateText(text) {
|
||||
const _queries = queries.slice()
|
||||
|
@ -16,6 +27,10 @@ const Query: FC<PipeProp> = ({data, onUpdate, Context}) => {
|
|||
onUpdate({queries: _queries})
|
||||
}
|
||||
|
||||
const onUpdateSize = (rawDataSize: RawDataSize): void => {
|
||||
onUpdate({rawDataSize})
|
||||
}
|
||||
|
||||
return useMemo(
|
||||
() => (
|
||||
<Context>
|
||||
|
@ -25,9 +40,10 @@ const Query: FC<PipeProp> = ({data, onUpdate, Context}) => {
|
|||
onSubmitScript={() => {}}
|
||||
autogrow
|
||||
/>
|
||||
<Results results={results} size={size} onUpdateSize={onUpdateSize} />
|
||||
</Context>
|
||||
),
|
||||
[query.text]
|
||||
[query.text, results, data.rawDataSize]
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
@import '@influxdata/clockface/dist/variables.scss';
|
||||
|
||||
$notebook-panel--bg: mix($g1-raven, $g2-kevlar, 50%);
|
||||
|
||||
.notebook-panel--markdown {
|
||||
font-size: 14px;
|
||||
line-height: 16px;
|
||||
|
|
|
@ -133,6 +133,7 @@
|
|||
.notebook-panel--body {
|
||||
border-radius: 0 0 $cf-radius $cf-radius;
|
||||
padding: $cf-marg-b;
|
||||
padding-left: $cf-marg-d;
|
||||
padding-top: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue