Input: Width prop (#23615)

* Add width property

* Remove unused import

* Spelling mistake

* Add width to interface

* Make width optional

* Remove size

* Update snapshot

* Remove size from places

* Add size prop for button

* Update width

* Update snapshots
This commit is contained in:
Tobias Skarhed 2020-04-21 10:42:57 +02:00 committed by GitHub
parent a2d741f60f
commit 9bbc007cb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 94 additions and 131 deletions

View File

@ -207,6 +207,7 @@
"@grafana/slate-react": "0.22.9-grafana",
"@reduxjs/toolkit": "1.3.4",
"@torkelo/react-select": "3.0.8",
"@types/braintree__sanitize-url": "4.0.0",
"@types/md5": "^2.1.33",
"@types/react-loadable": "5.5.2",
"@types/react-virtualized-auto-sizer": "1.0.0",

View File

@ -57,7 +57,6 @@ export const withCustomValue = () => {
formatCreateLabel={val => onCreateLabel + val}
initialValue="Custom Initial Value"
onSelect={val => console.log(val)}
size="md"
/>
);
};

View File

@ -3,7 +3,6 @@ import { Icon } from '../Icon/Icon';
import RCCascader from 'rc-cascader';
import { Select } from '../Select/Select';
import { FormInputSize } from '../Forms/types';
import { Input } from '../Input/Input';
import { SelectableValue } from '@grafana/data';
import { css } from 'emotion';
@ -15,7 +14,8 @@ interface CascaderProps {
placeholder?: string;
options: CascaderOption[];
onSelect(val: string): void;
size?: FormInputSize;
/** Sets the width to a multiple of 8px. Should only be used with inline forms. Setting width of the container is preferred in other cases.*/
width?: number;
initialValue?: string;
allowCustomValue?: boolean;
/** A function for formatting the message for custom value creation. Only applies when allowCustomValue is set to true*/
@ -174,7 +174,7 @@ export class Cascader extends React.PureComponent<CascaderProps, CascaderState>
};
render() {
const { size, allowCustomValue, placeholder } = this.props;
const { allowCustomValue, placeholder, width } = this.props;
const { focusCascade, isSearching, searchableOptions, rcValue, activeLabel } = this.state;
return (
@ -187,9 +187,9 @@ export class Cascader extends React.PureComponent<CascaderProps, CascaderState>
onChange={this.onSelect}
onBlur={this.onBlur}
options={searchableOptions}
size={size}
onCreateOption={this.onCreateOption}
formatCreateLabel={this.props.formatCreateLabel}
width={width}
/>
) : (
<RCCascader
@ -206,7 +206,7 @@ export class Cascader extends React.PureComponent<CascaderProps, CascaderState>
>
<div className={disableDivFocus}>
<Input
size={size}
width={width}
placeholder={placeholder}
onBlur={this.onBlurCascade}
value={activeLabel}

View File

@ -70,24 +70,18 @@ const renderForm = (defaultValues?: Partial<FormDTO>) => (
<Legend>Edit user</Legend>
<Field label="Name" invalid={!!errors.name} error="Name is required">
<Input name="name" placeholder="Roger Waters" size="md" ref={register({ required: true })} />
<Input name="name" placeholder="Roger Waters" ref={register({ required: true })} />
</Field>
<Field label="Email" invalid={!!errors.email} error="E-mail is required">
<Input
id="email"
name="email"
placeholder="roger.waters@grafana.com"
size="md"
ref={register({ required: true })}
/>
<Input id="email" name="email" placeholder="roger.waters@grafana.com" ref={register({ required: true })} />
</Field>
<Field label="Username">
<Input name="username" placeholder="mr.waters" size="md" ref={register} />
<Input name="username" placeholder="mr.waters" ref={register} />
</Field>
<Field label="Nested object">
<Input name="nested.path" placeholder="Nested path" size="md" ref={register} />
<Input name="nested.path" placeholder="Nested path" ref={register} />
</Field>
<Field label="Textarea" invalid={!!errors.text} error="Text is required">
@ -185,7 +179,6 @@ export const asyncValidation = () => {
<Input
name="name"
placeholder="Roger Waters"
size="md"
ref={register({ validate: validateAsync(passAsyncValidation) })}
/>
</Field>

View File

@ -1,6 +1,7 @@
import React, { useEffect } from 'react';
import { useForm, Mode, OnSubmit, DeepPartial } from 'react-hook-form';
import { FormAPI } from '../../types';
import { css } from 'emotion';
interface FormProps<T> {
validateOn?: Mode;
@ -9,6 +10,8 @@ interface FormProps<T> {
defaultValues?: DeepPartial<T>;
onSubmit: OnSubmit<T>;
children: (api: FormAPI<T>) => React.ReactNode;
/** Sets max-width for container. Use it instead of setting individual widths on inputs.*/
maxWidth?: number;
}
export function Form<T>({
@ -18,6 +21,7 @@ export function Form<T>({
validateFieldsOnMount,
children,
validateOn = 'onSubmit',
maxWidth = 400,
}: FormProps<T>) {
const { handleSubmit, register, errors, control, triggerValidation, getValues, formState } = useForm<T>({
mode: validateOn,
@ -30,5 +34,14 @@ export function Form<T>({
}
}, []);
return <form onSubmit={handleSubmit(onSubmit)}>{children({ register, errors, control, getValues, formState })}</form>;
return (
<form
className={css`
max-width: ${maxWidth}px;
`}
onSubmit={handleSubmit(onSubmit)}
>
{children({ register, errors, control, getValues, formState })}
</form>
);
}

View File

@ -12,11 +12,11 @@ Used for regular text input. For an array of data or tree-structured data, consi
To add more context to the input you can add either text or an icon before or after the input. You can use the `prefix` and `suffix` props for this. Try some examples in the canvas!
```jsx
<Input prefix={<Icon name="search" />} size="sm" />
<Input prefix={<Icon name="search" />} />
```
<Preview>
<Input prefix={<Icon name="search" />} size="sm" />
<Input prefix={<Icon name="search" />} />
</Preview>
## Usage in forms with Field

View File

@ -50,6 +50,7 @@ export const simple = () => {
const VISUAL_GROUP = 'Visual options';
// ---
const width = number('Width', 0, undefined, VISUAL_GROUP);
const placeholder = text('Placeholder', 'Enter your name here...', VISUAL_GROUP);
const before = boolean('Addon before', false, VISUAL_GROUP);
const after = boolean('Addon after', false, VISUAL_GROUP);
@ -84,6 +85,7 @@ export const simple = () => {
<div style={{ width: containerWidth }}>
<Input
disabled={disabled}
width={width}
prefix={prefixEl}
invalid={invalid}
suffix={suffixEl}

View File

@ -1,13 +1,14 @@
import React, { HTMLProps, ReactNode } from 'react';
import { GrafanaTheme } from '@grafana/data';
import { css, cx } from 'emotion';
import { getFocusStyle, inputSizes, sharedInputStyle } from '../Forms/commonStyles';
import { getFocusStyle, sharedInputStyle } from '../Forms/commonStyles';
import { stylesFactory, useTheme } from '../../themes';
import { Icon } from '../Icon/Icon';
import { useClientRect } from '../../utils/useClientRect';
import { FormInputSize } from '../Forms/types';
export interface Props extends Omit<HTMLProps<HTMLInputElement>, 'prefix' | 'size'> {
/** Sets the width to a multiple of 8px. Should only be used with inline forms. Setting width of the container is preferred in other cases.*/
width?: number;
/** Show an invalid state around the input */
invalid?: boolean;
/** Show an icon as a prefix in the input */
@ -20,15 +21,15 @@ export interface Props extends Omit<HTMLProps<HTMLInputElement>, 'prefix' | 'siz
addonBefore?: ReactNode;
/** Add a component as an addon after the input */
addonAfter?: ReactNode;
size?: FormInputSize;
}
interface StyleDeps {
theme: GrafanaTheme;
invalid: boolean;
width?: number;
}
export const getInputStyles = stylesFactory(({ theme, invalid = false }: StyleDeps) => {
export const getInputStyles = stylesFactory(({ theme, invalid = false, width }: StyleDeps) => {
const { palette, colors } = theme;
const borderRadius = theme.border.radius.sm;
const height = theme.spacing.formInputHeight;
@ -56,7 +57,7 @@ export const getInputStyles = stylesFactory(({ theme, invalid = false }: StyleDe
css`
label: input-wrapper;
display: flex;
width: 100%;
width: ${width ? `${8 * width}px` : '100%'};
height: ${height}px;
border-radius: ${borderRadius};
&:hover {
@ -213,7 +214,7 @@ export const getInputStyles = stylesFactory(({ theme, invalid = false }: StyleDe
});
export const Input = React.forwardRef<HTMLInputElement, Props>((props, ref) => {
const { className, addonAfter, addonBefore, prefix, suffix, invalid, loading, size = 'auto', ...restProps } = props;
const { className, addonAfter, addonBefore, prefix, suffix, invalid, loading, width = 0, ...restProps } = props;
/**
* Prefix & suffix are positioned absolutely within inputWrapper. We use client rects below to apply correct padding to the input
* when prefix/suffix is larger than default (28px = 16px(icon) + 12px(left/right paddings)).
@ -223,10 +224,10 @@ export const Input = React.forwardRef<HTMLInputElement, Props>((props, ref) => {
const [suffixRect, suffixRef] = useClientRect<HTMLDivElement>();
const theme = useTheme();
const styles = getInputStyles({ theme, invalid: !!invalid });
const styles = getInputStyles({ theme, invalid: !!invalid, width });
return (
<div className={cx(styles.wrapper, inputSizes()[size], className)}>
<div className={cx(styles.wrapper, className)}>
{!!addonBefore && <div className={styles.addon}>{addonBefore}</div>}
<div className={styles.inputWrapper}>

View File

@ -29,7 +29,7 @@ Used for horizontally aligning several elements (e.g. Button, Select) with a pre
<Preview>
<HorizontalGroup>
<Select
size="sm"
width={25}
onChange={() => {}}
options={[
{ value: 1, label: "Option 1" },
@ -37,7 +37,7 @@ Used for horizontally aligning several elements (e.g. Button, Select) with a pre
]}
/>
<Select
size="sm"
width={25}
onChange={() => {}}
options={[
{ value: 1, label: "Option 1" },
@ -45,7 +45,7 @@ Used for horizontally aligning several elements (e.g. Button, Select) with a pre
]}
/>
<Select
size="sm"
width={25}
onChange={() => {}}
options={[
{ value: 1, label: "Option 1" },
@ -69,7 +69,7 @@ Used for vertically aligning several elements (e.g. Button, Select) with a prede
<Preview>
<VerticalGroup justify="center">
<Select
size="sm"
width={25}
onChange={() => {}}
options={[
{ value: 1, label: "Option 1" },
@ -77,7 +77,7 @@ Used for vertically aligning several elements (e.g. Button, Select) with a prede
]}
/>
<Select
size="sm"
width={25}
onChange={() => {}}
options={[
{ value: 1, label: "Option 1" },
@ -85,7 +85,7 @@ Used for vertically aligning several elements (e.g. Button, Select) with a prede
]}
/>
<Select
size="sm"
width={25}
onChange={() => {}}
options={[
{ value: 1, label: "Option 1" },

View File

@ -80,7 +80,6 @@ const basicSelectAsync = () => {
onChange={v => {
setValue(v);
}}
size="md"
/>
);
};
@ -117,7 +116,6 @@ const multiSelect = () => {
onChange={v => {
setValue(v);
}}
size="md"
/>
</>
);

View File

@ -3,7 +3,7 @@ import { Select, AsyncSelect, MultiSelect, AsyncMultiSelect } from './Select';
import { withCenteredStory, withHorizontallyCenteredStory } from '../../utils/storybook/withCenteredStory';
import { SelectableValue } from '@grafana/data';
import { getAvailableIcons, IconName } from '../../types';
import { select, boolean } from '@storybook/addon-knobs';
import { select, boolean, number } from '@storybook/addon-knobs';
import { Icon } from '../Icon/Icon';
import { Button } from '../Button';
import { ButtonSelect } from './ButtonSelect';
@ -50,6 +50,7 @@ const getKnobs = () => {
const VISUAL_GROUP = 'Visual options';
// ---
const prefix = select('Prefix', prefixSuffixOpts, null, VISUAL_GROUP);
const width = number('Width', 0, undefined, VISUAL_GROUP);
let prefixEl: any = prefix;
if (prefix && prefix.match(/icon-/g)) {
@ -57,6 +58,7 @@ const getKnobs = () => {
}
return {
width,
disabled,
invalid,
loading,
@ -67,6 +69,7 @@ const getKnobs = () => {
const getDynamicProps = () => {
const knobs = getKnobs();
return {
width: knobs.width,
disabled: knobs.disabled,
isLoading: knobs.loading,
invalid: knobs.invalid,
@ -85,7 +88,6 @@ export const basic = () => {
onChange={v => {
setValue(v);
}}
size="md"
{...getDynamicProps()}
/>
</>
@ -105,7 +107,6 @@ export const basicSelectPlainValue = () => {
onChange={v => {
setValue(v.value);
}}
size="md"
{...getDynamicProps()}
/>
</>
@ -138,7 +139,6 @@ export const SelectWithOptionDescriptions = () => {
onChange={v => {
setValue(v.value);
}}
size="md"
{...getDynamicProps()}
/>
</>
@ -159,7 +159,6 @@ export const multiPlainValue = () => {
onChange={v => {
setValue(v.map((v: any) => v.value));
}}
size="md"
{...getDynamicProps()}
/>
</>
@ -177,7 +176,6 @@ export const multiSelect = () => {
onChange={v => {
setValue(v);
}}
size="md"
{...getDynamicProps()}
/>
</>
@ -195,7 +193,6 @@ export const multiSelectAsync = () => {
onChange={v => {
setValue(v);
}}
size="md"
allowCustomValue
{...getDynamicProps()}
/>
@ -212,7 +209,6 @@ export const buttonSelect = () => {
onChange={v => {
setValue(v);
}}
size="md"
allowCustomValue
icon={icon}
{...getDynamicProps()}
@ -231,7 +227,6 @@ export const basicSelectAsync = () => {
onChange={v => {
setValue(v);
}}
size="md"
{...getDynamicProps()}
/>
);
@ -247,7 +242,6 @@ export const customizedControl = () => {
onChange={v => {
setValue(v);
}}
size="md"
renderControl={React.forwardRef(({ isOpen, value, ...otherProps }, ref) => {
return (
<Button {...otherProps} ref={ref}>
@ -266,14 +260,13 @@ export const autoMenuPlacement = () => {
return (
<>
<div style={{ height: '95vh', display: 'flex', alignItems: 'flex-end' }}>
<div style={{ width: '100%', height: '95vh', display: 'flex', alignItems: 'flex-end' }}>
<Select
options={generateOptions()}
value={value}
onChange={v => {
setValue(v);
}}
size="md"
{...getDynamicProps()}
/>
</div>
@ -293,7 +286,6 @@ export const customValueCreation = () => {
onChange={v => {
setValue(v);
}}
size="md"
allowCustomValue
onCreateOption={v => {
const customValue: SelectableValue<string> = { value: kebabCase(v), label: v };

View File

@ -1,5 +1,4 @@
import React, { useCallback } from 'react';
import { deprecationWarning } from '@grafana/data';
// @ts-ignore
import { default as ReactSelect } from '@torkelo/react-select';
// @ts-ignore
@ -11,7 +10,6 @@ import { default as AsyncCreatable } from '@torkelo/react-select/async-creatable
import { Icon } from '../Icon/Icon';
import { css, cx } from 'emotion';
import { inputSizesPixels } from '../Forms/commonStyles';
import resetSelectStyles from './resetSelectStyles';
import { SelectMenu, SelectMenuOptions } from './SelectMenu';
import { IndicatorsContainer } from './IndicatorsContainer';
@ -100,7 +98,6 @@ export function SelectBase<T>({
placeholder = 'Choose',
prefix,
renderControl,
size = 'auto',
tabSelectsValue = true,
className,
value,
@ -178,13 +175,6 @@ export function SelectBase<T>({
value: isMulti ? selectedValue : selectedValue[0],
};
// width property is deprecated in favor of size or className
let widthClass = '';
if (width) {
deprecationWarning('Select', 'width property', 'size or className');
widthClass = 'width-' + width;
}
if (allowCustomValue) {
ReactSelectComponent = Creatable;
creatableProps.formatCreateLabel = formatCreateLabel ?? ((input: string) => `Create: ${input}`);
@ -275,10 +265,10 @@ export function SelectBase<T>({
}),
container: () => ({
position: 'relative',
width: inputSizesPixels(size),
width: width ? `${8 * width}px` : '100%',
}),
}}
className={cx('select-container', widthClass, className)}
className={cx('select-container', className)}
{...commonSelectProps}
{...creatableProps}
{...asyncSelectProps}

View File

@ -1,6 +1,5 @@
import { SelectableValue } from '@grafana/data';
import React from 'react';
import { FormInputSize } from '../Forms/types';
export type SelectValue<T> = T | SelectableValue<T> | T[] | Array<SelectableValue<T>>;
@ -45,9 +44,9 @@ export interface SelectCommonProps<T> {
prefix?: JSX.Element | string | null;
/** Use a custom element to control Select. A proper ref to the renderControl is needed if 'portal' isn't set to null*/
renderControl?: ControlComponent<T>;
size?: FormInputSize;
tabSelectsValue?: boolean;
value?: SelectValue<T>;
/** Sets the width to a multiple of 8px. Should only be used with inline forms. Setting width of the container is preferred in other cases.*/
width?: number;
}

View File

@ -25,7 +25,6 @@ TimeZonePickerStories.add('default', () => {
action('on selected')(newValue);
updateValue({ value: newValue });
}}
size="sm"
/>
);
}}

View File

@ -1,16 +1,15 @@
import React, { FC } from 'react';
import { getTimeZoneGroups } from '@grafana/data';
import { Cascader } from '../index';
import { FormInputSize } from '../Forms/types';
interface Props {
value: string;
size?: FormInputSize;
width?: number;
onChange: (newValue: string) => void;
}
export const TimeZonePicker: FC<Props> = ({ onChange, value, size = 'md' }) => {
export const TimeZonePicker: FC<Props> = ({ onChange, value, width }) => {
const timeZoneGroups = getTimeZoneGroups();
const groupOptions = timeZoneGroups.map(group => {
@ -41,7 +40,7 @@ export const TimeZonePicker: FC<Props> = ({ onChange, value, size = 'md' }) => {
options={groupOptions}
initialValue={selectedValue?.value}
onSelect={(newValue: string) => onChange(newValue)}
size={size}
width={width}
placeholder="Select timezone"
/>
);

View File

@ -195,7 +195,7 @@ exports[`TimePicker renders buttons correctly 1`] = `
"listItem": "-1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.3)",
},
"spacing": Object {
"d": "14px",
"d": "16px",
"formButtonHeight": 32,
"formFieldsetMargin": "16px",
"formInputAffixPaddingHorizontal": "4px",
@ -511,7 +511,7 @@ exports[`TimePicker renders content correctly after beeing open 1`] = `
"listItem": "-1px -1px 0 0 hsla(0, 0%, 100%, 0.1), 1px 1px 0 0 rgba(0, 0, 0, 0.3)",
},
"spacing": Object {
"d": "14px",
"d": "16px",
"formButtonHeight": 32,
"formFieldsetMargin": "16px",
"formInputAffixPaddingHorizontal": "4px",

View File

@ -25,7 +25,7 @@ export function ValuePicker<T>({
options,
onChange,
variant,
size,
size = 'sm',
isFullWidth = true,
}: ValuePickerProps<T>) {
const [isPicking, setIsPicking] = useState(false);

View File

@ -75,7 +75,7 @@ const theme: GrafanaThemeCommons = {
},
spacing: {
insetSquishMd: '4px 8px',
d: '14px',
d: '16px',
xxs: '2px',
xs: '4px',
sm: '8px',

View File

@ -15,8 +15,8 @@ export const FilterInput: FC<Props> = props => (
// Replaces the usage of ref
autoFocus
prefix={<Icon name="search" />}
width={40}
type="text"
size="md"
value={props.value ? unEscapeStringFromRegex(props.value) : ''}
onChange={event => props.onChange(escapeStringForRegex(event.currentTarget.value))}
placeholder={props.placeholder ?? ''}

View File

@ -3,14 +3,13 @@ import { debounce } from 'lodash';
import { useAsyncFn } from 'react-use';
import { SelectableValue } from '@grafana/data';
import { AsyncSelect } from '@grafana/ui';
import { FormInputSize } from '@grafana/ui/src/components/Forms/types';
import { backendSrv } from 'app/core/services/backend_srv';
import { DashboardSearchHit, DashboardDTO } from 'app/types';
export interface Props {
onSelected: (dashboard: DashboardDTO) => void;
currentDashboard?: SelectableValue<number>;
size?: FormInputSize;
width?: number;
isClearable?: boolean;
invalid?: boolean;
disabled?: boolean;
@ -29,7 +28,7 @@ const getDashboards = (query = '') => {
export const DashboardPicker: FC<Props> = ({
onSelected,
currentDashboard,
size = 'md',
width,
isClearable = false,
invalid,
disabled,
@ -43,7 +42,7 @@ export const DashboardPicker: FC<Props> = ({
return (
<AsyncSelect
size={size}
width={width}
isLoading={state.loading}
isClearable={isClearable}
defaultOptions={true}

View File

@ -1,21 +1,20 @@
import React, { FC } from 'react';
import { OrgRole } from '@grafana/data';
import { Select, FormInputSize } from '@grafana/ui';
import { Select } from '@grafana/ui';
interface Props {
value: OrgRole;
size?: FormInputSize;
onChange: (role: OrgRole) => void;
}
const options = Object.keys(OrgRole).map(key => ({ label: key, value: key }));
export const OrgRolePicker: FC<Props> = ({ value, onChange, size }) => (
export const OrgRolePicker: FC<Props> = ({ value, onChange, ...restProps }) => (
<Select
size={size}
value={value}
options={options}
onChange={val => onChange(val.value as OrgRole)}
placeholder="Choose role..."
{...restProps}
/>
);

View File

@ -37,15 +37,15 @@ const UserCreatePage: React.FC<UserCreatePageProps> = ({ navModel, updateLocatio
return (
<>
<Field label="Name" required invalid={!!errors.name} error={!!errors.name && 'Name is required'}>
<Input name="name" size="md" ref={register({ required: true })} />
<Input name="name" ref={register({ required: true })} />
</Field>
<Field label="E-mail">
<Input name="email" size="md" ref={register} />
<Input name="email" ref={register} />
</Field>
<Field label="Username">
<Input name="login" size="md" ref={register} />
<Input name="login" ref={register} />
</Field>
<Field
label="Password"
@ -54,7 +54,6 @@ const UserCreatePage: React.FC<UserCreatePageProps> = ({ navModel, updateLocatio
error={!!errors.password && 'Password is required and must contain at least 4 characters'}
>
<Input
size="md"
type="password"
name="password"
ref={register({

View File

@ -43,7 +43,7 @@ const UserListAdminPageUnConnected: React.FC<Props> = props => {
<div>
<HorizontalGroup justify="space-between">
<Input
size="md"
width={40}
type="text"
placeholder="Search user by login,email or name"
tabIndex={1}

View File

@ -267,7 +267,6 @@ export class UserProfileRow extends PureComponent<UserProfileRowProps, UserProfi
<td className="width-25" colSpan={2}>
{this.state.editing ? (
<Input
size="md"
type={inputType}
defaultValue={value}
onBlur={this.onInputBlur}

View File

@ -90,7 +90,6 @@ export const OverrideFieldConfigEditor: React.FC<Props> = props => {
<ValuePicker
icon="plus"
label="Add override"
size="md"
variant="secondary"
options={fieldMatchersUI
.list()

View File

@ -161,7 +161,6 @@ export const OverrideEditor: React.FC<OverrideEditorProps> = ({
<ValuePicker
label="Add override property"
variant="secondary"
size="sm"
icon="plus"
options={configPropertiesOptions}
onChange={o => {

View File

@ -52,7 +52,6 @@ export class TransformationsEditor extends React.PureComponent<Props> {
return (
<ValuePicker
size="md"
variant="secondary"
label="Add transformation"
options={availableTransformers}

View File

@ -2,7 +2,7 @@
exports[`MetaInfoText should render component 1`] = `
<div
className="css-1u65mbf"
className="css-1jdkzq2"
>
<Memo(MetaInfoItem)
key="0-label"

View File

@ -85,7 +85,6 @@ class DashboardImportUnConnected extends PureComponent<Props> {
{({ register, errors }) => (
<Field invalid={!!errors.gcomDashboard} error={errors.gcomDashboard && errors.gcomDashboard.message}>
<Input
size="md"
name="gcomDashboard"
placeholder="Grafana.com dashboard url or id"
type="text"

View File

@ -1,7 +1,6 @@
import React, { FC, useEffect, useState } from 'react';
import {
Button,
Forms,
FormAPI,
FormsOnSubmit,
HorizontalGroup,
@ -9,6 +8,7 @@ import {
Input,
Field,
InputControl,
Legend,
} from '@grafana/ui';
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
import DataSourcePicker from 'app/core/components/Select/DataSourcePicker';
@ -51,11 +51,10 @@ export const ImportDashboardForm: FC<Props> = ({
return (
<>
<Forms.Legend>Options</Forms.Legend>
<Legend>Options</Legend>
<Field label="Name" invalid={!!errors.title} error={errors.title && errors.title.message}>
<Input
name="title"
size="md"
type="text"
ref={register({
required: 'Name is required',
@ -83,18 +82,13 @@ export const ImportDashboardForm: FC<Props> = ({
<>
{!uidReset ? (
<Input
size="md"
name="uid"
disabled
ref={register({ validate: async (v: string) => await validateUid(v) })}
addonAfter={!uidReset && <Button onClick={onUidReset}>Change uid</Button>}
/>
) : (
<Input
size="md"
name="uid"
ref={register({ required: true, validate: async (v: string) => await validateUid(v) })}
/>
<Input name="uid" ref={register({ required: true, validate: async (v: string) => await validateUid(v) })} />
)}
</>
</Field>
@ -129,12 +123,7 @@ export const ImportDashboardForm: FC<Props> = ({
invalid={errors.constants && !!errors.constants[index]}
key={constantIndex}
>
<Input
ref={register({ required: true })}
name={`${constantIndex}`}
size="md"
defaultValue={input.value}
/>
<Input ref={register({ required: true })} name={`${constantIndex}`} defaultValue={input.value} />
</Field>
);
})}

View File

@ -55,7 +55,6 @@ export const NewOrgPage: FC<PropsWithState> = ({ navModel }) => {
<>
<Field label="Organization name" invalid={!!errors.name} error={errors.name && errors.name.message}>
<Input
size="md"
placeholder="Org. name"
name="name"
ref={register({

View File

@ -63,10 +63,10 @@ export const UserInviteForm: FC<Props> = ({ updateLocation }) => {
error={!!errors.loginOrEmail && 'Email or Username is required'}
label="Email or Username"
>
<Input size="md" name="loginOrEmail" placeholder="email@example.com" ref={register({ required: true })} />
<Input name="loginOrEmail" placeholder="email@example.com" ref={register({ required: true })} />
</Field>
<Field invalid={!!errors.name} label="Name">
<Input size="md" name="name" placeholder="(optional)" ref={register} />
<Input name="name" placeholder="(optional)" ref={register} />
</Field>
<Field invalid={!!errors.role} label="Role">
<InputControl as={RadioButtonGroup} control={control} options={roles} name="role" />

View File

@ -66,20 +66,19 @@ export const SignupForm: FC<Props> = props => {
<>
{verifyEmailEnabled && (
<Field label="Email verification code (sent to your email)">
<Input name="code" size="md" ref={register} placeholder="Code" />
<Input name="code" ref={register} placeholder="Code" />
</Field>
)}
{!autoAssignOrg && (
<Field label="Org. name">
<Input size="md" name="orgName" placeholder="Org. name" ref={register} />
<Input name="orgName" placeholder="Org. name" ref={register} />
</Field>
)}
<Field label="Your name">
<Input size="md" name="name" placeholder="(optional)" ref={register} />
<Input name="name" placeholder="(optional)" ref={register} />
</Field>
<Field label="Email" invalid={!!errors.email} error={!!errors.email && errors.email.message}>
<Input
size="md"
name="email"
type="email"
placeholder="Email"
@ -94,7 +93,6 @@ export const SignupForm: FC<Props> = props => {
</Field>
<Field label="Password" invalid={!!errors.password} error={!!errors.password && errors.password.message}>
<Input
size="md"
name="password"
type="password"
placeholder="Password"

View File

@ -11,6 +11,7 @@ interface SearchFieldProps extends Omit<React.InputHTMLAttributes<HTMLInputEleme
onChange: (query: string) => void;
onKeyDown?: (e: React.KeyboardEvent<HTMLInputElement>) => void;
clearable?: boolean;
width?: number;
}
const getSearchFieldStyles = (theme: GrafanaTheme) => ({

View File

@ -58,7 +58,6 @@ export const SearchResultsFilter: FC<Props> = ({
) : (
<HorizontalGroup spacing="md">
<Select
size="sm"
placeholder="Filter by starred"
key={starredFilterOptions?.find(f => f.value === selectedStarredFilter)?.label}
options={starredFilterOptions}
@ -66,7 +65,6 @@ export const SearchResultsFilter: FC<Props> = ({
/>
<TagFilter
size="sm"
placeholder="Filter by tag"
tags={selectedTagFilter}
tagOptions={searchSrv.getDashboardTags}

View File

@ -75,7 +75,6 @@ const SingupInvitedPageUnconnected: FC<DispatchProps & ConnectedProps> = ({ code
<>
<Field invalid={!!errors.email} error={!!errors.email && errors.email.message} label="Email">
<Input
size="md"
placeholder="email@example.com"
name="email"
ref={register({
@ -88,19 +87,13 @@ const SingupInvitedPageUnconnected: FC<DispatchProps & ConnectedProps> = ({ code
/>
</Field>
<Field invalid={!!errors.name} error={!!errors.name && errors.name.message} label="Name">
<Input size="md" placeholder="Name (optional)" name="name" ref={register} />
<Input placeholder="Name (optional)" name="name" ref={register} />
</Field>
<Field invalid={!!errors.username} error={!!errors.username && errors.username.message} label="Username">
<Input
size="md"
placeholder="Username"
name="username"
ref={register({ required: 'Username is required' })}
/>
<Input placeholder="Username" name="username" ref={register({ required: 'Username is required' })} />
</Field>
<Field invalid={!!errors.password} error={!!errors.password && errors.password.message} label="Password">
<Input
size="md"
type="password"
placeholder="Password"
name="password"

View File

@ -163,7 +163,13 @@ export class PromQueryEditor extends PureComponent<Props, State> {
<div className="gf-form">
<div className="gf-form-label width-7">Format</div>
<Select isSearchable={false} options={FORMAT_OPTIONS} onChange={this.onFormatChange} value={formatOption} />
<Select
width={16}
isSearchable={false}
options={FORMAT_OPTIONS}
onChange={this.onFormatChange}
value={formatOption}
/>
<Switch label="Instant" checked={instant} onChange={this.onInstantChange} />
<FormLabel width={10} tooltip="Link to Graph in Prometheus">

View File

@ -148,6 +148,7 @@ exports[`Render PromQueryEditor with basic options should render 1`] = `
"value": "time_series",
}
}
width={16}
/>
<Switch
checked={false}

View File

@ -29,7 +29,7 @@ $space-md: 16px !default;
$space-lg: 24px !default;
$space-xl: 32px !default;
$spacer: 14px !default;
$spacer: 16px !default;
$spacer-x: $spacer !default;
$spacer-y: $spacer !default;
$spacers: (

View File

@ -8,20 +8,20 @@
// widths
@for $i from 1 through 30 {
.width-#{$i} {
width: ($spacer * $i) - $space-xs !important;
width: ($spacer * $i) !important;
}
}
@for $i from 1 through 30 {
.max-width-#{$i} {
max-width: ($spacer * $i) - $space-xs !important;
max-width: ($spacer * $i) !important;
flex-grow: 1;
}
}
@for $i from 1 through 30 {
.min-width-#{$i} {
min-width: ($spacer * $i) - $space-xs !important;
min-width: ($spacer * $i) !important;
}
}