Skip to content

Commit 1cd906c

Browse files
Maxim Mordasoviskhakov
Maxim Mordasov
andauthored
Fix integrations and escalations autoselect, improve GList (#1601)
# What this PR does ## Which issue(s) this PR fixes ## Checklist - [ ] Tests updated - [ ] Documentation added - [ ] `CHANGELOG.md` updated --------- Co-authored-by: Ildar Iskhakov <[email protected]>
1 parent ed5b5e1 commit 1cd906c

File tree

9 files changed

+106
-62
lines changed

9 files changed

+106
-62
lines changed

grafana-plugin/src/components/GList/GList.tsx

+22-6
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,17 @@ const GList = <T extends WithId>(props: GListProps<T>) => {
2929
};
3030
}, []);
3131

32-
const selectedRef = useRef<HTMLDivElement>();
32+
const itemsRef = useRef(null);
3333

3434
useEffect(() => {
35-
if (autoScroll && selectedRef.current) {
36-
const selectedElement = selectedRef.current;
35+
if (autoScroll && selectedId) {
36+
const map = getMap();
37+
const selectedElement = map.get(selectedId);
38+
39+
if (!selectedElement) {
40+
return;
41+
}
42+
3743
const divToScroll = selectedElement.parentElement.parentElement;
3844

3945
const maxScroll = Math.max(0, selectedElement.parentElement.offsetHeight - divToScroll.offsetHeight);
@@ -49,16 +55,26 @@ const GList = <T extends WithId>(props: GListProps<T>) => {
4955
behavior: 'smooth',
5056
});
5157
}
52-
}, [autoScroll, selectedRef.current]);
58+
}, [selectedId, autoScroll]);
59+
60+
function getMap() {
61+
if (!itemsRef.current) {
62+
itemsRef.current = new Map();
63+
}
64+
return itemsRef.current;
65+
}
5366

5467
return (
5568
<div className={cx('root')}>
5669
{items ? (
5770
items.map((item) => (
5871
<div
5972
ref={(node) => {
60-
if (item.id === selectedId) {
61-
selectedRef.current = node;
73+
const map = getMap();
74+
if (node) {
75+
map.set(item.id, node);
76+
} else {
77+
map.delete(item.id);
6278
}
6379
}}
6480
key={item.id}

grafana-plugin/src/components/ManualAlertGroup/ManualAlertGroup.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const ManualAlertGroup: FC<ManualAlertGroupProps> = (props) => {
2727
const [userResponders, setUserResponders] = useState([]);
2828
const [scheduleResponders, setScheduleResponders] = useState([]);
2929
const { onHide, onCreate } = props;
30-
const data = {};
30+
const data = { team: store.userStore.currentUser?.current_team };
3131

3232
const handleFormSubmit = async (data) => {
3333
store.directPagingStore

grafana-plugin/src/containers/AlertReceiveChannelCard/AlertReceiveChannelCard.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -84,13 +84,13 @@ const AlertReceiveChannelCard = observer((props: AlertReceiveChannelCardProps) =
8484
</PluginLink>
8585
)}
8686
</HorizontalGroup>
87-
<HorizontalGroup>
87+
<HorizontalGroup spacing="xs">
8888
<IntegrationLogo scale={0.08} integration={integration} />
8989
<Text type="secondary" size="small">
9090
{integration?.display_name}
9191
</Text>
9292
</HorizontalGroup>
93-
<TeamName team={grafanaTeamStore.items[alertReceiveChannel.team]} />
93+
<TeamName team={grafanaTeamStore.items[alertReceiveChannel.team]} size="small" />
9494
</VerticalGroup>
9595
</HorizontalGroup>
9696
</div>

grafana-plugin/src/containers/EscalationChainCard/EscalationChainCard.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const EscalationChainCard = observer((props: AlertReceiveChannelCardProps) => {
4545
}
4646
/>
4747
</HorizontalGroup>
48-
<TeamName team={grafanaTeamStore.items[escalationChain.team]} />
48+
<TeamName team={grafanaTeamStore.items[escalationChain.team]} size="small" />
4949
</VerticalGroup>
5050
</HorizontalGroup>
5151
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.avatar {
2+
margin-right: 4px;
3+
}

grafana-plugin/src/containers/TeamName/TeamName.tsx

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
import React from 'react';
22

33
import { Badge, Tooltip } from '@grafana/ui';
4+
import cn from 'classnames/bind';
45
import { observer } from 'mobx-react';
56

67
import Avatar from 'components/Avatar/Avatar';
78
import Text from 'components/Text/Text';
89
import { GrafanaTeam } from 'models/grafana_team/grafana_team.types';
910

11+
import styles from './TeamName.module.css';
12+
13+
const cx = cn.bind(styles);
14+
1015
interface TeamNameProps {
1116
team: GrafanaTeam;
1217
size?: 'small' | 'medium' | 'large';
@@ -15,17 +20,17 @@ interface TeamNameProps {
1520
const TeamName = observer((props: TeamNameProps) => {
1621
const { team, size } = props;
1722
if (!team) {
18-
return <></>;
23+
return null;
1924
}
2025
if (team.id === 'null') {
2126
return <Badge text={team.name} color={'blue'} tooltip={'Resource is not assigned to any team (ex General team)'} />;
2227
}
2328
return (
2429
<Text type="secondary" size={size ? size : 'medium'}>
25-
<Avatar size="small" src={team.avatar_url} />{' '}
30+
<Avatar size="small" src={team.avatar_url} className={cx('avatar')} />
2631
<Tooltip placement="top" content={'Resource is assigned to ' + team.name}>
2732
<span>{team.name}</span>
28-
</Tooltip>{' '}
33+
</Tooltip>
2934
</Text>
3035
);
3136
});

grafana-plugin/src/containers/TeamsList/TeamsList.tsx

+19-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useCallback, useState } from 'react';
22

3-
import { Badge, Button, Field, HorizontalGroup, Modal, RadioButtonGroup, Tooltip, VerticalGroup } from '@grafana/ui';
3+
import { Badge, Button, Field, HorizontalGroup, Modal, RadioButtonList, Tooltip, VerticalGroup } from '@grafana/ui';
44
import { observer } from 'mobx-react';
55

66
import Avatar from 'components/Avatar/Avatar';
@@ -146,12 +146,14 @@ const TeamModal = ({ teamId, onHide }: TeamModalProps) => {
146146
const { grafanaTeamStore } = store;
147147
const team = grafanaTeamStore.items[teamId];
148148

149-
const [shareResourceToAll, setShareResourceToAll] = useState<boolean>(team.is_sharing_resources_to_all);
149+
const [shareResourceToAll, setShareResourceToAll] = useState<string>(
150+
String(Number(team.is_sharing_resources_to_all))
151+
);
150152

151153
const handleSubmit = useCallback(() => {
152-
Promise.all([grafanaTeamStore.updateTeam(teamId, { is_sharing_resources_to_all: shareResourceToAll })]).then(
153-
onHide
154-
);
154+
Promise.all([
155+
grafanaTeamStore.updateTeam(teamId, { is_sharing_resources_to_all: Boolean(Number(shareResourceToAll)) }),
156+
]).then(onHide);
155157
}, [shareResourceToAll]);
156158

157159
return (
@@ -167,14 +169,17 @@ const TeamModal = ({ teamId, onHide }: TeamModalProps) => {
167169
<WithPermissionControlTooltip userAction={UserActions.AlertGroupsWrite}>
168170
<VerticalGroup>
169171
<Field label="Who can see the team name and access the team resources">
170-
<RadioButtonGroup
171-
options={[
172-
{ label: 'All Users', value: true },
173-
{ label: 'Team members and admins', value: false },
174-
]}
175-
value={shareResourceToAll}
176-
onChange={setShareResourceToAll}
177-
/>
172+
<div style={{ marginTop: '8px' }}>
173+
<RadioButtonList
174+
name="shareResourceToAll"
175+
options={[
176+
{ label: 'All Users', value: '1' },
177+
{ label: 'Team members and admins', value: '0' },
178+
]}
179+
value={shareResourceToAll}
180+
onChange={setShareResourceToAll}
181+
/>
182+
</div>
178183
</Field>
179184
</VerticalGroup>
180185
</WithPermissionControlTooltip>
@@ -184,7 +189,7 @@ const TeamModal = ({ teamId, onHide }: TeamModalProps) => {
184189
Cancel
185190
</Button>
186191
<Button onClick={handleSubmit} variant="primary">
187-
Submit
192+
Save
188193
</Button>
189194
</HorizontalGroup>
190195
</Modal>

grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx

+27-15
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
6969

7070
const { escalationChainStore } = store;
7171

72-
const searchResult = escalationChainStore.getSearchResult();
73-
7472
let selectedEscalationChain: EscalationChain['id'];
7573
if (id) {
7674
let escalationChain = await escalationChainStore
@@ -87,22 +85,25 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
8785
}
8886
}
8987

90-
if (!selectedEscalationChain) {
91-
selectedEscalationChain = searchResult[0]?.id;
92-
}
93-
9488
if (selectedEscalationChain) {
9589
this.enrichExtraEscalationChainsAndSelect(selectedEscalationChain);
90+
} else {
91+
this.setState({ selectedEscalationChain: undefined });
9692
}
9793
};
9894

95+
handleEsclalationSelect = (id: EscalationChain['id']) => {
96+
const { history } = this.props;
97+
98+
history.push(`${PLUGIN_ROOT}/escalations/${id}${window.location.search}`);
99+
};
100+
99101
setSelectedEscalationChain = async (escalationChainId: EscalationChain['id']) => {
100-
const { store, history } = this.props;
102+
const { store } = this.props;
101103

102104
const { escalationChainStore } = store;
103105

104106
this.setState({ selectedEscalationChain: escalationChainId }, () => {
105-
history.push(`${PLUGIN_ROOT}/escalations/${escalationChainId || ''}${window.location.search}`);
106107
if (escalationChainId) {
107108
escalationChainStore.updateEscalationChainDetails(escalationChainId);
108109
}
@@ -167,7 +168,7 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
167168
selectedId={selectedEscalationChain}
168169
items={data}
169170
itemKey="id"
170-
onSelect={this.setSelectedEscalationChain}
171+
onSelect={this.handleEsclalationSelect}
171172
>
172173
{(item) => <EscalationChainCard id={item.id} />}
173174
</GList>
@@ -234,8 +235,14 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
234235
}
235236

236237
handleFiltersChange = (filters: FiltersValues, isOnMount = false) => {
238+
const {
239+
match: {
240+
params: { id },
241+
},
242+
} = this.props;
243+
237244
this.setState({ escalationChainsFilters: filters, extraEscalationChains: undefined }, () => {
238-
if (isOnMount) {
245+
if (isOnMount && id) {
239246
this.applyFilters().then(this.parseQueryParams);
240247
} else {
241248
this.applyFilters().then(this.autoSelectEscalationChain);
@@ -244,14 +251,15 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
244251
};
245252

246253
autoSelectEscalationChain = () => {
247-
const { store } = this.props;
254+
const { store, history } = this.props;
248255
const { selectedEscalationChain } = this.state;
249256
const { escalationChainStore } = store;
250257

251258
const searchResult = escalationChainStore.getSearchResult();
252259

253260
if (!searchResult.find((escalationChain: EscalationChain) => escalationChain.id === selectedEscalationChain)) {
254-
this.setSelectedEscalationChain(searchResult[0]?.id);
261+
const id = searchResult[0]?.id;
262+
history.push(`${PLUGIN_ROOT}/escalations/${id || ''}${window.location.search}`);
255263
}
256264
};
257265

@@ -358,7 +366,11 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
358366
};
359367

360368
handleEscalationChainCreate = async (id: EscalationChain['id']) => {
361-
this.enrichExtraEscalationChainsAndSelect(id);
369+
const { history } = this.props;
370+
371+
await this.applyFilters();
372+
373+
history.push(`${PLUGIN_ROOT}/escalations/${id}${window.location.search}`);
362374
};
363375

364376
enrichExtraEscalationChainsAndSelect = async (id: EscalationChain['id']) => {
@@ -389,7 +401,7 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
389401
};
390402

391403
handleDeleteEscalationChain = () => {
392-
const { store } = this.props;
404+
const { store, history } = this.props;
393405
const { escalationChainStore } = store;
394406
const { selectedEscalationChain, extraEscalationChains } = this.state;
395407

@@ -413,7 +425,7 @@ class EscalationChainsPage extends React.Component<EscalationChainsPageProps, Es
413425

414426
const newSelected = escalationChains[index - 1] || escalationChains[0];
415427

416-
this.setSelectedEscalationChain(newSelected?.id);
428+
history.push(`${PLUGIN_ROOT}/escalations/${newSelected?.id || ''}${window.location.search}`);
417429
});
418430
};
419431

grafana-plugin/src/pages/integrations/Integrations.tsx

+23-20
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
8686
} = this.props;
8787
const { alertReceiveChannelStore } = store;
8888

89-
let selectedAlertReceiveChannel = store.selectedAlertReceiveChannel;
89+
let selectedAlertReceiveChannel = undefined;
9090

9191
if (id) {
9292
let alertReceiveChannel = await alertReceiveChannelStore
@@ -110,6 +110,8 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
110110

111111
if (selectedAlertReceiveChannel) {
112112
this.enrichAlertReceiveChannelsAndSelect(selectedAlertReceiveChannel);
113+
} else {
114+
store.selectedAlertReceiveChannel = undefined;
113115
}
114116
};
115117

@@ -309,28 +311,29 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
309311
delete alertReceiveChanneltoPoll[alertReceiveChannelId];
310312
}
311313

312-
alertReceiveChannelStore.deleteAlertReceiveChannel(alertReceiveChannelId).then(async () => {
313-
await this.applyFilters();
314+
alertReceiveChannelStore
315+
.deleteAlertReceiveChannel(alertReceiveChannelId)
316+
.then(this.applyFilters)
317+
.then(() => {
318+
if (alertReceiveChannelId === store.selectedAlertReceiveChannel) {
319+
if (extraAlertReceiveChannels) {
320+
const newExtraAlertReceiveChannels = extraAlertReceiveChannels.filter(
321+
(alertReceiveChannel) => alertReceiveChannel.id !== alertReceiveChannelId
322+
);
323+
324+
this.setState({ extraAlertReceiveChannels: newExtraAlertReceiveChannels });
325+
}
326+
327+
const searchResult = alertReceiveChannelStore.getSearchResult();
314328

315-
if (alertReceiveChannelId === store.selectedAlertReceiveChannel) {
316-
if (extraAlertReceiveChannels) {
317-
const newExtraAlertReceiveChannels = extraAlertReceiveChannels.filter(
318-
(alertReceiveChannel) => alertReceiveChannel.id !== alertReceiveChannelId
329+
const index = searchResult.findIndex(
330+
(alertReceiveChannel: AlertReceiveChannel) => alertReceiveChannel.id === store.selectedAlertReceiveChannel
319331
);
332+
const newSelected = searchResult[index - 1] || searchResult[0];
320333

321-
this.setState({ extraAlertReceiveChannels: newExtraAlertReceiveChannels });
334+
history.push(`${PLUGIN_ROOT}/integrations/${newSelected?.id || ''}${window.location.search}`);
322335
}
323-
324-
const searchResult = alertReceiveChannelStore.getSearchResult();
325-
326-
const index = searchResult.findIndex(
327-
(alertReceiveChannel: AlertReceiveChannel) => alertReceiveChannel.id === store.selectedAlertReceiveChannel
328-
);
329-
const newSelected = searchResult[index - 1] || searchResult[0];
330-
331-
history.push(`${PLUGIN_ROOT}/integrations/${newSelected?.id || ''}${window.location.search}`);
332-
}
333-
});
336+
});
334337
};
335338

336339
applyFilters = () => {
@@ -363,7 +366,7 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
363366
},
364367
} = this.props;
365368

366-
this.setState({ integrationsFilters }, () => {
369+
this.setState({ integrationsFilters, extraAlertReceiveChannels: undefined }, () => {
367370
this.applyFilters().then(() => {
368371
if (isOnMount && id) {
369372
this.parseQueryParams();

0 commit comments

Comments
 (0)