mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
fad81e1696
commit
07bb6b8dae
@ -3,6 +3,9 @@ import { cx } from 'emotion';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
import { SegmentSelect, useExpandableLabel, SegmentProps } from './';
|
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'> {
|
export interface SegmentSyncProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLDivElement>, 'value' | 'onChange'> {
|
||||||
value?: T | SelectableValue<T>;
|
value?: T | SelectableValue<T>;
|
||||||
@ -18,19 +21,32 @@ export function Segment<T>({
|
|||||||
className,
|
className,
|
||||||
allowCustomValue,
|
allowCustomValue,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
disabled,
|
||||||
...rest
|
...rest
|
||||||
}: React.PropsWithChildren<SegmentSyncProps<T>>) {
|
}: React.PropsWithChildren<SegmentSyncProps<T>>) {
|
||||||
const [Label, width, expanded, setExpanded] = useExpandableLabel(false);
|
const [Label, width, expanded, setExpanded] = useExpandableLabel(false);
|
||||||
|
const styles = useStyles(getSegmentStyles);
|
||||||
|
|
||||||
if (!expanded) {
|
if (!expanded) {
|
||||||
const label = _.isObject(value) ? value.label : value;
|
const label = _.isObject(value) ? value.label : value;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Label
|
<Label
|
||||||
|
disabled={disabled}
|
||||||
Component={
|
Component={
|
||||||
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}
|
{label || placeholder}
|
||||||
</a>
|
</InlineLabel>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -6,6 +6,9 @@ import { SelectableValue } from '@grafana/data';
|
|||||||
import { useExpandableLabel, SegmentProps } from '.';
|
import { useExpandableLabel, SegmentProps } from '.';
|
||||||
import { useAsyncFn } from 'react-use';
|
import { useAsyncFn } from 'react-use';
|
||||||
import { AsyncState } from 'react-use/lib/useAsync';
|
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'> {
|
export interface SegmentAsyncProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLDivElement>, 'value' | 'onChange'> {
|
||||||
value?: T | SelectableValue<T>;
|
value?: T | SelectableValue<T>;
|
||||||
@ -20,22 +23,35 @@ export function SegmentAsync<T>({
|
|||||||
Component,
|
Component,
|
||||||
className,
|
className,
|
||||||
allowCustomValue,
|
allowCustomValue,
|
||||||
|
disabled,
|
||||||
placeholder,
|
placeholder,
|
||||||
...rest
|
...rest
|
||||||
}: React.PropsWithChildren<SegmentAsyncProps<T>>) {
|
}: React.PropsWithChildren<SegmentAsyncProps<T>>) {
|
||||||
const [state, fetchOptions] = useAsyncFn(loadOptions, [loadOptions]);
|
const [state, fetchOptions] = useAsyncFn(loadOptions, [loadOptions]);
|
||||||
const [Label, width, expanded, setExpanded] = useExpandableLabel(false);
|
const [Label, width, expanded, setExpanded] = useExpandableLabel(false);
|
||||||
|
const styles = useStyles(getSegmentStyles);
|
||||||
|
|
||||||
if (!expanded) {
|
if (!expanded) {
|
||||||
const label = _.isObject(value) ? value.label : value;
|
const label = _.isObject(value) ? value.label : value;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Label
|
<Label
|
||||||
onClick={fetchOptions}
|
onClick={fetchOptions}
|
||||||
|
disabled={disabled}
|
||||||
Component={
|
Component={
|
||||||
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}
|
{label || placeholder}
|
||||||
</a>
|
</InlineLabel>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -3,6 +3,9 @@ import { cx, css } from 'emotion';
|
|||||||
import useClickAway from 'react-use/lib/useClickAway';
|
import useClickAway from 'react-use/lib/useClickAway';
|
||||||
import { measureText } from '../../utils/measureText';
|
import { measureText } from '../../utils/measureText';
|
||||||
import { useExpandableLabel, SegmentProps } from '.';
|
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'> {
|
export interface SegmentInputProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange'> {
|
||||||
value: string | number;
|
value: string | number;
|
||||||
@ -18,6 +21,7 @@ export function SegmentInput<T>({
|
|||||||
Component,
|
Component,
|
||||||
className,
|
className,
|
||||||
placeholder,
|
placeholder,
|
||||||
|
disabled,
|
||||||
autofocus = false,
|
autofocus = false,
|
||||||
...rest
|
...rest
|
||||||
}: React.PropsWithChildren<SegmentInputProps<T>>) {
|
}: React.PropsWithChildren<SegmentInputProps<T>>) {
|
||||||
@ -25,6 +29,7 @@ export function SegmentInput<T>({
|
|||||||
const [value, setValue] = useState<number | string>(initialValue);
|
const [value, setValue] = useState<number | string>(initialValue);
|
||||||
const [inputWidth, setInputWidth] = useState<number>(measureText((initialValue || '').toString(), FONT_SIZE).width);
|
const [inputWidth, setInputWidth] = useState<number>(measureText((initialValue || '').toString(), FONT_SIZE).width);
|
||||||
const [Label, , expanded, setExpanded] = useExpandableLabel(autofocus);
|
const [Label, , expanded, setExpanded] = useExpandableLabel(autofocus);
|
||||||
|
const styles = useStyles(getSegmentStyles);
|
||||||
|
|
||||||
useClickAway(ref, () => {
|
useClickAway(ref, () => {
|
||||||
setExpanded(false);
|
setExpanded(false);
|
||||||
@ -34,11 +39,21 @@ export function SegmentInput<T>({
|
|||||||
if (!expanded) {
|
if (!expanded) {
|
||||||
return (
|
return (
|
||||||
<Label
|
<Label
|
||||||
|
disabled={disabled}
|
||||||
Component={
|
Component={
|
||||||
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}
|
{initialValue || placeholder}
|
||||||
</a>
|
</InlineLabel>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
21
packages/grafana-ui/src/components/Segment/styles.ts
Normal file
21
packages/grafana-ui/src/components/Segment/styles.ts
Normal 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;
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
};
|
@ -5,4 +5,5 @@ export interface SegmentProps<T> {
|
|||||||
className?: string;
|
className?: string;
|
||||||
allowCustomValue?: boolean;
|
allowCustomValue?: boolean;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import React, { useState, useRef, ReactElement } from 'react';
|
|||||||
interface LabelProps {
|
interface LabelProps {
|
||||||
Component: ReactElement;
|
Component: ReactElement;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useExpandableLabel = (
|
export const useExpandableLabel = (
|
||||||
@ -12,18 +13,22 @@ export const useExpandableLabel = (
|
|||||||
const [expanded, setExpanded] = useState<boolean>(initialExpanded);
|
const [expanded, setExpanded] = useState<boolean>(initialExpanded);
|
||||||
const [width, setWidth] = useState(0);
|
const [width, setWidth] = useState(0);
|
||||||
|
|
||||||
const Label: React.FC<LabelProps> = ({ Component, onClick }) => (
|
const Label: React.FC<LabelProps> = ({ Component, onClick, disabled }) => (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
onClick={() => {
|
onClick={
|
||||||
setExpanded(true);
|
disabled
|
||||||
if (ref && ref.current) {
|
? undefined
|
||||||
setWidth(ref.current.clientWidth * 1.25);
|
: () => {
|
||||||
}
|
setExpanded(true);
|
||||||
if (onClick) {
|
if (ref && ref.current) {
|
||||||
onClick();
|
setWidth(ref.current.clientWidth * 1.25);
|
||||||
}
|
}
|
||||||
}}
|
if (onClick) {
|
||||||
|
onClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{Component}
|
{Component}
|
||||||
</div>
|
</div>
|
||||||
|
@ -107,9 +107,9 @@ describe('QueryEditor', () => {
|
|||||||
const props = setup();
|
const props = setup();
|
||||||
props.query.region = (null as unknown) as string;
|
props.query.region = (null as unknown) as string;
|
||||||
const wrapper = mount(<MetricsQueryEditor {...props} />);
|
const wrapper = mount(<MetricsQueryEditor {...props} />);
|
||||||
expect(wrapper.find('.gf-form-inline').first().find('.gf-form-label.query-part').first().text()).toEqual(
|
expect(
|
||||||
'default'
|
wrapper.find('.gf-form-inline').first().find('Segment').find('InlineLabel').find('label').text()
|
||||||
);
|
).toEqual('default');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user