diff --git a/.betterer.results b/.betterer.results index b3610ed75af..02141dd6154 100644 --- a/.betterer.results +++ b/.betterer.results @@ -2261,6 +2261,22 @@ exports[`better eslint`] = { [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], [0, 0, 0, "No untranslated strings. Wrap text with ", "5"] ], + "public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v1.tsx:5381": [ + [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "8"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "9"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "10"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "11"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "12"], + [0, 0, 0, "No untranslated strings. Wrap text with ", "13"] + ], "public/app/features/alerting/unified/components/rules/GrafanaRules.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], @@ -2313,22 +2329,6 @@ exports[`better eslint`] = { "public/app/features/alerting/unified/components/rules/RuleStats.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"] ], - "public/app/features/alerting/unified/components/rules/RulesFilter.tsx:5381": [ - [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "2"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "3"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "4"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "5"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "6"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "7"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "8"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "9"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "10"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "11"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "12"], - [0, 0, 0, "No untranslated strings. Wrap text with ", "13"] - ], "public/app/features/alerting/unified/components/rules/RulesGroup.tsx:5381": [ [0, 0, 0, "No untranslated strings. Wrap text with ", "0"], [0, 0, 0, "No untranslated strings. Wrap text with ", "1"], diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 7f0aa1136fd..2d41dd21494 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -201,6 +201,7 @@ export interface FeatureToggles { bodyScrolling?: boolean; cloudwatchMetricInsightsCrossAccount?: boolean; prometheusAzureOverrideAudience?: boolean; + alertingFilterV2?: boolean; backgroundPluginInstaller?: boolean; dataplaneAggregator?: boolean; newFiltersUI?: boolean; diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index a6ef6a2845a..37b15c4c272 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -1385,6 +1385,12 @@ var ( Stage: FeatureStageDeprecated, Owner: grafanaPartnerPluginsSquad, Expression: "true", // Enabled by default for now + }, { + Name: "alertingFilterV2", + Description: "Enable the new alerting search experience", + Stage: FeatureStageExperimental, + Owner: grafanaAlertingSquad, + HideFromDocs: true, }, { Name: "backgroundPluginInstaller", diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index f2a315980b9..61c8867773b 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -182,6 +182,7 @@ cloudWatchRoundUpEndTime,GA,@grafana/aws-datasources,false,false,false bodyScrolling,preview,@grafana/grafana-frontend-platform,false,false,true cloudwatchMetricInsightsCrossAccount,preview,@grafana/aws-datasources,false,false,true prometheusAzureOverrideAudience,deprecated,@grafana/partner-datasources,false,false,false +alertingFilterV2,experimental,@grafana/alerting-squad,false,false,false backgroundPluginInstaller,experimental,@grafana/plugins-platform-backend,false,true,false dataplaneAggregator,experimental,@grafana/grafana-app-platform-squad,false,true,false newFiltersUI,experimental,@grafana/dashboards-squad,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index cf5406b00c2..0662f74328c 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -739,6 +739,10 @@ const ( // Deprecated. Allow override default AAD audience for Azure Prometheus endpoint. Enabled by default. This feature should no longer be used and will be removed in the future. FlagPrometheusAzureOverrideAudience = "prometheusAzureOverrideAudience" + // FlagAlertingFilterV2 + // Enable the new alerting search experience + FlagAlertingFilterV2 = "alertingFilterV2" + // FlagBackgroundPluginInstaller // Enable background plugin installer FlagBackgroundPluginInstaller = "backgroundPluginInstaller" diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 10a1b9d560f..7e69fb7ba05 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -162,6 +162,19 @@ "hideFromDocs": true } }, + { + "metadata": { + "name": "alertingFilterV2", + "resourceVersion": "1723028774805", + "creationTimestamp": "2024-08-07T11:06:14Z" + }, + "spec": { + "description": "Enable the new alerting search experience", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad", + "hideFromDocs": true + } + }, { "metadata": { "name": "alertingInsights", diff --git a/public/app/features/alerting/unified/components/HoverCard.tsx b/public/app/features/alerting/unified/components/HoverCard.tsx index 0572cfa7794..9df1f2a5066 100644 --- a/public/app/features/alerting/unified/components/HoverCard.tsx +++ b/public/app/features/alerting/unified/components/HoverCard.tsx @@ -6,7 +6,7 @@ import { cloneElement, ReactElement, ReactNode, useRef } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { Popover as GrafanaPopover, PopoverController, useStyles2, Stack } from '@grafana/ui'; -export interface HoverCardProps { +export interface PopupCardProps { children: ReactElement; header?: ReactNode; content: ReactElement; @@ -16,9 +16,10 @@ export interface HoverCardProps { disabled?: boolean; showAfter?: number; arrow?: boolean; + showOn?: 'click' | 'hover'; } -export const HoverCard = ({ +export const PopupCard = ({ children, header, content, @@ -27,8 +28,9 @@ export const HoverCard = ({ showAfter = 300, wrapperClassName, disabled = false, + showOn = 'hover', ...rest -}: HoverCardProps) => { +}: PopupCardProps) => { const popoverRef = useRef(null); const styles = useStyles2(getStyles); @@ -36,6 +38,9 @@ export const HoverCard = ({ return children; } + const showOnHover = showOn === 'hover'; + const showOnClick = showOn === 'click'; + const body = ( {header && {header}} @@ -47,6 +52,21 @@ export const HoverCard = ({ return ( {(showPopper, hidePopper, popperProps) => { + // support hover and click interaction + const onClickProps = { + onClick: showPopper, + }; + + const onHoverProps = { + onMouseLeave: hidePopper, + onMouseEnter: showPopper, + }; + + const blurFocusProps = { + onBlur: hidePopper, + onFocus: showPopper, + }; + return ( <> {popoverRef.current && ( @@ -54,22 +74,24 @@ export const HoverCard = ({ {...popperProps} {...rest} wrapperClassName={classnames(styles.popover, wrapperClassName)} - onMouseLeave={hidePopper} - onMouseEnter={showPopper} - onFocus={showPopper} - onBlur={hidePopper} referenceElement={popoverRef.current} renderArrow={arrow} + // @TODO + // if we want interaction with the content we should not pass blur / focus handlers but then clicking outside doesn't close the popper + {...blurFocusProps} + // if we want hover interaction we have to make sure we add the leave / enter handlers + {...(showOnHover ? onHoverProps : {})} /> )} {cloneElement(children, { ref: popoverRef, - onMouseEnter: showPopper, - onMouseLeave: hidePopper, onFocus: showPopper, onBlur: hidePopper, tabIndex: 0, + // make sure we pass the correct interaction handlers here to the element we want to interact with + ...(showOnHover ? onHoverProps : {}), + ...(showOnClick ? onClickProps : {}), })} > ); @@ -83,7 +105,7 @@ const getStyles = (theme: GrafanaTheme2) => ({ borderRadius: theme.shape.radius.default, boxShadow: theme.shadows.z3, background: theme.colors.background.primary, - border: `1px solid ${theme.colors.border.medium}`, + border: `1px solid ${theme.colors.border.weak}`, }), card: { body: css({ diff --git a/public/app/features/alerting/unified/components/Tokenize.tsx b/public/app/features/alerting/unified/components/Tokenize.tsx index 7802f2d3c26..c1ecc6d42a6 100644 --- a/public/app/features/alerting/unified/components/Tokenize.tsx +++ b/public/app/features/alerting/unified/components/Tokenize.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { Badge, useStyles2 } from '@grafana/ui'; -import { HoverCard } from './HoverCard'; +import { PopupCard } from './HoverCard'; import { keywords as KEYWORDS, builtinFunctions as FUNCTIONS } from './receivers/editor/language'; const VARIABLES = ['$', '.', '"']; @@ -83,7 +83,7 @@ function Token({ content, description, type }: TokenProps) { const disableCard = Boolean(type) === false; return ( - - + ); } diff --git a/public/app/features/alerting/unified/components/expressions/Expression.tsx b/public/app/features/alerting/unified/components/expressions/Expression.tsx index c9d7d8e2e17..9110dbbf8fa 100644 --- a/public/app/features/alerting/unified/components/expressions/Expression.tsx +++ b/public/app/features/alerting/unified/components/expressions/Expression.tsx @@ -20,7 +20,7 @@ import { import { AlertQuery, PromAlertingRuleState } from 'app/types/unified-alerting-dto'; import { usePagination } from '../../hooks/usePagination'; -import { HoverCard } from '../HoverCard'; +import { PopupCard } from '../HoverCard'; import { Spacer } from '../Spacer'; import { AlertStateTag } from '../rules/AlertStateTag'; @@ -424,7 +424,7 @@ const TimeseriesRow: FC = ({ frame, index }) => {name} - = ({ frame, index }) => } > Time series data - + diff --git a/public/app/features/alerting/unified/components/notification-policies/Matchers.tsx b/public/app/features/alerting/unified/components/notification-policies/Matchers.tsx index 914ba8067de..87f81464a00 100644 --- a/public/app/features/alerting/unified/components/notification-policies/Matchers.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/Matchers.tsx @@ -7,7 +7,7 @@ import { getTagColorsFromName, useStyles2, Stack } from '@grafana/ui'; import { ObjectMatcher } from 'app/plugins/datasource/alertmanager/types'; import { MatcherFormatter, matcherFormatter } from '../../utils/matchers'; -import { HoverCard } from '../HoverCard'; +import { PopupCard } from '../HoverCard'; type MatchersProps = { matchers: ObjectMatcher[]; formatter?: MatcherFormatter }; @@ -29,7 +29,7 @@ const Matchers: FC = ({ matchers, formatter = 'default' }) => { ))} {/* TODO hover state to show all matchers we're not showing */} {hasMoreMatchers && ( - = ({ matchers, formatter = 'default' }) => { {`and ${rest.length} more`} - + )} diff --git a/public/app/features/alerting/unified/components/notification-policies/Policy.tsx b/public/app/features/alerting/unified/components/notification-policies/Policy.tsx index d90c5307532..0dbbc61072e 100644 --- a/public/app/features/alerting/unified/components/notification-policies/Policy.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/Policy.tsx @@ -42,7 +42,7 @@ import { createContactPointLink, createMuteTimingLink } from '../../utils/misc'; import { InheritableProperties, getInheritedProperties } from '../../utils/notification-policies'; import { InsertPosition } from '../../utils/routeTree'; import { Authorize } from '../Authorize'; -import { HoverCard } from '../HoverCard'; +import { PopupCard } from '../HoverCard'; import { Label } from '../Label'; import { MetaText } from '../MetaText'; import { ProvisioningBadge } from '../Provisioning'; @@ -633,7 +633,7 @@ const ProvisionedTooltip = (children: ReactNode) => ( ); const Errors: FC<{ errors: React.ReactNode[] }> = ({ errors }) => ( - = ({ errors }) => ( - + ); const ContinueMatchingIndicator: FC = () => { @@ -697,7 +697,7 @@ function AutogeneratedRootIndicator() { } const InheritedProperties: FC<{ properties: InheritableProperties }> = ({ properties }) => ( - = ({ proper {pluralize('property', Object.keys(properties).length, true)} - + ); const TimeIntervals: FC<{ timings: string[]; alertManagerSourceName: string }> = ({ @@ -884,7 +884,7 @@ const ContactPointsHoverDetails: FC = ({ const groupedIntegrations = groupBy(details.grafana_managed_receiver_configs, (config) => config.type); return ( - = ({ > {contactPoint} - + ); }; diff --git a/public/app/features/alerting/unified/components/notification-policies/PromDurationInput.tsx b/public/app/features/alerting/unified/components/notification-policies/PromDurationInput.tsx index 697f49531b0..e90c9aba186 100644 --- a/public/app/features/alerting/unified/components/notification-policies/PromDurationInput.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/PromDurationInput.tsx @@ -2,7 +2,7 @@ import { forwardRef } from 'react'; import { Icon, Input } from '@grafana/ui'; -import { HoverCard } from '../HoverCard'; +import { PopupCard } from '../HoverCard'; import { PromDurationDocs } from './PromDurationDocs'; @@ -10,9 +10,9 @@ export const PromDurationInput = forwardRef} disabled={false}> + } disabled={false}> - + } {...props} ref={ref} diff --git a/public/app/features/alerting/unified/components/receivers/TemplateDataDocs.tsx b/public/app/features/alerting/unified/components/receivers/TemplateDataDocs.tsx index cb6caa193b3..fd3db87ce62 100644 --- a/public/app/features/alerting/unified/components/receivers/TemplateDataDocs.tsx +++ b/public/app/features/alerting/unified/components/receivers/TemplateDataDocs.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2, Stack } from '@grafana/ui'; -import { HoverCard } from '../HoverCard'; +import { PopupCard } from '../HoverCard'; import { AlertTemplateData, @@ -35,13 +35,13 @@ export function TemplateDataDocs() { dataItems={GlobalTemplateData} typeRenderer={(type) => type === '[]Alert' ? ( - + {type} - + ) : type === 'KeyValue' ? ( - }> + }> {type} - + ) : ( type ) diff --git a/public/app/features/alerting/unified/components/rule-list/RuleList.v1.tsx b/public/app/features/alerting/unified/components/rule-list/RuleList.v1.tsx index 8b0fe0707d9..edc6065eef2 100644 --- a/public/app/features/alerting/unified/components/rule-list/RuleList.v1.tsx +++ b/public/app/features/alerting/unified/components/rule-list/RuleList.v1.tsx @@ -1,10 +1,9 @@ -import { css } from '@emotion/css'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useLocation } from 'react-router-dom-v5-compat'; import { useAsyncFn, useInterval } from 'react-use'; -import { GrafanaTheme2, urlUtil } from '@grafana/data'; -import { Button, LinkButton, useStyles2, withErrorBoundary } from '@grafana/ui'; +import { urlUtil } from '@grafana/data'; +import { Button, LinkButton, Stack, withErrorBoundary } from '@grafana/ui'; import { useQueryParams } from 'app/core/hooks/useQueryParams'; import { useDispatch } from 'app/types'; @@ -18,13 +17,13 @@ import { fetchAllPromAndRulerRulesAction } from '../../state/actions'; import { RULE_LIST_POLL_INTERVAL_MS } from '../../utils/constants'; import { getAllRulesSourceNames } from '../../utils/datasource'; import { AlertingPageWrapper } from '../AlertingPageWrapper'; +import RulesFilter from '../rules/Filter/RulesFilter'; import { NoRulesSplash } from '../rules/NoRulesCTA'; import { INSTANCES_DISPLAY_LIMIT } from '../rules/RuleDetails'; import { RuleListErrors } from '../rules/RuleListErrors'; import { RuleListGroupView } from '../rules/RuleListGroupView'; import { RuleListStateView } from '../rules/RuleListStateView'; import { RuleStats } from '../rules/RuleStats'; -import RulesFilter from '../rules/RulesFilter'; const VIEWS = { groups: RuleListGroupView, @@ -37,7 +36,6 @@ const LIMIT_ALERTS = INSTANCES_DISPLAY_LIMIT + 1; const RuleList = withErrorBoundary( () => { const dispatch = useDispatch(); - const styles = useStyles2(getStyles); const rulesDataSourceNames = useMemo(getAllRulesSourceNames, []); const [expandAll, setExpandAll] = useState(false); @@ -106,26 +104,20 @@ const RuleList = withErrorBoundary( // We show separate indicators for Grafana-managed and Cloud rules }> - + {hasAlertRulesCreated && ( - <> - - - - {view === 'groups' && hasActiveFilters && ( - setExpandAll(!expandAll)} - > - {expandAll ? 'Collapse all' : 'Expand all'} - - )} - - - - > + + {view === 'groups' && hasActiveFilters && ( + setExpandAll(!expandAll)} + > + {expandAll ? 'Collapse all' : 'Expand all'} + + )} + + )} {hasNoAlertRulesCreatedYet && } {hasAlertRulesCreated && } @@ -135,28 +127,6 @@ const RuleList = withErrorBoundary( { style: 'page' } ); -const getStyles = (theme: GrafanaTheme2) => ({ - break: css({ - width: '100%', - height: 0, - marginBottom: theme.spacing(2), - borderBottom: `solid 1px ${theme.colors.border.medium}`, - }), - buttonsContainer: css({ - marginBottom: theme.spacing(2), - display: 'flex', - justifyContent: 'space-between', - }), - statsContainer: css({ - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - }), - expandAllButton: css({ - marginRight: theme.spacing(1), - }), -}); - export default RuleList; export function CreateAlertButton() { diff --git a/public/app/features/alerting/unified/components/rule-list/RuleList.v2.tsx b/public/app/features/alerting/unified/components/rule-list/RuleList.v2.tsx index 4cd337802a1..de62ae914ae 100644 --- a/public/app/features/alerting/unified/components/rule-list/RuleList.v2.tsx +++ b/public/app/features/alerting/unified/components/rule-list/RuleList.v2.tsx @@ -18,11 +18,11 @@ import { RULE_LIST_POLL_INTERVAL_MS } from '../../utils/constants'; import { getAllRulesSourceNames, getRulesSourceUniqueKey, getApplicationFromRulesSource } from '../../utils/datasource'; import { makeFolderAlertsLink } from '../../utils/misc'; import { AlertingPageWrapper } from '../AlertingPageWrapper'; +import RulesFilter from '../rules/Filter/RulesFilter'; import { NoRulesSplash } from '../rules/NoRulesCTA'; import { INSTANCES_DISPLAY_LIMIT } from '../rules/RuleDetails'; import { RuleListErrors } from '../rules/RuleListErrors'; import { RuleStats } from '../rules/RuleStats'; -import RulesFilter from '../rules/RulesFilter'; import { EvaluationGroupWithRules } from './EvaluationGroupWithRules'; import Namespace from './Namespace'; @@ -101,7 +101,7 @@ const RuleList = withErrorBoundary( // We show separate indicators for Grafana-managed and Cloud rules }> - + {hasAlertRulesCreated && ( <> diff --git a/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.tsx b/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.tsx new file mode 100644 index 00000000000..8a7b0a83a9c --- /dev/null +++ b/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.tsx @@ -0,0 +1,18 @@ +import { Suspense, lazy } from 'react'; + +import { config } from '@grafana/runtime'; + +import RulesFilterV1 from './RulesFilter.v1'; + +const RulesFilterV2 = lazy(() => import('./RulesFilter.v2')); + +interface RulesFilerProps { + onClear?: () => void; +} + +const RulesFilter = (props: RulesFilerProps) => { + const newView = config.featureToggles.alertingFilterV2; + return {newView ? : }; +}; + +export default RulesFilter; diff --git a/public/app/features/alerting/unified/components/rules/RulesFilter.tsx b/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v1.tsx similarity index 93% rename from public/app/features/alerting/unified/components/rules/RulesFilter.tsx rename to public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v1.tsx index af929c592cd..b7907e7409e 100644 --- a/public/app/features/alerting/unified/components/rules/RulesFilter.tsx +++ b/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v1.tsx @@ -16,17 +16,16 @@ import { trackRulesListViewChange, trackRulesSearchComponentInteraction, trackRulesSearchInputInteraction, -} from '../../Analytics'; -import { useRulesFilter } from '../../hooks/useFilteredRules'; -import { useURLSearchParams } from '../../hooks/useURLSearchParams'; -import { useAlertingHomePageExtensions } from '../../plugins/useAlertingHomePageExtensions'; -import { RuleHealth } from '../../search/rulesSearchParser'; -import { AlertmanagerProvider } from '../../state/AlertmanagerContext'; -import { GRAFANA_RULES_SOURCE_NAME } from '../../utils/datasource'; -import { alertStateToReadable } from '../../utils/rules'; -import { HoverCard } from '../HoverCard'; - -import { MultipleDataSourcePicker } from './MultipleDataSourcePicker'; +} from '../../../Analytics'; +import { useRulesFilter } from '../../../hooks/useFilteredRules'; +import { useURLSearchParams } from '../../../hooks/useURLSearchParams'; +import { useAlertingHomePageExtensions } from '../../../plugins/useAlertingHomePageExtensions'; +import { RuleHealth } from '../../../search/rulesSearchParser'; +import { AlertmanagerProvider } from '../../../state/AlertmanagerContext'; +import { GRAFANA_RULES_SOURCE_NAME } from '../../../utils/datasource'; +import { alertStateToReadable } from '../../../utils/rules'; +import { PopupCard } from '../../HoverCard'; +import { MultipleDataSourcePicker } from '../MultipleDataSourcePicker'; const ViewOptions: SelectableValue[] = [ { @@ -64,7 +63,7 @@ const RuleHealthOptions: SelectableValue[] = [ ]; interface RulesFilerProps { - onFilterCleared?: () => void; + onClear?: () => void; } const RuleStateOptions = Object.entries(PromAlertingRuleState).map(([key, value]) => ({ @@ -72,7 +71,7 @@ const RuleStateOptions = Object.entries(PromAlertingRuleState).map(([key, value] value, })); -const RulesFilter = ({ onFilterCleared = () => undefined }: RulesFilerProps) => { +const RulesFilter = ({ onClear = () => undefined }: RulesFilerProps) => { const styles = useStyles2(getStyles); const [queryParams, updateQueryParams] = useURLSearchParams(); const { pluginsFilterEnabled } = usePluginsFilterStatus(); @@ -136,7 +135,7 @@ const RulesFilter = ({ onFilterCleared = () => undefined }: RulesFilerProps) => const handleClearFiltersClick = () => { setSearchQuery(undefined); - onFilterCleared(); + onClear(); setTimeout(() => setFilterKey(filterKey + 1), 100); }; @@ -291,9 +290,9 @@ const RulesFilter = ({ onFilterCleared = () => undefined }: RulesFilerProps) => Search - }> + }> - + } diff --git a/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v2.tsx b/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v2.tsx new file mode 100644 index 00000000000..7faa69f1f83 --- /dev/null +++ b/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v2.tsx @@ -0,0 +1,222 @@ +import { css } from '@emotion/css'; +import { useCallback, useMemo, useState } from 'react'; + +import { GrafanaTheme2 } from '@grafana/data'; +import { + Badge, + Button, + Grid, + IconButton, + Input, + InteractiveTable, + Label, + RadioButtonGroup, + Select, + Stack, + Tab, + TabsBar, + useStyles2, +} from '@grafana/ui'; +import { Trans } from 'app/core/internationalization'; + +import { PopupCard } from '../../HoverCard'; +import MoreButton from '../../MoreButton'; + +type RulesFilterProps = { + onClear?: () => void; +}; + +type ActiveTab = 'custom' | 'saved'; + +export default function RulesFilter({ onClear = () => {} }: RulesFilterProps) { + const styles = useStyles2(getStyles); + const [activeTab, setActiveTab] = useState('custom'); + + const filterOptions = useMemo(() => { + return ( + + {activeTab === 'custom' && } + {activeTab === 'saved' && } + + } + header={ + + setActiveTab('custom')} + /> + setActiveTab('saved')} + /> + + } + > + + + ); + }, [activeTab, styles.content, styles.fixTabsMargin]); + + return ( + + + Search + + + + + + ); +} + +const FilterOptions = () => { + return ( + + + + Folder / Namespace + + {}}> + + Alerting rule name + + + + Evaluation group + + + + Labels + + + + Data source + + {}}> + + State + + + + Type + + + + Health + + + + + + Clear + + + Apply + + + + ); +}; + +type TableColumns = { + name: string; + default?: boolean; +}; + +const SavedSearches = () => { + const applySearch = useCallback((name: string) => {}, []); + + return ( + <> + + + Save current search + + + columns={[ + { + id: 'name', + header: 'Saved search name', + cell: ({ row }) => ( + + {row.original.name} + {row.original.default ? : null} + + ), + }, + { + id: 'actions', + cell: ({ row }) => ( + + applySearch(row.original.name)}> + Apply + + + + ), + }, + ]} + data={[ + { + name: 'My saved search', + default: true, + }, + { + name: 'Another saved search', + }, + { + name: 'This one has a really long name and some emojis too 🥒', + }, + ]} + getRowId={(row) => row.name} + /> + + Close + + + > + ); +}; + +function getStyles(theme: GrafanaTheme2) { + return { + content: css({ + padding: theme.spacing(1), + maxWidth: 500, + }), + fixTabsMargin: css({ + marginTop: theme.spacing(-1), + }), + }; +} diff --git a/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx b/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx index b4d5205e574..f2df2ef5747 100644 --- a/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx +++ b/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx @@ -8,7 +8,7 @@ import * as analytics from '../../Analytics'; import { MockDataSourceSrv } from '../../mocks'; import { setupPluginsExtensionsHook } from '../../testSetup/plugins'; -import RulesFilter from './RulesFilter'; +import RulesFilter from './Filter/RulesFilter'; setupMswServer(); jest.spyOn(analytics, 'logInfo'); diff --git a/public/app/features/alerting/unified/components/rules/state-history/LokiStateHistory.tsx b/public/app/features/alerting/unified/components/rules/state-history/LokiStateHistory.tsx index 99397446a0c..688eae2cea8 100644 --- a/public/app/features/alerting/unified/components/rules/state-history/LokiStateHistory.tsx +++ b/public/app/features/alerting/unified/components/rules/state-history/LokiStateHistory.tsx @@ -10,7 +10,7 @@ import { Alert, Button, Field, Icon, Input, Label, Stack, Tooltip, useStyles2 } import { stateHistoryApi } from '../../../api/stateHistoryApi'; import { combineMatcherStrings } from '../../../utils/alertmanager'; import { AlertLabels } from '../../AlertLabels'; -import { HoverCard } from '../../HoverCard'; +import { PopupCard } from '../../HoverCard'; import { LogRecordViewerByTimestamp } from './LogRecordViewer'; import { LogTimelineViewer } from './LogTimelineViewer'; @@ -186,7 +186,7 @@ const SearchFieldInput = React.forwardRef Filter instances - Use label matcher expression (like {'{foo=bar}'}) or click on an instance label to @@ -195,7 +195,7 @@ const SearchFieldInput = React.forwardRef - + } diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index cb543308190..3db019a317b 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -225,6 +225,19 @@ "update-rule": { "success": "Rule updated successfully" } + }, + "search": { + "property": { + "data-source": "Data source", + "evaluation-group": "Evaluation group", + "labels": "Labels", + "namespace": "Folder / Namespace", + "rule-health": "Health", + "rule-name": "Alerting rule name", + "rule-type": "Type", + "state": "State" + }, + "save-query": "Save current search" } }, "annotations": { @@ -367,11 +380,15 @@ } }, "common": { + "apply": "Apply", "cancel": "Cancel", + "clear": "Clear", + "close": "Close", "locale": { "default": "Default" }, - "save": "Save" + "save": "Save", + "search": "Search" }, "configuration-tracker": { "config-card": { diff --git a/public/locales/pseudo-LOCALE/grafana.json b/public/locales/pseudo-LOCALE/grafana.json index f3244f3faea..5706d731682 100644 --- a/public/locales/pseudo-LOCALE/grafana.json +++ b/public/locales/pseudo-LOCALE/grafana.json @@ -225,6 +225,19 @@ "update-rule": { "success": "Ŗūľę ūpđäŧęđ şūččęşşƒūľľy" } + }, + "search": { + "property": { + "data-source": "Đäŧä şőūřčę", + "evaluation-group": "Ēväľūäŧįőʼn ģřőūp", + "labels": "Ŀäþęľş", + "namespace": "Főľđęř / Ńämęşpäčę", + "rule-health": "Ħęäľŧĥ", + "rule-name": "Åľęřŧįʼnģ řūľę ʼnämę", + "rule-type": "Ŧypę", + "state": "Ŝŧäŧę" + }, + "save-query": "Ŝävę čūřřęʼnŧ şęäřčĥ" } }, "annotations": { @@ -367,11 +380,15 @@ } }, "common": { + "apply": "Åppľy", "cancel": "Cäʼnčęľ", + "clear": "Cľęäř", + "close": "Cľőşę", "locale": { "default": "Đęƒäūľŧ" }, - "save": "Ŝävę" + "save": "Ŝävę", + "search": "Ŝęäřčĥ" }, "configuration-tracker": { "config-card": {
{'{foo=bar}'}