mirror of
https://github.com/grafana/grafana.git
synced 2024-11-25 02:10:45 -06:00
GrafanaUI: Size AutoSizeInput correctly when used with suffix/prefix (#96575)
* GrafanaUI: Size AutoSizeInput correctly when used with suffix/prefix * comment * reword comment
This commit is contained in:
parent
efb420dd78
commit
4cc28c76fb
@ -3,6 +3,7 @@ import * as React from 'react';
|
||||
|
||||
import { measureText } from '../../utils/measureText';
|
||||
|
||||
import { AutoSizeInputContext } from './AutoSizeInputContext';
|
||||
import { Input, Props as InputProps } from './Input';
|
||||
|
||||
export interface Props extends InputProps {
|
||||
@ -44,34 +45,38 @@ export const AutoSizeInput = React.forwardRef<HTMLInputElement, Props>((props, r
|
||||
}, [placeholder, value, minWidth, maxWidth]);
|
||||
|
||||
return (
|
||||
<Input
|
||||
{...restProps}
|
||||
placeholder={placeholder}
|
||||
ref={ref}
|
||||
value={value.toString()}
|
||||
onChange={(event) => {
|
||||
if (onChange) {
|
||||
onChange(event);
|
||||
}
|
||||
setValue(event.currentTarget.value);
|
||||
}}
|
||||
width={inputWidth}
|
||||
onBlur={(event) => {
|
||||
if (onBlur) {
|
||||
onBlur(event);
|
||||
} else if (onCommitChange) {
|
||||
onCommitChange(event);
|
||||
}
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (onKeyDown) {
|
||||
onKeyDown(event);
|
||||
} else if (event.key === 'Enter' && onCommitChange) {
|
||||
onCommitChange(event);
|
||||
}
|
||||
}}
|
||||
data-testid={'autosize-input'}
|
||||
/>
|
||||
// Used to tell Input to increase the width properly of the input to fit the text.
|
||||
// See comment in Input.tsx for more details
|
||||
<AutoSizeInputContext.Provider value={true}>
|
||||
<Input
|
||||
{...restProps}
|
||||
placeholder={placeholder}
|
||||
ref={ref}
|
||||
value={value.toString()}
|
||||
onChange={(event) => {
|
||||
if (onChange) {
|
||||
onChange(event);
|
||||
}
|
||||
setValue(event.currentTarget.value);
|
||||
}}
|
||||
onBlur={(event) => {
|
||||
if (onBlur) {
|
||||
onBlur(event);
|
||||
} else if (onCommitChange) {
|
||||
onCommitChange(event);
|
||||
}
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (onKeyDown) {
|
||||
onKeyDown(event);
|
||||
} else if (event.key === 'Enter' && onCommitChange) {
|
||||
onCommitChange(event);
|
||||
}
|
||||
}}
|
||||
width={inputWidth}
|
||||
data-testid="autosize-input"
|
||||
/>
|
||||
</AutoSizeInputContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -0,0 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
// Used to tell Input to increase the width properly of the input to fit the text.
|
||||
// See comment in Input.tsx for more details
|
||||
export const AutoSizeInputContext = React.createContext(false);
|
||||
AutoSizeInputContext.displayName = 'AutoSizeInputContext';
|
@ -1,5 +1,5 @@
|
||||
import { css, cx } from '@emotion/css';
|
||||
import { forwardRef, HTMLProps, ReactNode } from 'react';
|
||||
import { forwardRef, HTMLProps, ReactNode, useContext } from 'react';
|
||||
import useMeasure from 'react-use/lib/useMeasure';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
@ -8,6 +8,8 @@ import { stylesFactory, useTheme2 } from '../../themes';
|
||||
import { getFocusStyle, sharedInputStyle } from '../Forms/commonStyles';
|
||||
import { Spinner } from '../Spinner/Spinner';
|
||||
|
||||
import { AutoSizeInputContext } from './AutoSizeInputContext';
|
||||
|
||||
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;
|
||||
@ -32,7 +34,17 @@ interface StyleDeps {
|
||||
}
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, Props>((props, ref) => {
|
||||
const { className, addonAfter, addonBefore, prefix, suffix, invalid, loading, width = 0, ...restProps } = props;
|
||||
const {
|
||||
className,
|
||||
addonAfter,
|
||||
addonBefore,
|
||||
prefix,
|
||||
suffix: suffixProp,
|
||||
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)).
|
||||
@ -41,13 +53,24 @@ export const Input = forwardRef<HTMLInputElement, Props>((props, ref) => {
|
||||
const [prefixRef, prefixRect] = useMeasure<HTMLDivElement>();
|
||||
const [suffixRef, suffixRect] = useMeasure<HTMLDivElement>();
|
||||
|
||||
// Yes, this is gross - When Input is being wrapped by AutoSizeInput, add the suffix/prefix width to the overall width
|
||||
// so the text content is not clipped. The intention is to make all the input's text appear without overflow/clipping,
|
||||
// which isn't normally how width is used in this component.
|
||||
// This behaviour is not controlled via a prop so we can limit API surface, and remove this as a 'breaking change' later
|
||||
// if a better solution is found.
|
||||
const isInAutoSizeInput = useContext(AutoSizeInputContext);
|
||||
const accessoriesWidth = (prefixRect.width || 0) + (suffixRect.width || 0);
|
||||
const finalWidth = isInAutoSizeInput && width ? width + accessoriesWidth / 8 : width;
|
||||
|
||||
const theme = useTheme2();
|
||||
const styles = getInputStyles({ theme, invalid: !!invalid, width });
|
||||
|
||||
const styles = getInputStyles({ theme, invalid: !!invalid, width: finalWidth });
|
||||
|
||||
const suffix = suffixProp || (loading && <Spinner inline={true} />);
|
||||
|
||||
return (
|
||||
<div className={cx(styles.wrapper, className)} data-testid={'input-wrapper'}>
|
||||
<div className={cx(styles.wrapper, className)} data-testid="input-wrapper">
|
||||
{!!addonBefore && <div className={styles.addon}>{addonBefore}</div>}
|
||||
|
||||
<div className={styles.inputWrapper}>
|
||||
{prefix && (
|
||||
<div className={styles.prefix} ref={prefixRef}>
|
||||
@ -65,14 +88,12 @@ export const Input = forwardRef<HTMLInputElement, Props>((props, ref) => {
|
||||
}}
|
||||
/>
|
||||
|
||||
{(suffix || loading) && (
|
||||
{suffix && (
|
||||
<div className={styles.suffix} ref={suffixRef}>
|
||||
{loading && <Spinner className={styles.loadingIndicator} inline={true} />}
|
||||
{suffix}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!!addonAfter && <div className={styles.addon}>{addonAfter}</div>}
|
||||
</div>
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user