diff --git a/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx b/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx index b6899a4caa3..9e7b275c120 100644 --- a/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx +++ b/packages/grafana-ui/src/components/Input/AutoSizeInput.tsx @@ -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((props, r }, [placeholder, value, minWidth, maxWidth]); return ( - { - 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 + + { + 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" + /> + ); }); diff --git a/packages/grafana-ui/src/components/Input/AutoSizeInputContext.ts b/packages/grafana-ui/src/components/Input/AutoSizeInputContext.ts new file mode 100644 index 00000000000..328e382895b --- /dev/null +++ b/packages/grafana-ui/src/components/Input/AutoSizeInputContext.ts @@ -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'; diff --git a/packages/grafana-ui/src/components/Input/Input.tsx b/packages/grafana-ui/src/components/Input/Input.tsx index 8bd92e180d8..1240569cb57 100644 --- a/packages/grafana-ui/src/components/Input/Input.tsx +++ b/packages/grafana-ui/src/components/Input/Input.tsx @@ -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, '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((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((props, ref) => { const [prefixRef, prefixRect] = useMeasure(); const [suffixRef, suffixRect] = useMeasure(); + // 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 && ); return ( -
+
{!!addonBefore &&
{addonBefore}
} -
{prefix && (
@@ -65,14 +88,12 @@ export const Input = forwardRef((props, ref) => { }} /> - {(suffix || loading) && ( + {suffix && (
- {loading && } {suffix}
)}
- {!!addonAfter &&
{addonAfter}
}
);