Templates&grouping tweaks&fixes (#2147)

# What this PR does

Templates&grouping tweaks \ fixes
- responsiveness fixes
- changed new route default template to be comment instead
- some other minor changes
This commit is contained in:
Rares Mardare 2023-06-09 12:56:21 +03:00 committed by GitHub
parent 1a6e30c249
commit a3d9b181c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 140 additions and 130 deletions

View file

@ -1,19 +0,0 @@
.hamburger-menu {
cursor: pointer;
color: var(--primary-text-color);
}
.hamburger-menu-withBackground {
display: inline-flex;
flex-direction: column;
align-items: center;
vertical-align: middle;
justify-content: center;
background-color: rgba(204, 204, 220, 0.16);
border: 1px solid transparent;
height: 32px;
width: 30px;
padding: 4px;
cursor: pointer;
color: var(--primary-text-color);
}

View file

@ -0,0 +1,35 @@
.hamburgerMenu {
cursor: pointer;
color: var(--primary-text-color);
&--withBackground {
display: inline-flex;
flex-direction: column;
align-items: center;
vertical-align: middle;
justify-content: center;
background-color: rgba(204, 204, 220, 0.16);
border: 1px solid transparent;
height: 32px;
width: 30px;
padding: 4px;
cursor: pointer;
color: var(--primary-text-color);
}
&--small {
display: inline-flex;
flex-direction: column;
align-items: center;
vertical-align: middle;
justify-content: center;
background-color: rgba(204, 204, 220, 0.16);
color: var(--secondary-background);
border: 1px solid transparent;
height: 24px;
width: 22px;
padding: 4px;
cursor: pointer;
color: var(--primary-text-color);
}
}

View file

@ -3,12 +3,13 @@ import React, { useRef } from 'react';
import { Icon } from '@grafana/ui';
import cn from 'classnames/bind';
import styles from './HamburgerMenu.module.css';
import styles from './HamburgerMenu.module.scss';
interface HamburgerMenuProps {
openMenu: React.MouseEventHandler<HTMLElement>;
listWidth: number;
listBorder: number;
stopPropagation?: boolean;
withBackground?: boolean;
className?: string;
}
@ -17,12 +18,16 @@ const cx = cn.bind(styles);
const HamburgerMenu: React.FC<HamburgerMenuProps> = (props) => {
const ref = useRef<HTMLDivElement>();
const { openMenu, listBorder, listWidth, withBackground, className } = props;
const { openMenu, listBorder, listWidth, withBackground, className, stopPropagation = false } = props;
return (
<div
ref={ref}
className={withBackground ? cx('hamburger-menu-withBackground') : cx('hamburger-menu', className)}
onClick={() => {
className={withBackground ? cx('hamburgerMenu--withBackground') : cx('hamburgerMenu', className)}
onClick={(e) => {
if (stopPropagation) {
e.stopPropagation();
}
const boundingRect = ref.current.getBoundingClientRect();
openMenu({

View file

@ -29,13 +29,9 @@ const CollapsedIntegrationRouteDisplay: React.FC<CollapsedIntegrationRouteDispla
const store = useStore();
const { escalationChainStore, alertReceiveChannelStore, telegramChannelStore } = store;
const [routeIdForDeletion, setRouteIdForDeletion] = useState<ChannelFilter['id']>(undefined);
const [telegramInfo, setTelegramInfo] = useState<Array<{ id: string; channel_name: string }>>([]);
useEffect(() => {
(async function () {
const telegram = await telegramChannelStore.getAll();
setTelegramInfo(telegram);
})();
telegramChannelStore.updateItems();
}, [channelFilterId]);
const channelFilter = alertReceiveChannelStore.channelFilters[channelFilterId];
@ -91,7 +87,7 @@ const CollapsedIntegrationRouteDisplay: React.FC<CollapsedIntegrationRouteDispla
content={
<div className={cx('spacing')}>
<VerticalGroup>
{IntegrationHelper.getChatOpsChannels(channelFilter, telegramInfo, store)
{IntegrationHelper.getChatOpsChannels(channelFilter, store)
.filter((it) => it)
.map((chatOpsChannel, key) => (
<HorizontalGroup key={key}>

View file

@ -32,19 +32,3 @@
background: var(--gray-9);
}
}
.hamburgerMenu-small {
display: inline-flex;
flex-direction: column;
align-items: center;
vertical-align: middle;
justify-content: center;
background-color: rgba(204, 204, 220, 0.16);
color: var(--secondary-background);
border: 1px solid transparent;
height: 24px;
width: 22px;
padding: 4px;
cursor: pointer;
color: var(--primary-text-color);
}

View file

@ -41,6 +41,9 @@ import { UserActions } from 'utils/authorization';
const cx = cn.bind(styles);
const ACTIONS_LIST_WIDTH = 200;
const ACTIONS_LIST_BORDER = 2;
interface ExpandedIntegrationRouteDisplayProps {
alertReceiveChannelId: AlertReceiveChannel['id'];
channelFilterId: ChannelFilter['id'];
@ -83,7 +86,7 @@ const ExpandedIntegrationRouteDisplay: React.FC<ExpandedIntegrationRouteDisplayP
useEffect(() => {
setIsLoading(true);
Promise.all([escalationChainStore.updateItems(), telegramChannelStore.updateTelegramChannels()]).then(() =>
Promise.all([escalationChainStore.updateItems(), telegramChannelStore.updateItems()]).then(() =>
setIsLoading(false)
);
}, []);
@ -168,12 +171,14 @@ const ExpandedIntegrationRouteDisplay: React.FC<ExpandedIntegrationRouteDisplayP
</IntegrationBlockItem>
)}
<IntegrationBlockItem>
<VerticalGroup spacing="md">
<Text type="primary">Publish to ChatOps</Text>
<ChatOpsConnectors channelFilterId={channelFilterId} showLineNumber={false} />
</VerticalGroup>
</IntegrationBlockItem>
{IntegrationHelper.hasChatopsInstalled(store) && (
<IntegrationBlockItem>
<VerticalGroup spacing="md">
<Text type="primary">Publish to ChatOps</Text>
<ChatOpsConnectors channelFilterId={channelFilterId} showLineNumber={false} />
</VerticalGroup>
</IntegrationBlockItem>
)}
<IntegrationBlockItem>
<VerticalGroup>
@ -363,15 +368,20 @@ export const RouteButtonsDisplay: React.FC<RouteButtonsDisplayProps> = ({
)}
>
{({ openMenu }) => (
<HamburgerMenu openMenu={openMenu} listBorder={2} listWidth={200} className={cx('hamburgerMenu-small')} />
<HamburgerMenu
openMenu={openMenu}
listBorder={ACTIONS_LIST_BORDER}
listWidth={ACTIONS_LIST_WIDTH}
className={'hamburgerMenu--small'}
stopPropagation={true}
/>
)}
</WithContextMenu>
)}
</HorizontalGroup>
);
function onDelete(e: React.SyntheticEvent) {
e.stopPropagation();
function onDelete() {
setRouteIdForDeletion();
}

View file

@ -32,23 +32,32 @@
min-width: min-content;
}
.template-block-list,
.template-block-codeeditor {
overflow-y: hidden;
}
.template-block-list,
.template-block-codeeditor,
.template-block-result,
.result {
height: 100%;
max-height: 100%;
}
.template-block-list {
width: 30%;
height: 100%;
}
.template-block-codeeditor {
width: 40%;
height: 100%;
}
.template-block-result {
width: 30%;
height: 100%;
overflow-y: scroll !important;
}
.result {
padding-left: 16px;
padding-bottom: 60px;
}
.template-block-codeeditor div[aria-label='Code editor container'] {

View file

@ -25,7 +25,7 @@ import { openErrorNotification } from 'utils';
import { waitForElement } from 'utils/DOM';
import LocationHelper from 'utils/LocationHelper';
import styles from './IntegrationTemplate.module.css';
import styles from './IntegrationTemplate.module.scss';
const cx = cn.bind(styles);

View file

@ -7,6 +7,8 @@
.template-block-list {
width: 30%;
height: 100%;
max-width: 100%;
overflow-y: hidden;
}
.alert-group-payload-view {

View file

@ -41,13 +41,13 @@ const TemplatesAlertGroupsList = (props: TemplatesAlertGroupsListProps) => {
}, []);
const getCodeEditorHeight = () => {
const mainDiv = document.getElementById('content-container-id');
const mainDiv = document.getElementById('alerts-content-container-id');
const height = mainDiv?.getBoundingClientRect().height - HEADER_OF_CONTAINER_HEIGHT;
return `${height}px`;
};
const getCodeEditorHeightWithBadge = () => {
const mainDiv = document.getElementById('content-container-id');
const mainDiv = document.getElementById('alerts-content-container-id');
const height = mainDiv?.getBoundingClientRect().height - HEADER_OF_CONTAINER_HEIGHT - BADGE_WITH_PADDINGS_HEIGHT;
return `${height}px`;
};
@ -80,7 +80,7 @@ const TemplatesAlertGroupsList = (props: TemplatesAlertGroupsListProps) => {
};
return (
<div className={cx('template-block-list')} id="content-container-id">
<div className={cx('template-block-list')} id="alerts-content-container-id">
{selectedAlertPayload ? (
<>
{isEditMode ? (

View file

@ -72,23 +72,30 @@ const IntegrationHelper = {
return totalDiffString;
},
getChatOpsChannels(
channelFilter: ChannelFilter,
telegramInfo: Array<{ id: string; channel_name: string }>,
store: RootStore
): Array<{ name: string; icon: IconName }> {
const channels: Array<{ name: string; icon: IconName }> = [];
hasChatopsInstalled(store: RootStore) {
const hasSlack = Boolean(store.teamStore.currentTeam?.slack_team_identity);
const hasTelegram =
store.hasFeature(AppFeature.Telegram) && store.telegramChannelStore.currentTeamToTelegramChannel?.length > 0;
return hasSlack || hasTelegram;
},
if (
store.hasFeature(AppFeature.Slack) &&
channelFilter.notify_in_slack &&
channelFilter.notify_in_slack &&
channelFilter.slack_channel?.display_name
) {
channels.push({ name: channelFilter.slack_channel.display_name, icon: 'slack' });
getChatOpsChannels(channelFilter: ChannelFilter, store: RootStore): Array<{ name: string; icon: IconName }> {
const channels: Array<{ name: string; icon: IconName }> = [];
const telegram = Object.keys(store.telegramChannelStore.items).map((k) => store.telegramChannelStore.items[k]);
if (store.hasFeature(AppFeature.Slack) && channelFilter.notify_in_slack) {
const matchingSlackChannel = store.teamStore.currentTeam?.slack_channel?.id
? store.slackChannelStore.items[store.teamStore.currentTeam.slack_channel?.id]
: undefined;
if (channelFilter.slack_channel?.display_name || matchingSlackChannel?.display_name) {
channels.push({
name: channelFilter.slack_channel?.display_name || matchingSlackChannel?.display_name,
icon: 'slack',
});
}
}
const matchingTelegram = telegramInfo?.find((t) => t.id === channelFilter.telegram_channel);
const matchingTelegram = telegram.find((t) => t.id === channelFilter.telegram_channel);
if (
store.hasFeature(AppFeature.Telegram) &&

View file

@ -66,7 +66,7 @@ import { openNotification, openErrorNotification } from 'utils';
import { getVar } from 'utils/DOM';
import LocationHelper from 'utils/LocationHelper';
import { UserActions } from 'utils/authorization';
import { DATASOURCE_GRAFANA, PLUGIN_ROOT } from 'utils/consts';
import { PLUGIN_ROOT } from 'utils/consts';
import sanitize from 'utils/sanitize';
const cx = cn.bind(styles);
@ -86,7 +86,7 @@ interface Integration2State extends PageBaseState {
const ACTIONS_LIST_WIDTH = 200;
const ACTIONS_LIST_BORDER = 2;
const NEW_ROUTE_DEFAULT = '{{ (payload.severity == "foo" and "bar" in payload.region) or True }}';
const NEW_ROUTE_DEFAULT = '{# (payload.severity == "foo" and "bar" in payload.region) or True #}';
@observer
class Integration2 extends React.Component<Integration2Props, Integration2State> {
@ -603,9 +603,8 @@ const IntegrationSendDemoPayloadModal: React.FC<IntegrationSendDemoPayloadModalP
}) => {
const store = useStore();
const { alertReceiveChannelStore } = store;
const stringifiedJson = JSON.stringify(alertReceiveChannel.demo_alert_payload, null, 2);
const initialDemoJSON = stringifiedJson.substring(1, stringifiedJson.length - 1);
const [demoPayload, setDemoPayload] = useState<string>(alertReceiveChannel.demo_alert_payload);
const initialDemoJSON = JSON.stringify(alertReceiveChannel.demo_alert_payload, null, 2);
const [demoPayload, setDemoPayload] = useState<string>(initialDemoJSON);
let onPayloadChangeDebounced = debounce(100, onPayloadChange);
return (
@ -916,8 +915,6 @@ const IntegrationActions: React.FC<IntegrationActionsProps> = ({ alertReceiveCha
const HowToConnectComponent: React.FC<{ id: AlertReceiveChannel['id'] }> = ({ id }) => {
const { alertReceiveChannelStore } = useStore();
const alertReceiveChannelCounter = alertReceiveChannelStore.counters[id];
const alertReceiveChannel = alertReceiveChannelStore.items[id];
const isGrafanaDatasource = alertReceiveChannel.integration === DATASOURCE_GRAFANA;
const hasAlerts = !!alertReceiveChannelCounter?.alerts_count;
return (
@ -949,7 +946,7 @@ const HowToConnectComponent: React.FC<{ id: AlertReceiveChannel['id'] }> = ({ id
</a>
</div>
}
content={isGrafanaDatasource || !hasAlerts ? renderContent() : null}
content={hasAlerts ? null : renderContent()}
/>
);
@ -963,20 +960,6 @@ const HowToConnectComponent: React.FC<{ id: AlertReceiveChannel['id'] }> = ({ id
<Text type={'primary'}>No alerts yet; try to send a demo alert</Text>
</HorizontalGroup>
)}
{isGrafanaDatasource && (
<HorizontalGroup spacing={'xs'}>
<Icon name="list-ui-alt" size="md" />
<a href={`/alerting/notifications?alertmanager=grafana`} target="_blank" rel="noreferrer">
<Text type={'link'}>Contact Point</Text>
</a>
<Text type={'secondary'}>and</Text>
<a href="/alerting/routes?alertmanager=grafana" target="_blank">
<Text type={'link'}>Notification Policy</Text>
</a>
<Text type={'secondary'}>created in Grafana Alerting</Text>
</HorizontalGroup>
)}
</VerticalGroup>
</div>
);

View file

@ -414,36 +414,34 @@ class Integrations extends React.Component<IntegrationsProps, IntegrationsState>
<div className="thin-line-break" />
<WithPermissionControlTooltip key="delete" userAction={UserActions.IntegrationsWrite}>
<>
<div className={cx('integrations-actionItem')}>
<div
onClick={() => {
this.setState({
confirmationModal: {
isOpen: true,
confirmText: 'Delete',
dismissText: 'Cancel',
onConfirm: () => this.handleDeleteAlertReceiveChannel(item.id),
title: 'Delete integration',
body: (
<Text type="primary">
Are you sure you want to delete <Emoji text={item.verbal_name} /> integration?
</Text>
),
},
});
}}
style={{ width: '100%' }}
>
<Text type="danger">
<HorizontalGroup spacing={'xs'}>
<Icon name="trash-alt" />
<span>Delete Integration</span>
</HorizontalGroup>
</Text>
</div>
<div className={cx('integrations-actionItem')}>
<div
onClick={() => {
this.setState({
confirmationModal: {
isOpen: true,
confirmText: 'Delete',
dismissText: 'Cancel',
onConfirm: () => this.handleDeleteAlertReceiveChannel(item.id),
title: 'Delete integration',
body: (
<Text type="primary">
Are you sure you want to delete <Emoji text={item.verbal_name} /> integration?
</Text>
),
},
});
}}
style={{ width: '100%' }}
>
<Text type="danger">
<HorizontalGroup spacing={'xs'}>
<Icon name="trash-alt" />
<span>Delete Integration</span>
</HorizontalGroup>
</Text>
</div>
</>
</div>
</WithPermissionControlTooltip>
</div>
)}