Commit 07bb6b8d by Oscar Kilhed Committed by GitHub

Grafana/UI: Add disable prop to Segment (#30539)

* Grafana/UI: Add disable prop to Segment

SegmentSync, SegmentAsync and SegmentInput had the disable prop inherited from HTMLProp but it did not disable the component. The disable prop should now disable the component.

* Use InlineLabel instead of span and remove some sass-classes in Segments

* Change MetricsQueryEditor test to reflect new layout of AsyncSegment

* Use useStyles hook to get themed styles for segment inputs

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
parent fad81e16
......@@ -3,6 +3,9 @@ import { cx } from 'emotion';
import _ from 'lodash';
import { SelectableValue } from '@grafana/data';
import { SegmentSelect, useExpandableLabel, SegmentProps } from './';
import { getSegmentStyles } from './styles';
import { InlineLabel } from '../Forms/InlineLabel';
import { useStyles } from '../../themes';
export interface SegmentSyncProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLDivElement>, 'value' | 'onChange'> {
value?: T | SelectableValue<T>;
......@@ -18,19 +21,32 @@ export function Segment<T>({
className,
allowCustomValue,
placeholder,
disabled,
...rest
}: React.PropsWithChildren<SegmentSyncProps<T>>) {
const [Label, width, expanded, setExpanded] = useExpandableLabel(false);
const styles = useStyles(getSegmentStyles);
if (!expanded) {
const label = _.isObject(value) ? value.label : value;
return (
<Label
disabled={disabled}
Component={
Component || (
<a className={cx('gf-form-label', 'query-part', !value && placeholder && 'query-placeholder', className)}>
<InlineLabel
className={cx(
styles.segment,
{
[styles.queryPlaceholder]: placeholder !== undefined && !value,
[styles.disabled]: disabled,
},
className
)}
>
{label || placeholder}
</a>
</InlineLabel>
)
}
/>
......
......@@ -6,6 +6,9 @@ import { SelectableValue } from '@grafana/data';
import { useExpandableLabel, SegmentProps } from '.';
import { useAsyncFn } from 'react-use';
import { AsyncState } from 'react-use/lib/useAsync';
import { getSegmentStyles } from './styles';
import { InlineLabel } from '../Forms/InlineLabel';
import { useStyles } from '../../themes';
export interface SegmentAsyncProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLDivElement>, 'value' | 'onChange'> {
value?: T | SelectableValue<T>;
......@@ -20,22 +23,35 @@ export function SegmentAsync<T>({
Component,
className,
allowCustomValue,
disabled,
placeholder,
...rest
}: React.PropsWithChildren<SegmentAsyncProps<T>>) {
const [state, fetchOptions] = useAsyncFn(loadOptions, [loadOptions]);
const [Label, width, expanded, setExpanded] = useExpandableLabel(false);
const styles = useStyles(getSegmentStyles);
if (!expanded) {
const label = _.isObject(value) ? value.label : value;
return (
<Label
onClick={fetchOptions}
disabled={disabled}
Component={
Component || (
<a className={cx('gf-form-label', 'query-part', !value && placeholder && 'query-placeholder', className)}>
<InlineLabel
className={cx(
styles.segment,
{
[styles.queryPlaceholder]: placeholder !== undefined && !value,
[styles.disabled]: disabled,
},
className
)}
>
{label || placeholder}
</a>
</InlineLabel>
)
}
/>
......
......@@ -3,6 +3,9 @@ import { cx, css } from 'emotion';
import useClickAway from 'react-use/lib/useClickAway';
import { measureText } from '../../utils/measureText';
import { useExpandableLabel, SegmentProps } from '.';
import { getSegmentStyles } from './styles';
import { InlineLabel } from '../Forms/InlineLabel';
import { useStyles } from '../../themes';
export interface SegmentInputProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange'> {
value: string | number;
......@@ -18,6 +21,7 @@ export function SegmentInput<T>({
Component,
className,
placeholder,
disabled,
autofocus = false,
...rest
}: React.PropsWithChildren<SegmentInputProps<T>>) {
......@@ -25,6 +29,7 @@ export function SegmentInput<T>({
const [value, setValue] = useState<number | string>(initialValue);
const [inputWidth, setInputWidth] = useState<number>(measureText((initialValue || '').toString(), FONT_SIZE).width);
const [Label, , expanded, setExpanded] = useExpandableLabel(autofocus);
const styles = useStyles(getSegmentStyles);
useClickAway(ref, () => {
setExpanded(false);
......@@ -34,11 +39,21 @@ export function SegmentInput<T>({
if (!expanded) {
return (
<Label
disabled={disabled}
Component={
Component || (
<a className={cx('gf-form-label', 'query-part', !value && placeholder && 'query-placeholder', className)}>
<InlineLabel
className={cx(
styles.segment,
{
[styles.queryPlaceholder]: placeholder !== undefined && !value,
[styles.disabled]: disabled,
},
className
)}
>
{initialValue || placeholder}
</a>
</InlineLabel>
)
}
/>
......
import { GrafanaTheme } from '@grafana/data';
import { css } from 'emotion';
export const getSegmentStyles = (theme: GrafanaTheme) => {
return {
segment: css`
cursor: pointer;
width: auto;
`,
queryPlaceholder: css`
color: ${theme.palette.gray2};
`,
disabled: css`
cursor: not-allowed;
opacity: 0.65;
box-shadow: none;
`,
};
};
......@@ -5,4 +5,5 @@ export interface SegmentProps<T> {
className?: string;
allowCustomValue?: boolean;
placeholder?: string;
disabled?: boolean;
}
......@@ -3,6 +3,7 @@ import React, { useState, useRef, ReactElement } from 'react';
interface LabelProps {
Component: ReactElement;
onClick?: () => void;
disabled?: boolean;
}
export const useExpandableLabel = (
......@@ -12,18 +13,22 @@ export const useExpandableLabel = (
const [expanded, setExpanded] = useState<boolean>(initialExpanded);
const [width, setWidth] = useState(0);
const Label: React.FC<LabelProps> = ({ Component, onClick }) => (
const Label: React.FC<LabelProps> = ({ Component, onClick, disabled }) => (
<div
ref={ref}
onClick={() => {
setExpanded(true);
if (ref && ref.current) {
setWidth(ref.current.clientWidth * 1.25);
}
if (onClick) {
onClick();
}
}}
onClick={
disabled
? undefined
: () => {
setExpanded(true);
if (ref && ref.current) {
setWidth(ref.current.clientWidth * 1.25);
}
if (onClick) {
onClick();
}
}
}
>
{Component}
</div>
......
......@@ -107,9 +107,9 @@ describe('QueryEditor', () => {
const props = setup();
props.query.region = (null as unknown) as string;
const wrapper = mount(<MetricsQueryEditor {...props} />);
expect(wrapper.find('.gf-form-inline').first().find('.gf-form-label.query-part').first().text()).toEqual(
'default'
);
expect(
wrapper.find('.gf-form-inline').first().find('Segment').find('InlineLabel').find('label').text()
).toEqual('default');
});
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment