Skip to content

Commit 9624da6

Browse files
Drill down metrics (#200)
* multiple dashboards on same page * fix * time range selector fix
1 parent 0ffaebc commit 9624da6

File tree

20 files changed

+412
-151
lines changed

20 files changed

+412
-151
lines changed

packages/grafana-ui/src/components/Button/Button.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { PopoverContent, Tooltip, TooltipPlacement } from '../Tooltip';
1414

1515
export type ButtonVariant = 'primary' | 'secondary' | 'destructive' | 'success';
1616
export const allButtonVariants: ButtonVariant[] = ['primary', 'secondary', 'destructive'];
17-
export type ButtonFill = 'solid' | 'outline' | 'text';
17+
export type ButtonFill = 'solid' | 'outline' | 'text' | 'ghost';
1818
export const allButtonFills: ButtonFill[] = ['solid', 'outline', 'text'];
1919

2020
type CommonProps = {
@@ -319,6 +319,17 @@ function getButtonVariantStyles(theme: GrafanaTheme2, color: ThemeRichColor, fil
319319
};
320320
}
321321

322+
if (fill === 'ghost') {
323+
return {
324+
background: '#5d5a5990',
325+
color: color.text,
326+
border: `1px solid ${borderColor}`,
327+
transition: theme.transitions.create(['background-color', 'box-shadow', 'border-color', 'color'], {
328+
duration: theme.transitions.duration.short,
329+
}),
330+
};
331+
}
332+
322333
return {
323334
background: color.main,
324335
color: color.contrastText,

packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimeRangeContent.tsx

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export const TimeRangeContent = (props: Props) => {
5858
onApply: onApplyFromProps,
5959
isReversed,
6060
fiscalYearStartMonth,
61-
onError,
61+
// onError,
6262
weekStart,
6363
} = props;
6464
const [fromValue, toValue] = valueToState(value.raw.from, value.raw.to, timeZone);
@@ -112,28 +112,28 @@ export const TimeRangeContent = (props: Props) => {
112112
}
113113
};
114114

115-
const onCopy = () => {
116-
const raw: RawTimeRange = { from: from.value, to: to.value };
117-
navigator.clipboard.writeText(JSON.stringify(raw));
118-
};
119-
120-
const onPaste = async () => {
121-
const raw = await navigator.clipboard.readText();
122-
let range;
123-
124-
try {
125-
range = JSON.parse(raw);
126-
} catch (error) {
127-
if (onError) {
128-
onError(raw);
129-
}
130-
return;
131-
}
132-
133-
const [fromValue, toValue] = valueToState(range.from, range.to, timeZone);
134-
setFrom(fromValue);
135-
setTo(toValue);
136-
};
115+
// const onCopy = () => {
116+
// const raw: RawTimeRange = { from: from.value, to: to.value };
117+
// navigator.clipboard.writeText(JSON.stringify(raw));
118+
// };
119+
120+
// const onPaste = async () => {
121+
// const raw = await navigator.clipboard.readText();
122+
// let range;
123+
124+
// try {
125+
// range = JSON.parse(raw);
126+
// } catch (error) {
127+
// if (onError) {
128+
// onError(raw);
129+
// }
130+
// return;
131+
// }
132+
133+
// const [fromValue, toValue] = valueToState(range.from, range.to, timeZone);
134+
// setFrom(fromValue);
135+
// setTo(toValue);
136+
// };
137137

138138
const fiscalYear = rangeUtil.convertRawToRange({ from: 'now/fy', to: 'now/fy' }, timeZone, fiscalYearStartMonth);
139139
const fiscalYearMessage = t('time-picker.range-content.fiscal-year', 'Fiscal year');
@@ -197,7 +197,7 @@ export const TimeRangeContent = (props: Props) => {
197197
</div>
198198

199199
<div className={style.buttonsContainer}>
200-
<Button
200+
{/* <Button
201201
data-testid={selectors.components.TimePicker.copyTimeRange}
202202
icon="copy"
203203
variant="secondary"
@@ -212,13 +212,13 @@ export const TimeRangeContent = (props: Props) => {
212212
tooltip={t('time-picker.copy-paste.tooltip-paste', 'Paste time range')}
213213
type="button"
214214
onClick={onPaste}
215-
/>
215+
/> */}
216216
<Button
217217
data-testid={selectors.components.TimePicker.applyTimeRange}
218218
type="button"
219219
onClick={onApply}
220220
style={{
221-
width: 193,
221+
width: 205,
222222
textAlign: 'center',
223223
paddingLeft: 45,
224224
}}

packages/grafana-ui/src/components/Pagination/Pagination.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const Pagination = ({
3434
const pages = [...new Array(numberOfPages).keys()];
3535

3636
const condensePages = numberOfPages > pageLengthToCondense;
37-
const getListItem = (page: number, fill?: 'outline') => (
37+
const getListItem = (page: number, fill?: 'outline' | 'ghost') => (
3838
<li key={page} className={styles.item}>
3939
<Button size="sm" onClick={() => onNavigate(page)} fill={fill}>
4040
{page}
@@ -44,7 +44,7 @@ export const Pagination = ({
4444

4545
return pages.reduce<JSX.Element[]>((pagesToRender, pageIndex) => {
4646
const page = pageIndex + 1;
47-
const fill: 'outline' | undefined = page === currentPage ? undefined : 'outline';
47+
const fill: 'outline' | 'ghost' = page === currentPage ? 'ghost' : 'outline';
4848

4949
// The indexes at which to start and stop condensing pages
5050
const lowerBoundIndex = pageLengthToCondense;

packages/grafana-ui/src/components/Table/RowsList.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ interface RowsListProps {
5353
initialRowIndex?: number;
5454
headerGroups: HeaderGroup[];
5555
longestField?: Field;
56+
onClickRow?: (row: Record<string, string | number>) => void;
5657
}
5758

5859
export const RowsList = (props: RowsListProps) => {
@@ -78,6 +79,7 @@ export const RowsList = (props: RowsListProps) => {
7879
initialRowIndex = undefined,
7980
headerGroups,
8081
longestField,
82+
onClickRow,
8183
} = props;
8284

8385
const [rowHighlightIndex, setRowHighlightIndex] = useState<number | undefined>(initialRowIndex);
@@ -303,13 +305,44 @@ export const RowsList = (props: RowsListProps) => {
303305
}
304306
const { key, ...rowProps } = row.getRowProps({ style, ...additionalProps });
305307

308+
const mapRowValues = () => {
309+
const rowValues: Record<string, string | number> = {};
310+
for (const [key, val] of Object.entries(row.values)) {
311+
const k = (row.cells[Number(key)].column as unknown as { field?: { name: string | undefined } })?.field?.name;
312+
if (k === undefined) {
313+
continue;
314+
}
315+
const camelCaseKey = k
316+
.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => (index === 0 ? word.toLowerCase() : word.toUpperCase()))
317+
.replace(/\s+/g, '');
318+
rowValues[camelCaseKey] = val;
319+
}
320+
return rowValues;
321+
};
322+
306323
return (
307324
<div
308325
key={key}
309326
{...rowProps}
310327
className={cx(tableStyles.row, expandedRowStyle)}
311328
onMouseEnter={() => onRowHover(index, data)}
312329
onMouseLeave={onRowLeave}
330+
onClick={() => {
331+
if (onClickRow) {
332+
onClickRow(mapRowValues());
333+
}
334+
}}
335+
role={onClickRow ? 'button' : undefined}
336+
tabIndex={onClickRow ? 0 : undefined}
337+
onKeyDown={
338+
onClickRow
339+
? (e) => {
340+
if (e.key === 'Enter' || e.key === ' ') {
341+
e.preventDefault();
342+
}
343+
}
344+
: undefined
345+
}
313346
>
314347
{/*add the nested data to the DOM first to prevent a 1px border CSS issue on the last cell of the row*/}
315348
{rowExpanded && (
@@ -343,6 +376,7 @@ export const RowsList = (props: RowsListProps) => {
343376
);
344377
},
345378
[
379+
onClickRow,
346380
cellHeight,
347381
data,
348382
nestedDataField,

packages/grafana-ui/src/components/Table/Table.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const Table = memo((props: Props) => {
5858
enableSharedCrosshair = false,
5959
initialRowIndex = undefined,
6060
fieldConfig,
61+
onClickRow,
6162
} = props;
6263

6364
const listRef = useRef<VariableSizeList>(null);
@@ -334,6 +335,7 @@ export const Table = memo((props: Props) => {
334335
enableSharedCrosshair={enableSharedCrosshair}
335336
initialRowIndex={initialRowIndex}
336337
longestField={longestField}
338+
onClickRow={onClickRow}
337339
/>
338340
</div>
339341
) : (

packages/grafana-ui/src/components/Table/styles.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ export function useTableStyles(theme: GrafanaTheme2, cellHeightOption: TableCell
5959
minHeight: `${rowHeight - 1}px`,
6060
wordBreak: textShouldWrap ? 'break-word' : undefined,
6161
whiteSpace: textShouldWrap && overflowOnHover ? 'normal' : 'nowrap',
62-
boxShadow: overflowOnHover ? `0 0 2px ${theme.colors.primary.main}` : undefined,
63-
background: rowStyled ? 'inherit' : (backgroundHover ?? theme.colors.background.primary),
62+
//boxShadow: overflowOnHover ? `0 0 2px ${theme.colors.primary.main}` : undefined,
63+
// background: 'inherit', //: (backgroundHover ?? theme.colors.background.primary),
6464
zIndex: 1,
6565
'.cellActions': {
6666
color: '#FFF',

packages/grafana-ui/src/components/Table/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ export interface Props {
100100
// The index of the field value that the table will initialize scrolled to
101101
initialRowIndex?: number;
102102
fieldConfig?: FieldConfigSource;
103+
onClickRow?: (row: Record<string, string | number>) => void;
103104
}
104105

105106
/**

public/app/core/components/Page/usePageTitle.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { useEffect } from 'react';
22

33
import { NavModel, NavModelItem } from '@grafana/data';
4-
import { FnGlobalState } from 'app/core/reducers/fn-slice';
54
import { HOME_NAV_ID } from 'app/core/reducers/navModel';
65
import { useSelector } from 'app/types';
76

@@ -10,7 +9,7 @@ import { buildBreadcrumbs } from '../Breadcrumbs/utils';
109

1110
export function usePageTitle(navModel?: NavModel, pageNav?: NavModelItem) {
1211
const homeNav = useSelector((state) => state.navIndex)[HOME_NAV_ID];
13-
const { FNDashboard, pageTitle } = useSelector<FnGlobalState>((state) => state.fnGlobalState);
12+
const { FNDashboard, pageTitle } = useSelector((state) => state.fnGlobalState);
1413

1514
useEffect(() => {
1615
const sectionNav = (navModel?.node !== navModel?.main ? navModel?.node : navModel?.main) ?? { text: 'Grafana' };

public/app/core/components/TimePicker/TimePickerWithHistory.tsx

Lines changed: 4 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
1-
import { isEqual, uniqBy } from 'lodash';
2-
import { CSSProperties, FC, useEffect, useRef } from 'react';
3-
// eslint-disable-next-line no-restricted-imports
4-
import { useDispatch, useSelector } from 'react-redux';
1+
import { uniqBy } from 'lodash';
2+
import { CSSProperties, FC } from 'react';
53

64
import { TimeRange, isDateTime, rangeUtil } from '@grafana/data';
75
import { TimeRangePickerProps, TimeRangePicker, useTheme2 } from '@grafana/ui';
8-
import { FnGlobalState, updatePartialFnStates } from 'app/core/reducers/fn-slice';
9-
import { StoreState } from 'app/types';
6+
import { useSelector } from 'app/types';
107

118
import { LocalStorageValueProvider } from '../LocalStorageValueProvider';
129

@@ -24,7 +21,7 @@ interface TimePickerHistoryItem {
2421
type LSTimePickerHistoryItem = TimePickerHistoryItem | TimeRange;
2522

2623
const FnText: React.FC = () => {
27-
const { FNDashboard } = useSelector<StoreState, FnGlobalState>(({ fnGlobalState }) => fnGlobalState);
24+
const { FNDashboard } = useSelector(({ fnGlobalState }) => fnGlobalState);
2825
const theme = useTheme2();
2926

3027
const FN_TEXT_STYLE: CSSProperties = { fontWeight: 700, fontSize: 14, marginLeft: 8 };
@@ -47,32 +44,9 @@ export interface PickerProps {
4744
}
4845

4946
export const Picker: FC<PickerProps> = ({ rawValues, onSaveToStore, pickerProps }) => {
50-
const { fnGlobalTimeRange } = useSelector<StoreState, FnGlobalState>(({ fnGlobalState }) => fnGlobalState);
51-
const dispatch = useDispatch();
52-
5347
const values = migrateHistory(rawValues);
5448
const history = deserializeHistory(values);
5549

56-
const didMountRef = useRef(false);
57-
useEffect(() => {
58-
/* The condition below skips the first run of useeffect that happens when this component gets mounted */
59-
if (didMountRef.current) {
60-
/* If the current timerange value has changed, update fnGlobalTimeRange */
61-
if (!isEqual(fnGlobalTimeRange?.raw, pickerProps.value.raw)) {
62-
dispatch(
63-
updatePartialFnStates({
64-
fnGlobalTimeRange: pickerProps.value,
65-
})
66-
);
67-
}
68-
} else if (fnGlobalTimeRange && !isEqual(fnGlobalTimeRange.raw, pickerProps.value.raw)) {
69-
/* If fnGlobalTimeRange exists in the initial render, set the time as that */
70-
pickerProps.onChange(fnGlobalTimeRange);
71-
}
72-
73-
didMountRef.current = true;
74-
}, [dispatch, fnGlobalTimeRange, pickerProps]);
75-
7650
return (
7751
<TimeRangePicker
7852
{...pickerProps}

0 commit comments

Comments
 (0)