mirror of https://github.com/grafana/grafana
Revert: Query editor components from grafana-ui (#57436)
* Revert: QueryEditor components from grafana-ui * Use local version of experimental * Use experimental for query editor components * Fix type issues in MSSQL * point to actual version of experimental package * point to latest version of experimental Co-authored-by: Erik Sundell <erik.sundell87@gmail.com>pull/57555/head
parent
3cbbf706cf
commit
c0b778134e
@ -1,23 +0,0 @@ |
||||
import { css, cx } from '@emotion/css'; |
||||
import React from 'react'; |
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data'; |
||||
|
||||
import { useTheme2, stylesFactory } from '../../themes'; |
||||
import { Button, ButtonProps } from '../Button'; |
||||
|
||||
interface AccessoryButtonProps extends ButtonProps {} |
||||
|
||||
export const AccessoryButton: React.FC<AccessoryButtonProps> = ({ className, ...props }) => { |
||||
const theme = useTheme2(); |
||||
const styles = getButtonStyles(theme); |
||||
|
||||
return <Button {...props} className={cx(className, styles.button)} />; |
||||
}; |
||||
|
||||
const getButtonStyles = stylesFactory((theme: GrafanaTheme2) => ({ |
||||
button: css({ |
||||
paddingLeft: theme.spacing(3 / 2), |
||||
paddingRight: theme.spacing(3 / 2), |
||||
}), |
||||
})); |
||||
@ -1,80 +0,0 @@ |
||||
import { css } from '@emotion/css'; |
||||
import React, { ComponentProps } from 'react'; |
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data'; |
||||
|
||||
import { stylesFactory, useTheme2 } from '../../themes'; |
||||
import { ReactUtils } from '../../utils'; |
||||
import { Field } from '../Forms/Field'; |
||||
import { Icon } from '../Icon/Icon'; |
||||
import { PopoverContent, Tooltip } from '../Tooltip'; |
||||
|
||||
import { Space } from './Space'; |
||||
|
||||
interface EditorFieldProps extends ComponentProps<typeof Field> { |
||||
label: string; |
||||
children: React.ReactElement; |
||||
width?: number | string; |
||||
optional?: boolean; |
||||
tooltip?: PopoverContent; |
||||
tooltipInteractive?: boolean; |
||||
} |
||||
|
||||
export const EditorField: React.FC<EditorFieldProps> = (props) => { |
||||
const { label, optional, tooltip, tooltipInteractive, children, width, ...fieldProps } = props; |
||||
|
||||
const theme = useTheme2(); |
||||
const styles = getStyles(theme, width); |
||||
|
||||
// Null check for backward compatibility
|
||||
const childInputId = fieldProps?.htmlFor || ReactUtils?.getChildId(children); |
||||
|
||||
const labelEl = ( |
||||
<> |
||||
<label className={styles.label} htmlFor={childInputId}> |
||||
{label} |
||||
{optional && <span className={styles.optional}> - optional</span>} |
||||
{tooltip && ( |
||||
<Tooltip placement="top" content={tooltip} theme="info" interactive={tooltipInteractive}> |
||||
<Icon name="info-circle" size="sm" className={styles.icon} /> |
||||
</Tooltip> |
||||
)} |
||||
</label> |
||||
<Space v={0.5} /> |
||||
</> |
||||
); |
||||
|
||||
return ( |
||||
<div className={styles.root}> |
||||
<Field className={styles.field} label={labelEl} {...fieldProps}> |
||||
{children} |
||||
</Field> |
||||
</div> |
||||
); |
||||
}; |
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme2, width?: number | string) => { |
||||
return { |
||||
root: css({ |
||||
minWidth: theme.spacing(width ?? 0), |
||||
}), |
||||
label: css({ |
||||
fontSize: 12, |
||||
fontWeight: theme.typography.fontWeightMedium, |
||||
}), |
||||
optional: css({ |
||||
fontStyle: 'italic', |
||||
color: theme.colors.text.secondary, |
||||
}), |
||||
field: css({ |
||||
marginBottom: 0, // GrafanaUI/Field has a bottom margin which we must remove
|
||||
}), |
||||
icon: css({ |
||||
color: theme.colors.text.secondary, |
||||
marginLeft: theme.spacing(1), |
||||
':hover': { |
||||
color: theme.colors.text.primary, |
||||
}, |
||||
}), |
||||
}; |
||||
}); |
||||
@ -1,9 +0,0 @@ |
||||
import React from 'react'; |
||||
|
||||
import { Stack } from './Stack'; |
||||
|
||||
interface EditorFieldGroupProps {} |
||||
|
||||
export const EditorFieldGroup = ({ children }: React.PropsWithChildren<EditorFieldGroupProps>) => { |
||||
return <Stack gap={1}>{children}</Stack>; |
||||
}; |
||||
@ -1,25 +0,0 @@ |
||||
import { css } from '@emotion/css'; |
||||
import React from 'react'; |
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data'; |
||||
|
||||
import { stylesFactory, useTheme2 } from '../../themes'; |
||||
|
||||
interface EditorHeaderProps {} |
||||
|
||||
export const EditorHeader = ({ children }: React.PropsWithChildren<EditorHeaderProps>) => { |
||||
const theme = useTheme2(); |
||||
const styles = getStyles(theme); |
||||
|
||||
return <div className={styles.root}>{children}</div>; |
||||
}; |
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme2) => ({ |
||||
root: css({ |
||||
display: 'flex', |
||||
flexWrap: 'wrap', |
||||
alignItems: 'center', |
||||
gap: theme.spacing(3), |
||||
minHeight: theme.spacing(4), |
||||
}), |
||||
})); |
||||
@ -1,49 +0,0 @@ |
||||
import React from 'react'; |
||||
|
||||
import { Button } from '../Button'; |
||||
|
||||
import { Stack } from './Stack'; |
||||
|
||||
interface EditorListProps<T> { |
||||
items: Array<Partial<T>>; |
||||
renderItem: ( |
||||
item: Partial<T>, |
||||
onChangeItem: (item: Partial<T>) => void, |
||||
onDeleteItem: () => void |
||||
) => React.ReactElement; |
||||
onChange: (items: Array<Partial<T>>) => void; |
||||
} |
||||
|
||||
export function EditorList<T>({ items, renderItem, onChange }: EditorListProps<T>) { |
||||
const onAddItem = () => { |
||||
const newItems = [...items, {}]; |
||||
|
||||
onChange(newItems); |
||||
}; |
||||
|
||||
const onChangeItem = (itemIndex: number, newItem: Partial<T>) => { |
||||
const newItems = [...items]; |
||||
newItems[itemIndex] = newItem; |
||||
onChange(newItems); |
||||
}; |
||||
|
||||
const onDeleteItem = (itemIndex: number) => { |
||||
const newItems = [...items]; |
||||
newItems.splice(itemIndex, 1); |
||||
onChange(newItems); |
||||
}; |
||||
return ( |
||||
<Stack> |
||||
{items.map((item, index) => ( |
||||
<div key={index}> |
||||
{renderItem( |
||||
item, |
||||
(newItem) => onChangeItem(index, newItem), |
||||
() => onDeleteItem(index) |
||||
)} |
||||
</div> |
||||
))} |
||||
<Button onClick={onAddItem} variant="secondary" size="md" icon="plus" aria-label="Add" type="button" /> |
||||
</Stack> |
||||
); |
||||
} |
||||
@ -1,30 +0,0 @@ |
||||
import { css } from '@emotion/css'; |
||||
import React from 'react'; |
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data'; |
||||
|
||||
import { useStyles2 } from '../../themes'; |
||||
|
||||
import { Stack } from './Stack'; |
||||
|
||||
interface EditorRowProps {} |
||||
|
||||
export const EditorRow = ({ children }: React.PropsWithChildren<EditorRowProps>) => { |
||||
const styles = useStyles2(getStyles); |
||||
|
||||
return ( |
||||
<div className={styles.root}> |
||||
<Stack gap={2}>{children}</Stack> |
||||
</div> |
||||
); |
||||
}; |
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => { |
||||
return { |
||||
root: css({ |
||||
padding: theme.spacing(1), |
||||
backgroundColor: theme.colors.background.secondary, |
||||
borderRadius: theme.shape.borderRadius(1), |
||||
}), |
||||
}; |
||||
}; |
||||
@ -1,13 +0,0 @@ |
||||
import React from 'react'; |
||||
|
||||
import { Stack } from './Stack'; |
||||
|
||||
interface EditorRowsProps {} |
||||
|
||||
export const EditorRows = ({ children }: React.PropsWithChildren<EditorRowsProps>) => { |
||||
return ( |
||||
<Stack gap={0.5} direction="column"> |
||||
{children} |
||||
</Stack> |
||||
); |
||||
}; |
||||
@ -1,25 +0,0 @@ |
||||
import { css } from '@emotion/css'; |
||||
import React, { ComponentProps } from 'react'; |
||||
|
||||
import { Switch } from '../Switch/Switch'; |
||||
|
||||
// Wrapper component around <Switch /> that properly aligns it in <EditorField />
|
||||
export const EditorSwitch: React.FC<ComponentProps<typeof Switch>> = (props) => { |
||||
const styles = getStyles(); |
||||
|
||||
return ( |
||||
<div className={styles.switch}> |
||||
<Switch {...props} /> |
||||
</div> |
||||
); |
||||
}; |
||||
|
||||
const getStyles = () => { |
||||
return { |
||||
switch: css({ |
||||
display: 'flex', |
||||
alignItems: 'center', |
||||
minHeight: 30, |
||||
}), |
||||
}; |
||||
}; |
||||
@ -1,10 +0,0 @@ |
||||
import React from 'react'; |
||||
|
||||
interface FlexItemProps { |
||||
grow?: number; |
||||
shrink?: number; |
||||
} |
||||
|
||||
export const FlexItem: React.FC<FlexItemProps> = ({ grow, shrink }) => { |
||||
return <div style={{ display: 'block', flexGrow: grow, flexShrink: shrink }} />; |
||||
}; |
||||
@ -1,89 +0,0 @@ |
||||
import { css, cx } from '@emotion/css'; |
||||
import React, { useState } from 'react'; |
||||
import { GroupBase } from 'react-select'; |
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data'; |
||||
|
||||
import { stylesFactory, useTheme2 } from '../../themes'; |
||||
import { Select } from '../Select/Select'; |
||||
import { SelectContainerProps, SelectContainer as BaseSelectContainer } from '../Select/SelectContainer'; |
||||
import { SelectCommonProps } from '../Select/types'; |
||||
|
||||
interface InlineSelectProps<T> extends SelectCommonProps<T> { |
||||
label?: string; |
||||
} |
||||
|
||||
export function InlineSelect<T>({ label: labelProp, ...props }: InlineSelectProps<T>) { |
||||
const theme = useTheme2(); |
||||
const [id] = useState(() => Math.random().toString(16).slice(2)); |
||||
const styles = getSelectStyles(theme); |
||||
const components = { |
||||
SelectContainer, |
||||
ValueContainer, |
||||
SingleValue: ValueContainer, |
||||
}; |
||||
|
||||
return ( |
||||
<div className={styles.root}> |
||||
{labelProp && ( |
||||
<label className={styles.label} htmlFor={id}> |
||||
{labelProp} |
||||
{':'} |
||||
</label> |
||||
)} |
||||
{/* @ts-ignore */} |
||||
<Select openMenuOnFocus inputId={id} {...props} components={components} /> |
||||
</div> |
||||
); |
||||
} |
||||
|
||||
const SelectContainer = <Option, isMulti extends boolean, Group extends GroupBase<Option>>( |
||||
props: SelectContainerProps<Option, isMulti, Group> |
||||
) => { |
||||
const { children } = props; |
||||
|
||||
const theme = useTheme2(); |
||||
const styles = getSelectStyles(theme); |
||||
|
||||
return ( |
||||
<BaseSelectContainer {...props} className={cx(props.className, styles.container)}> |
||||
{children} |
||||
</BaseSelectContainer> |
||||
); |
||||
}; |
||||
|
||||
const ValueContainer = <Option, isMulti extends boolean, Group extends GroupBase<Option>>( |
||||
props: SelectContainerProps<Option, isMulti, Group> |
||||
) => { |
||||
const { className, children } = props; |
||||
const theme = useTheme2(); |
||||
const styles = getSelectStyles(theme); |
||||
|
||||
return <div className={cx(className, styles.valueContainer)}>{children}</div>; |
||||
}; |
||||
|
||||
const getSelectStyles = stylesFactory((theme: GrafanaTheme2) => ({ |
||||
root: css({ |
||||
display: 'flex', |
||||
fontSize: 12, |
||||
alignItems: 'center', |
||||
}), |
||||
|
||||
label: css({ |
||||
color: theme.colors.text.secondary, |
||||
whiteSpace: 'nowrap', |
||||
}), |
||||
|
||||
container: css({ |
||||
background: 'none', |
||||
borderColor: 'transparent', |
||||
}), |
||||
|
||||
valueContainer: css({ |
||||
display: 'flex', |
||||
alignItems: 'center', |
||||
flex: 'initial', |
||||
color: theme.colors.text.secondary, |
||||
fontSize: 12, |
||||
}), |
||||
})); |
||||
@ -1,65 +0,0 @@ |
||||
import { ComponentMeta } from '@storybook/react'; |
||||
import React from 'react'; |
||||
|
||||
import { Stack } from '..'; |
||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; |
||||
import { Input } from '../Input/Input'; |
||||
import { Select } from '../Select/Select'; |
||||
|
||||
import { AccessoryButton } from './AccessoryButton'; |
||||
import { InputGroup } from './InputGroup'; |
||||
|
||||
const meta: ComponentMeta<typeof InputGroup> = { |
||||
title: 'Experimental/InputGroup', |
||||
component: InputGroup, |
||||
decorators: [withCenteredStory], |
||||
}; |
||||
|
||||
export function WithTextInputs() { |
||||
return ( |
||||
<InputGroup> |
||||
<Input placeholder="One" /> |
||||
<Input placeholder="Two" /> |
||||
</InputGroup> |
||||
); |
||||
} |
||||
|
||||
export function WithAccessoryButton() { |
||||
return ( |
||||
<InputGroup> |
||||
<Select value={selectOptions[0]} options={selectOptions} onChange={() => {}} /> |
||||
<AccessoryButton aria-label="Remove group by column" icon="times" variant="secondary" /> |
||||
</InputGroup> |
||||
); |
||||
} |
||||
|
||||
export function WithSelectsAndInput() { |
||||
return ( |
||||
<Stack direction="column"> |
||||
<InputGroup> |
||||
<Input invalid placeholder="LHS" /> |
||||
<Select value={comparitorOptions[0]} options={comparitorOptions} onChange={() => {}} /> |
||||
<Input placeholder="RHS" /> |
||||
</InputGroup> |
||||
<InputGroup> |
||||
<Input placeholder="LHS" /> |
||||
<Select invalid value={comparitorOptions[0]} options={comparitorOptions} onChange={() => {}} /> |
||||
<Input placeholder="RHS" /> |
||||
</InputGroup> |
||||
<InputGroup> |
||||
<Input placeholder="LHS" /> |
||||
<Select value={comparitorOptions[0]} options={comparitorOptions} onChange={() => {}} /> |
||||
<Input invalid placeholder="RHS" /> |
||||
</InputGroup> |
||||
</Stack> |
||||
); |
||||
} |
||||
|
||||
const selectOptions = [{ label: 'Prometheus', value: 1 }]; |
||||
const comparitorOptions = [ |
||||
{ label: '=', value: 1 }, |
||||
{ label: '<', value: 2 }, |
||||
{ label: '>', value: 3 }, |
||||
]; |
||||
|
||||
export default meta; |
||||
@ -1,80 +0,0 @@ |
||||
import { css, cx } from '@emotion/css'; |
||||
import React, { Children, cloneElement, isValidElement, ReactElement } from 'react'; |
||||
|
||||
import { useStyles2 } from '../../themes'; |
||||
|
||||
type Child = string | undefined | ReactElement<{ className?: string; invalid?: unknown }>; |
||||
interface InputGroupProps { |
||||
// we type the children props so we can test them later on
|
||||
children: Child | Child[]; |
||||
} |
||||
|
||||
export const InputGroup = ({ children }: InputGroupProps) => { |
||||
const styles = useStyles2(getStyles); |
||||
|
||||
// Find children with an invalid prop, and set a class name to raise their z-index so all
|
||||
// of the invalid border is visible
|
||||
const modifiedChildren = Children.map(children, (child) => { |
||||
if (isValidElement(child) && child.props.invalid) { |
||||
return cloneElement(child, { className: cx(child.props.className, styles.invalidChild) }); |
||||
} |
||||
|
||||
return child; |
||||
}); |
||||
|
||||
return <div className={styles.root}>{modifiedChildren}</div>; |
||||
}; |
||||
|
||||
// The later in the array the higher the priority for showing that element's border
|
||||
const borderPriority = [ |
||||
'' as const, // lowest priority
|
||||
'base' as const, |
||||
'hovered' as const, |
||||
'invalid' as const, |
||||
'focused' as const, // highest priority
|
||||
]; |
||||
|
||||
const getStyles = () => ({ |
||||
root: css({ |
||||
display: 'flex', |
||||
|
||||
// Style the direct children of the component
|
||||
'> *': { |
||||
'&:not(:first-child)': { |
||||
// Negative margin hides the double-border on adjacent selects
|
||||
marginLeft: -1, |
||||
}, |
||||
|
||||
'&:first-child': { |
||||
borderTopRightRadius: 0, |
||||
borderBottomRightRadius: 0, |
||||
}, |
||||
|
||||
'&:last-child': { |
||||
borderTopLeftRadius: 0, |
||||
borderBottomLeftRadius: 0, |
||||
}, |
||||
|
||||
'&:not(:first-child):not(:last-child)': { |
||||
borderRadius: 0, |
||||
}, |
||||
|
||||
//
|
||||
position: 'relative', |
||||
zIndex: borderPriority.indexOf('base'), |
||||
|
||||
// Adjacent borders are overlapping, so raise children up when hovering etc
|
||||
// so all that child's borders are visible.
|
||||
'&:hover': { |
||||
zIndex: borderPriority.indexOf('hovered'), |
||||
}, |
||||
'&:focus-within': { |
||||
zIndex: borderPriority.indexOf('focused'), |
||||
}, |
||||
}, |
||||
}), |
||||
|
||||
invalidChild: css({ |
||||
zIndex: borderPriority.indexOf('invalid'), |
||||
}), |
||||
}); |
||||
@ -1,40 +0,0 @@ |
||||
import { css, cx } from '@emotion/css'; |
||||
import React from 'react'; |
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data'; |
||||
|
||||
import { stylesFactory, useTheme2 } from '../../themes'; |
||||
|
||||
export interface SpaceProps { |
||||
v?: number; |
||||
h?: number; |
||||
layout?: 'block' | 'inline'; |
||||
} |
||||
|
||||
export const Space = (props: SpaceProps) => { |
||||
const theme = useTheme2(); |
||||
const styles = getStyles(theme, props); |
||||
|
||||
return <span className={cx(styles.wrapper)} />; |
||||
}; |
||||
|
||||
Space.defaultProps = { |
||||
v: 0, |
||||
h: 0, |
||||
layout: 'block', |
||||
}; |
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme2, props: SpaceProps) => ({ |
||||
wrapper: css([ |
||||
{ |
||||
paddingRight: theme.spacing(props.h ?? 0), |
||||
paddingBottom: theme.spacing(props.v ?? 0), |
||||
}, |
||||
props.layout === 'inline' && { |
||||
display: 'inline-block', |
||||
}, |
||||
props.layout === 'block' && { |
||||
display: 'block', |
||||
}, |
||||
]), |
||||
})); |
||||
@ -1,31 +0,0 @@ |
||||
import { css } from '@emotion/css'; |
||||
import React, { CSSProperties, useCallback } from 'react'; |
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data'; |
||||
|
||||
import { useStyles2 } from '../../themes'; |
||||
|
||||
interface StackProps { |
||||
direction?: CSSProperties['flexDirection']; |
||||
alignItems?: CSSProperties['alignItems']; |
||||
wrap?: boolean; |
||||
gap?: number; |
||||
flexGrow?: CSSProperties['flexGrow']; |
||||
} |
||||
|
||||
export const Stack = ({ children, ...props }: React.PropsWithChildren<StackProps>) => { |
||||
const styles = useStyles2(useCallback((theme) => getStyles(theme, props), [props])); |
||||
|
||||
return <div className={styles.root}>{children}</div>; |
||||
}; |
||||
|
||||
const getStyles = (theme: GrafanaTheme2, props: StackProps) => ({ |
||||
root: css({ |
||||
display: 'flex', |
||||
flexDirection: props.direction ?? 'row', |
||||
flexWrap: props.wrap ?? true ? 'wrap' : undefined, |
||||
alignItems: props.alignItems, |
||||
gap: theme.spacing(props.gap ?? 2), |
||||
flexGrow: props.flexGrow, |
||||
}), |
||||
}); |
||||
@ -1,13 +0,0 @@ |
||||
export { AccessoryButton } from './AccessoryButton'; |
||||
export { EditorFieldGroup } from './EditorFieldGroup'; |
||||
export { EditorHeader } from './EditorHeader'; |
||||
export { EditorField } from './EditorField'; |
||||
export { EditorRow } from './EditorRow'; |
||||
export { EditorList } from './EditorList'; |
||||
export { EditorRows } from './EditorRows'; |
||||
export { EditorSwitch } from './EditorSwitch'; |
||||
export { FlexItem } from './FlexItem'; |
||||
export { Stack } from './Stack'; |
||||
export { InlineSelect } from './InlineSelect'; |
||||
export { InputGroup } from './InputGroup'; |
||||
export { Space } from './Space'; |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue