mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
GrafanaUI: InputGroup: Fix invalid children borders (#56169)
* GrafanaUI: InputGroup: Fix invalid children borders * tidy story * zindex priority stack * clarify comment * fix inputgroup children type
This commit is contained in:
parent
a84edb274b
commit
d405f3a877
@ -0,0 +1,65 @@
|
||||
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,16 +1,39 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
import { css, cx } from '@emotion/css';
|
||||
import React, { Children, cloneElement, isValidElement, ReactElement } from 'react';
|
||||
|
||||
import { useStyles2 } from '../../themes';
|
||||
|
||||
interface InputGroupProps {}
|
||||
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: React.FC<InputGroupProps> = ({ children }) => {
|
||||
export const InputGroup = ({ children }: InputGroupProps) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return <div className={styles.root}>{children}</div>;
|
||||
// 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',
|
||||
@ -38,14 +61,20 @@ const getStyles = () => ({
|
||||
|
||||
//
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
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: 2,
|
||||
zIndex: borderPriority.indexOf('hovered'),
|
||||
},
|
||||
'&:focus-within': {
|
||||
zIndex: 2,
|
||||
zIndex: borderPriority.indexOf('focused'),
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
invalidChild: css({
|
||||
zIndex: borderPriority.indexOf('invalid'),
|
||||
}),
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user