Connect Explore child components to store

This commit is contained in:
David Kaltschmidt 2019-01-13 23:10:23 +01:00
parent 607f7c25de
commit 546a3a9d98
7 changed files with 394 additions and 324 deletions

View File

@ -6,86 +6,58 @@ import { AutoSizer } from 'react-virtualized';
import { RawTimeRange, TimeRange } from '@grafana/ui';
import { DataSourceSelectItem } from 'app/types/datasources';
import {
ExploreItemState,
ExploreUrlState,
HistoryItem,
QueryTransaction,
RangeScanner,
ExploreId,
} from 'app/types/explore';
import { ExploreItemState, ExploreUrlState, RangeScanner, ExploreId } from 'app/types/explore';
import { DataQuery } from 'app/types/series';
import { StoreState } from 'app/types';
import store from 'app/core/store';
import { LAST_USED_DATASOURCE_KEY, ensureQueries, DEFAULT_RANGE } from 'app/core/utils/explore';
import { DataSourcePicker } from 'app/core/components/Select/DataSourcePicker';
import { Emitter } from 'app/core/utils/emitter';
import { LogsModel } from 'app/core/logs_model';
import TableModel from 'app/core/table_model';
import {
addQueryRow,
changeDatasource,
changeQuery,
changeSize,
changeTime,
clickClear,
clickCloseSplit,
clickExample,
clickGraphButton,
clickLogsButton,
clickSplit,
clickTableButton,
highlightLogsExpression,
initializeExplore,
modifyQueries,
removeQueryRow,
runQueries,
scanStart,
scanStop,
} from './state/actions';
import Panel from './Panel';
import QueryRows from './QueryRows';
import Graph from './Graph';
import Logs from './Logs';
import Table from './Table';
import ErrorBoundary from './ErrorBoundary';
import { Alert } from './Error';
import ErrorBoundary from './ErrorBoundary';
import GraphContainer from './GraphContainer';
import LogsContainer from './LogsContainer';
import QueryRows from './QueryRows';
import TableContainer from './TableContainer';
import TimePicker, { parseTime } from './TimePicker';
interface ExploreProps {
StartPage?: any;
addQueryRow: typeof addQueryRow;
changeDatasource: typeof changeDatasource;
changeQuery: typeof changeQuery;
changeSize: typeof changeSize;
changeTime: typeof changeTime;
clickClear: typeof clickClear;
clickCloseSplit: typeof clickCloseSplit;
clickExample: typeof clickExample;
clickGraphButton: typeof clickGraphButton;
clickLogsButton: typeof clickLogsButton;
clickSplit: typeof clickSplit;
clickTableButton: typeof clickTableButton;
datasourceError: string;
datasourceInstance: any;
datasourceLoading: boolean | null;
datasourceMissing: boolean;
exploreDatasources: DataSourceSelectItem[];
exploreId: ExploreId;
graphResult?: any[];
highlightLogsExpression: typeof highlightLogsExpression;
history: HistoryItem[];
initialDatasource?: string;
initialQueries: DataQuery[];
initializeExplore: typeof initializeExplore;
initialized: boolean;
logsHighlighterExpressions?: string[];
logsResult?: LogsModel;
loading: boolean;
modifyQueries: typeof modifyQueries;
queryTransactions: QueryTransaction[];
removeQueryRow: typeof removeQueryRow;
range: RawTimeRange;
runQueries: typeof runQueries;
scanner?: RangeScanner;
@ -94,14 +66,10 @@ interface ExploreProps {
scanStart: typeof scanStart;
scanStop: typeof scanStop;
split: boolean;
showingGraph: boolean;
showingLogs: boolean;
showingStartPage?: boolean;
showingTable: boolean;
supportsGraph: boolean | null;
supportsLogs: boolean | null;
supportsTable: boolean | null;
tableResult?: TableModel;
urlState: ExploreUrlState;
}
@ -172,24 +140,10 @@ export class Explore extends React.PureComponent<ExploreProps> {
this.el = el;
};
onAddQueryRow = index => {
this.props.addQueryRow(this.props.exploreId, index);
};
onChangeDatasource = async option => {
this.props.changeDatasource(this.props.exploreId, option.value);
};
onChangeQuery = (query: DataQuery, index: number, override?: boolean) => {
const { changeQuery, datasourceInstance, exploreId } = this.props;
changeQuery(exploreId, query, index, override);
if (query && !override && datasourceInstance.getHighlighterExpression && index === 0) {
// Live preview of log search matches. Only use on first row for now
this.updateLogsHighlights(query);
}
};
onChangeTime = (range: TimeRange, changedByScanner?: boolean) => {
if (this.props.scanning && !changedByScanner) {
this.onStopScanning();
@ -205,14 +159,6 @@ export class Explore extends React.PureComponent<ExploreProps> {
this.props.clickCloseSplit();
};
onClickGraphButton = () => {
this.props.clickGraphButton(this.props.exploreId);
};
onClickLogsButton = () => {
this.props.clickLogsButton(this.props.exploreId);
};
// Use this in help pages to set page to a single query
onClickExample = (query: DataQuery) => {
this.props.clickExample(this.props.exploreId, query);
@ -222,10 +168,6 @@ export class Explore extends React.PureComponent<ExploreProps> {
this.props.clickSplit();
};
onClickTableButton = () => {
this.props.clickTableButton(this.props.exploreId);
};
onClickLabel = (key: string, value: string) => {
this.onModifyQueries({ type: 'ADD_FILTER', key, value });
};
@ -238,10 +180,6 @@ export class Explore extends React.PureComponent<ExploreProps> {
}
};
onRemoveQueryRow = index => {
this.props.removeQueryRow(this.props.exploreId, index);
};
onResize = (size: { height: number; width: number }) => {
this.props.changeSize(this.props.exploreId, size);
};
@ -265,14 +203,6 @@ export class Explore extends React.PureComponent<ExploreProps> {
this.props.runQueries(this.props.exploreId);
};
updateLogsHighlights = _.debounce((value: DataQuery) => {
const { datasourceInstance } = this.props;
if (datasourceInstance.getHighlighterExpression) {
const expressions = [datasourceInstance.getHighlighterExpression(value)];
this.props.highlightLogsExpression(this.props.exploreId, expressions);
}
}, 500);
render() {
const {
StartPage,
@ -282,34 +212,19 @@ export class Explore extends React.PureComponent<ExploreProps> {
datasourceMissing,
exploreDatasources,
exploreId,
graphResult,
history,
loading,
initialQueries,
logsHighlighterExpressions,
logsResult,
queryTransactions,
range,
scanning,
scanRange,
showingGraph,
showingLogs,
showingStartPage,
showingTable,
split,
supportsGraph,
supportsLogs,
supportsTable,
tableResult,
} = this.props;
const graphHeight = showingGraph && showingTable ? '200px' : '400px';
const exploreClass = split ? 'explore explore-split' : 'explore';
const selectedDatasource = datasourceInstance
? exploreDatasources.find(d => d.name === datasourceInstance.name)
: undefined;
const graphLoading = queryTransactions.some(qt => qt.resultType === 'Graph' && !qt.done);
const tableLoading = queryTransactions.some(qt => qt.resultType === 'Table' && !qt.done);
const logsLoading = queryTransactions.some(qt => qt.resultType === 'Logs' && !qt.done);
const loading = queryTransactions.some(qt => !qt.done);
return (
<div className={exploreClass} ref={this.getRef}>
@ -372,19 +287,7 @@ export class Explore extends React.PureComponent<ExploreProps> {
{datasourceInstance &&
!datasourceError && (
<div className="explore-container">
<QueryRows
datasource={datasourceInstance}
history={history}
initialQueries={initialQueries}
onAddQueryRow={this.onAddQueryRow}
onChangeQuery={this.onChangeQuery}
onClickHintFix={this.onModifyQueries}
onExecuteQuery={this.onSubmit}
onRemoveQueryRow={this.onRemoveQueryRow}
transactions={queryTransactions}
exploreEvents={this.exploreEvents}
range={range}
/>
<QueryRows exploreEvents={this.exploreEvents} exploreId={exploreId} initialQueries={initialQueries} />
<AutoSizer onResize={this.onResize} disableHeight>
{({ width }) => (
<main className="m-t-2" style={{ width }}>
@ -392,55 +295,16 @@ export class Explore extends React.PureComponent<ExploreProps> {
{showingStartPage && <StartPage onClickExample={this.onClickExample} />}
{!showingStartPage && (
<>
{supportsGraph && (
<Panel
label="Graph"
isOpen={showingGraph}
loading={graphLoading}
onToggle={this.onClickGraphButton}
>
<Graph
data={graphResult}
height={graphHeight}
id={`explore-graph-${exploreId}`}
onChangeTime={this.onChangeTime}
range={range}
split={split}
/>
</Panel>
)}
{supportsTable && (
<Panel
label="Table"
loading={tableLoading}
isOpen={showingTable}
onToggle={this.onClickTableButton}
>
<Table data={tableResult} loading={tableLoading} onClickCell={this.onClickLabel} />
</Panel>
)}
{supportsGraph && <GraphContainer exploreId={exploreId} />}
{supportsTable && <TableContainer exploreId={exploreId} onClickCell={this.onClickLabel} />}
{supportsLogs && (
<Panel
label="Logs"
loading={logsLoading}
isOpen={showingLogs}
onToggle={this.onClickLogsButton}
>
<Logs
data={logsResult}
exploreId={exploreId}
key={logsResult.id}
highlighterExpressions={logsHighlighterExpressions}
loading={logsLoading}
onChangeTime={this.onChangeTime}
onClickLabel={this.onClickLabel}
onStartScanning={this.onStartScanning}
onStopScanning={this.onStopScanning}
range={range}
scanning={scanning}
scanRange={scanRange}
/>
</Panel>
<LogsContainer
exploreId={exploreId}
onChangeTime={this.onChangeTime}
onClickLabel={this.onClickLabel}
onStartScanning={this.onStartScanning}
onStopScanning={this.onStopScanning}
/>
)}
</>
)}
@ -466,26 +330,17 @@ function mapStateToProps(state: StoreState, { exploreId }) {
datasourceLoading,
datasourceMissing,
exploreDatasources,
graphResult,
initialDatasource,
initialQueries,
initialized,
history,
logsHighlighterExpressions,
logsResult,
queryTransactions,
range,
scanning,
scanRange,
showingGraph,
showingLogs,
showingStartPage,
showingTable,
supportsGraph,
supportsLogs,
supportsTable,
tableResult,
} = item;
const loading = queryTransactions.some(qt => !qt.done);
return {
StartPage,
datasourceError,
@ -493,46 +348,30 @@ function mapStateToProps(state: StoreState, { exploreId }) {
datasourceLoading,
datasourceMissing,
exploreDatasources,
graphResult,
initialDatasource,
initialQueries,
initialized,
history,
logsHighlighterExpressions,
logsResult,
loading,
queryTransactions,
range,
scanning,
scanRange,
showingGraph,
showingLogs,
showingStartPage,
showingTable,
split,
supportsGraph,
supportsLogs,
supportsTable,
tableResult,
};
}
const mapDispatchToProps = {
addQueryRow,
changeDatasource,
changeQuery,
changeSize,
changeTime,
clickClear,
clickCloseSplit,
clickExample,
clickGraphButton,
clickLogsButton,
clickSplit,
clickTableButton,
highlightLogsExpression,
initializeExplore,
modifyQueries,
removeQueryRow,
runQueries,
scanStart,
scanStop,

View File

@ -0,0 +1,61 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import { RawTimeRange, TimeRange } from '@grafana/ui';
import { ExploreId, ExploreItemState } from 'app/types/explore';
import { StoreState } from 'app/types';
import { clickGraphButton } from './state/actions';
import Graph from './Graph';
import Panel from './Panel';
interface GraphContainerProps {
onChangeTime: (range: TimeRange) => void;
clickGraphButton: typeof clickGraphButton;
exploreId: ExploreId;
graphResult?: any[];
loading: boolean;
range: RawTimeRange;
showingGraph: boolean;
showingTable: boolean;
split: boolean;
}
export class GraphContainer extends PureComponent<GraphContainerProps> {
onClickGraphButton = () => {
this.props.clickGraphButton(this.props.exploreId);
};
render() {
const { exploreId, graphResult, loading, onChangeTime, showingGraph, showingTable, range, split } = this.props;
const graphHeight = showingGraph && showingTable ? '200px' : '400px';
return (
<Panel label="Graph" isOpen={showingGraph} loading={loading} onToggle={this.onClickGraphButton}>
<Graph
data={graphResult}
height={graphHeight}
id={`explore-graph-${exploreId}`}
onChangeTime={onChangeTime}
range={range}
split={split}
/>
</Panel>
);
}
}
function mapStateToProps(state: StoreState, { exploreId }) {
const explore = state.explore;
const { split } = explore;
const item: ExploreItemState = explore[exploreId];
const { graphResult, queryTransactions, range, showingGraph, showingTable } = item;
const loading = queryTransactions.some(qt => qt.resultType === 'Graph' && !qt.done);
return { graphResult, loading, range, showingGraph, showingTable, split };
}
const mapDispatchToProps = {
clickGraphButton,
};
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(GraphContainer));

View File

@ -0,0 +1,91 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import { RawTimeRange, TimeRange } from '@grafana/ui';
import { ExploreId, ExploreItemState } from 'app/types/explore';
import { LogsModel } from 'app/core/logs_model';
import { StoreState } from 'app/types';
import { clickLogsButton } from './state/actions';
import Logs from './Logs';
import Panel from './Panel';
interface LogsContainerProps {
clickLogsButton: typeof clickLogsButton;
exploreId: ExploreId;
loading: boolean;
logsHighlighterExpressions?: string[];
logsResult?: LogsModel;
onChangeTime: (range: TimeRange) => void;
onClickLabel: (key: string, value: string) => void;
onStartScanning: () => void;
onStopScanning: () => void;
range: RawTimeRange;
scanning?: boolean;
scanRange?: RawTimeRange;
showingLogs: boolean;
}
export class LogsContainer extends PureComponent<LogsContainerProps> {
onClickLogsButton = () => {
this.props.clickLogsButton(this.props.exploreId);
};
render() {
const {
exploreId,
loading,
logsHighlighterExpressions,
logsResult,
onChangeTime,
onClickLabel,
onStartScanning,
onStopScanning,
range,
showingLogs,
scanning,
scanRange,
} = this.props;
return (
<Panel label="Logs" loading={loading} isOpen={showingLogs} onToggle={this.onClickLogsButton}>
<Logs
data={logsResult}
exploreId={exploreId}
key={logsResult.id}
highlighterExpressions={logsHighlighterExpressions}
loading={loading}
onChangeTime={onChangeTime}
onClickLabel={onClickLabel}
onStartScanning={onStartScanning}
onStopScanning={onStopScanning}
range={range}
scanning={scanning}
scanRange={scanRange}
/>
</Panel>
);
}
}
function mapStateToProps(state: StoreState, { exploreId }) {
const explore = state.explore;
const item: ExploreItemState = explore[exploreId];
const { logsHighlighterExpressions, logsResult, queryTransactions, scanning, scanRange, showingLogs, range } = item;
const loading = queryTransactions.some(qt => qt.resultType === 'Logs' && !qt.done);
return {
loading,
logsHighlighterExpressions,
logsResult,
scanning,
scanRange,
showingLogs,
range,
};
}
const mapDispatchToProps = {
clickLogsButton,
};
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(LogsContainer));

View File

@ -0,0 +1,163 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import { RawTimeRange } from '@grafana/ui';
import _ from 'lodash';
import { QueryTransaction, HistoryItem, QueryHint, ExploreItemState, ExploreId } from 'app/types/explore';
import { Emitter } from 'app/core/utils/emitter';
import { DataQuery, StoreState } from 'app/types';
// import DefaultQueryField from './QueryField';
import QueryEditor from './QueryEditor';
import QueryTransactionStatus from './QueryTransactionStatus';
import {
addQueryRow,
changeQuery,
highlightLogsExpression,
modifyQueries,
removeQueryRow,
runQueries,
} from './state/actions';
function getFirstHintFromTransactions(transactions: QueryTransaction[]): QueryHint {
const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0);
if (transaction) {
return transaction.hints[0];
}
return undefined;
}
interface QueryRowProps {
addQueryRow: typeof addQueryRow;
changeQuery: typeof changeQuery;
className?: string;
exploreId: ExploreId;
datasourceInstance: any;
highlightLogsExpression: typeof highlightLogsExpression;
history: HistoryItem[];
index: number;
initialQuery: DataQuery;
modifyQueries: typeof modifyQueries;
queryTransactions: QueryTransaction[];
exploreEvents: Emitter;
range: RawTimeRange;
removeQueryRow: typeof removeQueryRow;
runQueries: typeof runQueries;
}
export class QueryRow extends PureComponent<QueryRowProps> {
onExecuteQuery = () => {
const { exploreId } = this.props;
this.props.runQueries(exploreId);
};
onChangeQuery = (query: DataQuery, override?: boolean) => {
const { datasourceInstance, exploreId, index } = this.props;
this.props.changeQuery(exploreId, query, index, override);
if (query && !override && datasourceInstance.getHighlighterExpression && index === 0) {
// Live preview of log search matches. Only use on first row for now
this.updateLogsHighlights(query);
}
};
onClickAddButton = () => {
const { exploreId, index } = this.props;
this.props.addQueryRow(exploreId, index);
};
onClickClearButton = () => {
this.onChangeQuery(null, true);
};
onClickHintFix = action => {
const { datasourceInstance, exploreId, index } = this.props;
if (datasourceInstance && datasourceInstance.modifyQuery) {
const modifier = (queries: DataQuery, action: any) => datasourceInstance.modifyQuery(queries, action);
this.props.modifyQueries(exploreId, action, index, modifier);
}
};
onClickRemoveButton = () => {
const { exploreId, index } = this.props;
this.props.removeQueryRow(exploreId, index);
};
updateLogsHighlights = _.debounce((value: DataQuery) => {
const { datasourceInstance } = this.props;
if (datasourceInstance.getHighlighterExpression) {
const expressions = [datasourceInstance.getHighlighterExpression(value)];
this.props.highlightLogsExpression(this.props.exploreId, expressions);
}
}, 500);
render() {
const { datasourceInstance, history, index, initialQuery, queryTransactions, exploreEvents, range } = this.props;
const transactions = queryTransactions.filter(t => t.rowIndex === index);
const transactionWithError = transactions.find(t => t.error !== undefined);
const hint = getFirstHintFromTransactions(transactions);
const queryError = transactionWithError ? transactionWithError.error : null;
const QueryField = datasourceInstance.pluginExports.ExploreQueryField;
return (
<div className="query-row">
<div className="query-row-status">
<QueryTransactionStatus transactions={transactions} />
</div>
<div className="query-row-field">
{QueryField ? (
<QueryField
datasource={datasourceInstance}
error={queryError}
hint={hint}
initialQuery={initialQuery}
history={history}
onClickHintFix={this.onClickHintFix}
onPressEnter={this.onExecuteQuery}
onQueryChange={this.onChangeQuery}
/>
) : (
<QueryEditor
datasource={datasourceInstance}
error={queryError}
onQueryChange={this.onChangeQuery}
onExecuteQuery={this.onExecuteQuery}
initialQuery={initialQuery}
exploreEvents={exploreEvents}
range={range}
/>
)}
</div>
<div className="query-row-tools">
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickClearButton}>
<i className="fa fa-times" />
</button>
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickAddButton}>
<i className="fa fa-plus" />
</button>
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickRemoveButton}>
<i className="fa fa-minus" />
</button>
</div>
</div>
);
}
}
function mapStateToProps(state: StoreState, { exploreId, index }) {
const explore = state.explore;
const item: ExploreItemState = explore[exploreId];
const { datasourceInstance, history, initialQueries, queryTransactions, range } = item;
const initialQuery = initialQueries[index];
return { datasourceInstance, history, initialQuery, queryTransactions, range };
}
const mapDispatchToProps = {
addQueryRow,
changeQuery,
highlightLogsExpression,
modifyQueries,
removeQueryRow,
runQueries,
};
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(QueryRow));

View File

@ -1,159 +1,25 @@
import React, { PureComponent } from 'react';
import { QueryTransaction, HistoryItem, QueryHint } from 'app/types/explore';
import { Emitter } from 'app/core/utils/emitter';
import { DataQuery } from 'app/types';
import { ExploreId } from 'app/types/explore';
// import DefaultQueryField from './QueryField';
import QueryEditor from './QueryEditor';
import QueryTransactionStatus from './QueryTransactionStatus';
import { DataSource, DataQuery } from 'app/types';
import { RawTimeRange } from '@grafana/ui';
import QueryRow from './QueryRow';
function getFirstHintFromTransactions(transactions: QueryTransaction[]): QueryHint {
const transaction = transactions.find(qt => qt.hints && qt.hints.length > 0);
if (transaction) {
return transaction.hints[0];
}
return undefined;
}
interface QueryRowEventHandlers {
onAddQueryRow: (index: number) => void;
onChangeQuery: (value: DataQuery, index: number, override?: boolean) => void;
onClickHintFix: (action: object, index?: number) => void;
onExecuteQuery: () => void;
onRemoveQueryRow: (index: number) => void;
}
interface QueryRowCommonProps {
interface QueryRowsProps {
className?: string;
datasource: DataSource;
history: HistoryItem[];
transactions: QueryTransaction[];
exploreEvents: Emitter;
range: RawTimeRange;
exploreId: ExploreId;
initialQueries: DataQuery[];
}
type QueryRowProps = QueryRowCommonProps &
QueryRowEventHandlers & {
index: number;
initialQuery: DataQuery;
};
class QueryRow extends PureComponent<QueryRowProps> {
onExecuteQuery = () => {
const { onExecuteQuery } = this.props;
onExecuteQuery();
};
onChangeQuery = (value: DataQuery, override?: boolean) => {
const { index, onChangeQuery } = this.props;
if (onChangeQuery) {
onChangeQuery(value, index, override);
}
};
onClickAddButton = () => {
const { index, onAddQueryRow } = this.props;
if (onAddQueryRow) {
onAddQueryRow(index);
}
};
onClickClearButton = () => {
this.onChangeQuery(null, true);
};
onClickHintFix = action => {
const { index, onClickHintFix } = this.props;
if (onClickHintFix) {
onClickHintFix(action, index);
}
};
onClickRemoveButton = () => {
const { index, onRemoveQueryRow } = this.props;
if (onRemoveQueryRow) {
onRemoveQueryRow(index);
}
};
onPressEnter = () => {
const { onExecuteQuery } = this.props;
if (onExecuteQuery) {
onExecuteQuery();
}
};
render() {
const { datasource, history, initialQuery, transactions, exploreEvents, range } = this.props;
const transactionWithError = transactions.find(t => t.error !== undefined);
const hint = getFirstHintFromTransactions(transactions);
const queryError = transactionWithError ? transactionWithError.error : null;
const QueryField = datasource.pluginExports.ExploreQueryField;
return (
<div className="query-row">
<div className="query-row-status">
<QueryTransactionStatus transactions={transactions} />
</div>
<div className="query-row-field">
{QueryField ? (
<QueryField
datasource={datasource}
error={queryError}
hint={hint}
initialQuery={initialQuery}
history={history}
onClickHintFix={this.onClickHintFix}
onPressEnter={this.onPressEnter}
onQueryChange={this.onChangeQuery}
/>
) : (
<QueryEditor
datasource={datasource}
error={queryError}
onQueryChange={this.onChangeQuery}
onExecuteQuery={this.onExecuteQuery}
initialQuery={initialQuery}
exploreEvents={exploreEvents}
range={range}
/>
)}
</div>
<div className="query-row-tools">
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickClearButton}>
<i className="fa fa-times" />
</button>
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickAddButton}>
<i className="fa fa-plus" />
</button>
<button className="btn navbar-button navbar-button--tight" onClick={this.onClickRemoveButton}>
<i className="fa fa-minus" />
</button>
</div>
</div>
);
}
}
type QueryRowsProps = QueryRowCommonProps &
QueryRowEventHandlers & {
initialQueries: DataQuery[];
};
export default class QueryRows extends PureComponent<QueryRowsProps> {
render() {
const { className = '', initialQueries, transactions, ...handlers } = this.props;
const { className = '', exploreEvents, exploreId, initialQueries } = this.props;
return (
<div className={className}>
{initialQueries.map((query, index) => (
<QueryRow
key={query.key}
index={index}
initialQuery={query}
transactions={transactions.filter(t => t.rowIndex === index)}
{...handlers}
/>
// TODO instead of relying on initialQueries, move to react key list in redux
<QueryRow key={query.key} exploreEvents={exploreEvents} exploreId={exploreId} index={index} />
))}
</div>
);

View File

@ -0,0 +1,49 @@
import React, { PureComponent } from 'react';
import { hot } from 'react-hot-loader';
import { connect } from 'react-redux';
import { ExploreId, ExploreItemState } from 'app/types/explore';
import { StoreState } from 'app/types';
import { clickTableButton } from './state/actions';
import Table from './Table';
import Panel from './Panel';
import TableModel from 'app/core/table_model';
interface TableContainerProps {
clickTableButton: typeof clickTableButton;
exploreId: ExploreId;
loading: boolean;
onClickLabel: (key: string, value: string) => void;
showingTable: boolean;
tableResult?: TableModel;
}
export class TableContainer extends PureComponent<TableContainerProps> {
onClickTableButton = () => {
this.props.clickTableButton(this.props.exploreId);
};
render() {
const { loading, onClickLabel, showingTable, tableResult } = this.props;
return (
<Panel label="Table" loading={loading} isOpen={showingTable} onToggle={this.onClickTableButton}>
<Table data={tableResult} loading={loading} onClickCell={onClickLabel} />
</Panel>
);
}
}
function mapStateToProps(state: StoreState, { exploreId }) {
const explore = state.explore;
const item: ExploreItemState = explore[exploreId];
const { queryTransactions, showingTable, tableResult } = item;
const loading = queryTransactions.some(qt => qt.resultType === 'Table' && !qt.done);
return { loading, showingTable, tableResult };
}
const mapDispatchToProps = {
clickTableButton,
};
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(TableContainer));

View File

@ -232,6 +232,7 @@ const itemReducer = (state, action: Action): ExploreItemState => {
initialQueries: action.initialQueries,
logsHighlighterExpressions: undefined,
modifiedQueries: action.initialQueries.slice(),
queryTransactions: [],
showingStartPage: action.showingStartPage,
supportsGraph: action.supportsGraph,
supportsLogs: action.supportsLogs,