From d5a0f719dffaa37cf14eb7092969e4c7d4791f6d Mon Sep 17 00:00:00 2001 From: Josh Hunt Date: Tue, 19 Oct 2021 12:29:33 +0100 Subject: [PATCH] Grafana-UI: Move Select container styles to outer most container (#40611) * Grafana-UI: Move Select container styles to outer most container * Update packages/grafana-ui/src/components/Select/Container.tsx Co-authored-by: Alex Khomenko * Rename file Co-authored-by: Alex Khomenko --- .../src/components/Forms/commonStyles.ts | 90 ++++++++++--------- .../src/components/Select/Container.tsx | 68 ++++++++++++++ .../src/components/Select/InputControl.tsx | 29 +++--- .../src/components/Select/Select.story.tsx | 1 - .../src/components/Select/SelectBase.tsx | 2 + .../src/components/Select/SelectContainer.tsx | 74 +++++++++++++++ 6 files changed, 204 insertions(+), 60 deletions(-) create mode 100644 packages/grafana-ui/src/components/Select/Container.tsx create mode 100644 packages/grafana-ui/src/components/Select/SelectContainer.tsx diff --git a/packages/grafana-ui/src/components/Forms/commonStyles.ts b/packages/grafana-ui/src/components/Forms/commonStyles.ts index a0ba0132352..04b00612770 100644 --- a/packages/grafana-ui/src/components/Forms/commonStyles.ts +++ b/packages/grafana-ui/src/components/Forms/commonStyles.ts @@ -1,4 +1,4 @@ -import { css } from '@emotion/css'; +import { css, cx } from '@emotion/css'; import { GrafanaTheme, GrafanaTheme2 } from '@grafana/data'; import { focusCss } from '../../themes/mixins'; import { ComponentSize } from '../../types/size'; @@ -19,51 +19,59 @@ export const sharedInputStyle = (theme: GrafanaTheme2, invalid = false) => { // Need to colors without alpha channel const autoFillBorder = theme.isDark ? '#2e2f35' : '#bab4ca'; - return css` - background: ${background}; - line-height: ${theme.typography.body.lineHeight}; - font-size: ${theme.typography.size.md}; - color: ${textColor}; - border: 1px solid ${borderColor}; - padding: ${theme.spacing(0, 1, 0, 1)}; + return cx( + inputPadding(theme), + css` + background: ${background}; + line-height: ${theme.typography.body.lineHeight}; + font-size: ${theme.typography.size.md}; + color: ${textColor}; + border: 1px solid ${borderColor}; + + &:-webkit-autofill, + &:-webkit-autofill:hover { + /* Welcome to 2005. This is a HACK to get rid od Chromes default autofill styling */ + box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0), inset 0 0 0 100px ${background}!important; + -webkit-text-fill-color: ${textColor} !important; + border-color: ${autoFillBorder}; + } - &:-webkit-autofill, - &:-webkit-autofill:hover { - /* Welcome to 2005. This is a HACK to get rid od Chromes default autofill styling */ - box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0), inset 0 0 0 100px ${background}!important; - -webkit-text-fill-color: ${textColor} !important; - border-color: ${autoFillBorder}; - } - - &:-webkit-autofill:focus { - /* Welcome to 2005. This is a HACK to get rid od Chromes default autofill styling */ - box-shadow: 0 0 0 2px ${theme.colors.background.primary}, 0 0 0px 4px ${theme.colors.primary.main}, - inset 0 0 0 1px rgba(255, 255, 255, 0), inset 0 0 0 100px ${background}!important; - -webkit-text-fill-color: ${textColor} !important; - } - - &:hover { - border-color: ${borderColorHover}; - } - - &:focus { - outline: none; - } - - &:disabled { - background-color: ${theme.colors.action.disabledBackground}; - color: ${theme.colors.action.disabledText}; - border: 1px solid ${theme.colors.action.disabledBackground}; + &:-webkit-autofill:focus { + /* Welcome to 2005. This is a HACK to get rid od Chromes default autofill styling */ + box-shadow: 0 0 0 2px ${theme.colors.background.primary}, 0 0 0px 4px ${theme.colors.primary.main}, + inset 0 0 0 1px rgba(255, 255, 255, 0), inset 0 0 0 100px ${background}!important; + -webkit-text-fill-color: ${textColor} !important; + } &:hover { - border-color: ${borderColor}; + border-color: ${borderColorHover}; + } + + &:focus { + outline: none; } - } - &::placeholder { - color: ${theme.colors.text.disabled}; - opacity: 1; - } + &:disabled { + background-color: ${theme.colors.action.disabledBackground}; + color: ${theme.colors.action.disabledText}; + border: 1px solid ${theme.colors.action.disabledBackground}; + + &:hover { + border-color: ${borderColor}; + } + } + + &::placeholder { + color: ${theme.colors.text.disabled}; + opacity: 1; + } + ` + ); +}; + +export const inputPadding = (theme: GrafanaTheme2) => { + return css` + padding: ${theme.spacing(0, 1, 0, 1)}; `; }; diff --git a/packages/grafana-ui/src/components/Select/Container.tsx b/packages/grafana-ui/src/components/Select/Container.tsx new file mode 100644 index 00000000000..d6636fab96e --- /dev/null +++ b/packages/grafana-ui/src/components/Select/Container.tsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { useTheme2 } from '../../themes/ThemeContext'; +import { sharedInputStyle } from '../Forms/commonStyles'; +import { getInputStyles } from '../Input/Input'; +import { css, cx } from '@emotion/css'; +import { stylesFactory } from '../../themes'; +import { GrafanaTheme2 } from '@grafana/data'; +import { focusCss } from '../../themes/mixins'; +import { components, ContainerProps, GroupTypeBase } from 'react-select'; + +export const SelectContainer = >( + props: ContainerProps & { isFocused: boolean } +) => { + const { + isDisabled, + isFocused, + children, + selectProps: { prefix }, + } = props; + + const theme = useTheme2(); + const styles = getSelectContainerStyles(theme, isFocused, isDisabled, !!prefix); + + return ( + + {children} + + ); +}; + +const getSelectContainerStyles = stylesFactory( + (theme: GrafanaTheme2, focused: boolean, disabled: boolean, withPrefix: boolean) => { + const styles = getInputStyles({ theme, invalid: false }); + + return { + wrapper: cx( + styles.wrapper, + sharedInputStyle(theme, false), + focused && + css` + ${focusCss(theme.v1)} + `, + disabled && styles.inputDisabled, + css` + position: relative; + box-sizing: border-box; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + + min-height: 32px; + height: auto; + max-width: 100%; + + /* Input padding is applied to the InputControl so the menu is aligned correctly */ + padding: 0; + cursor: ${disabled ? 'not-allowed' : 'pointer'}; + `, + withPrefix && + css` + padding-left: 0; + ` + ), + }; + } +); diff --git a/packages/grafana-ui/src/components/Select/InputControl.tsx b/packages/grafana-ui/src/components/Select/InputControl.tsx index 4b6531248e1..0121f601eab 100644 --- a/packages/grafana-ui/src/components/Select/InputControl.tsx +++ b/packages/grafana-ui/src/components/Select/InputControl.tsx @@ -1,11 +1,10 @@ import React from 'react'; import { useTheme2 } from '../../themes/ThemeContext'; -import { sharedInputStyle } from '../Forms/commonStyles'; +import { inputPadding } from '../Forms/commonStyles'; import { getInputStyles } from '../Input/Input'; import { css, cx } from '@emotion/css'; import { stylesFactory } from '../../themes'; import { GrafanaTheme2 } from '@grafana/data'; -import { focusCss } from '../../themes/mixins'; interface InputControlProps { /** Show an icon as a prefix in the input */ @@ -21,28 +20,22 @@ const getInputControlStyles = stylesFactory( const styles = getInputStyles({ theme, invalid }); return { - wrapper: cx( - styles.wrapper, - sharedInputStyle(theme, invalid), - focused && - css` - ${focusCss(theme.v1)} - `, - disabled && styles.inputDisabled, + input: cx( + inputPadding(theme), css` - min-height: 32px; - height: auto; - flex-direction: row; - padding-right: 0; + width: 100%; max-width: 100%; - align-items: center; - cursor: default; + display: flex; + flex-direction: row; + align-items: center; flex-wrap: wrap; justify-content: space-between; + + padding-right: 0; + position: relative; box-sizing: border-box; - cursor: ${disabled ? 'not-allowed' : 'pointer'}; `, withPrefix && css` @@ -64,7 +57,7 @@ export const InputControl = React.forwardRef +
{prefix &&
{prefix}
} {children}
diff --git a/packages/grafana-ui/src/components/Select/Select.story.tsx b/packages/grafana-ui/src/components/Select/Select.story.tsx index 23c42b0f65a..140748416e1 100644 --- a/packages/grafana-ui/src/components/Select/Select.story.tsx +++ b/packages/grafana-ui/src/components/Select/Select.story.tsx @@ -98,7 +98,6 @@ export const Basic: Story = (args) => { setValue(v); action('onChange')(v); }} - prefix={getPrefix(args.icon)} {...args} /> diff --git a/packages/grafana-ui/src/components/Select/SelectBase.tsx b/packages/grafana-ui/src/components/Select/SelectBase.tsx index dc673c1bad4..5fcdf98833e 100644 --- a/packages/grafana-ui/src/components/Select/SelectBase.tsx +++ b/packages/grafana-ui/src/components/Select/SelectBase.tsx @@ -12,6 +12,7 @@ import { SelectMenu, SelectMenuOptions } from './SelectMenu'; import { IndicatorsContainer } from './IndicatorsContainer'; import { ValueContainer } from './ValueContainer'; import { InputControl } from './InputControl'; +import { SelectContainer } from './SelectContainer'; import { DropdownIndicator } from './DropdownIndicator'; import { SelectOptionGroup } from './SelectOptionGroup'; import { SingleValue } from './SingleValue'; @@ -334,6 +335,7 @@ export function SelectBase({ }, MultiValueContainer: MultiValueContainer, MultiValueRemove: MultiValueRemove, + SelectContainer, ...components, }} styles={{ diff --git a/packages/grafana-ui/src/components/Select/SelectContainer.tsx b/packages/grafana-ui/src/components/Select/SelectContainer.tsx new file mode 100644 index 00000000000..0b2e1b8b4c1 --- /dev/null +++ b/packages/grafana-ui/src/components/Select/SelectContainer.tsx @@ -0,0 +1,74 @@ +import React from 'react'; +import { useTheme2 } from '../../themes/ThemeContext'; +import { sharedInputStyle } from '../Forms/commonStyles'; +import { getInputStyles } from '../Input/Input'; +import { css, cx } from '@emotion/css'; +import { stylesFactory } from '../../themes'; +import { GrafanaTheme2 } from '@grafana/data'; +import { focusCss } from '../../themes/mixins'; +import { components, ContainerProps, GroupTypeBase } from 'react-select'; + +// isFocus prop is actually available, but its not in the types for the version we have. +interface CorrectContainerProps> + extends ContainerProps { + isFocused: boolean; +} + +export const SelectContainer = >( + props: CorrectContainerProps +) => { + const { + isDisabled, + isFocused, + children, + selectProps: { prefix }, + } = props; + + const theme = useTheme2(); + const styles = getSelectContainerStyles(theme, isFocused, isDisabled, !!prefix); + + return ( + + {children} + + ); +}; + +const getSelectContainerStyles = stylesFactory( + (theme: GrafanaTheme2, focused: boolean, disabled: boolean, withPrefix: boolean) => { + const styles = getInputStyles({ theme, invalid: false }); + + return { + wrapper: cx( + styles.wrapper, + sharedInputStyle(theme, false), + focused && + css` + ${focusCss(theme.v1)} + `, + disabled && styles.inputDisabled, + css` + position: relative; + box-sizing: border-box; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + + min-height: 32px; + height: auto; + max-width: 100%; + + /* Input padding is applied to the InputControl so the menu is aligned correctly */ + padding: 0; + cursor: ${disabled ? 'not-allowed' : 'pointer'}; + `, + withPrefix && + css` + padding-left: 0; + ` + ), + }; + } +);