Explore: Limit number of columns shown in Explore table (#98726)

pull/98768/head^2
Piotr Jamróz 6 months ago committed by GitHub
parent e38bab43db
commit f7e0710f53
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 56
      public/app/features/explore/Graph/GraphContainer.tsx
  2. 48
      public/app/features/explore/LimitedDataDisclaimer.tsx
  3. 47
      public/app/features/explore/Table/TableContainer.tsx
  4. 5
      public/app/features/explore/state/query.ts
  5. 6
      public/locales/en-US/grafana.json
  6. 6
      public/locales/pseudo-LOCALE/grafana.json

@ -1,4 +1,3 @@
import { css } from '@emotion/css';
import { useCallback, useMemo, useState } from 'react';
import { useToggle } from 'react-use';
@ -10,21 +9,13 @@ import {
SplitOpen,
LoadingState,
ThresholdsConfig,
GrafanaTheme2,
TimeRange,
} from '@grafana/data';
import {
GraphThresholdsStyleConfig,
PanelChrome,
PanelChromeProps,
Icon,
Button,
useStyles2,
Tooltip,
} from '@grafana/ui';
import { GraphThresholdsStyleConfig, PanelChrome, PanelChromeProps } from '@grafana/ui';
import { t, Trans } from 'app/core/internationalization';
import { ExploreGraphStyle } from 'app/types';
import { LimitedDataDisclaimer } from '../LimitedDataDisclaimer';
import { storeGraphStyle } from '../state/utils';
import { ExploreGraph } from './ExploreGraph';
@ -65,7 +56,6 @@ export const GraphContainer = ({
}: Props) => {
const [showAllSeries, toggleShowAllSeries] = useToggle(false);
const [graphStyle, setGraphStyle] = useState(loadGraphStyle);
const styles = useStyles2(getStyles);
const onGraphStyleChange = useCallback((graphStyle: ExploreGraphStyle) => {
storeGraphStyle(graphStyle);
@ -81,24 +71,20 @@ export const GraphContainer = ({
title={t('graph.container.title', 'Graph')}
titleItems={[
!showAllSeries && MAX_NUMBER_OF_TIME_SERIES < data.length && (
<div key="disclaimer" className={styles.timeSeriesDisclaimer}>
<span className={styles.warningMessage}>
<Icon name="exclamation-triangle" aria-hidden="true" />
<LimitedDataDisclaimer
key="disclaimer"
toggleShowAllSeries={toggleShowAllSeries}
info={
<Trans i18nKey={'graph.container.show-only-series'}>
Showing only {{ MAX_NUMBER_OF_TIME_SERIES }} series
</Trans>
</span>
<Tooltip
content={t(
'graph.container.content',
'Rendering too many series in a single panel may impact performance and make data harder to read. Consider refining your queries.'
)}
>
<Button variant="secondary" size="sm" onClick={toggleShowAllSeries}>
<Trans i18nKey={'graph.container.show-all-series'}>Show all {{ length: data.length }}</Trans>
</Button>
</Tooltip>
</div>
}
buttonLabel={<Trans i18nKey={'graph.container.show-all-series'}>Show all {{ length: data.length }}</Trans>}
tooltip={t(
'graph.container.content',
'Rendering too many series in a single panel may impact performance and make data harder to read. Consider refining your queries.'
)}
/>
),
].filter(Boolean)}
width={width}
@ -127,19 +113,3 @@ export const GraphContainer = ({
</PanelChrome>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
timeSeriesDisclaimer: css({
label: 'time-series-disclaimer',
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1),
}),
warningMessage: css({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(0.5),
color: theme.colors.warning.main,
fontSize: theme.typography.bodySmall.fontSize,
}),
});

@ -0,0 +1,48 @@
import { css } from '@emotion/css';
import React from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { Button, Icon, Tooltip, useStyles2 } from '@grafana/ui';
type Props = {
toggleShowAllSeries: () => void;
info: React.ReactNode;
tooltip: string;
buttonLabel: React.ReactNode;
};
export function LimitedDataDisclaimer(props: Props) {
const { toggleShowAllSeries, info, tooltip, buttonLabel } = props;
const styles = useStyles2(getStyles);
return (
<div key="disclaimer" className={styles.disclaimer}>
<span className={styles.warningMessage}>
<Icon name="exclamation-triangle" aria-hidden="true" />
{info}
</span>
<Tooltip content={tooltip}>
<Button variant="secondary" size="sm" onClick={toggleShowAllSeries}>
{buttonLabel}
</Button>
</Tooltip>
</div>
);
}
const getStyles = (theme: GrafanaTheme2) => ({
disclaimer: css({
label: 'series-disclaimer',
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1),
}),
warningMessage: css({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(0.5),
color: theme.colors.warning.main,
fontSize: theme.typography.bodySmall.fontSize,
}),
});

@ -7,7 +7,7 @@ import { getTemplateSrv } from '@grafana/runtime';
import { TimeZone } from '@grafana/schema';
import { Table, AdHocFilterItem, PanelChrome, withTheme2, Themeable2 } from '@grafana/ui';
import { config } from 'app/core/config';
import { t } from 'app/core/internationalization';
import { t, Trans } from 'app/core/internationalization';
import {
hasDeprecatedParentRowIndex,
migrateFromParentRowIndexToNestedFrames,
@ -15,10 +15,13 @@ import {
import { StoreState } from 'app/types';
import { ExploreItemState } from 'app/types/explore';
import { LimitedDataDisclaimer } from '../LimitedDataDisclaimer';
import { MetaInfoText } from '../MetaInfoText';
import { selectIsWaitingForData } from '../state/query';
import { exploreDataLinkPostProcessorFactory } from '../utils/links';
const MAX_NUMBER_OF_COLUMNS = 20;
interface TableContainerProps extends Themeable2 {
ariaLabel?: string;
exploreId: string;
@ -40,8 +43,13 @@ function mapStateToProps(state: StoreState, { exploreId }: TableContainerProps)
const connector = connect(mapStateToProps, {});
type Props = TableContainerProps & ConnectedProps<typeof connector>;
type State = {
showAll: boolean;
};
export class TableContainer extends PureComponent<Props, State> {
state = { showAll: false };
export class TableContainer extends PureComponent<Props> {
hasSubFrames = (data: DataFrame) => data.fields.some((f) => f.type === FieldType.nestedFrames);
getTableHeight(rowCount: number, hasSubFrames: boolean) {
@ -64,16 +72,35 @@ export class TableContainer extends PureComponent<Props> {
: t('explore.table.title', 'Table');
}
showAll() {
this.setState({
showAll: true,
});
}
render() {
const { loading, onCellFilterAdded, tableResult, width, splitOpenFn, range, ariaLabel, timeZone, theme } =
this.props;
const { showAll } = this.state;
let dataFrames = hasDeprecatedParentRowIndex(tableResult)
? migrateFromParentRowIndexToNestedFrames(tableResult)
: tableResult;
const dataLinkPostProcessor = exploreDataLinkPostProcessorFactory(splitOpenFn, range);
let dataLimited = false;
if (dataFrames?.length) {
dataFrames = dataFrames.map((frame) => {
frame.fields.forEach((field, index) => {
const hidden = showAll ? false : index >= MAX_NUMBER_OF_COLUMNS;
field.config.custom = { hidden };
dataLimited = dataLimited || hidden;
});
return frame;
});
dataFrames = applyFieldOverrides({
data: dataFrames,
timeZone,
@ -104,6 +131,22 @@ export class TableContainer extends PureComponent<Props> {
<PanelChrome
key={data.refId || `table-${i}`}
title={this.getTableTitle(dataFrames, data, i)}
titleItems={[
!showAll && dataLimited && (
<LimitedDataDisclaimer
toggleShowAllSeries={() => this.showAll()}
info={
<Trans i18nKey={'table.container.show-only-series'}>
Showing only {{ MAX_NUMBER_OF_COLUMNS }} columns
</Trans>
}
tooltip={
'Showing too many columns in a single table may impact performance and make data harder to read. Consider refining your queries.'
}
buttonLabel={<Trans i18nKey={'table.container.show-all-series'}>Show all columns</Trans>}
/>
),
]}
width={width}
height={this.getTableHeight(data.length, this.hasSubFrames(data))}
loadingState={loading ? LoadingState.Loading : undefined}

@ -1303,10 +1303,7 @@ export const queryReducer = (state: ExploreItemState, action: AnyAction): Explor
return state;
};
export const processQueryResponse = (
state: ExploreItemState,
action: PayloadAction<QueryEndedPayload>
): ExploreItemState => {
const processQueryResponse = (state: ExploreItemState, action: PayloadAction<QueryEndedPayload>): ExploreItemState => {
const { response } = action.payload;
const {
request,

@ -3216,6 +3216,12 @@
"url-column-header": "Snapshot url",
"view-button": "View"
},
"table": {
"container": {
"show-all-series": "Show all columns",
"show-only-series": "Showing only {{MAX_NUMBER_OF_COLUMNS}} columns"
}
},
"tag-filter": {
"clear-button": "Clear tags",
"loading": "Loading...",

@ -3216,6 +3216,12 @@
"url-column-header": "Ŝʼnäpşĥőŧ ūřľ",
"view-button": "Vįęŵ"
},
"table": {
"container": {
"show-all-series": "Ŝĥőŵ äľľ čőľūmʼnş",
"show-only-series": "Ŝĥőŵįʼnģ őʼnľy {{MAX_NUMBER_OF_COLUMNS}} čőľūmʼnş"
}
},
"tag-filter": {
"clear-button": "Cľęäř ŧäģş",
"loading": "Ŀőäđįʼnģ...",

Loading…
Cancel
Save