diff --git a/eslint.config.js b/eslint.config.js index 8c4910b7310..3b29e36521b 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -262,6 +262,7 @@ module.exports = [ 'prefer-const': 'error', 'react/no-unused-prop-types': 'error', 'react/self-closing-comp': 'error', + 'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }], 'unicorn/no-unused-properties': 'error', }, }, diff --git a/public/app/features/alerting/unified/RuleViewer.tsx b/public/app/features/alerting/unified/RuleViewer.tsx index 9beaea7325a..4d4889eab84 100644 --- a/public/app/features/alerting/unified/RuleViewer.tsx +++ b/public/app/features/alerting/unified/RuleViewer.tsx @@ -15,7 +15,7 @@ import { stringifyErrorLike } from './utils/misc'; import { getRuleIdFromPathname, parse as parseRuleId } from './utils/rule-id'; import { withPageErrorBoundary } from './withPageErrorBoundary'; -const RuleViewer = (): JSX.Element => { +const RuleViewer = () => { const params = useParams(); const id = getRuleIdFromPathname(params); @@ -48,11 +48,7 @@ const RuleViewer = (): JSX.Element => { } if (loading) { - return ( - - <> - - ); + return ; } if (rule) { @@ -73,7 +69,7 @@ const RuleViewer = (): JSX.Element => { } // we should never get to this state - return <>; + return null; }; export const defaultPageNav: NavModelItem = { diff --git a/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx b/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx index 68b011d7d22..e6dd4a486f8 100644 --- a/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx +++ b/public/app/features/alerting/unified/components/AlertingPageWrapper.tsx @@ -1,4 +1,4 @@ -import { PropsWithChildren } from 'react'; +import { PropsWithChildren, ReactNode } from 'react'; import { useLocation } from 'react-use'; import { Page } from 'app/core/components/Page/Page'; @@ -12,9 +12,10 @@ import { NoAlertManagerWarning } from './NoAlertManagerWarning'; /** * This is the main alerting page wrapper, used by the alertmanager page wrapper and the alert rules list view */ -interface AlertingPageWrapperProps extends PageProps { +type AlertingPageWrapperProps = Omit & { isLoading?: boolean; -} + children?: ReactNode; +}; export const AlertingPageWrapper = ({ children, isLoading, ...rest }: AlertingPageWrapperProps) => ( diff --git a/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx b/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx index c7a637e489f..dfe2d07589c 100644 --- a/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx +++ b/public/app/features/alerting/unified/components/contact-points/ContactPoint.tsx @@ -223,15 +223,13 @@ const ContactPointReceiverMetadataRow = ({ diagnostics, sendingResolved }: Conta {/* this is shown when the last delivery failed – we don't show any additional metadata */} {failedToSend ? ( - <> - - - - Last delivery attempt failed - - - - + + + + Last delivery attempt failed + + + ) : ( <> {/* this is shown when we have a last delivery attempt */} diff --git a/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.tsx b/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.tsx index 475b6f358d3..ea90e5397fe 100644 --- a/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.tsx @@ -51,32 +51,34 @@ export const AmRootRouteForm = ({ actionButtons, alertManagerSourceName, onSubmi }); return (
- - <> -
- ( - handleContactPointSelect(changeValue, onChange), - }} - selectedContactPointName={value} - /> - )} - control={control} - name="receiver" - rules={{ required: { value: true, message: 'Required.' } }} - /> - or - - Create a contact point - -
- + +
+ ( + handleContactPointSelect(changeValue, onChange), + }} + selectedContactPointName={value} + /> + )} + control={control} + name="receiver" + rules={{ required: { value: true, message: 'Required.' } }} + /> + or + + Create a contact point + +
{ const showMore = moreCount > 0; return ( - <> - -
- {/* continueMatching and showMatchesAllLabelsWarning are mutually exclusive so the icons can't overlap */} - {continueMatching && } - {showMatchesAllLabelsWarning && } - -
- - {/* Matchers and actions */} -
- - {hasChildPolicies ? ( - - ) : null} - {isImmutablePolicy ? ( - isAutogeneratedPolicyRoot ? ( - - ) : ( - - ) - ) : hasMatchers ? ( - + +
+ {/* continueMatching and showMatchesAllLabelsWarning are mutually exclusive so the icons can't overlap */} + {continueMatching && } + {showMatchesAllLabelsWarning && } + +
+ + {/* Matchers and actions */} +
+ + {hasChildPolicies ? ( + + ) : null} + {isImmutablePolicy ? ( + isAutogeneratedPolicyRoot ? ( + ) : ( - - No matchers - - )} - - {/* TODO maybe we should move errors to the gutter instead? */} - {errors.length > 0 && } - {provisioned && } - - {!isAutoGenerated && !readOnly && ( - - - {isDefaultPolicy ? ( + + ) + ) : hasMatchers ? ( + + ) : ( + + No matchers + + )} + + {/* TODO maybe we should move errors to the gutter instead? */} + {errors.length > 0 && } + {provisioned && } + + {!isAutoGenerated && !readOnly && ( + + + {isDefaultPolicy ? ( + + ) : ( + + onAddPolicy(currentRoute, 'above')} + /> + onAddPolicy(currentRoute, 'below')} + /> + + onAddPolicy(currentRoute, 'child')} + /> + + } + > - ) : ( - - onAddPolicy(currentRoute, 'above')} - /> - onAddPolicy(currentRoute, 'below')} - /> - - onAddPolicy(currentRoute, 'child')} - /> - - } - > - - - )} - - - )} - {dropdownMenuActions.length > 0 && ( - {dropdownMenuActions}}> - - - )} - + + )} + + + )} + {dropdownMenuActions.length > 0 && ( + {dropdownMenuActions}}> + + + )} -
- - {/* Metadata row */} - -
-
+ +
+ + {/* Metadata row */} + +
-
- {showPolicyChildren && ( - <> - {pageOfChildren.map((child) => { - const childInheritedProperties = getInheritedProperties(currentRoute, child, inheritedProperties); - // This child is autogenerated if it's the autogenerated root or if it's a child of an autogenerated policy. - const isThisChildAutoGenerated = isAutoGeneratedRootAndSimplifiedEnabled(child) || isAutoGenerated; - /* pass the "readOnly" prop from the parent, because for any child policy , if its parent it's not editable, +
+
+ {showPolicyChildren && ( + <> + {pageOfChildren.map((child) => { + const childInheritedProperties = getInheritedProperties(currentRoute, child, inheritedProperties); + // This child is autogenerated if it's the autogenerated root or if it's a child of an autogenerated policy. + const isThisChildAutoGenerated = isAutoGeneratedRootAndSimplifiedEnabled(child) || isAutoGenerated; + /* pass the "readOnly" prop from the parent, because for any child policy , if its parent it's not editable, then the child policy should not be editable either */ - const isThisChildReadOnly = readOnly || provisioned || isAutoGenerated; - - return ( - - ); - })} - {showMore && ( - - )} - - )} -
- {showExportDrawer && } -
- + const isThisChildReadOnly = readOnly || provisioned || isAutoGenerated; + + return ( + + ); + })} + {showMore && ( + + )} + + )} +
+ {showExportDrawer && } + ); }; @@ -513,14 +511,12 @@ function MetadataRow({ )} {timingOptions && } {hasInheritedProperties && ( - <> - - - Inherited - - - - + + + Inherited + + + )}
diff --git a/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx b/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx index cd3e7c5d284..96bca41b8db 100644 --- a/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx +++ b/public/app/features/alerting/unified/components/receivers/AlertInstanceModalSelector.tsx @@ -113,9 +113,7 @@ export function AlertInstanceModalSelector({ >
{ruleName}
- <> - {filteredRules[ruleName][0].labels.grafana_folder ?? ''} - + {filteredRules[ruleName][0].labels.grafana_folder ?? ''}
); diff --git a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx index 29186ded954..fdc83f84204 100644 --- a/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx +++ b/public/app/features/alerting/unified/components/receivers/TemplateForm.tsx @@ -317,18 +317,16 @@ export const TemplateForm = ({ originalTemplate, prefill, alertmanager }: Props) {/* preview column – full height and half-width */} {isGrafanaAlertManager && ( - <> -
-
- -
- +
+
+ +
)}
diff --git a/public/app/features/alerting/unified/components/receivers/form/GenerateAlertDataModal.tsx b/public/app/features/alerting/unified/components/receivers/form/GenerateAlertDataModal.tsx index 417ccb4c3ba..4726ab7d27a 100644 --- a/public/app/features/alerting/unified/components/receivers/form/GenerateAlertDataModal.tsx +++ b/public/app/features/alerting/unified/components/receivers/form/GenerateAlertDataModal.tsx @@ -97,31 +97,29 @@ export const GenerateAlertDataModal = ({ isOpen, onDismiss, onAccept }: Props) = setStatus('firing'); }} > - <> - - -
- -
-
- -
-
- setStatus(value)} /> - -
-
-
- + + +
+ +
+
+ +
+
+ setStatus(value)} /> + +
+
+
{alerts.length > 0 && ( diff --git a/public/app/features/alerting/unified/components/receivers/form/ReceiverForm.tsx b/public/app/features/alerting/unified/components/receivers/form/ReceiverForm.tsx index b3bee2c5074..74c2058a999 100644 --- a/public/app/features/alerting/unified/components/receivers/form/ReceiverForm.tsx +++ b/public/app/features/alerting/unified/components/receivers/form/ReceiverForm.tsx @@ -197,38 +197,36 @@ export function ReceiverForm({ /> ); })} - <> + {isEditable && ( + + )} +
{isEditable && ( - + <> + {isSubmitting && ( + + )} + {!isSubmitting && } + )} -
- {isEditable && ( - <> - {isSubmitting && ( - - )} - {!isSubmitting && } - - )} - - Cancel - -
- + + Cancel + +
); diff --git a/public/app/features/alerting/unified/components/receivers/form/fields/DeletedSubform.tsx b/public/app/features/alerting/unified/components/receivers/form/fields/DeletedSubform.tsx index 256b20ecfa3..bb5537cbbee 100644 --- a/public/app/features/alerting/unified/components/receivers/form/fields/DeletedSubform.tsx +++ b/public/app/features/alerting/unified/components/receivers/form/fields/DeletedSubform.tsx @@ -17,5 +17,5 @@ export function DeletedSubForm({ pathPrefix }: Props): JSX.Element { register(`${pathPrefix}.__deleted`); }, [register, pathPrefix]); - return <>; + return <>{null}; } diff --git a/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx b/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx index 5bdcb964f13..7d1d429b312 100644 --- a/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/NotificationsStep.tsx @@ -240,16 +240,12 @@ function NeedHelpInfoForNotificationPolicy() { contentText={ - <> - Firing alert instances are routed to notification policies based on matching labels. The default - notification policy matches all alert instances. - + Firing alert instances are routed to notification policies based on matching labels. The default + notification policy matches all alert instances. - <> - Custom labels change the way your notifications are routed. First, add labels to your alert rule and then - connect them to your notification policy by adding label matchers. - + Custom labels change the way your notifications are routed. First, add labels to your alert rule and then + connect them to your notification policy by adding label matchers. - - -
e.preventDefault()}> -
- - {/* Step 1 */} - - {/* Step 2 */} - - {/* Step 3-4-5 */} - - - {/* Step 4 & 5 */} - - {/* Notifications step*/} - - {/* Annotations only for cloud and Grafana */} - - -
-
- {exportData && } -
- + + +
e.preventDefault()}> +
+ + {/* Step 1 */} + + {/* Step 2 */} + + {/* Step 3-4-5 */} + + + {/* Step 4 & 5 */} + + {/* Notifications step*/} + + {/* Annotations only for cloud and Grafana */} + + +
+
+ {exportData && } +
); } diff --git a/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx b/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx index 8650ba3e3fb..35741d4b2ee 100644 --- a/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/notificaton-preview/NotificationRouteDetailsModal.tsx @@ -81,13 +81,11 @@ export function NotificationRouteDetailsModal({ {isDefault &&
Default policy
}
{!isDefault && ( - <> - - + )}
diff --git a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/CloudDataSourceSelector.tsx b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/CloudDataSourceSelector.tsx index 5bcbaa7e620..c450216bb64 100644 --- a/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/CloudDataSourceSelector.tsx +++ b/public/app/features/alerting/unified/components/rule-editor/query-and-alert-condition/CloudDataSourceSelector.tsx @@ -23,38 +23,36 @@ export const CloudDataSourceSelector = ({ disabled, onChangeCloudDatasource }: C const ruleFormType = watch('type'); return ( - <> -
- {(ruleFormType === RuleFormType.cloudAlerting || ruleFormType === RuleFormType.cloudRecording) && ( - - ( - { - // reset expression as they don't need to persist after changing datasources - setValue('expression', ''); - onChange(ds?.name ?? null); - onChangeCloudDatasource(ds?.uid ?? null); - }} - /> - )} - name="dataSourceName" - control={control} - rules={{ - required: { value: true, message: 'Please select a data source' }, - }} - /> - - )} -
- +
+ {(ruleFormType === RuleFormType.cloudAlerting || ruleFormType === RuleFormType.cloudRecording) && ( + + ( + { + // reset expression as they don't need to persist after changing datasources + setValue('expression', ''); + onChange(ds?.name ?? null); + onChangeCloudDatasource(ds?.uid ?? null); + }} + /> + )} + name="dataSourceName" + control={control} + rules={{ + required: { value: true, message: 'Please select a data source' }, + }} + /> + + )} +
); }; diff --git a/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx b/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx index ae15d299c37..0d748604cf4 100644 --- a/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx +++ b/public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx @@ -316,124 +316,113 @@ export function EditRuleGroupModalForm(props: ModalFormProps): React.ReactElemen return (
- <> - {!props.hideFolder && ( - - - {nameSpaceLabel} - - } - invalid={Boolean(errors.namespaceName) ? true : undefined} - error={errors.namespaceName?.message} - > - - - {isGrafanaManagedGroup && props.folderUrl && ( - - )} - - )} - - Evaluation group - - } - invalid={!!errors.groupName} - error={errors.groupName?.message} - > - - - - Evaluation interval - - } - invalid={Boolean(errors.groupInterval) ? true : undefined} - error={errors.groupInterval?.message} - > - + {!props.hideFolder && ( + + + {nameSpaceLabel} + + } + invalid={Boolean(errors.namespaceName) ? true : undefined} + error={errors.namespaceName?.message} + > - setValue('groupInterval', value, { shouldValidate: true, shouldDirty: true })} + id="namespaceName" + readOnly={intervalEditOnly || isGrafanaManagedGroup} + value={folderTitle} + {...register('namespaceName', { + required: 'Namespace name is required.', + })} /> - - - - {/* if we're dealing with a Grafana-managed group, check if the evaluation interval is valid / permitted */} - {isGrafanaManagedGroup && checkEvaluationIntervalGlobalLimit(watch('groupInterval')).exceedsLimit && ( - - )} - - {!hasSomeNoRecordingRules &&
This group does not contain alert rules.
} - {hasSomeNoRecordingRules && ( - <> -
List of rules that belong to this group
-
- #Eval column represents the number of evaluations needed before alert starts firing. -
- - - )} - {error && {stringifyErrorLike(error)}} -
- - - - -
- + icon="folder-open" + target="_blank" + /> + )} + + )} + + Evaluation group + + } + invalid={!!errors.groupName} + error={errors.groupName?.message} + > + + + + Evaluation interval + + } + invalid={Boolean(errors.groupInterval) ? true : undefined} + error={errors.groupInterval?.message} + > + + + setValue('groupInterval', value, { shouldValidate: true, shouldDirty: true })} + /> + + + + {/* if we're dealing with a Grafana-managed group, check if the evaluation interval is valid / permitted */} + {isGrafanaManagedGroup && checkEvaluationIntervalGlobalLimit(watch('groupInterval')).exceedsLimit && ( + + )} + + {!hasSomeNoRecordingRules &&
This group does not contain alert rules.
} + {hasSomeNoRecordingRules && ( + <> +
List of rules that belong to this group
+
+ #Eval column represents the number of evaluations needed before alert starts firing. +
+ + + )} + {error && {stringifyErrorLike(error)}} +
+ + + + +
); 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 index 1c682cc689e..aa5c9edafec 100644 --- a/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v2.tsx +++ b/public/app/features/alerting/unified/components/rules/Filter/RulesFilter.v2.tsx @@ -158,54 +158,52 @@ const SavedSearches = () => { const applySearch = useCallback((name: string) => {}, []); return ( - <> - - - - columns={[ - { - id: 'name', - header: 'Saved search name', - cell: ({ row }) => ( - - {row.original.name} - {row.original.default ? : null} - - ), - }, - { - id: 'actions', - cell: ({ row }) => ( - - - - - ), - }, - ]} - 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} - /> - - - + + + + columns={[ + { + id: 'name', + header: 'Saved search name', + cell: ({ row }) => ( + + {row.original.name} + {row.original.default ? : null} + + ), + }, + { + id: 'actions', + cell: ({ row }) => ( + + + + + ), + }, + ]} + 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} + /> + + ); }; diff --git a/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx b/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx index f33db5d9962..37d977b004d 100644 --- a/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx +++ b/public/app/features/alerting/unified/components/rules/NoRulesCTA.tsx @@ -82,17 +82,15 @@ export const NoRulesSplash = () => { ) : null } > - <> - - You can also define rules through file provisioning or Terraform.{' '} - - Learn more - - - + + You can also define rules through file provisioning or Terraform.{' '} + + Learn more + +
); 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 1484cfaa4ec..720e0ed1275 100644 --- a/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx +++ b/public/app/features/alerting/unified/components/rules/RulesFilter.test.tsx @@ -16,7 +16,7 @@ jest.mock('./MultipleDataSourcePicker', () => { const original = jest.requireActual('./MultipleDataSourcePicker'); return { ...original, - MultipleDataSourcePicker: () => <>, + MultipleDataSourcePicker: () => null, }; }); diff --git a/public/app/features/alerting/unified/components/rules/central-state-history/EventDetails.tsx b/public/app/features/alerting/unified/components/rules/central-state-history/EventDetails.tsx index a037703c420..cfc14895d4c 100644 --- a/public/app/features/alerting/unified/components/rules/central-state-history/EventDetails.tsx +++ b/public/app/features/alerting/unified/components/rules/central-state-history/EventDetails.tsx @@ -220,19 +220,17 @@ const Annotations = ({ rule }: AnnotationsProps) => { return null; } return ( - <> -
- {Object.entries(annotations).map(([name, value]) => { - const capitalizedName = capitalize(name); - return ( - - {capitalizedName} - - - ); - })} -
- +
+ {Object.entries(annotations).map(([name, value]) => { + const capitalizedName = capitalize(name); + return ( + + {capitalizedName} + + + ); + })} +
); }; interface ValueInTransitionProps { 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 76ba417dd20..a449b7ceee3 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 @@ -114,16 +114,14 @@ const LokiStateHistory = ({ ruleUID }: Props) => { )} {isEmpty(frameSubset) ? ( - <> -
- {emptyStateMessage} - {totalRecordsCount > 0 && ( - - )} -
- +
+ {emptyStateMessage} + {totalRecordsCount > 0 && ( + + )} +
) : ( <>
diff --git a/public/app/features/alerting/unified/home/Insights.tsx b/public/app/features/alerting/unified/home/Insights.tsx index 9d85797751b..14bd54b0682 100644 --- a/public/app/features/alerting/unified/home/Insights.tsx +++ b/public/app/features/alerting/unified/home/Insights.tsx @@ -176,24 +176,22 @@ export function getInsightsScenes() { component: SectionSubheader, props: { children: ( - <> - - Monitor the status of your system{' '} - - Alerting insights provides pre-built dashboards to monitor your alerting data. -
-
- You can identify patterns in why things go wrong and discover trends in alerting performance - within your organization. -
- } - > - - - - + + Monitor the status of your system{' '} + + Alerting insights provides pre-built dashboards to monitor your alerting data. +
+
+ You can identify patterns in why things go wrong and discover trends in alerting performance within + your organization. +
+ } + > + + + ), }, }), diff --git a/public/app/features/alerting/unified/rule-list/components/RuleGroupActionsMenu.tsx b/public/app/features/alerting/unified/rule-list/components/RuleGroupActionsMenu.tsx index 7c2735ea5cd..5b61f83d543 100644 --- a/public/app/features/alerting/unified/rule-list/components/RuleGroupActionsMenu.tsx +++ b/public/app/features/alerting/unified/rule-list/components/RuleGroupActionsMenu.tsx @@ -3,20 +3,18 @@ import { t } from 'app/core/internationalization'; export function RuleGroupActionsMenu() { return ( - <> - - - - - - - - } - > - - - + + + + + + + + } + > + + ); }