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:
parent
e3181a5afb
commit
b93413c032
8 changed files with 67 additions and 26 deletions
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -292,6 +292,7 @@ class AlertRules extends React.Component<AlertRulesProps, AlertRulesState> {
|
|||
)}
|
||||
</div>
|
||||
</Block>
|
||||
|
||||
{alertReceiveChannel.description && (
|
||||
<div className={cx('description-style')}>
|
||||
<Alert
|
||||
|
|
|
|||
|
|
@ -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')}>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue