mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
RadioButton: Make description appear in a Tooltip component (#78010)
This commit is contained in:
parent
1c270b1dc2
commit
e422a92eae
@ -55,7 +55,7 @@ describe('Exemplars', () => {
|
|||||||
cy.contains(dataSourceName).scrollIntoView().should('be.visible').click();
|
cy.contains(dataSourceName).scrollIntoView().should('be.visible').click();
|
||||||
|
|
||||||
// Switch to code editor
|
// Switch to code editor
|
||||||
cy.contains('label', 'Code').click();
|
e2e.components.RadioButton.container().filter(':contains("Code")').click();
|
||||||
|
|
||||||
// we need to wait for the query-field being lazy-loaded, in two steps:
|
// we need to wait for the query-field being lazy-loaded, in two steps:
|
||||||
// 1. first we wait for the text 'Loading...' to appear
|
// 1. first we wait for the text 'Loading...' to appear
|
||||||
|
@ -37,7 +37,7 @@ describe('Loki Query Editor', () => {
|
|||||||
e2e.components.DataSourcePicker.container().should('be.visible').click();
|
e2e.components.DataSourcePicker.container().should('be.visible').click();
|
||||||
cy.contains(dataSourceName).scrollIntoView().should('be.visible').click();
|
cy.contains(dataSourceName).scrollIntoView().should('be.visible').click();
|
||||||
|
|
||||||
cy.contains('Code').click();
|
e2e.components.RadioButton.container().filter(':contains("Code")').click();
|
||||||
|
|
||||||
// Wait for lazy loading
|
// Wait for lazy loading
|
||||||
const monacoLoadingText = 'Loading...';
|
const monacoLoadingText = 'Loading...';
|
||||||
|
@ -78,7 +78,8 @@ describe('Loki query builder', () => {
|
|||||||
cy.contains(finalQuery).should('be.visible');
|
cy.contains(finalQuery).should('be.visible');
|
||||||
|
|
||||||
// Change to code editor
|
// Change to code editor
|
||||||
cy.contains('label', 'Code').click();
|
e2e.components.RadioButton.container().filter(':contains("Code")').click();
|
||||||
|
|
||||||
// We need to test this manually because the final query is split into separate DOM elements using cy.contains(finalQuery).should('be.visible'); does not detect the query.
|
// We need to test this manually because the final query is split into separate DOM elements using cy.contains(finalQuery).should('be.visible'); does not detect the query.
|
||||||
cy.contains('rate').should('be.visible');
|
cy.contains('rate').should('be.visible');
|
||||||
cy.contains('instance1|instance2').should('be.visible');
|
cy.contains('instance1|instance2').should('be.visible');
|
||||||
|
@ -35,7 +35,8 @@ describe('MySQL datasource', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('code editor autocomplete should handle table name escaping/quoting', () => {
|
it('code editor autocomplete should handle table name escaping/quoting', () => {
|
||||||
cy.get("label[for^='option-code']").should('be.visible').click();
|
e2e.components.RadioButton.container().filter(':contains("Code")').click();
|
||||||
|
|
||||||
cy.get('textarea').type('S{downArrow}{enter}');
|
cy.get('textarea').type('S{downArrow}{enter}');
|
||||||
cy.wait('@tables');
|
cy.wait('@tables');
|
||||||
cy.get('.suggest-widget').contains(tableNameWithSpecialCharacter).should('be.visible');
|
cy.get('.suggest-widget').contains(tableNameWithSpecialCharacter).should('be.visible');
|
||||||
|
@ -12,7 +12,7 @@ describe('Query editor', () => {
|
|||||||
cy.contains('gdev-prometheus').scrollIntoView().should('be.visible').click();
|
cy.contains('gdev-prometheus').scrollIntoView().should('be.visible').click();
|
||||||
const queryText = `rate(http_requests_total{job="grafana"}[5m])`;
|
const queryText = `rate(http_requests_total{job="grafana"}[5m])`;
|
||||||
|
|
||||||
cy.contains('label', 'Code').click();
|
e2e.components.RadioButton.container().filter(':contains("Code")').click();
|
||||||
|
|
||||||
// we need to wait for the query-field being lazy-loaded, in two steps:
|
// we need to wait for the query-field being lazy-loaded, in two steps:
|
||||||
// it is a two-step process:
|
// it is a two-step process:
|
||||||
|
@ -10,7 +10,7 @@ describe('Visualization suggestions', () => {
|
|||||||
|
|
||||||
// Try visualization suggestions
|
// Try visualization suggestions
|
||||||
e2e.components.PanelEditor.toggleVizPicker().click();
|
e2e.components.PanelEditor.toggleVizPicker().click();
|
||||||
cy.contains('Suggestions').click();
|
e2e.components.RadioButton.container().filter(':contains("Suggestions")').click();
|
||||||
|
|
||||||
// Verify we see suggestions
|
// Verify we see suggestions
|
||||||
e2e.components.VisualizationPreview.card('Line chart').should('be.visible');
|
e2e.components.VisualizationPreview.card('Line chart').should('be.visible');
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
* @alpha
|
* @alpha
|
||||||
*/
|
*/
|
||||||
export const Components = {
|
export const Components = {
|
||||||
|
RadioButton: {
|
||||||
|
container: 'data-testid radio-button',
|
||||||
|
},
|
||||||
Breadcrumbs: {
|
Breadcrumbs: {
|
||||||
breadcrumb: (title: string) => `data-testid ${title} breadcrumb`,
|
breadcrumb: (title: string) => `data-testid ${title} breadcrumb`,
|
||||||
},
|
},
|
||||||
|
@ -2,10 +2,11 @@ import { css } from '@emotion/css';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { StringSelector } from '@grafana/e2e-selectors';
|
import { StringSelector, selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
import { useStyles2 } from '../../../themes';
|
import { useStyles2 } from '../../../themes';
|
||||||
import { getFocusStyles, getMouseFocusStyles } from '../../../themes/mixins';
|
import { getFocusStyles, getMouseFocusStyles } from '../../../themes/mixins';
|
||||||
|
import { Tooltip } from '../../Tooltip/Tooltip';
|
||||||
import { getPropertiesForButtonSize } from '../commonStyles';
|
import { getPropertiesForButtonSize } from '../commonStyles';
|
||||||
|
|
||||||
export type RadioButtonSize = 'sm' | 'md';
|
export type RadioButtonSize = 'sm' | 'md';
|
||||||
@ -43,20 +44,32 @@ export const RadioButton = React.forwardRef<HTMLInputElement, RadioButtonProps>(
|
|||||||
) => {
|
) => {
|
||||||
const styles = useStyles2(getRadioButtonStyles, size, fullWidth);
|
const styles = useStyles2(getRadioButtonStyles, size, fullWidth);
|
||||||
|
|
||||||
return (
|
const inputRadioButton = (
|
||||||
<div className={styles.radioOption}>
|
<input
|
||||||
<input
|
type="radio"
|
||||||
type="radio"
|
className={styles.radio}
|
||||||
className={styles.radio}
|
onChange={onChange}
|
||||||
onChange={onChange}
|
onClick={onClick}
|
||||||
onClick={onClick}
|
disabled={disabled}
|
||||||
disabled={disabled}
|
id={id}
|
||||||
id={id}
|
checked={active}
|
||||||
checked={active}
|
name={name}
|
||||||
name={name}
|
aria-label={ariaLabel}
|
||||||
aria-label={ariaLabel || description}
|
ref={ref}
|
||||||
ref={ref}
|
/>
|
||||||
/>
|
);
|
||||||
|
return description ? (
|
||||||
|
<div className={styles.radioOption} data-testid={selectors.components.RadioButton.container}>
|
||||||
|
<Tooltip content={description} placement="bottom">
|
||||||
|
{inputRadioButton}
|
||||||
|
</Tooltip>
|
||||||
|
<label className={styles.radioLabel} htmlFor={id} title={description || ariaLabel}>
|
||||||
|
{children}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className={styles.radioOption} data-testid={selectors.components.RadioButton.container}>
|
||||||
|
{inputRadioButton}
|
||||||
<label className={styles.radioLabel} htmlFor={id} title={description || ariaLabel}>
|
<label className={styles.radioLabel} htmlFor={id} title={description || ariaLabel}>
|
||||||
{children}
|
{children}
|
||||||
</label>
|
</label>
|
||||||
@ -86,15 +99,16 @@ const getRadioButtonStyles = (theme: GrafanaTheme2, size: RadioButtonSize, fullW
|
|||||||
radio: css({
|
radio: css({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
opacity: 0,
|
opacity: 0,
|
||||||
zIndex: -1000,
|
zIndex: 2,
|
||||||
width: '100% !important',
|
width: '100% !important',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
|
cursor: 'pointer',
|
||||||
|
|
||||||
'&:checked + label': {
|
'&:checked + label': {
|
||||||
color: theme.colors.text.primary,
|
color: theme.colors.text.primary,
|
||||||
fontWeight: theme.typography.fontWeightMedium,
|
fontWeight: theme.typography.fontWeightMedium,
|
||||||
background: theme.colors.action.selected,
|
background: theme.colors.action.selected,
|
||||||
zIndex: 3,
|
zIndex: 1,
|
||||||
},
|
},
|
||||||
|
|
||||||
'&:focus + label, &:focus-visible + label': getFocusStyles(theme),
|
'&:focus + label, &:focus-visible + label': getFocusStyles(theme),
|
||||||
@ -119,7 +133,6 @@ const getRadioButtonStyles = (theme: GrafanaTheme2, size: RadioButtonSize, fullW
|
|||||||
borderRadius: theme.shape.radius.default,
|
borderRadius: theme.shape.radius.default,
|
||||||
background: theme.colors.background.primary,
|
background: theme.colors.background.primary,
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
zIndex: 1,
|
|
||||||
userSelect: 'none',
|
userSelect: 'none',
|
||||||
whiteSpace: 'nowrap',
|
whiteSpace: 'nowrap',
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
|
@ -76,7 +76,7 @@ export function RadioButtonGroup<T>({
|
|||||||
<div
|
<div
|
||||||
role="radiogroup"
|
role="radiogroup"
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
className={cx(styles.radioGroup, fullWidth && styles.fullWidth, className)}
|
className={cx(styles.radioGroup, fullWidth && styles.fullWidth, invalid && styles.invalid, className)}
|
||||||
>
|
>
|
||||||
{options.map((opt, i) => {
|
{options.map((opt, i) => {
|
||||||
const isItemDisabled = disabledOptions && opt.value && disabledOptions.includes(opt.value);
|
const isItemDisabled = disabledOptions && opt.value && disabledOptions.includes(opt.value);
|
||||||
|
@ -81,15 +81,12 @@ export const Tooltip = React.forwardRef<HTMLElement, TooltipProps>(
|
|||||||
[forwardedRef, setTriggerRef]
|
[forwardedRef, setTriggerRef]
|
||||||
);
|
);
|
||||||
|
|
||||||
// if the child has an aria-label, this should take precedence over the tooltip content
|
|
||||||
const childHasAriaLabel = 'aria-label' in children.props;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{React.cloneElement(children, {
|
{React.cloneElement(children, {
|
||||||
ref: handleRef,
|
ref: handleRef,
|
||||||
tabIndex: 0, // tooltip trigger should be keyboard focusable
|
tabIndex: 0, // tooltip trigger should be keyboard focusable
|
||||||
'aria-describedby': !childHasAriaLabel && visible ? tooltipId : undefined,
|
'aria-describedby': visible ? tooltipId : undefined,
|
||||||
})}
|
})}
|
||||||
{visible && (
|
{visible && (
|
||||||
<Portal>
|
<Portal>
|
||||||
|
Loading…
Reference in New Issue
Block a user