Rares/templates tweaks 3 (#2052)

# What this PR does

- Added option to expand tree view on main block click
- Fixed showing contact policy for grafana alerting
This commit is contained in:
Rares Mardare 2023-05-31 10:40:02 +03:00 committed by GitHub
parent e3181a5afb
commit b93413c032
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 67 additions and 26 deletions

View file

@ -11,8 +11,8 @@ const cx = cn.bind(styles);
export interface IntegrationCollapsibleItem {
customIcon?: IconName;
canHoverIcon: boolean;
expandedView: React.ReactNode;
collapsedView: React.ReactNode;
collapsedView: (toggle?: () => void) => React.ReactNode; // needs toggle param for toggling on click
expandedView: () => React.ReactNode; // for consistency, this is also a function
isCollapsible: boolean;
isExpanded?: boolean;
onStateChange?(): void;
@ -116,10 +116,10 @@ const IntegrationCollapsibleTreeItem: React.FC<{
)}
</div>
<div className={cx('integrationTree__element', { 'integrationTree__element--visible': isExpanded })}>
{item.expandedView}
{item.expandedView?.()}
</div>
<div className={cx('integrationTree__element', { 'integrationTree__element--visible': !isExpanded })}>
{item.collapsedView}
{item.collapsedView?.(onClick)}
</div>
</div>
);

View file

@ -1,6 +1,7 @@
import React from 'react';
import cn from 'classnames/bind';
import { noop } from 'lodash-es';
import Block from 'components/GBlock/Block';
@ -13,13 +14,20 @@ interface IntegrationBlockProps {
hasCollapsedBorder: boolean;
heading: React.ReactNode;
content: React.ReactNode;
toggle?: () => void;
}
const IntegrationBlock: React.FC<IntegrationBlockProps> = ({ heading, content, hasCollapsedBorder, className }) => {
const IntegrationBlock: React.FC<IntegrationBlockProps> = ({
heading,
content,
hasCollapsedBorder,
className,
toggle = noop,
}) => {
return (
<div className={cx('integrationBlock', className)}>
{heading && (
<Block bordered shadowed className={cx('integrationBlock__heading')}>
<Block bordered shadowed className={cx('integrationBlock__heading')} onClick={toggle}>
{heading}
</Block>
)}
@ -28,6 +36,7 @@ const IntegrationBlock: React.FC<IntegrationBlockProps> = ({ heading, content, h
className={cx('integrationBlock__content', {
'integrationBlock__content--collapsedBorder': hasCollapsedBorder,
})}
onClick={toggle}
>
{content}
</div>

View file

@ -292,6 +292,7 @@ class AlertRules extends React.Component<AlertRulesProps, AlertRulesState> {
)}
</div>
</Block>
{alertReceiveChannel.description && (
<div className={cx('description-style')}>
<Alert

View file

@ -21,10 +21,11 @@ interface CollapsedIntegrationRouteDisplayProps {
alertReceiveChannelId: AlertReceiveChannel['id'];
channelFilterId: ChannelFilter['id'];
routeIndex: number;
toggle: () => void;
}
const CollapsedIntegrationRouteDisplay: React.FC<CollapsedIntegrationRouteDisplayProps> = observer(
({ channelFilterId, alertReceiveChannelId, routeIndex }) => {
({ channelFilterId, alertReceiveChannelId, routeIndex, toggle }) => {
const { escalationChainStore, alertReceiveChannelStore } = useStore();
const [routeIdForDeletion, setRouteIdForDeletion] = useState<ChannelFilter['id']>(undefined);
@ -44,6 +45,7 @@ const CollapsedIntegrationRouteDisplay: React.FC<CollapsedIntegrationRouteDispla
<IntegrationBlock
hasCollapsedBorder={false}
key={channelFilterId}
toggle={toggle}
heading={
<div className={cx('heading-container')}>
<div className={cx('heading-container__item', 'heading-container__item--large')}>

View file

@ -336,18 +336,25 @@ export const RouteButtonsDisplay: React.FC<RouteButtonsDisplayProps> = ({
{!channelFilter.is_default && (
<WithPermissionControlTooltip userAction={UserActions.IntegrationsWrite}>
<Tooltip placement="top" content={'Delete'}>
<Button variant={'secondary'} icon={'trash-alt'} size={'sm'} onClick={setRouteIdForDeletion} />
<Button variant={'secondary'} icon={'trash-alt'} size={'sm'} onClick={onDelete} />
</Tooltip>
</WithPermissionControlTooltip>
)}
</HorizontalGroup>
);
function onRouteMoveDown() {
function onDelete(e: React.SyntheticEvent) {
e.stopPropagation();
setRouteIdForDeletion();
}
function onRouteMoveDown(e: React.SyntheticEvent) {
e.stopPropagation();
alertReceiveChannelStore.moveChannelFilterToPosition(alertReceiveChannelId, routeIndex, routeIndex + 1);
}
function onRouteMoveUp() {
function onRouteMoveUp(e: React.SyntheticEvent) {
e.stopPropagation();
alertReceiveChannelStore.moveChannelFilterToPosition(alertReceiveChannelId, routeIndex, routeIndex - 1);
}
};

View file

@ -104,7 +104,14 @@ $LARGE-MARGIN: 24px;
.loadingPlaceholder {
margin-bottom: 0;
margin-right: 4px;
animation: none;
}
.integration__description-alert {
padding-top: 24px;
a {
color: var(--primary-text-link);
}
}
.customise-button button {

View file

@ -12,9 +12,10 @@ import {
IconButton,
ConfirmModal,
Drawer,
Alert,
} from '@grafana/ui';
import cn from 'classnames/bind';
import { get } from 'lodash-es';
import { get, noop } from 'lodash-es';
import { observer } from 'mobx-react';
import CopyToClipboard from 'react-copy-to-clipboard';
import Emoji from 'react-emoji-render';
@ -64,7 +65,8 @@ import { openNotification, openErrorNotification } from 'utils';
import { getVar } from 'utils/DOM';
import LocationHelper from 'utils/LocationHelper';
import { UserActions } from 'utils/authorization';
import { DATASOURCE_ALERTING, PLUGIN_ROOT } from 'utils/consts';
import { DATASOURCE_GRAFANA, PLUGIN_ROOT } from 'utils/consts';
import sanitize from 'utils/sanitize';
const cx = cn.bind(styles);
@ -209,6 +211,17 @@ class Integration2 extends React.Component<Integration2Props, Integration2State>
integration={integration}
/>
</div>
{alertReceiveChannel.description && (
<div className={cx('integration__description-alert')}>
<Alert
style={{ marginBottom: '0' }}
// @ts-ignore
title={<div dangerouslySetInnerHTML={{ __html: sanitize(alertReceiveChannel.description) }}></div>}
severity="info"
/>
</div>
)}
</div>
<IntegrationCollapsibleTreeView
@ -218,14 +231,14 @@ class Integration2 extends React.Component<Integration2Props, Integration2State>
customIcon: 'plug',
canHoverIcon: false,
collapsedView: null,
expandedView: <HowToConnectComponent id={id} />,
expandedView: () => <HowToConnectComponent id={id} />,
},
{
customIcon: 'layer-group',
isExpanded: false,
isCollapsible: false,
canHoverIcon: false,
expandedView: (
expandedView: () => (
<IntegrationBlock
hasCollapsedBorder
heading={
@ -290,7 +303,7 @@ class Integration2 extends React.Component<Integration2Props, Integration2State>
isCollapsible: false,
collapsedView: null,
canHoverIcon: false,
expandedView: (
expandedView: () => (
<div className={cx('routesSection')}>
<VerticalGroup spacing="md">
<Text type={'primary'} className={cx('routesSection__heading')}>
@ -420,14 +433,15 @@ class Integration2 extends React.Component<Integration2Props, Integration2State>
this.setState((prevState) => ({ newRoutes: prevState.newRoutes.filter((r) => r !== channelFilterId) }));
}
},
collapsedView: (
collapsedView: (toggle) => (
<CollapsedIntegrationRouteDisplay
alertReceiveChannelId={id}
channelFilterId={channelFilterId}
routeIndex={routeIndex}
toggle={toggle}
/>
),
expandedView: (
expandedView: () => (
<ExpandedIntegrationRouteDisplay
alertReceiveChannelId={id}
channelFilterId={channelFilterId}
@ -884,12 +898,13 @@ const HowToConnectComponent: React.FC<{ id: AlertReceiveChannel['id'] }> = ({ id
const { alertReceiveChannelStore } = useStore();
const alertReceiveChannelCounter = alertReceiveChannelStore.counters[id];
const alertReceiveChannel = alertReceiveChannelStore.items[id];
const isAlertManager = alertReceiveChannel.integration === DATASOURCE_ALERTING;
const isGrafanaDatasource = alertReceiveChannel.integration === DATASOURCE_GRAFANA;
const hasAlerts = !!alertReceiveChannelCounter?.alerts_count;
return (
<IntegrationBlock
hasCollapsedBorder={false}
toggle={noop}
heading={
<div className={cx('how-to-connect__container')}>
<Tag
@ -915,7 +930,7 @@ const HowToConnectComponent: React.FC<{ id: AlertReceiveChannel['id'] }> = ({ id
</a>
</div>
}
content={isAlertManager || !hasAlerts ? renderContent() : null}
content={isGrafanaDatasource || !hasAlerts ? renderContent() : null}
/>
);
@ -930,14 +945,14 @@ const HowToConnectComponent: React.FC<{ id: AlertReceiveChannel['id'] }> = ({ id
</HorizontalGroup>
)}
{isAlertManager && (
{isGrafanaDatasource && (
<HorizontalGroup spacing={'xs'}>
<Icon name="list-ui-alt" size="md" />
<a href="/alerting/notifications" target="_blank">
<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" target="_blank">
<a href="/alerting/routes?alertmanager=grafana" target="_blank">
<Text type={'link'}>Notification Policy</Text>
</a>
<Text type={'secondary'}>created in Grafana Alerting</Text>
@ -974,8 +989,8 @@ const IntegrationHeader: React.FC<IntegrationHeaderProps> = ({
>
<TooltipBadge
borderType="primary"
tooltipTitle={undefined}
tooltipContent={getAlertReceiveChannelCounterTooltip()}
tooltipTitle={getAlertReceiveChannelCounterTooltip()}
tooltipContent={undefined}
text={alertReceiveChannelCounter?.alerts_count + '/' + alertReceiveChannelCounter?.alert_groups_count}
/>
</PluginLink>

View file

@ -34,4 +34,4 @@ export const DOCS_TELEGRAM_SETUP = 'https://grafana.com/docs/oncall/latest/chat-
// Make sure if you chage max-width here you also change it in responsive.css
export const TABLE_COLUMN_MAX_WIDTH = 1500;
export const DATASOURCE_ALERTING = 'alertmanager';
export const DATASOURCE_GRAFANA = 'grafana';