diff --git a/packages/grafana-prometheus/src/querybuilder/QueryPatternsModal.tsx b/packages/grafana-prometheus/src/querybuilder/QueryPatternsModal.tsx index a4f17087b21..2364bc80a86 100644 --- a/packages/grafana-prometheus/src/querybuilder/QueryPatternsModal.tsx +++ b/packages/grafana-prometheus/src/querybuilder/QueryPatternsModal.tsx @@ -111,7 +111,6 @@ export const QueryPatternsModal = (props: Props) => { } )} isOpen={isOpen} - collapsible={true} onToggle={() => { const action = isOpen ? 'close' : 'open'; reportInteraction(`grafana_prom_kickstart_toggle_pattern_card`, { diff --git a/packages/grafana-prometheus/src/querybuilder/shared/QueryOptionGroup.tsx b/packages/grafana-prometheus/src/querybuilder/shared/QueryOptionGroup.tsx index 554201a820d..8253d195ea0 100644 --- a/packages/grafana-prometheus/src/querybuilder/shared/QueryOptionGroup.tsx +++ b/packages/grafana-prometheus/src/querybuilder/shared/QueryOptionGroup.tsx @@ -20,7 +20,6 @@ export function QueryOptionGroup({ title, children, collapsedInfo }: Props) {
= { children: 'Panel data', isOpen: false, label: 'Collapse panel', - collapsible: true, }, argTypes: { onToggle: { action: 'toggled' }, @@ -57,4 +59,39 @@ Controlled.parameters = { }, }; +export const WithCustomLabel: StoryFn = (args) => { + const [, updateArgs] = useArgs(); + return ( + { + action('onToggle')({ isOpen: !args.isOpen }); + updateArgs({ isOpen: !args.isOpen }); + }} + label={ + + Collapse panel + + { + event.stopPropagation(); + action('onDeleteClick')(); + }} + aria-label="Delete" + name="trash-alt" + /> + + + } + > +

{args.children}

+
+ ); +}; +WithCustomLabel.parameters = { + controls: { + exclude: [...EXCLUDED_PROPS, 'label'], + }, +}; + export default meta; diff --git a/packages/grafana-ui/src/components/Collapse/Collapse.test.tsx b/packages/grafana-ui/src/components/Collapse/Collapse.test.tsx index 432ff8ef56e..817df10153d 100644 --- a/packages/grafana-ui/src/components/Collapse/Collapse.test.tsx +++ b/packages/grafana-ui/src/components/Collapse/Collapse.test.tsx @@ -42,7 +42,7 @@ describe('Collapse', () => { const onToggle = jest.fn(); const { user } = setup( - +
{contentText}
); diff --git a/packages/grafana-ui/src/components/Collapse/Collapse.tsx b/packages/grafana-ui/src/components/Collapse/Collapse.tsx index d0f8757e1ca..195ea24b4d1 100644 --- a/packages/grafana-ui/src/components/Collapse/Collapse.tsx +++ b/packages/grafana-ui/src/components/Collapse/Collapse.tsx @@ -1,12 +1,11 @@ import { css, cx } from '@emotion/css'; -import { useState } from 'react'; +import { useId, useState } from 'react'; import * as React from 'react'; import { GrafanaTheme2 } from '@grafana/data'; import { useStyles2 } from '../../themes/ThemeContext'; -import { clearButtonStyles } from '../Button/Button'; -import { Icon } from '../Icon/Icon'; +import { IconButton } from '../IconButton/IconButton'; const getStyles = (theme: GrafanaTheme2) => ({ collapse: css({ @@ -74,25 +73,21 @@ const getStyles = (theme: GrafanaTheme2) => ({ }, }), header: css({ + cursor: 'pointer', label: 'collapse__header', - padding: theme.spacing(1, 2, 1, 2), + padding: theme.spacing(1), display: 'flex', + gap: theme.spacing(1), }), - headerCollapsed: css({ - label: 'collapse__header--collapsed', - padding: theme.spacing(1, 2, 1, 2), + button: css({ + marginRight: 0, }), headerLabel: css({ label: 'collapse__header-label', fontWeight: theme.typography.fontWeightMedium, - marginRight: theme.spacing(1), fontSize: theme.typography.size.md, display: 'flex', - flex: '0 0 100%', - }), - icon: css({ - label: 'collapse__icon', - margin: theme.spacing(0.25, 1, 0, -1), + flex: 1, }), }); @@ -103,12 +98,12 @@ export interface Props { label: React.ReactNode; /** Indicates loading state of the content */ loading?: boolean; - /** Toggle collapsed header icon */ - collapsible?: boolean; /** Callback for the toggle functionality */ onToggle?: (isOpen: boolean) => void; /** Additional class name for the root element */ className?: string; + /** @deprecated this prop is no longer used and will be removed in Grafana 13 */ + collapsible?: boolean; } export const ControlledCollapse = ({ isOpen, onToggle, ...otherProps }: React.PropsWithChildren) => { @@ -116,7 +111,6 @@ export const ControlledCollapse = ({ isOpen, onToggle, ...otherProps }: React.Pr return ( { setOpen(!open); @@ -133,35 +127,38 @@ export const ControlledCollapse = ({ isOpen, onToggle, ...otherProps }: React.Pr * * https://developers.grafana.com/ui/latest/index.html?path=/docs/layout-collapse--docs */ -export const Collapse = ({ - isOpen, - label, - loading, - collapsible, - onToggle, - className, - children, -}: React.PropsWithChildren) => { - const buttonStyles = useStyles2(clearButtonStyles); +export const Collapse = ({ isOpen, label, loading, onToggle, className, children }: React.PropsWithChildren) => { const style = useStyles2(getStyles); + const labelId = useId(); + const contentId = useId(); + const onClickToggle = () => { if (onToggle) { onToggle(!isOpen); } }; - const panelClass = cx([style.collapse, className]); - const loaderClass = loading ? cx([style.loader, style.loaderActive]) : cx([style.loader]); - const headerClass = collapsible ? cx([style.header]) : cx([style.headerCollapsed]); + const loaderClass = loading ? cx([style.loader, style.loaderActive]) : style.loader; return (
- + {/* the inner button handles keyboard a11y. this is a convenience for mouse users */} + {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */} +
+ +
+ {label} +
+
{isOpen && ( -
+
{children}
diff --git a/packages/grafana-ui/src/components/IconButton/IconButton.tsx b/packages/grafana-ui/src/components/IconButton/IconButton.tsx index d4d77c33fab..f2d44eb2fa1 100644 --- a/packages/grafana-ui/src/components/IconButton/IconButton.tsx +++ b/packages/grafana-ui/src/components/IconButton/IconButton.tsx @@ -41,7 +41,12 @@ interface BasePropsWithAriaLabel extends BaseProps { ['aria-label']: string; } -export type Props = BasePropsWithTooltip | BasePropsWithAriaLabel; +interface BasePropsWithAriaLabelledBy extends BaseProps { + /** Reference to an element id that labels the button. No tooltip will be set in this case. */ + ['aria-labelledby']: string; +} + +export type Props = BasePropsWithTooltip | BasePropsWithAriaLabel | BasePropsWithAriaLabelledBy; /** * This component looks just like an icon but behaves like a button. diff --git a/public/app/features/alerting/unified/components/import-to-gma/ConfirmConvertModal.tsx b/public/app/features/alerting/unified/components/import-to-gma/ConfirmConvertModal.tsx index 4f2c364859a..bf2a253d7e8 100644 --- a/public/app/features/alerting/unified/components/import-to-gma/ConfirmConvertModal.tsx +++ b/public/app/features/alerting/unified/components/import-to-gma/ConfirmConvertModal.tsx @@ -385,7 +385,6 @@ function TargetFolderNotEmptyWarning({ targetFolderRules }: { targetFolderRules: )} isOpen={showTargetRules} onToggle={toggleShowTargetRules} - collapsible={true} > diff --git a/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.tsx b/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.tsx index bfdb5b3303c..d88d446642d 100644 --- a/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.tsx +++ b/public/app/features/alerting/unified/components/import-to-gma/ImportToGMARules.tsx @@ -150,7 +150,6 @@ const ImportToGMARules = () => { label={t('alerting.import-to-gma.additional-settings', 'Additional settings')} isOpen={optionsShowing} onToggle={toggleOptions} - collapsible={true} > 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 dc87060c03d..61ce391d779 100644 --- a/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.tsx +++ b/public/app/features/alerting/unified/components/notification-policies/EditDefaultPolicyForm.tsx @@ -130,7 +130,6 @@ export const AmRootRouteForm = ({ actionButtons, alertManagerSourceName, onSubmi /> { })} { setIsLabelDescOpen(!isLabelDescOpen); @@ -201,7 +200,6 @@ export const CorrelationHelper = ({ exploreId, correlations }: Props) => { { setIsTransformOpen(!isTransformOpen); diff --git a/public/app/features/explore/Logs/LogsContainer.tsx b/public/app/features/explore/Logs/LogsContainer.tsx index 6ff6c9a248c..13bc78b5849 100644 --- a/public/app/features/explore/Logs/LogsContainer.tsx +++ b/public/app/features/explore/Logs/LogsContainer.tsx @@ -24,7 +24,7 @@ import { import { t } from '@grafana/i18n'; import { getDataSourceSrv } from '@grafana/runtime'; import { DataQuery } from '@grafana/schema'; -import { Collapse } from '@grafana/ui'; +import { PanelChrome } from '@grafana/ui'; import { MIXED_DATASOURCE_NAME } from 'app/plugins/datasource/mixed/MixedDataSource'; import { GetFieldLinksFn } from 'app/plugins/panel/logs/types'; import { ExploreItemState } from 'app/types/explore'; @@ -309,7 +309,7 @@ class LogsContainer extends PureComponent - + {(controls) => ( )} - + } isOpen={enabled} - collapsible={true} onToggle={onToggleLogsSampleCollapse} > diff --git a/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFilters.tsx b/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFilters.tsx index d9909845984..d48dabdf1fa 100644 --- a/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFilters.tsx +++ b/public/app/features/explore/TraceView/components/TracePageHeader/SpanFilters/SpanFilters.tsx @@ -151,7 +151,7 @@ export const SpanFilters = memo((props: SpanFilterProps) => { return (
- + diff --git a/public/app/features/logs/components/panel/LogLineContext.tsx b/public/app/features/logs/components/panel/LogLineContext.tsx index 59a34bb9cae..1fab2336c3d 100644 --- a/public/app/features/logs/components/panel/LogLineContext.tsx +++ b/public/app/features/logs/components/panel/LogLineContext.tsx @@ -329,7 +329,6 @@ export const LogLineContext = memo(
{getLogRowContextUi(log, updateResults)}
)} setShowLog(!showLog)} className={styles.referenceLogLine} diff --git a/public/app/features/logs/components/panel/LogLineDetailsComponent.tsx b/public/app/features/logs/components/panel/LogLineDetailsComponent.tsx index 240bc546313..36bdbf2bdde 100644 --- a/public/app/features/logs/components/panel/LogLineDetailsComponent.tsx +++ b/public/app/features/logs/components/panel/LogLineDetailsComponent.tsx @@ -131,7 +131,6 @@ export const LogLineDetailsComponent = memo( handleToggle('logLineOpen', isOpen)} > @@ -140,7 +139,6 @@ export const LogLineDetailsComponent = memo( {displayedFields.length > 0 && setDisplayedFields && ( handleToggle('displayedFieldsOpen', isOpen)} > @@ -151,7 +149,6 @@ export const LogLineDetailsComponent = memo( handleToggle('linksOpen', isOpen)} > @@ -161,7 +158,6 @@ export const LogLineDetailsComponent = memo( {trace && ( handleToggle('traceOpen', isOpen)} > @@ -174,7 +170,6 @@ export const LogLineDetailsComponent = memo( className={styles.collapsable} key={'fields'} label={t('logs.log-line-details.fields-section', 'Fields')} - collapsible isOpen={fieldsOpen} onToggle={(isOpen: boolean) => handleToggle('fieldsOpen', isOpen)} > @@ -186,7 +181,6 @@ export const LogLineDetailsComponent = memo( className={styles.collapsable} key={group} label={group} - collapsible isOpen={store.getBool(`${logOptionsStorageKey}.log-details.${groupOptionName(group)}`, true)} onToggle={(isOpen: boolean) => handleToggle(groupOptionName(group), isOpen)} > @@ -199,7 +193,6 @@ export const LogLineDetailsComponent = memo( className={styles.collapsable} key={'fields'} label={t('logs.log-line-details.fields-section', 'Fields')} - collapsible isOpen={fieldsOpen} onToggle={(isOpen: boolean) => handleToggle('fieldsOpen', isOpen)} > diff --git a/public/app/features/visualization/data-hover/DataHoverRows.tsx b/public/app/features/visualization/data-hover/DataHoverRows.tsx index d99fcfd04f9..0c9ca3759d7 100644 --- a/public/app/features/visualization/data-hover/DataHoverRows.tsx +++ b/public/app/features/visualization/data-hover/DataHoverRows.tsx @@ -38,7 +38,6 @@ export const DataHoverRows = ({ layers, activeTabIndex }: Props) => { return shouldDisplayCollapse ? ( { diff --git a/public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/AzureCheatSheet.tsx b/public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/AzureCheatSheet.tsx index 5116a7b86fb..7ed4304b881 100644 --- a/public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/AzureCheatSheet.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/AzureCheatSheet.tsx @@ -186,7 +186,6 @@ const AzureCheatSheet = (props: AzureCheatSheetProps) => { return ( setAreDropdownsOpen({ ...areDropdownsOpen, [category]: isOpen })} key={category} diff --git a/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/AdvancedMulti.tsx b/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/AdvancedMulti.tsx index d16cfff82e9..f9d5cee1d55 100644 --- a/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/AdvancedMulti.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/ResourcePicker/AdvancedMulti.tsx @@ -19,7 +19,6 @@ const AdvancedMulti = ({ resources, onChange, renderAdvanced }: ResourcePickerPr return (
setIsAdvancedOpen(!isAdvancedOpen)} diff --git a/public/app/plugins/datasource/cloudwatch/components/CheatSheet/LogsCheatSheet.tsx b/public/app/plugins/datasource/cloudwatch/components/CheatSheet/LogsCheatSheet.tsx index 86fcbd00857..082a8eaf44a 100644 --- a/public/app/plugins/datasource/cloudwatch/components/CheatSheet/LogsCheatSheet.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/CheatSheet/LogsCheatSheet.tsx @@ -80,7 +80,7 @@ interface CollapseProps { const CheatSheetCollapse = (props: CollapseProps) => { const [isOpen, setIsOpen] = useState(false); return ( - + {props.children} ); diff --git a/public/app/plugins/datasource/grafana-postgresql-datasource/configuration/ConfigurationEditor.tsx b/public/app/plugins/datasource/grafana-postgresql-datasource/configuration/ConfigurationEditor.tsx index 8a76ff319f4..3a7ffccdbb7 100644 --- a/public/app/plugins/datasource/grafana-postgresql-datasource/configuration/ConfigurationEditor.tsx +++ b/public/app/plugins/datasource/grafana-postgresql-datasource/configuration/ConfigurationEditor.tsx @@ -113,7 +113,7 @@ export const PostgresConfigEditor = (props: DataSourcePluginOptionsEditorProps

- setIsOpen((x) => !x)}> + setIsOpen((x) => !x)}> The database user should only be granted SELECT permissions on the specified database & tables you want to query.
Grafana does not validate that queries are safe so queries can contain any SQL statement. For example, diff --git a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryOptionGroup.tsx b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryOptionGroup.tsx index c6c3dad3719..df27a4b83d8 100644 --- a/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryOptionGroup.tsx +++ b/public/app/plugins/datasource/grafana-pyroscope-datasource/QueryEditor/QueryOptionGroup.tsx @@ -19,7 +19,6 @@ export function QueryOptionGroup({ title, children, collapsedInfo }: Props) {

{ window.localStorage.setItem(IS_LOKI_LOG_CONTEXT_UI_OPEN, (!isOpen).toString()); diff --git a/public/app/plugins/datasource/loki/querybuilder/components/QueryPatternsModal.tsx b/public/app/plugins/datasource/loki/querybuilder/components/QueryPatternsModal.tsx index 9be0cc98ec7..7841d9f1edc 100644 --- a/public/app/plugins/datasource/loki/querybuilder/components/QueryPatternsModal.tsx +++ b/public/app/plugins/datasource/loki/querybuilder/components/QueryPatternsModal.tsx @@ -99,7 +99,6 @@ export const QueryPatternsModal = (props: Props) => { key={patternType} label={`${capitalize(patternType)} query starters`} isOpen={openTabs.includes(patternType)} - collapsible={true} onToggle={() => setOpenTabs((tabs) => // close tab if it's already open, otherwise open it diff --git a/public/app/plugins/datasource/mysql/configuration/ConfigurationEditor.tsx b/public/app/plugins/datasource/mysql/configuration/ConfigurationEditor.tsx index bf0acc0731d..46cd7d8b3e3 100644 --- a/public/app/plugins/datasource/mysql/configuration/ConfigurationEditor.tsx +++ b/public/app/plugins/datasource/mysql/configuration/ConfigurationEditor.tsx @@ -60,7 +60,7 @@ export const ConfigurationEditor = (props: DataSourcePluginOptionsEditorProps - setIsOpen((x) => !x)}> + setIsOpen((x) => !x)}> The database user should only be granted SELECT permissions on the specified database & tables you want to query.
Grafana does not validate that queries are safe so queries can contain any SQL statement. For example, diff --git a/public/app/plugins/datasource/tempo/_importedDependencies/datasources/prometheus/QueryOptionGroup.tsx b/public/app/plugins/datasource/tempo/_importedDependencies/datasources/prometheus/QueryOptionGroup.tsx index 4541016132b..dd68fa412ab 100644 --- a/public/app/plugins/datasource/tempo/_importedDependencies/datasources/prometheus/QueryOptionGroup.tsx +++ b/public/app/plugins/datasource/tempo/_importedDependencies/datasources/prometheus/QueryOptionGroup.tsx @@ -25,7 +25,6 @@ export function QueryOptionGroup({ title, children, collapsedInfo, queryStats, o