Skip to content

Commit 36025bf

Browse files
multiple dashboards on same page
1 parent 755f999 commit 36025bf

File tree

18 files changed

+355
-122
lines changed

18 files changed

+355
-122
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: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { useDispatch, useSelector } from 'react-redux';
55

66
import { TimeRange, isDateTime, rangeUtil } from '@grafana/data';
77
import { TimeRangePickerProps, TimeRangePicker, useTheme2 } from '@grafana/ui';
8-
import { FnGlobalState, updatePartialFnStates } from 'app/core/reducers/fn-slice';
8+
import { FnGlobalState, updateFnTimeRange } from 'app/core/reducers/fn-slice';
99
import { StoreState } from 'app/types';
1010

1111
import { LocalStorageValueProvider } from '../LocalStorageValueProvider';
@@ -59,11 +59,7 @@ export const Picker: FC<PickerProps> = ({ rawValues, onSaveToStore, pickerProps
5959
if (didMountRef.current) {
6060
/* If the current timerange value has changed, update fnGlobalTimeRange */
6161
if (!isEqual(fnGlobalTimeRange?.raw, pickerProps.value.raw)) {
62-
dispatch(
63-
updatePartialFnStates({
64-
fnGlobalTimeRange: pickerProps.value,
65-
})
66-
);
62+
dispatch(updateFnTimeRange(pickerProps.value));
6763
}
6864
} else if (fnGlobalTimeRange && !isEqual(fnGlobalTimeRange.raw, pickerProps.value.raw)) {
6965
/* If fnGlobalTimeRange exists in the initial render, set the time as that */

public/app/core/reducers/fn-slice.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,36 @@ import { GrafanaThemeType, TimeRange } from '@grafana/data';
44

55
import { AnyObject } from '../../fn-app/types';
66

7-
export interface FnGlobalState {
8-
FNDashboard: boolean;
7+
export interface FnState {
98
uid: string;
109
slug: string;
1110
version: number;
12-
mode: GrafanaThemeType.Light | GrafanaThemeType.Dark;
1311
controlsContainer: string | null;
1412
pageTitle: string;
1513
queryParams: AnyObject;
1614
hiddenVariables: readonly string[];
17-
fnGlobalTimeRange: TimeRange | null;
1815
metadata: {
1916
teams: string[];
17+
eventListener: (<T>(event: { type: string; data: T }) => void) | null;
2018
};
19+
portalContainerID: string;
2120
}
2221

23-
export type UpdateFNGlobalStateAction = PayloadAction<Partial<FnGlobalState>>;
22+
export type UpdateFNGlobalStateAction = PayloadAction<Partial<Omit<FnGlobalState, 'uid'>> & { uid: string }>;
2423

2524
export type SetFnStateAction = PayloadAction<Omit<FnGlobalState, 'hiddenVariables'>>;
2625

2726
export type FnPropMappedFromState = Extract<
2827
keyof FnGlobalState,
2928
'FNDashboard' | 'hiddenVariables' | 'mode' | 'uid' | 'queryParams' | 'slug' | 'version' | 'controlsContainer'
3029
>;
31-
export type FnStateProp = keyof FnGlobalState;
30+
export type FnStateProp = keyof FnState;
3231

3332
export type FnPropsMappedFromState = Pick<FnGlobalState, FnPropMappedFromState>;
3433

3534
export const fnStateProps: FnStateProp[] = [
36-
'FNDashboard',
3735
'controlsContainer',
3836
'hiddenVariables',
39-
'mode',
4037
'pageTitle',
4138
'queryParams',
4239
'slug',
@@ -48,40 +45,54 @@ const INITIAL_MODE = GrafanaThemeType.Light;
4845

4946
export const FN_STATE_KEY = 'fnGlobalState';
5047

51-
export const INITIAL_FN_STATE: FnGlobalState = {
48+
export const INITIAL_FN_STATE: FnState = {
5249
// NOTE: initial value is false
53-
FNDashboard: false,
5450
uid: '',
5551
slug: '',
5652
version: 1,
57-
mode: INITIAL_MODE,
5853
controlsContainer: null,
5954
pageTitle: '',
6055
queryParams: {},
6156
hiddenVariables: [],
62-
fnGlobalTimeRange: null,
6357
metadata: {
6458
teams: [],
59+
eventListener: null,
6560
},
61+
portalContainerID: 'grafana-portal',
6662
} as const;
6763

64+
export interface FnGlobalState extends FnState {
65+
FNDashboard: boolean;
66+
mode: GrafanaThemeType.Light | GrafanaThemeType.Dark;
67+
fnGlobalTimeRange: TimeRange | null;
68+
}
69+
6870
const reducers: SliceCaseReducers<FnGlobalState> = {
69-
updateFnState: (state, action: SetFnStateAction) => {
70-
return { ...state, ...action.payload };
71+
updateFnTimeRange: (state, action: PayloadAction<TimeRange | null>) => {
72+
return {
73+
...state,
74+
fnGlobalTimeRange: action.payload,
75+
};
7176
},
7277
updatePartialFnStates: (state, action: UpdateFNGlobalStateAction) => {
7378
return {
7479
...state,
7580
...action.payload,
81+
FNDashboard: true,
7682
};
7783
},
7884
};
7985

8086
const fnSlice = createSlice<FnGlobalState, SliceCaseReducers<FnGlobalState>, string, SliceSelectors<FnGlobalState>>({
8187
name: FN_STATE_KEY,
82-
initialState: INITIAL_FN_STATE,
88+
initialState: {
89+
...INITIAL_FN_STATE,
90+
FNDashboard: false,
91+
mode: INITIAL_MODE,
92+
fnGlobalTimeRange: null,
93+
},
8394
reducers,
8495
});
8596

86-
export const { updatePartialFnStates, updateFnState } = fnSlice.actions;
97+
export const { updatePartialFnStates, updateFnTimeRange } = fnSlice.actions;
8798
export const fnSliceReducer = fnSlice.reducer;

0 commit comments

Comments
 (0)