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>
This commit is contained in:
Oscar Kilhed 2021-01-26 09:53:39 +01:00 committed by GitHub
parent fad81e1696
commit 07bb6b8dae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 19 deletions

View File

@ -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>
)
}
/>

View File

@ -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>
)
}
/>

View File

@ -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>
)
}
/>

View File

@ -0,0 +1,21 @@
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;
`,
};
};

View File

@ -5,4 +5,5 @@ export interface SegmentProps<T> {
className?: string;
allowCustomValue?: boolean;
placeholder?: string;
disabled?: boolean;
}

View File

@ -3,6 +3,7 @@ import React, { useState, useRef, ReactElement } from 'react';
interface LabelProps {
Component: ReactElement;
onClick?: () => void;
disabled?: boolean;
}
export const useExpandableLabel = (
@ -12,10 +13,13 @@ 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={() => {
onClick={
disabled
? undefined
: () => {
setExpanded(true);
if (ref && ref.current) {
setWidth(ref.current.clientWidth * 1.25);
@ -23,7 +27,8 @@ export const useExpandableLabel = (
if (onClick) {
onClick();
}
}}
}
}
>
{Component}
</div>

View File

@ -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');
});
});