Skip to content

Commit c35bd84

Browse files
authored
Grafana-UI: Refactor legacy inline form components (grafana#27660)
* Move styles to emotion * Move FormLabel to forms * Add mdx file * Setup InlineField * Add InlineField docs * Add grow prop * Add isKeyword prop * Add filled Field * Keep legacy form label * InlineFormLabel => InlineLabel * Update references * Add multiple elements example * Revert label * Add InlineFieldRow * Adjust label width * Export InlineFieldRow * Expand props from base components * Remove isKeyword prop * Remove fill prop * Update docs * Update docs [2]
1 parent d40bfd4 commit c35bd84

File tree

14 files changed

+378
-10
lines changed

14 files changed

+378
-10
lines changed

packages/grafana-ui/src/components/DataSourceSettings/DataSourceHttpSettings.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Input } from '../Forms/Legacy/Input/Input';
1010
import { Switch } from '../Forms/Legacy/Switch/Switch';
1111
import { Icon } from '../Icon/Icon';
1212
import { FormField } from '../FormField/FormField';
13-
import { FormLabel } from '../FormLabel/FormLabel';
13+
import { InlineFormLabel } from '../FormLabel/FormLabel';
1414
import { TagsInput } from '../TagsInput/TagsInput';
1515
import { useTheme } from '../../themes';
1616
import { HttpSettingsProps } from './types';
@@ -148,12 +148,12 @@ export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = props => {
148148
)}
149149
{dataSourceConfig.access === 'proxy' && (
150150
<div className="gf-form">
151-
<FormLabel
151+
<InlineFormLabel
152152
width={11}
153153
tooltip="Grafana Proxy deletes forwarded cookies by default. Specify cookies by name that should be forwarded to the data source."
154154
>
155155
Whitelisted Cookies
156-
</FormLabel>
156+
</InlineFormLabel>
157157
<TagsInput
158158
tags={dataSourceConfig.jsonData.keepCookies}
159159
onChange={cookies =>

packages/grafana-ui/src/components/FormField/FormField.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { InputHTMLAttributes, FunctionComponent } from 'react';
2-
import { FormLabel } from '../FormLabel/FormLabel';
3-
import { PopoverContent } from '../Tooltip/Tooltip';
42
import { cx } from 'emotion';
3+
import { InlineFormLabel } from '../FormLabel/FormLabel';
4+
import { PopoverContent } from '../Tooltip/Tooltip';
55

66
export interface Props extends InputHTMLAttributes<HTMLInputElement> {
77
label: string;
@@ -32,9 +32,9 @@ export const FormField: FunctionComponent<Props> = ({
3232
}) => {
3333
return (
3434
<div className={cx('form-field', className)}>
35-
<FormLabel width={labelWidth} tooltip={tooltip}>
35+
<InlineFormLabel width={labelWidth} tooltip={tooltip}>
3636
{label}
37-
</FormLabel>
37+
</InlineFormLabel>
3838
{inputEl || (
3939
<input type="text" className={`gf-form-input ${inputWidth ? `width-${inputWidth}` : ''}`} {...inputProps} />
4040
)}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Props } from "@storybook/addon-docs/blocks";
2+
import { InlineField } from "./InlineField";
3+
4+
# InlineField
5+
6+
A basic component for rendering form elements, like `Input`, `Select`, `Checkbox`, etc, inline together with `InlineLabel`. If the child element has `id` specified, the label's `htmlFor` attribute, pointing to the id, will be added.
7+
The width of the `InlineLabel` can be modified via `labelWidth` prop. If `tooltip` prop is provided, an info icon with supplied tooltip content will be rendered inside the label.
8+
9+
# Usage
10+
11+
```jsx
12+
<InlineField label="Inline field">
13+
<Input placeholder="Inline input" />
14+
</InlineField>
15+
```
16+
17+
<Props of={InlineField} />
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import React from 'react';
2+
import { action } from '@storybook/addon-actions';
3+
import { Input } from '../Input/Input';
4+
import { Select } from '../Select/Select';
5+
import { InlineField } from './InlineField';
6+
import mdx from './InlineField.mdx';
7+
8+
export default {
9+
title: 'Forms/InlineField',
10+
component: InlineField,
11+
parameters: {
12+
docs: {
13+
page: mdx,
14+
},
15+
},
16+
};
17+
18+
export const basic = () => {
19+
return (
20+
<InlineField label="Inline field">
21+
<Input placeholder="Inline input" />
22+
</InlineField>
23+
);
24+
};
25+
26+
export const withTooltip = () => {
27+
return (
28+
<InlineField label="Label" tooltip="Tooltip">
29+
<Input placeholder="Inline input" />
30+
</InlineField>
31+
);
32+
};
33+
34+
export const grow = () => {
35+
return (
36+
<InlineField label="Label" grow>
37+
<Input placeholder="Inline input" />
38+
</InlineField>
39+
);
40+
};
41+
42+
export const withSelect = () => {
43+
return (
44+
<InlineField label="Select option">
45+
<Select
46+
width={16}
47+
onChange={action('item selected')}
48+
options={[
49+
{ value: 1, label: 'One' },
50+
{ value: 2, label: 'Two' },
51+
]}
52+
/>
53+
</InlineField>
54+
);
55+
};
56+
57+
export const multiple = () => {
58+
return (
59+
<>
60+
<InlineField label="Field 1">
61+
<Input placeholder="Inline input" />
62+
</InlineField>
63+
<InlineField label="Field 2">
64+
<Input placeholder="Inline input" />
65+
</InlineField>
66+
<InlineField label="Field 3">
67+
<Input placeholder="Inline input" />
68+
</InlineField>
69+
</>
70+
);
71+
};
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import React, { FC } from 'react';
2+
import { cx, css } from 'emotion';
3+
import { GrafanaTheme } from '@grafana/data';
4+
import { useTheme } from '../../themes';
5+
import { InlineLabel } from './InlineLabel';
6+
import { PopoverContent } from '../Tooltip/Tooltip';
7+
import { FieldProps } from './Field';
8+
9+
export interface Props extends Omit<FieldProps, 'css' | 'horizontal' | 'description' | 'error'> {
10+
/** Content for the label's tooltip */
11+
tooltip?: PopoverContent;
12+
/** Custom width for the label */
13+
labelWidth?: number | 'auto';
14+
/** Make the field's child to fill the width of the row. Equivalent to setting `flex-grow:1` on the field */
15+
grow?: boolean;
16+
}
17+
18+
export const InlineField: FC<Props> = ({
19+
children,
20+
label,
21+
tooltip,
22+
labelWidth = 'auto',
23+
invalid,
24+
loading,
25+
disabled,
26+
className,
27+
grow,
28+
...htmlProps
29+
}) => {
30+
const theme = useTheme();
31+
const styles = getStyles(theme, grow);
32+
const child = React.Children.only(children);
33+
let inputId;
34+
35+
if (child) {
36+
inputId = (child as React.ReactElement<{ id?: string }>).props.id;
37+
}
38+
const labelElement =
39+
typeof label === 'string' ? (
40+
<InlineLabel width={labelWidth} tooltip={tooltip} htmlFor={inputId}>
41+
{label}
42+
</InlineLabel>
43+
) : (
44+
label
45+
);
46+
47+
return (
48+
<div className={cx(styles.container, className)} {...htmlProps}>
49+
{labelElement}
50+
{React.cloneElement(children, { invalid, disabled, loading })}
51+
</div>
52+
);
53+
};
54+
55+
InlineField.displayName = 'InlineField';
56+
57+
const getStyles = (theme: GrafanaTheme, grow?: boolean) => {
58+
return {
59+
container: css`
60+
display: flex;
61+
flex-direction: row;
62+
align-items: flex-start;
63+
text-align: left;
64+
position: relative;
65+
flex: ${grow ? 1 : 0} 0 auto;
66+
margin: 0 ${theme.spacing.xs} ${theme.spacing.xs} 0;
67+
`,
68+
wrapper: css`
69+
display: flex;
70+
width: 100%;
71+
`,
72+
73+
fillContainer: css`
74+
flex-grow: 1;
75+
`,
76+
};
77+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# InlineFieldRow
2+
Used to align multiple `InlineField` components in one row. The row will wrap if the width of the children exceeds its own. Equivalent to the div with `gf-form-inline` class name.
3+
Multiple `InlineFieldRow`s vertically stack on each other.
4+
5+
### Usage
6+
7+
```jsx
8+
<InlineFieldRow>
9+
<InlineField label="Label Row 1">
10+
<Input placeholder="Label" />
11+
</InlineField>
12+
<InlineField label="Label Row 1">
13+
<Input placeholder="Label" />
14+
</InlineField>
15+
</InlineFieldRow>
16+
```
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from 'react';
2+
import { InlineFieldRow } from './InlineFieldRow';
3+
import mdx from './InlineFieldRow.mdx';
4+
import { InlineField } from './InlineField';
5+
import { Input } from '../Input/Input';
6+
7+
export default {
8+
title: 'Forms/InlineFieldRow',
9+
component: InlineFieldRow,
10+
parameters: {
11+
docs: {
12+
page: mdx,
13+
},
14+
},
15+
};
16+
17+
export const single = () => {
18+
return (
19+
<div style={{ width: '100%' }}>
20+
<InlineFieldRow>
21+
<InlineField label="Label Row 1">
22+
<Input placeholder="Label" />
23+
</InlineField>
24+
<InlineField label="Label Row 1">
25+
<Input placeholder="Label" />
26+
</InlineField>
27+
</InlineFieldRow>
28+
<InlineFieldRow>
29+
<InlineField label="Label Row 2">
30+
<Input placeholder="Label" />
31+
</InlineField>
32+
<InlineField label="Label Row 2">
33+
<Input placeholder="Label" />
34+
</InlineField>
35+
<InlineField label="Label Row 2 Grow" grow>
36+
<Input placeholder="Label" />
37+
</InlineField>
38+
</InlineFieldRow>
39+
</div>
40+
);
41+
};
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import React, { FC, ReactNode, HTMLProps } from 'react';
2+
import { css, cx } from 'emotion';
3+
import { useStyles } from '../../themes';
4+
5+
export interface Props extends Omit<HTMLProps<HTMLDivElement>, 'css'> {
6+
children: ReactNode | ReactNode[];
7+
}
8+
9+
export const InlineFieldRow: FC<Props> = ({ children, className, ...htmlProps }) => {
10+
const styles = useStyles(getStyles);
11+
return (
12+
<div className={cx(styles.container, className)} {...htmlProps}>
13+
{children}
14+
</div>
15+
);
16+
};
17+
18+
const getStyles = () => {
19+
return {
20+
container: css`
21+
display: flex;
22+
flex-direction: row;
23+
flex-wrap: wrap;
24+
align-content: flex-start;
25+
`,
26+
};
27+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Props } from "@storybook/addon-docs/blocks";
2+
import { InlineLabel } from "./InlineLabel";
3+
4+
# InlineLabel
5+
6+
A horizontal variant of `Label`, primarily used in query editors. Can be combined with form components that expect a label, eg. `Input`, `Select`, `Checkbox`.
7+
If you need to add additional explanation, use the tooltip prop, which will render an info icon with tooltip inside the label.
8+
For query editor readability, the label text should be as short as possible (4 words or fewer).
9+
10+
# Usage
11+
```jsx
12+
<InlineLabel width="auto" tooltip="Tooltip content">
13+
Simple label
14+
</InlineLabel>
15+
```
16+
17+
<Props of={InlineLabel}/>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import { InlineLabel } from './InlineLabel';
3+
import mdx from './InlineLabel.mdx';
4+
5+
export default {
6+
title: 'Forms/InlineLabel',
7+
component: InlineLabel,
8+
parameters: {
9+
docs: {
10+
page: mdx,
11+
},
12+
},
13+
};
14+
15+
export const basic = () => {
16+
return <InlineLabel width="auto">Simple label</InlineLabel>;
17+
};
18+
19+
export const withTooltip = () => {
20+
return (
21+
<InlineLabel width="auto" tooltip="Tooltip content">
22+
Simple label
23+
</InlineLabel>
24+
);
25+
};

0 commit comments

Comments
 (0)