From 797886e2531710599801b032a9dc66c645cde884 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Wed, 6 Aug 2025 16:04:03 +0100 Subject: [PATCH] Accessibility: Add lint rule to prevent text anchor usage (#109207) * attempt at a lint rule * add comment * fix the specific alerting links * fix lint rule violations * update translations * fix unit tests * kick CI * add missing external --- eslint.config.js | 8 +++++ .../CallToActionCard.test.tsx | 36 ++++++++++++++++--- .../src/components/Card/Card.story.tsx | 21 +++++------ .../TimeRangePicker/TimePickerContent.tsx | 12 ++----- .../PanelChrome/PanelChrome.story.tsx | 11 +++--- .../src/components/Tooltip/Tooltip.test.tsx | 6 ++-- .../admin/AdminFeatureTogglesPage.tsx | 9 +++-- public/app/features/admin/UserOrgs.tsx | 22 +++--------- .../features/admin/Users/OrgUsersTable.tsx | 8 ++--- .../alerting/unified/home/GettingStarted.tsx | 4 +-- .../NoAccessModal/NoAccessModal.tsx | 2 +- .../correlations/CorrelationsPage.tsx | 11 ++---- .../correlations/Forms/QueryEditorField.tsx | 9 +++-- .../sharing/ExportButton/ExportAsImage.tsx | 11 ++---- .../dashboard-scene/sharing/ShareLinkTab.tsx | 11 ++---- .../panel-share/SharePanelInternally.tsx | 11 ++---- .../components/HelpWizard/HelpWizard.tsx | 2 +- .../dashboard/dashgrid/DashboardEmpty.tsx | 8 +++-- .../components/CheatSheet/LogsCheatSheet.tsx | 13 +++---- .../Errors/ThrottlingErrorMessage.tsx | 19 ++++------ .../VariableQueryEditor.tsx | 16 ++++----- .../shared/LogGroups/LogGroupsSelector.tsx | 20 ++++++++--- .../MetricStatEditor/MetricStatEditor.tsx | 9 +++-- .../graphite/components/FunctionEditor.tsx | 11 ++---- .../components/editor/config/ConfigEditor.tsx | 6 ++-- .../plugins/datasource/jaeger/CheatSheet.tsx | 23 +++--------- .../loki/components/LokiCheatSheet.tsx | 6 ++-- .../configuration/ConfigEditorPackage.tsx | 6 ++-- .../SearchTraceQLEditor/TraceQLSearch.tsx | 4 +-- .../datasource/tempo/ServiceGraphSection.tsx | 17 +++------ .../configuration/ServiceGraphSettings.tsx | 28 +++++---------- .../tempo/configuration/StreamingSection.tsx | 26 ++------------ .../datasource/tempo/traceql/QueryEditor.tsx | 15 +++----- .../traceql/TempoQueryBuilderOptions.tsx | 9 +++-- public/locales/en-US/grafana.json | 4 +-- 35 files changed, 183 insertions(+), 251 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index c31f2f77930..075aaaf17e7 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -157,6 +157,14 @@ module.exports = [ '@typescript-eslint/no-redeclare': ['error'], 'unicorn/no-empty-file': 'error', 'no-constant-condition': 'error', + 'no-restricted-syntax': [ + 'error', + { + // value regex is to filter out whitespace-only text nodes (e.g. new lines and spaces in the JSX) + selector: "JSXElement[openingElement.name.name='a'] > JSXText[value!=/^\\s*$/]", + message: 'No bare anchor nodes containing only text. Use `TextLink` instead.', + }, + ], }, }, { diff --git a/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.test.tsx b/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.test.tsx index 2e2391da18a..d78ee23ecc7 100644 --- a/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.test.tsx +++ b/packages/grafana-ui/src/components/CallToActionCard/CallToActionCard.test.tsx @@ -1,24 +1,48 @@ import { render, screen } from '@testing-library/react'; +import { TextLink } from '../Link/TextLink'; + import { CallToActionCard } from './CallToActionCard'; describe('CallToActionCard', () => { describe('rendering', () => { it('should render callToActionElement', () => { - render(Click me} />); + render( + + Click me + + } + /> + ); expect(screen.getByRole('link', { name: 'Click me' })).toBeInTheDocument(); }); it('should render message when provided', () => { render( - Click me} /> + + Click me + + } + /> ); expect(screen.getByText('Click button below')).toBeInTheDocument(); }); it('should render footer when provided', () => { render( - Click me} /> + + Click me + + } + /> ); expect(screen.getByText('footer content')).toBeInTheDocument(); }); @@ -28,7 +52,11 @@ describe('CallToActionCard', () => { Click me} + callToActionElement={ + + Click me + + } /> ); expect(screen.getByText('Click button below')).toBeInTheDocument(); diff --git a/packages/grafana-ui/src/components/Card/Card.story.tsx b/packages/grafana-ui/src/components/Card/Card.story.tsx index 34b25d96a05..df9f978c531 100644 --- a/packages/grafana-ui/src/components/Card/Card.story.tsx +++ b/packages/grafana-ui/src/components/Card/Card.story.tsx @@ -2,6 +2,7 @@ import { Meta, StoryFn } from '@storybook/react'; import { Button } from '../Button/Button'; import { IconButton } from '../IconButton/IconButton'; +import { TextLink } from '../Link/TextLink'; import { TagList } from '../Tags/TagList'; import { Card } from './Card'; @@ -74,9 +75,9 @@ export const MultipleMetadataWithCustomSeparator: StoryFn = (args) Test dashboard Grafana - + https://ops-us-east4.grafana.net/api/prom - + ); @@ -171,9 +172,9 @@ export const WithMediaElements: StoryFn = (args) => { Grafana - + https://ops-us-east4.grafana.net/api/prom - + ); @@ -190,9 +191,9 @@ export const ActionCards: StoryFn = (args) => { 1-ops-tools1-fallback Prometheus - + https://ops-us-east4.grafana.net/api/prom - + Prometheus Logo @@ -223,9 +224,9 @@ export const DisabledState: StoryFn = (args) => { 1-ops-tools1-fallback Grafana - + https://ops-us-east4.grafana.net/api/prom - + Grafana Logo @@ -269,9 +270,9 @@ export const Full: StoryFn = (args) => { {['Subtitle', 'Meta info 1', 'Meta info 2']} - + https://ops-us-east4.grafana.net/api/prom - + Prometheus Logo diff --git a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerContent.tsx b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerContent.tsx index 0bf13180e1a..65566666c99 100644 --- a/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerContent.tsx +++ b/packages/grafana-ui/src/components/DateTimePickers/TimeRangePicker/TimePickerContent.tsx @@ -9,6 +9,7 @@ import { useStyles2, useTheme2 } from '../../../themes/ThemeContext'; import { getFocusStyles } from '../../../themes/mixins'; import { FilterInput } from '../../FilterInput/FilterInput'; import { Icon } from '../../Icon/Icon'; +import { TextLink } from '../../Link/TextLink'; import { WeekStart } from '../WeekStartPicker'; import { TimePickerFooter } from './TimePickerFooter'; @@ -234,13 +235,9 @@ const EmptyRecentList = memo(() => {
- + Read the documentation - + to find out more about how to enter custom time ranges.
@@ -371,7 +368,4 @@ const getEmptyListStyles = (theme: GrafanaTheme2) => ({ fontSize: '13px', }, }), - link: css({ - color: theme.colors.text.link, - }), }); diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx index 0bbe166ccff..e312691bc4c 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx +++ b/packages/grafana-ui/src/components/PanelChrome/PanelChrome.story.tsx @@ -11,6 +11,7 @@ import { Button } from '../Button/Button'; import { RadioButtonGroup } from '../Forms/RadioButtonGroup/RadioButtonGroup'; import { Icon } from '../Icon/Icon'; import { Stack } from '../Layout/Stack/Stack'; +import { TextLink } from '../Link/TextLink'; import { Menu } from '../Menu/Menu'; import { PanelChromeProps } from './PanelChrome'; @@ -252,10 +253,9 @@ export const Examples = () => { {renderPanel('Panel with action link', { title: 'Panel with action link', actions: ( - + Error details - - + ), })} {renderPanel('Action and menu (should be rare)', { @@ -322,10 +322,9 @@ export const ExamplesHoverHeader = () => { title: 'With link in hover header', hoverHeader: true, actions: ( - + Error details - - + ), })} diff --git a/packages/grafana-ui/src/components/Tooltip/Tooltip.test.tsx b/packages/grafana-ui/src/components/Tooltip/Tooltip.test.tsx index 2c92413df8b..42eb4ca48d0 100644 --- a/packages/grafana-ui/src/components/Tooltip/Tooltip.test.tsx +++ b/packages/grafana-ui/src/components/Tooltip/Tooltip.test.tsx @@ -2,15 +2,17 @@ import userEvent from '@testing-library/user-event'; import { MutableRefObject } from 'react'; +import { TextLink } from '../Link/TextLink'; + import { Tooltip } from './Tooltip'; describe('Tooltip', () => { it('renders correctly', () => { render( - + Link with tooltip - + ); expect(screen.getByText('Link with tooltip')).toBeInTheDocument(); diff --git a/public/app/features/admin/AdminFeatureTogglesPage.tsx b/public/app/features/admin/AdminFeatureTogglesPage.tsx index 9d3301b6ea3..113847983e1 100644 --- a/public/app/features/admin/AdminFeatureTogglesPage.tsx +++ b/public/app/features/admin/AdminFeatureTogglesPage.tsx @@ -4,7 +4,7 @@ import { useAsync } from 'react-use'; import { GrafanaTheme2 } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; -import { useStyles2, Icon } from '@grafana/ui'; +import { useStyles2, Icon, TextLink } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { getTogglesAPI } from './AdminFeatureTogglesAPI'; @@ -45,13 +45,12 @@ export default function AdminFeatureTogglesPage() {
View and edit feature toggles. Read more about feature toggles at{' '} - grafana.com - + .
diff --git a/public/app/features/admin/UserOrgs.tsx b/public/app/features/admin/UserOrgs.tsx index 201df124ec8..b7083bf7051 100644 --- a/public/app/features/admin/UserOrgs.tsx +++ b/public/app/features/admin/UserOrgs.tsx @@ -15,6 +15,7 @@ import { useStyles2, withTheme2, Stack, + TextLink, } from '@grafana/ui'; import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker'; import { fetchRoleOptions, updateUserRoles } from 'app/core/components/RolePicker/api'; @@ -446,14 +447,9 @@ export function ChangeOrgButton({ This user's role is not editable because it is synchronized from your auth provider. Refer to the  - + Grafana authentication docs - +  for details. @@ -496,14 +492,9 @@ export const ExternalUserTooltip = ({ lockMessage }: ExternalUserTooltipProps) = This user's built-in role is not editable because it is synchronized from your auth provider. Refer to the  - + Grafana authentication docs - +  for details. @@ -519,9 +510,6 @@ const getTooltipStyles = (theme: GrafanaTheme2) => ({ disabledTooltip: css({ display: 'flex', }), - tooltipItemLink: css({ - color: theme.v1.palette.blue95, - }), lockMessageClass: css({ fontStyle: 'italic', marginLeft: '1.8rem', diff --git a/public/app/features/admin/Users/OrgUsersTable.tsx b/public/app/features/admin/Users/OrgUsersTable.tsx index 42f69b89cf2..33d88211766 100644 --- a/public/app/features/admin/Users/OrgUsersTable.tsx +++ b/public/app/features/admin/Users/OrgUsersTable.tsx @@ -18,6 +18,7 @@ import { Stack, Tag, Text, + TextLink, Tooltip, } from '@grafana/ui'; import { UserRolePicker } from 'app/core/components/RolePicker/UserRolePicker'; @@ -189,15 +190,14 @@ export const OrgUsersTable = ({ This user's role is not editable because it is synchronized from your auth provider. Refer to the  - Grafana authentication docs - +  for details. diff --git a/public/app/features/alerting/unified/home/GettingStarted.tsx b/public/app/features/alerting/unified/home/GettingStarted.tsx index f8f829fdb1f..b45fe9d764a 100644 --- a/public/app/features/alerting/unified/home/GettingStarted.tsx +++ b/public/app/features/alerting/unified/home/GettingStarted.tsx @@ -196,9 +196,7 @@ function WelcomeCTABox({ title, description, href, hrefText }: WelcomeCTABoxProp
{description}
- - {hrefText} - + {hrefText}
); diff --git a/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx b/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx index 5e742af2bb2..6e55f960824 100644 --- a/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx +++ b/public/app/features/connections/tabs/ConnectData/NoAccessModal/NoAccessModal.tsx @@ -87,7 +87,7 @@ export function NoAccessModal({ item, isOpen, onDismiss }: NoAccessModalProps) {

Editors cannot add new connections. You may check to see if it is already configured in{' '} - Data sources. + Data sources.

diff --git a/public/app/features/correlations/CorrelationsPage.tsx b/public/app/features/correlations/CorrelationsPage.tsx index 60ce5a5b166..34859378863 100644 --- a/public/app/features/correlations/CorrelationsPage.tsx +++ b/public/app/features/correlations/CorrelationsPage.tsx @@ -17,7 +17,7 @@ import { type CellProps, type SortByFn, Pagination, - Icon, + TextLink, } from '@grafana/ui'; import { Page } from 'app/core/components/Page/Page'; import { contextSrv } from 'app/core/core'; @@ -160,14 +160,9 @@ export default function CorrelationsPage() { <> Define how data living in different data sources relates to each other. Read more in the{' '} - + documentation - - + } diff --git a/public/app/features/correlations/Forms/QueryEditorField.tsx b/public/app/features/correlations/Forms/QueryEditorField.tsx index 9c432700446..676c7fee804 100644 --- a/public/app/features/correlations/Forms/QueryEditorField.tsx +++ b/public/app/features/correlations/Forms/QueryEditorField.tsx @@ -4,7 +4,7 @@ import { useAsync } from 'react-use'; import { CoreApp } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { getDataSourceSrv } from '@grafana/runtime'; -import { Field, LoadingPlaceholder, Alert } from '@grafana/ui'; +import { Field, LoadingPlaceholder, Alert, TextLink } from '@grafana/ui'; interface Props { dsUid?: string; @@ -34,13 +34,12 @@ export const QueryEditorField = ({ dsUid, invalid, error, name }: Props) => { Define the query that is run when the link is clicked. You can use{' '} - variables - {' '} + {' '} to access specific field values. diff --git a/public/app/features/dashboard-scene/sharing/ExportButton/ExportAsImage.tsx b/public/app/features/dashboard-scene/sharing/ExportButton/ExportAsImage.tsx index 54202039923..e35b9e6248b 100644 --- a/public/app/features/dashboard-scene/sharing/ExportButton/ExportAsImage.tsx +++ b/public/app/features/dashboard-scene/sharing/ExportButton/ExportAsImage.tsx @@ -7,7 +7,7 @@ import { GrafanaTheme2 } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { config } from '@grafana/runtime'; import { SceneComponentProps, SceneObjectBase } from '@grafana/scenes'; -import { Alert, Button, useStyles2 } from '@grafana/ui'; +import { Alert, Button, TextLink, useStyles2 } from '@grafana/ui'; import { DashboardInteractions } from 'app/features/dashboard-scene/utils/interactions'; import { getDashboardSceneFor } from 'app/features/dashboard-scene/utils/utils'; @@ -152,14 +152,9 @@ function RendererAlert() {

To render an image, you must install the{' '} - + Grafana image renderer plugin - + . Please contact your Grafana administrator to install the plugin.
diff --git a/public/app/features/dashboard-scene/sharing/ShareLinkTab.tsx b/public/app/features/dashboard-scene/sharing/ShareLinkTab.tsx index 91c4a9baa03..0800e9fe0ad 100644 --- a/public/app/features/dashboard-scene/sharing/ShareLinkTab.tsx +++ b/public/app/features/dashboard-scene/sharing/ShareLinkTab.tsx @@ -4,7 +4,7 @@ import { Trans, t } from '@grafana/i18n'; import { config } from '@grafana/runtime'; import { SceneComponentProps, sceneGraph, SceneObjectBase, SceneObjectRef, VizPanel } from '@grafana/scenes'; import { TimeZone } from '@grafana/schema'; -import { Alert, ClipboardButton, Field, FieldSet, Icon, Input, Switch } from '@grafana/ui'; +import { Alert, ClipboardButton, Field, FieldSet, Icon, Input, Switch, TextLink } from '@grafana/ui'; import { createDashboardShareUrl, createShortLink, getShareUrlParams } from 'app/core/utils/shortLinks'; import { ThemePicker } from 'app/features/dashboard/components/ShareModal/ThemePicker'; import { getTrackingSource, shareDashboardType } from 'app/features/dashboard/components/ShareModal/utils'; @@ -215,14 +215,9 @@ function ShareLinkTabRenderer({ model }: SceneComponentProps) { > To render an image, you must install the{' '} - + Grafana image renderer plugin - + . Please contact your Grafana administrator to install the plugin. diff --git a/public/app/features/dashboard-scene/sharing/panel-share/SharePanelInternally.tsx b/public/app/features/dashboard-scene/sharing/panel-share/SharePanelInternally.tsx index 668a04a9b32..6e076218e82 100644 --- a/public/app/features/dashboard-scene/sharing/panel-share/SharePanelInternally.tsx +++ b/public/app/features/dashboard-scene/sharing/panel-share/SharePanelInternally.tsx @@ -4,7 +4,7 @@ import { GrafanaTheme2 } from '@grafana/data'; import { Trans, t } from '@grafana/i18n'; import { config } from '@grafana/runtime'; import { SceneComponentProps } from '@grafana/scenes'; -import { Alert, ClipboardButton, Divider, Stack, Text, useStyles2 } from '@grafana/ui'; +import { Alert, ClipboardButton, Divider, Stack, Text, TextLink, useStyles2 } from '@grafana/ui'; import { getDashboardSceneFor } from '../../utils/utils'; import ShareInternallyConfiguration from '../ShareInternallyConfiguration'; @@ -79,14 +79,9 @@ function SharePanelInternallyRenderer({ model }: SceneComponentProps To render an image, you must install the{' '} - + Grafana image renderer plugin - + . Please contact your Grafana administrator to install the plugin. diff --git a/public/app/features/dashboard/components/HelpWizard/HelpWizard.tsx b/public/app/features/dashboard/components/HelpWizard/HelpWizard.tsx index ff10cbf0367..b2b99aa766a 100644 --- a/public/app/features/dashboard/components/HelpWizard/HelpWizard.tsx +++ b/public/app/features/dashboard/components/HelpWizard/HelpWizard.tsx @@ -89,7 +89,7 @@ export function HelpWizard({ panel, plugin, onClose }: Props) { You can also retrieve a support bundle containing information concerning your Grafana instance and - configured datasources in the support bundles section. + configured datasources in the support bundles section. )} diff --git a/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx b/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx index 38f57b344e5..9555bea733e 100644 --- a/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx +++ b/public/app/features/dashboard/dashgrid/DashboardEmpty.tsx @@ -4,7 +4,7 @@ import { GrafanaTheme2 } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { Trans } from '@grafana/i18n'; import { locationService } from '@grafana/runtime'; -import { Button, useStyles2, Text, Box, Stack } from '@grafana/ui'; +import { Button, useStyles2, Text, Box, Stack, TextLink } from '@grafana/ui'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { onAddLibraryPanel as onAddLibraryPanelImpl, @@ -115,7 +115,11 @@ const DashboardEmpty = ({ dashboard, canCreate }: Props) => { - Import dashboards from files or grafana.com. + Import dashboards from files or{' '} + + grafana.com + + . diff --git a/public/app/plugins/datasource/cloudwatch/components/CheatSheet/LogsCheatSheet.tsx b/public/app/plugins/datasource/cloudwatch/components/CheatSheet/LogsCheatSheet.tsx index 0eda7a46736..86fcbd00857 100644 --- a/public/app/plugins/datasource/cloudwatch/components/CheatSheet/LogsCheatSheet.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/CheatSheet/LogsCheatSheet.tsx @@ -3,7 +3,7 @@ import Prism from 'prismjs'; import { useState } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; -import { Collapse, useStyles2, Text } from '@grafana/ui'; +import { Collapse, useStyles2, Text, TextLink } from '@grafana/ui'; import { flattenTokens } from '@grafana/ui/internal'; import { trackSampleQuerySelection } from '../../tracking'; @@ -145,14 +145,12 @@ const LogsCheatSheet = (props: Props) => { ))}
Note: If you are seeing masked data, you may have CloudWatch logs data protection enabled.{' '} - See documentation for details - + .
@@ -165,9 +163,6 @@ const getStyles = (theme: GrafanaTheme2) => ({ heading: css({ marginBottom: theme.spacing(2), }), - link: css({ - textDecoration: 'underline', - }), cheatSheetExample: css({ margin: theme.spacing(0.5, 0), // element is interactive, clear button styles diff --git a/public/app/plugins/datasource/cloudwatch/components/Errors/ThrottlingErrorMessage.tsx b/public/app/plugins/datasource/cloudwatch/components/Errors/ThrottlingErrorMessage.tsx index 7446472de2d..e591d6871f9 100644 --- a/public/app/plugins/datasource/cloudwatch/components/Errors/ThrottlingErrorMessage.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/Errors/ThrottlingErrorMessage.tsx @@ -1,3 +1,5 @@ +import { TextLink } from '@grafana/ui'; + export interface Props { region: string; } @@ -5,23 +7,16 @@ export interface Props { export const ThrottlingErrorMessage = ({ region }: Props) => (

Please visit the  - AWS Service Quotas console - +  to request a quota increase or see our  - + documentation - +  to learn more.

); diff --git a/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.tsx index c2d39f88c8f..480e494fddd 100644 --- a/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/VariableQueryEditor/VariableQueryEditor.tsx @@ -3,7 +3,7 @@ import { css } from '@emotion/css'; import { GrafanaTheme2, QueryEditorProps, SelectableValue, toOption } from '@grafana/data'; import { EditorField } from '@grafana/plugin-ui'; import { config } from '@grafana/runtime'; -import { useStyles2 } from '@grafana/ui'; +import { TextLink, useStyles2 } from '@grafana/ui'; import { CloudWatchDatasource } from '../../datasource'; import { @@ -257,13 +257,12 @@ export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => { tooltip={ <> {'Attribute or tag to query on. Tags should be formatted "Tags.". '} - See the documentation for more details - + } /> @@ -272,13 +271,12 @@ export const VariableQueryEditor = ({ query, datasource, onChange }: Props) => { tooltipInteractive tooltip={ <> - Pre-defined ec2:DescribeInstances filters/tags - + {' and the values to filter on. Tags should be formatted tag:.'} } diff --git a/public/app/plugins/datasource/cloudwatch/components/shared/LogGroups/LogGroupsSelector.tsx b/public/app/plugins/datasource/cloudwatch/components/shared/LogGroups/LogGroupsSelector.tsx index 6922bb7ed44..170359cdfcb 100644 --- a/public/app/plugins/datasource/cloudwatch/components/shared/LogGroups/LogGroupsSelector.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/shared/LogGroups/LogGroupsSelector.tsx @@ -2,7 +2,18 @@ import { useEffect, useMemo, useState } from 'react'; import { SelectableValue } from '@grafana/data'; import { EditorField } from '@grafana/plugin-ui'; -import { Button, Checkbox, Icon, Label, LoadingPlaceholder, Modal, Select, Space, useStyles2 } from '@grafana/ui'; +import { + Button, + Checkbox, + Icon, + Label, + LoadingPlaceholder, + Modal, + Select, + Space, + TextLink, + useStyles2, +} from '@grafana/ui'; import { DescribeLogGroupsRequest, ResourceResponse, LogGroupResponse } from '../../../resources/types'; import { LogGroup } from '../../../types'; @@ -146,13 +157,12 @@ export const LogGroupsSelector = ({ search.

A{' '} - maximum{' '} - {' '} + {' '} of 50 Cloudwatch log groups can be queried at one time.

diff --git a/public/app/plugins/datasource/cloudwatch/components/shared/MetricStatEditor/MetricStatEditor.tsx b/public/app/plugins/datasource/cloudwatch/components/shared/MetricStatEditor/MetricStatEditor.tsx index 0ff2c3c3689..cf98588d377 100644 --- a/public/app/plugins/datasource/cloudwatch/components/shared/MetricStatEditor/MetricStatEditor.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/shared/MetricStatEditor/MetricStatEditor.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { SelectableValue } from '@grafana/data'; import { EditorField, EditorFieldGroup, EditorRow, EditorRows, EditorSwitch } from '@grafana/plugin-ui'; import { config } from '@grafana/runtime'; -import { Select } from '@grafana/ui'; +import { Select, TextLink } from '@grafana/ui'; import { CloudWatchDatasource } from '../../../datasource'; import { useAccountOptions, useMetrics, useNamespaces } from '../../../hooks'; @@ -152,13 +152,12 @@ export const MetricStatEditor = ({ { 'Only show metrics that contain exactly the dimensions defined in the query and match the specified values. If this is enabled, all dimensions of the metric being queried must be specified so that the ' } - metric schema - + { ' matches exactly. If this is disabled, metrics that match the schema and have additional dimensions will also be returned.' } diff --git a/public/app/plugins/datasource/graphite/components/FunctionEditor.tsx b/public/app/plugins/datasource/graphite/components/FunctionEditor.tsx index 69907938374..00146aa328a 100644 --- a/public/app/plugins/datasource/graphite/components/FunctionEditor.tsx +++ b/public/app/plugins/datasource/graphite/components/FunctionEditor.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/css'; import { memo } from 'react'; import { GrafanaTheme2 } from '@grafana/data'; -import { Icon, Tooltip, useStyles2, type PopoverContent } from '@grafana/ui'; +import { Icon, TextLink, Tooltip, useStyles2, type PopoverContent } from '@grafana/ui'; import { FuncInstance } from '../gfunc'; @@ -64,14 +64,9 @@ const TooltipContent = memo(() => { return ( This function is not supported. Check your function for typos and{' '} - + read the docs - {' '} + {' '} to see whether you need to upgrade your data source’s version to make this function available. ); diff --git a/public/app/plugins/datasource/influxdb/components/editor/config/ConfigEditor.tsx b/public/app/plugins/datasource/influxdb/components/editor/config/ConfigEditor.tsx index 9958745eddb..3bb5ccc3941 100644 --- a/public/app/plugins/datasource/influxdb/components/editor/config/ConfigEditor.tsx +++ b/public/app/plugins/datasource/influxdb/components/editor/config/ConfigEditor.tsx @@ -8,7 +8,7 @@ import { updateDatasourcePluginJsonDataOption, } from '@grafana/data'; import { config } from '@grafana/runtime'; -import { Alert, DataSourceHttpSettings, InlineField, Select, Field, Input, FieldSet } from '@grafana/ui'; +import { Alert, DataSourceHttpSettings, InlineField, Select, Field, Input, FieldSet, TextLink } from '@grafana/ui'; import { BROWSER_MODE_DISABLED_MESSAGE } from '../../../constants'; import { InfluxOptions, InfluxOptionsV1, InfluxVersion } from '../../../types'; @@ -130,9 +130,9 @@ export class ConfigEditor extends PureComponent {

Please report any issues to:
- + https://github.com/grafana/grafana/issues - +

)} diff --git a/public/app/plugins/datasource/jaeger/CheatSheet.tsx b/public/app/plugins/datasource/jaeger/CheatSheet.tsx index 2f37a3df0e9..217c45c828b 100644 --- a/public/app/plugins/datasource/jaeger/CheatSheet.tsx +++ b/public/app/plugins/datasource/jaeger/CheatSheet.tsx @@ -1,7 +1,7 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; -import { useStyles2 } from '@grafana/ui'; +import { TextLink, useStyles2 } from '@grafana/ui'; export default function CheatSheet() { const styles = useStyles2(getStyles); @@ -11,14 +11,9 @@ export default function CheatSheet() {

This cheat sheet provides a quick overview of the query types that are available. For more details about the Jaeger data source, check out{' '} - + the documentation - + .

@@ -32,14 +27,9 @@ export default function CheatSheet() {
  • JSON File - you can upload a JSON file that contains a single trace to visualize it. If the file has multiple traces then the first trace is used for visualization. An example of a valid JSON file can be found in{' '} - + this section - {' '} + {' '} of the documentation.
  • @@ -48,9 +38,6 @@ export default function CheatSheet() { } const getStyles = (theme: GrafanaTheme2) => ({ - anchorTag: css({ - color: theme.colors.text.link, - }), unorderedList: css({ listStyleType: 'none', }), diff --git a/public/app/plugins/datasource/loki/components/LokiCheatSheet.tsx b/public/app/plugins/datasource/loki/components/LokiCheatSheet.tsx index dce5bc0221c..6774554a924 100644 --- a/public/app/plugins/datasource/loki/components/LokiCheatSheet.tsx +++ b/public/app/plugins/datasource/loki/components/LokiCheatSheet.tsx @@ -4,7 +4,7 @@ import { PureComponent } from 'react'; import { GrafanaTheme2, QueryEditorHelpProps } from '@grafana/data'; import { reportInteraction } from '@grafana/runtime'; -import { Themeable2, withTheme2 } from '@grafana/ui'; +import { TextLink, Themeable2, withTheme2 } from '@grafana/ui'; import LokiLanguageProvider from '../LanguageProvider'; import { escapeLabelValueInExactSelector } from '../languageUtils'; @@ -135,9 +135,9 @@ class UnthemedLokiCheatSheet extends PureComponent< {this.renderExpression('{app="cassandra"} |~ "(duration|latency)s*(=|is|of)s*[d.]+"')} {this.renderExpression('{app="cassandra"} |= "exact match"')} {this.renderExpression('{app="cassandra"} != "do not match"')} - + LogQL - {' '} + {' '} supports exact and regular expression filters. {LOGQL_EXAMPLES.map((item) => ( diff --git a/public/app/plugins/datasource/prometheus/configuration/ConfigEditorPackage.tsx b/public/app/plugins/datasource/prometheus/configuration/ConfigEditorPackage.tsx index 16a65d8a89d..3b061820c04 100644 --- a/public/app/plugins/datasource/prometheus/configuration/ConfigEditorPackage.tsx +++ b/public/app/plugins/datasource/prometheus/configuration/ConfigEditorPackage.tsx @@ -6,7 +6,7 @@ import { DataSourcePluginOptionsEditorProps, GrafanaTheme2 } from '@grafana/data import { AdvancedHttpSettings, ConfigSection, DataSourceDescription } from '@grafana/plugin-ui'; import { AlertingSettingsOverhaul, PromOptions, PromSettings } from '@grafana/prometheus'; import { config } from '@grafana/runtime'; -import { Alert, FieldValidationMessage, useTheme2 } from '@grafana/ui'; +import { Alert, FieldValidationMessage, TextLink, useTheme2 } from '@grafana/ui'; import { AzureAuthSettings } from './AzureAuthSettings'; import { AzurePromDataSourceSettings, setDefaultCredentials, resetCredentials } from './AzureCredentialsConfig'; @@ -78,9 +78,9 @@ export function docsTip(url?: string) { const docsUrl = 'https://grafana.com/docs/grafana/latest/datasources/prometheus/#configure-the-data-source'; return ( - + Visit docs for more details here. - + ); } diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx index acc2820a8ba..f8dd962a9ce 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/TraceQLSearch.tsx @@ -4,7 +4,7 @@ import { useCallback, useEffect, useState } from 'react'; import { CoreApp, GrafanaTheme2, TimeRange } from '@grafana/data'; import { TemporaryAlert } from '@grafana/o11y-ds-frontend'; import { config, FetchError, getTemplateSrv, reportInteraction } from '@grafana/runtime'; -import { Alert, Button, Stack, Select, useStyles2 } from '@grafana/ui'; +import { Alert, Button, Stack, Select, useStyles2, TextLink } from '@grafana/ui'; import { RawQuery } from '../_importedDependencies/datasources/prometheus/RawQuery'; import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen'; @@ -304,7 +304,7 @@ const TraceQLSearch = ({ {error ? ( Please ensure that Tempo is configured with search enabled. If you would like to hide this tab, you can - configure it in the datasource settings. + configure it in the datasource settings. ) : null} {alertText && } diff --git a/public/app/plugins/datasource/tempo/ServiceGraphSection.tsx b/public/app/plugins/datasource/tempo/ServiceGraphSection.tsx index 0879eaadc28..7aedac8c16a 100644 --- a/public/app/plugins/datasource/tempo/ServiceGraphSection.tsx +++ b/public/app/plugins/datasource/tempo/ServiceGraphSection.tsx @@ -3,7 +3,7 @@ import { useEffect, useState } from 'react'; import useAsync from 'react-use/lib/useAsync'; import { GrafanaTheme2 } from '@grafana/data'; -import { Alert, InlineField, InlineFieldRow, useStyles2 } from '@grafana/ui'; +import { Alert, InlineField, InlineFieldRow, TextLink, useStyles2 } from '@grafana/ui'; import { AdHocFilter } from './_importedDependencies/components/AdHocFilter/AdHocFilter'; import { AdHocVariableFilter } from './_importedDependencies/components/AdHocFilter/types'; @@ -116,18 +116,13 @@ export function ServiceGraphSection({ ); } -function getWarning(title: string, description: string, styles: { alert: string; link: string }) { +function getWarning(title: string, description: string, styles: { alert: string }) { return ( {description} according to the{' '} - + Tempo documentation - + . ); @@ -157,8 +152,4 @@ const getStyles = (theme: GrafanaTheme2) => ({ maxWidth: '75ch', marginTop: theme.spacing(2), }), - link: css({ - color: theme.colors.text.link, - textDecoration: 'underline', - }), }); diff --git a/public/app/plugins/datasource/tempo/configuration/ServiceGraphSettings.tsx b/public/app/plugins/datasource/tempo/configuration/ServiceGraphSettings.tsx index 24bbb101458..4eebfdaffca 100644 --- a/public/app/plugins/datasource/tempo/configuration/ServiceGraphSettings.tsx +++ b/public/app/plugins/datasource/tempo/configuration/ServiceGraphSettings.tsx @@ -4,7 +4,7 @@ import { updateDatasourcePluginJsonDataOption, } from '@grafana/data'; import { DataSourcePicker } from '@grafana/runtime'; -import { Button, InlineField, InlineFieldRow, useStyles2, Combobox } from '@grafana/ui'; +import { Button, InlineField, InlineFieldRow, useStyles2, Combobox, TextLink } from '@grafana/ui'; import { TempoJsonData } from '../types'; @@ -31,40 +31,28 @@ export function ServiceGraphSettings({ options, onOptionsChange }: Props) { function metricsGeneratorDocsLink() { return ( - + Tempo metrics generator - + ); } function prometheusNativeHistogramsDocsLink() { return ( - + Prometheus - + ); } function mimirNativeHistogramsDocsLink() { return ( - Mimir - + ); } diff --git a/public/app/plugins/datasource/tempo/configuration/StreamingSection.tsx b/public/app/plugins/datasource/tempo/configuration/StreamingSection.tsx index d5a389da04a..a1aed261d42 100644 --- a/public/app/plugins/datasource/tempo/configuration/StreamingSection.tsx +++ b/public/app/plugins/datasource/tempo/configuration/StreamingSection.tsx @@ -1,14 +1,12 @@ -import { css } from '@emotion/css'; import React from 'react'; import { DataSourceJsonData, DataSourcePluginOptionsEditorProps, - GrafanaTheme2, updateDatasourcePluginJsonDataOption, } from '@grafana/data'; import { ConfigSection } from '@grafana/plugin-ui'; -import { InlineFieldRow, InlineField, InlineSwitch, Alert, Stack, useStyles2 } from '@grafana/ui'; +import { InlineFieldRow, InlineField, InlineSwitch, Alert, Stack, TextLink } from '@grafana/ui'; import { FeatureName, featuresToTempoVersion } from '../datasource'; @@ -21,7 +19,6 @@ interface StreamingOptions extends DataSourceJsonData { interface Props extends DataSourcePluginOptionsEditorProps {} export const StreamingSection = ({ options, onOptionsChange }: Props) => { - const styles = useStyles2(getStyles); return ( { description={
    Enable streaming for different Tempo features.
    - + Learn more - +
    } > @@ -87,15 +79,3 @@ export const StreamingSection = ({ options, onOptionsChange }: Props) => {
    ); }; -const getStyles = (theme: GrafanaTheme2) => { - return { - a: css({ - color: theme.colors.text.link, - textDecoration: 'underline', - marginLeft: '5px', - '&:hover': { - textDecoration: 'none', - }, - }), - }; -}; diff --git a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx index 9e9144776c9..3b1467ce81d 100644 --- a/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx +++ b/public/app/plugins/datasource/tempo/traceql/QueryEditor.tsx @@ -4,7 +4,7 @@ import { useState } from 'react'; import { CoreApp, GrafanaTheme2, QueryEditorProps } from '@grafana/data'; import { config, reportInteraction } from '@grafana/runtime'; -import { Alert, Button, Icon, InlineLabel, useStyles2 } from '@grafana/ui'; +import { Alert, Button, InlineLabel, TextLink, useStyles2 } from '@grafana/ui'; import { TempoDatasource } from '../datasource'; import { defaultQuery, MyDataSourceOptions, TempoQuery } from '../types'; @@ -32,14 +32,9 @@ export function QueryEditor(props: Props) { Please note that TraceQL metrics is an experimental feature and should not be used in production. Read more about it in{' '} - + documentation - - + . ); @@ -50,9 +45,9 @@ export function QueryEditor(props: Props) { {inAlerting && alertingWarning} Build complex queries using TraceQL to select a list of traces.{' '} - + Documentation - + {!showCopyFromSearchButton && (
    diff --git a/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx b/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx index 96c577d0c6e..3003f1dc08f 100644 --- a/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx +++ b/public/app/plugins/datasource/tempo/traceql/TempoQueryBuilderOptions.tsx @@ -4,7 +4,7 @@ import { useToggle } from 'react-use'; import { CoreApp, GrafanaTheme2 } from '@grafana/data'; import { EditorField, EditorRow } from '@grafana/plugin-ui'; -import { AutoSizeInput, RadioButtonGroup, useStyles2 } from '@grafana/ui'; +import { AutoSizeInput, RadioButtonGroup, TextLink, useStyles2 } from '@grafana/ui'; import { QueryOptionGroup } from '../_importedDependencies/datasources/prometheus/QueryOptionGroup'; import { SearchTableType, MetricsQueryType } from '../dataquery.gen'; @@ -209,15 +209,14 @@ const StreamingTooltip = () => { Indicates if streaming is currently enabled. Streaming allows you to view partial query results before the entire query completes. - Learn more - +
    ); }; diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index 6d069a6aecf..b794b8c6e1c 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -4319,7 +4319,7 @@ "source-label": "Source", "sub-text": "<0>Define what data source will display the correlation, and what data will replace previously defined variables." }, - "sub-title": "Define how data living in different data sources relates to each other. Read more in the <2>documentation<1>", + "sub-title": "Define how data living in different data sources relates to each other. Read more in the <2>documentation", "target-form": { "control-rules": "This field is required.", "sub-text": "<0>Define what the correlation will link to. With the query type, a query will run when the correlation is clicked. With the external type, clicking the correlation will open a URL.", @@ -4722,7 +4722,7 @@ "add-visualization-body": "Select a data source and then query and visualize your data with charts, stats and tables or create lists, markdowns and other widgets.", "add-visualization-button": "Add visualization", "add-visualization-header": "Start your new dashboard by adding a visualization", - "import-a-dashboard-body": "Import dashboards from files or <1>grafana.com.", + "import-a-dashboard-body": "Import dashboards from files or <2>grafana.com.", "import-a-dashboard-header": "Import a dashboard", "import-dashboard-button": "Import dashboard" },