218 lines
6.6 KiB
TypeScript
218 lines
6.6 KiB
TypeScript
import { css, cx } from '@emotion/css';
|
|
import { PureComponent } from 'react';
|
|
import * as React from 'react';
|
|
|
|
import { GrafanaTheme2, VariableOption } from '@grafana/data';
|
|
import { selectors } from '@grafana/e2e-selectors';
|
|
import { Tooltip, Themeable2, withTheme2, clearButtonStyles, stylesFactory } from '@grafana/ui';
|
|
import { Trans, t } from 'app/core/internationalization';
|
|
|
|
import { ALL_VARIABLE_VALUE } from '../../constants';
|
|
|
|
export interface Props extends React.HTMLProps<HTMLUListElement>, Themeable2 {
|
|
multi: boolean;
|
|
values: VariableOption[];
|
|
selectedValues: VariableOption[];
|
|
highlightIndex: number;
|
|
onToggle: (option: VariableOption, clearOthers: boolean) => void;
|
|
onToggleAll: () => void;
|
|
/**
|
|
* Used for aria-controls
|
|
*/
|
|
id: string;
|
|
}
|
|
|
|
class VariableOptions extends PureComponent<Props> {
|
|
onToggle = (option: VariableOption) => (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
const clearOthers = event.shiftKey || event.ctrlKey || event.metaKey;
|
|
this.handleEvent(event);
|
|
this.props.onToggle(option, clearOthers);
|
|
};
|
|
|
|
onToggleAll = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
this.handleEvent(event);
|
|
this.props.onToggleAll();
|
|
};
|
|
|
|
handleEvent(event: React.MouseEvent<HTMLButtonElement>) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
|
|
render() {
|
|
// Don't want to pass faulty rest props to the div
|
|
const { multi, values, highlightIndex, selectedValues, onToggle, onToggleAll, theme, ...restProps } = this.props;
|
|
const styles = getStyles(theme);
|
|
|
|
return (
|
|
<div className={styles.variableValueDropdown}>
|
|
<div className={styles.variableOptionsWrapper}>
|
|
<ul
|
|
className={styles.variableOptionsColumn}
|
|
aria-label={selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownDropDown}
|
|
{...restProps}
|
|
>
|
|
{this.renderMultiToggle()}
|
|
{values.map((option, index) => this.renderOption(option, index))}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
renderOption(option: VariableOption, index: number) {
|
|
const { highlightIndex, multi, theme } = this.props;
|
|
const styles = getStyles(theme);
|
|
|
|
const isAllOption = option.value === ALL_VARIABLE_VALUE;
|
|
|
|
return (
|
|
<li key={`${option.value}`}>
|
|
<button
|
|
data-testid={selectors.components.Variables.variableOption}
|
|
role="checkbox"
|
|
type="button"
|
|
aria-checked={option.selected}
|
|
className={cx(
|
|
clearButtonStyles(theme),
|
|
styles.variableOption,
|
|
{
|
|
[styles.highlighted]: index === highlightIndex,
|
|
[styles.variableAllOption]: isAllOption,
|
|
},
|
|
styles.noStyledButton
|
|
)}
|
|
onClick={this.onToggle(option)}
|
|
>
|
|
<span
|
|
className={cx(styles.variableOptionIcon, {
|
|
[styles.variableOptionIconSelected]: option.selected,
|
|
[styles.hideVariableOptionIcon]: !multi,
|
|
})}
|
|
></span>
|
|
<span data-testid={selectors.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts(`${option.text}`)}>
|
|
{isAllOption ? t('variable.picker.option-all', 'All') : option.text}
|
|
</span>
|
|
</button>
|
|
</li>
|
|
);
|
|
}
|
|
|
|
renderMultiToggle() {
|
|
const { multi, selectedValues, theme, values } = this.props;
|
|
const styles = getStyles(theme);
|
|
const isAllOptionConfigured = values.some((option) => option.value === ALL_VARIABLE_VALUE);
|
|
|
|
if (!multi) {
|
|
return null;
|
|
}
|
|
|
|
const tooltipContent = () => <Trans i18nKey="variable.picker.option-tooltip">Clear selections</Trans>;
|
|
return (
|
|
<Tooltip content={tooltipContent} placement={'top'}>
|
|
<button
|
|
className={cx(
|
|
clearButtonStyles(theme),
|
|
styles.variableOption,
|
|
styles.variableOptionColumnHeader,
|
|
styles.noStyledButton,
|
|
{ [styles.noPaddingBotton]: isAllOptionConfigured }
|
|
)}
|
|
role="checkbox"
|
|
aria-checked={selectedValues.length > 1 ? 'mixed' : 'false'}
|
|
onClick={this.onToggleAll}
|
|
aria-label="Toggle all values"
|
|
data-placement="top"
|
|
>
|
|
<span
|
|
className={cx(styles.variableOptionIcon, {
|
|
[styles.variableOptionIconManySelected]: selectedValues.length > 1,
|
|
})}
|
|
></span>
|
|
<Trans i18nKey="variable.picker.option-selected-values">Selected</Trans> ({selectedValues.length})
|
|
</button>
|
|
</Tooltip>
|
|
);
|
|
}
|
|
}
|
|
|
|
const getStyles = stylesFactory((theme: GrafanaTheme2) => {
|
|
const checkboxImageUrl = theme.isDark ? 'public/img/checkbox.png' : 'public/img/checkbox_white.png';
|
|
|
|
return {
|
|
hideVariableOptionIcon: css({
|
|
display: 'none',
|
|
}),
|
|
highlighted: css({
|
|
backgroundColor: theme.colors.action.hover,
|
|
}),
|
|
noStyledButton: css({
|
|
width: '100%',
|
|
textAlign: 'left',
|
|
}),
|
|
variableOption: css({
|
|
display: 'block',
|
|
padding: '2px 27px 0 8px',
|
|
position: 'relative',
|
|
whiteSpace: 'nowrap',
|
|
minWidth: '115px',
|
|
['&:hover']: {
|
|
backgroundColor: theme.colors.action.hover,
|
|
},
|
|
}),
|
|
variableOptionColumnHeader: css({
|
|
paddingTop: '5px',
|
|
paddingBottom: '5px',
|
|
marginBottom: '5px',
|
|
}),
|
|
variableOptionIcon: css({
|
|
display: 'inline-block',
|
|
width: '24px',
|
|
height: '18px',
|
|
position: 'relative',
|
|
top: '4px',
|
|
background: `url(${checkboxImageUrl}) left top no-repeat`,
|
|
}),
|
|
variableOptionIconManySelected: css({
|
|
background: `url(${checkboxImageUrl}) 0px -36px no-repeat`,
|
|
}),
|
|
variableOptionIconSelected: css({
|
|
background: `url(${checkboxImageUrl}) 0px -18px no-repeat`,
|
|
}),
|
|
variableValueDropdown: css({
|
|
backgroundColor: theme.colors.background.primary,
|
|
border: `1px solid ${theme.colors.border.weak}`,
|
|
borderRadius: theme.shape.borderRadius(2),
|
|
boxShadow: theme.shadows.z2,
|
|
position: 'absolute',
|
|
top: theme.spacing(theme.components.height.md),
|
|
maxHeight: '400px',
|
|
minHeight: '150px',
|
|
minWidth: '150px',
|
|
overflowY: 'auto',
|
|
overflowX: 'hidden',
|
|
zIndex: theme.zIndex.typeahead,
|
|
}),
|
|
variableOptionsColumn: css({
|
|
maxHeight: '350px',
|
|
display: 'table-cell',
|
|
lineHeight: '26px',
|
|
listStyleType: 'none',
|
|
}),
|
|
variableOptionsWrapper: css({
|
|
display: 'table',
|
|
width: '100%',
|
|
}),
|
|
variableAllOption: css({
|
|
borderBottom: `1px solid ${theme.colors.border.weak}`,
|
|
paddingBottom: theme.spacing(1),
|
|
}),
|
|
|
|
noPaddingBotton: css({
|
|
paddingBottom: 0,
|
|
}),
|
|
};
|
|
});
|
|
|
|
export default withTheme2(VariableOptions);
|