diff --git a/packages/eds-core-react/src/components/Input/Input.docs.mdx b/packages/eds-core-react/src/components/Input/Input.docs.mdx index 0267ac8c02..db48ceaebe 100644 --- a/packages/eds-core-react/src/components/Input/Input.docs.mdx +++ b/packages/eds-core-react/src/components/Input/Input.docs.mdx @@ -58,3 +58,8 @@ Compact `Input` using `EdsProvider`. + +### Casted as textarea + + + diff --git a/packages/eds-core-react/src/components/Input/Input.stories.tsx b/packages/eds-core-react/src/components/Input/Input.stories.tsx index 9504bcf265..d34a53c7b4 100644 --- a/packages/eds-core-react/src/components/Input/Input.stories.tsx +++ b/packages/eds-core-react/src/components/Input/Input.stories.tsx @@ -215,9 +215,7 @@ export const WithAdornments: Story = () => { type="text" id="adornments-default" placeholder="Placeholder text Placeholder text" - leftAdornmentsWidth={40} leftAdornments={IT} - rightAdornmentsWidth={52} rightAdornments={ <> unit @@ -230,9 +228,7 @@ export const WithAdornments: Story = () => { type="text" id="adornments-error" variant="error" - leftAdornmentsWidth={40} leftAdornments={IT} - rightAdornmentsWidth={52} rightAdornments={ <> unit @@ -245,9 +241,7 @@ export const WithAdornments: Story = () => { type="text" id="adornments-warning" variant="warning" - leftAdornmentsWidth={40} leftAdornments={IT} - rightAdornmentsWidth={52} rightAdornments={ <> unit @@ -260,9 +254,7 @@ export const WithAdornments: Story = () => { type="text" id="adornments-success" variant="success" - leftAdornmentsWidth={40} leftAdornments={IT} - rightAdornmentsWidth={52} rightAdornments={ <> unit @@ -277,7 +269,6 @@ export const WithAdornments: Story = () => { disabled placeholder="Placeholder text Placeholder text" value="Some text Some textSome textSome text" - leftAdornmentsWidth={40} leftAdornments={ <> @@ -285,7 +276,6 @@ export const WithAdornments: Story = () => { } - rightAdornmentsWidth={52} rightAdornments={ <> unit @@ -298,13 +288,11 @@ export const WithAdornments: Story = () => { type="text" id="adornments-readonly" readOnly - leftAdornmentsWidth={40} leftAdornments={ <> IT } - rightAdornmentsWidth={52} rightAdornments={ <> unit @@ -325,3 +313,7 @@ WithAdornments.decorators = [ ) }, ] + +export const casted: Story = (args) => { + return +} diff --git a/packages/eds-core-react/src/components/Input/Input.tsx b/packages/eds-core-react/src/components/Input/Input.tsx index 9eb15c8b45..cead041dcb 100644 --- a/packages/eds-core-react/src/components/Input/Input.tsx +++ b/packages/eds-core-react/src/components/Input/Input.tsx @@ -1,4 +1,12 @@ -import { InputHTMLAttributes, forwardRef, ReactNode, useMemo } from 'react' +import { + forwardRef, + ReactNode, + useState, + useCallback, + CSSProperties, + ElementType, + ComponentPropsWithoutRef, +} from 'react' import styled, { css } from 'styled-components' import { ComponentToken } from '@equinor/eds-tokens' import { @@ -6,10 +14,11 @@ import { spacingsTemplate, outlineTemplate, useToken, + OverridableComponent, } from '@equinor/eds-utils' import { inputToken as tokens } from './Input.tokens' import type { InputToken } from './Input.tokens' -import type { Variants } from '../TextField/types' +import type { Variants } from '../types' import { useEds } from '../EdsProvider' const Container = styled.div(({ token, disabled, readOnly }: StyledProps) => { @@ -25,7 +34,6 @@ const Container = styled.div(({ token, disabled, readOnly }: StyledProps) => { flex-direction: row; border: none; box-sizing: border-box; - height: ${token.height}; box-shadow: ${token.boxShadow}; background: ${token.background}; ${outlineTemplate(token.outline)} @@ -53,25 +61,30 @@ const Container = styled.div(({ token, disabled, readOnly }: StyledProps) => { ` }) -const StyledInput = styled.input(({ token }: StyledProps) => { - return css` - width: 100%; - border: none; - background: transparent; - ${spacingsTemplate(token.spacings)} - ${typographyMixin(token.typography)} - outline: none; - - &::placeholder { - color: ${token.entities.placeholder.typography.color}; - } - - &:disabled { - color: var(--eds-input-color); - cursor: not-allowed; - } - ` -}) +const StyledInput = styled.input( + ({ token, paddingLeft, paddingRight }: StyledProps) => { + return css` + width: 100%; + border: none; + background: transparent; + ${spacingsTemplate(token.spacings)} + ${typographyMixin(token.typography)} + outline: none; + + padding-left: ${paddingLeft}; + padding-right: ${paddingRight}; + + &::placeholder { + color: ${token.entities.placeholder.typography.color}; + } + + &:disabled { + color: var(--eds-input-color); + cursor: not-allowed; + } + ` + }, +) type AdornmentProps = { token: InputToken @@ -80,8 +93,8 @@ type AdornmentProps = { const Adornments = styled.div(({ token }) => { return css` position: absolute; - top: 0; - bottom: 0; + top: ${token.spacings.top}; + bottom: ${token.spacings.bottom}; display: flex; align-items: center; ${typographyMixin(token.entities.adornment.typography)} @@ -102,9 +115,10 @@ const RightAdornments = styled(Adornments)( padding-right: ${token.entities.adornment.spacings.right}; `, ) - type StyledProps = { token: InputToken + paddingLeft?: string + paddingRight?: string } & Required> export type InputProps = { @@ -120,90 +134,110 @@ export type InputProps = { readOnly?: boolean /** Left adornments */ leftAdornments?: ReactNode - /** Left adornments width */ - leftAdornmentsWidth?: number /** Right adornments */ rightAdornments?: ReactNode - /** Right adornments width */ - rightAdornmentsWidth?: number -} & InputHTMLAttributes - -export const Input = forwardRef(function Input( - { - variant = 'default', - disabled = false, - type = 'text', - leftAdornments, - rightAdornments, - leftAdornmentsWidth, - rightAdornmentsWidth, - readOnly, - className, - style, - ...other - }, - ref, -) { - const actualVariant = variant === 'default' ? 'input' : variant - const inputVariant = tokens[actualVariant] - const { density } = useEds() - const token = useToken({ density }, inputVariant)() - - const updatedToken = useMemo( - (): ComponentToken => ({ - ...token, - spacings: { - ...token.spacings, - left: - typeof leftAdornmentsWidth !== 'undefined' - ? `${leftAdornmentsWidth}px` - : token.spacings.left, - right: - typeof rightAdornmentsWidth !== 'undefined' - ? `${rightAdornmentsWidth}px` - : token.spacings.right, - }, - }), - [leftAdornmentsWidth, rightAdornmentsWidth, token], - ) - const inputProps = { + /** Left adornments props */ + leftAdornmentsProps?: ComponentPropsWithoutRef<'div'> + /** Right adornments props */ + rightAdornmentsProps?: ComponentPropsWithoutRef<'div'> + /** Cast the input to another element */ + as?: ElementType + /** */ + className?: string + style?: CSSProperties +} + +export const Input: OverridableComponent = + forwardRef(function Input( + { + variant, + disabled = false, + type = 'text', + leftAdornments, + rightAdornments, + readOnly, + className, + style, + leftAdornmentsProps, + rightAdornmentsProps, + ...other + }, ref, - type, - disabled, - readOnly, - token: updatedToken, - ...other, - } - - const containerProps = { - disabled, - readOnly, - className, - style, - token: updatedToken, - } - - const leftAdornmentProps = { - token: updatedToken, - } - const rightAdornmentProps = { - token: updatedToken, - } - - return ( - // Not using because of cascading styling messing with adornments - - {leftAdornments ? ( - - {leftAdornments} - - ) : null} - - {rightAdornments ? ( - - {rightAdornments} - - ) : null} - - ) -}) + ) { + const inputVariant = tokens[variant] ? tokens[variant] : tokens.input + const { density } = useEds() + const _token = useToken({ density }, inputVariant)() + + const [rightAdornmentsRef, setRightAdornmentsRef] = + useState() + const [leftAdornmentsRef, setLeftAdornmentsRef] = useState() + + const token = useCallback((): ComponentToken => { + const leftAdornmentsWidth = leftAdornmentsRef + ? leftAdornmentsRef.clientWidth + : 0 + const rightAdornmentsWidth = rightAdornmentsRef + ? rightAdornmentsRef.clientWidth + : 0 + return { + ..._token, + spacings: { + ..._token.spacings, + left: `${leftAdornmentsWidth + parseInt(_token.spacings.left)}px`, + right: `${rightAdornmentsWidth + parseInt(_token.spacings.right)}px`, + }, + } + }, [leftAdornmentsRef, rightAdornmentsRef, _token])() + + const inputProps = { + ref, + type, + disabled, + readOnly, + token, + style: { + resize: 'none', + }, + ...other, + } + + const containerProps = { + disabled, + readOnly, + className, + style, + token, + } + + const _leftAdornmentProps = { + ...leftAdornmentsProps, + ref: setLeftAdornmentsRef, + token, + } + const _rightAdornmentProps = { + ...rightAdornmentsProps, + ref: setRightAdornmentsRef, + token, + } + + return ( + // Not using because of cascading styling messing with adornments + + {leftAdornments ? ( + + {leftAdornments} + + ) : null} + + {rightAdornments ? ( + + {rightAdornments} + + ) : null} + + ) + }) diff --git a/packages/eds-core-react/src/components/Input/__snapshots__/Input.test.tsx.snap b/packages/eds-core-react/src/components/Input/__snapshots__/Input.test.tsx.snap index e7eb5b8df3..13a71fa95e 100644 --- a/packages/eds-core-react/src/components/Input/__snapshots__/Input.test.tsx.snap +++ b/packages/eds-core-react/src/components/Input/__snapshots__/Input.test.tsx.snap @@ -16,7 +16,6 @@ exports[`Input Matches snapshot 1`] = ` flex-direction: row; border: none; box-sizing: border-box; - height: 36px; box-shadow: inset 0px -1px 0px 0px var(--eds_text__static_icons__tertiary,rgba(111,111,111,1)); background: var(--eds_ui_background__light,rgba(247,247,247,1)); outline: 1px solid transparent; @@ -78,6 +77,7 @@ exports[`Input Matches snapshot 1`] = ` diff --git a/packages/eds-core-react/src/components/InputWrapper/HelperText/HelperText.token.ts b/packages/eds-core-react/src/components/InputWrapper/HelperText/HelperText.token.ts new file mode 100644 index 0000000000..541e839cc2 --- /dev/null +++ b/packages/eds-core-react/src/components/InputWrapper/HelperText/HelperText.token.ts @@ -0,0 +1,39 @@ +import { tokens } from '@equinor/eds-tokens' +import type { Spacing, Typography } from '@equinor/eds-tokens' + +const { + colors, + spacings: { comfortable }, + typography, +} = tokens + +export type HelperTextProps = { + background: string + typography: Typography + spacings: { + comfortable: Spacing + compact: Spacing + } +} + +export const helperText: HelperTextProps = { + background: colors.ui.background__light.hex, + typography: { + ...typography.input.helper, + color: colors.text.static_icons__tertiary.rgba, + }, + spacings: { + comfortable: { + left: comfortable.small, + right: comfortable.small, + top: comfortable.small, + bottom: '6px', + }, + compact: { + left: comfortable.small, + right: comfortable.small, + top: comfortable.xx_small, + bottom: '6px', + }, + }, +} diff --git a/packages/eds-core-react/src/components/InputWrapper/HelperText.tsx b/packages/eds-core-react/src/components/InputWrapper/HelperText/HelperText.tsx similarity index 64% rename from packages/eds-core-react/src/components/InputWrapper/HelperText.tsx rename to packages/eds-core-react/src/components/InputWrapper/HelperText/HelperText.tsx index 2d901a72a7..24f6d20591 100644 --- a/packages/eds-core-react/src/components/InputWrapper/HelperText.tsx +++ b/packages/eds-core-react/src/components/InputWrapper/HelperText/HelperText.tsx @@ -1,7 +1,7 @@ import { forwardRef, ReactNode, HTMLAttributes } from 'react' import styled, { css } from 'styled-components' -import { tokens } from '@equinor/eds-tokens' -import { Typography } from '../Typography' +import { typographyMixin } from '@equinor/eds-utils' +import { helperText as tokens } from './HelperText.token' type ContainerProps = { color?: string @@ -17,6 +17,10 @@ const Container = styled.div(({ color }) => color, }), ) +const Text = styled.p` + margin: 0; + ${typographyMixin(tokens.typography)}; +` export type HelperTextProps = { /** Helper text */ @@ -27,14 +31,9 @@ export type HelperTextProps = { color?: string } & HTMLAttributes -export const HelperText = forwardRef( +const TextfieldHelperText = forwardRef( function TextfieldHelperText( - { - text, - icon, - color = tokens.colors.text.static_icons__tertiary.rgba, - ...rest - }, + { text, icon, color = tokens.typography.color, ...rest }, ref, ) { if (!text) { @@ -44,10 +43,10 @@ export const HelperText = forwardRef( return ( {icon} - - {text} - + {text} ) }, ) + +export { TextfieldHelperText as HelperText } diff --git a/packages/eds-core-react/src/components/InputWrapper/HelperText/index.ts b/packages/eds-core-react/src/components/InputWrapper/HelperText/index.ts new file mode 100644 index 0000000000..a5e6cf6966 --- /dev/null +++ b/packages/eds-core-react/src/components/InputWrapper/HelperText/index.ts @@ -0,0 +1 @@ +export * from './HelperText' diff --git a/packages/eds-core-react/src/components/InputWrapper/InputWrapper.stories.tsx b/packages/eds-core-react/src/components/InputWrapper/InputWrapper.stories.tsx index 448958f55d..ae3d1b3129 100644 --- a/packages/eds-core-react/src/components/InputWrapper/InputWrapper.stories.tsx +++ b/packages/eds-core-react/src/components/InputWrapper/InputWrapper.stories.tsx @@ -1,8 +1,8 @@ import { Story, ComponentMeta } from '@storybook/react' import { accessible, dropper, search } from '@equinor/eds-icons' +import { Stack } from './../../../.storybook/components' import { InputWrapper, InputWrapperProps, Input, Icon, Button } from '../..' import styled from 'styled-components' -import { useId } from '@equinor/eds-utils' export default { title: 'Inputs/InputWrapper', @@ -16,35 +16,20 @@ const SmallButton = styled(Button)` export const Introduction: Story = (args) => { const { color } = args - const inputId = useId(null, 'inputwrapper-input') - const helperTextId = useId(null, 'inputwrapper-helpertext') - + const inputId = 'some-input-id' + const helperTextId = 'some-helper-id' + const helperProps = { + text: 'helperText', + } return ( , - }} + helperProps={helperProps} labelProps={{ - htmlFor: inputId, - meta: 'meta tag', + label: "I'm a label, play with me!", }} {...args} > - } - rightAdornmentsWidth={24 + 8} - rightAdornments={ - - - - } - /> + ) } diff --git a/packages/eds-core-react/src/components/InputWrapper/InputWrapper.tsx b/packages/eds-core-react/src/components/InputWrapper/InputWrapper.tsx index 8d777ab083..ff9fd18f24 100644 --- a/packages/eds-core-react/src/components/InputWrapper/InputWrapper.tsx +++ b/packages/eds-core-react/src/components/InputWrapper/InputWrapper.tsx @@ -1,10 +1,11 @@ -import { HTMLAttributes, forwardRef, useMemo, ReactNode } from 'react' +import { HTMLAttributes, forwardRef, ReactNode, useCallback } from 'react' import styled, { ThemeProvider } from 'styled-components' import { useToken } from '@equinor/eds-utils' import { Label as _Label, LabelProps } from '../Label' import { HelperText as _HelperText, HelperTextProps } from './HelperText' import { useEds } from './../EdsProvider' import { inputToken as tokens } from './InputWrapper.tokens' +import { Variants } from '../types' const Container = styled.div`` @@ -26,19 +27,29 @@ export type InputWrapperProps = { /** Read Only */ readOnly?: boolean /** Highlight color */ - color?: 'error' | 'warning' | 'success' + color?: Variants /** Label props */ - labelProps: LabelProps + labelProps?: LabelProps /** Helpertext props */ helperProps?: HelperTextProps - /** Input or Textarea elements */ + /** Helper Icon */ + helperIcon?: ReactNode + /** Input or Textarea element */ children: ReactNode } & HTMLAttributes /** InputWrapper is a internal skeleton component for structering form elements */ export const InputWrapper = forwardRef( function InputWrapper( - { children, color, label, labelProps = {}, helperProps = {}, ...other }, + { + children, + color, + label, + labelProps = {}, + helperProps = {}, + helperIcon, + ...other + }, ref, ) { const { density } = useEds() @@ -46,23 +57,26 @@ export const InputWrapper = forwardRef( const inputToken = tokens[actualVariant] const token = useToken({ density }, inputToken) - const helperTextColor = useMemo(() => { + const helperTextColor = useCallback(() => { const _token = token() return other.disabled ? _token.entities.helperText.states.disabled.typography.color : _token.entities.helperText.typography.color - }, [token, other.disabled]) + }, [token, other.disabled])() - const hasHelperText = Boolean(helperProps.text) - const hasLabel = Boolean(label || labelProps.label) + const hasHelperText = Boolean(helperProps?.text) + const hasLabel = Boolean(label || labelProps?.label) return ( - {hasLabel && diff --git a/packages/eds-core-react/src/components/Search/__snapshots__/Search.test.tsx.snap b/packages/eds-core-react/src/components/Search/__snapshots__/Search.test.tsx.snap index f23dc921ed..08bb898764 100644 --- a/packages/eds-core-react/src/components/Search/__snapshots__/Search.test.tsx.snap +++ b/packages/eds-core-react/src/components/Search/__snapshots__/Search.test.tsx.snap @@ -116,7 +116,6 @@ exports[`Search Matches snapshot 1`] = ` flex-direction: row; border: none; box-sizing: border-box; - height: 36px; box-shadow: inset 0px -1px 0px 0px var(--eds_text__static_icons__tertiary,rgba(111,111,111,1)); background: var(--eds_ui_background__light,rgba(247,247,247,1)); outline: 1px solid transparent; @@ -321,6 +320,7 @@ exports[`Search Matches snapshot 1`] = ` aria-label="search input" class="c3" role="searchbox" + style="padding-left: 8px; padding-right: 8px;" type="search" value="" /> diff --git a/packages/eds-core-react/src/components/Select/SingleSelect/__snapshots__/SingleSelect.test.tsx.snap b/packages/eds-core-react/src/components/Select/SingleSelect/__snapshots__/SingleSelect.test.tsx.snap index 3632b3af1b..121deca9df 100644 --- a/packages/eds-core-react/src/components/Select/SingleSelect/__snapshots__/SingleSelect.test.tsx.snap +++ b/packages/eds-core-react/src/components/Select/SingleSelect/__snapshots__/SingleSelect.test.tsx.snap @@ -42,7 +42,6 @@ exports[`SingleSelect Matches snapshot 1`] = ` flex-direction: row; border: none; box-sizing: border-box; - height: 36px; box-shadow: inset 0px -1px 0px 0px var(--eds_text__static_icons__tertiary,rgba(111,111,111,1)); background: var(--eds_ui_background__light,rgba(247,247,247,1)); outline: 1px solid transparent; @@ -285,6 +284,7 @@ exports[`SingleSelect Matches snapshot 1`] = ` autocomplete="off" class="c6" id="downshift-0-input" + style="padding-left: 8px; padding-right: 8px;" type="text" value="" /> diff --git a/packages/eds-core-react/src/components/TextField/Field.tsx b/packages/eds-core-react/src/components/TextField/Field.tsx deleted file mode 100644 index 7e4cc161a4..0000000000 --- a/packages/eds-core-react/src/components/TextField/Field.tsx +++ /dev/null @@ -1,247 +0,0 @@ -import { ReactNode, forwardRef, Ref } from 'react' -import { useTextField } from './TextField.context' -import { Input } from '../Input' -import { Icon } from './Icon' -import type { Variants } from './types' -import type { TextFieldToken } from './TextField.tokens' -import styled, { css } from 'styled-components' -import { typographyTemplate, outlineTemplate } from '@equinor/eds-utils' -import * as tokens from './TextField.tokens' -import { Textarea } from '../Textarea' -import { useEds } from './../EdsProvider' - -const { textfield } = tokens - -const Variation = ({ - variant, - isFocused, - token, -}: { - variant: string - token: TextFieldToken - isFocused: boolean -}) => { - if (!variant) { - return `` - } - - return css` - box-shadow: ${isFocused - ? `none` - : variant === 'default' - ? `inset 0 -1px 0 0 ${ - token.border?.type === 'border' && token.border?.color - }` - : `0 0 0 1px ${token.border?.type === 'border' && token.border?.color}`}; - ${isFocused && outlineTemplate(token.states.focus.outline)} - ` -} - -const StrippedInput = styled(Input)` - outline: none; - - &:active, - &:focus { - outline: none; - box-shadow: none; - } -` - -const StrippedTextarea = styled(Textarea)` - outline: none; - - &:active, - &:focus { - outline: none; - box-shadow: none; - } -` - -type InputWrapperType = { - isFocused: boolean - isDisabled: boolean - isReadOnly: boolean - variant: string - token: TextFieldToken - inputIcon?: ReactNode - unit?: string - multiline?: boolean -} - -export const InputWrapper = styled.div( - ({ inputIcon, unit, isDisabled, isReadOnly, multiline, variant }) => css` - ${Variation} - ${(inputIcon || unit) && - css` - display: flex; - align-items: center; - background: ${textfield.background}; - padding-right: ${textfield.spacings.right}; - `} - ${isReadOnly && - css` - box-shadow: ${textfield.states.readOnly.boxShadow}; - background: ${textfield.states.readOnly.background}; - `} - ${isDisabled && - css` - box-shadow: none; - cursor: not-allowed; - outline: none; - `} - ${multiline && - variant === 'default' && - !inputIcon && - !unit && - css` - box-shadow: none; - `} - `, -) - -type UnitType = { - isDisabled: boolean -} - -const Unit = styled.span` - ${typographyTemplate(textfield.entities.unit.typography)}; - /* Yes, we don't like magic numbers, but if you have both unit and icon, - the unit is slightly off due to line-height and font */ - display: inline-block; - margin-top: 3px; - ${({ isDisabled }) => - isDisabled && { - color: textfield.entities.unit.states.disabled.typography.color, - }} -` - -type AdornmentsType = { - multiline: boolean -} - -const Adornments = styled.div` - display: flex; - align-items: center; - justify-content: center; - height: 100%; - margin-left: ${textfield.spacings.left}; - & div:nth-child(2) { - margin-left: ${textfield.spacings.left}; - } - ${({ multiline, theme }) => - multiline && { - marginTop: theme.spacings.top, - alignSelf: 'start', - }} -` - -type FieldProps = { - /** Specifies if input should be multiline*/ - multiline?: boolean - /** Placeholder */ - placeholder?: string - /** Variant */ - variant?: Variants - /** Disabled state */ - disabled?: boolean - /** Type */ - type?: string - /** Read Only */ - readOnly?: boolean - /** Unit text */ - unit?: string - /* Input icon */ - inputIcon?: ReactNode - /** Specifies max rows for multiline input */ - rowsMax?: number -} & React.HTMLAttributes - -export const Field = forwardRef< - HTMLTextAreaElement | HTMLInputElement, - FieldProps ->(function Field( - { - multiline, - variant, - disabled, - readOnly, - type, - unit, - inputIcon, - rowsMax, - onBlur, - onFocus, - ...other - }, - ref, -) { - const { handleFocus, handleBlur, isFocused } = useTextField() - const { density } = useEds() - const iconSize = density === 'compact' ? 16 : 24 - const actualVariant = variant === 'default' ? 'textfield' : variant - const inputVariant = tokens[actualVariant] - const isError = actualVariant === 'error' - - const focusHandler = ( - e: React.FocusEvent, - ) => { - handleFocus() - onFocus && onFocus(e) - } - - const blurHandler = ( - e: React.FocusEvent, - ) => { - handleBlur() - onBlur && onBlur(e) - } - - const inputWrapperProps = { - isFocused, - isDisabled: disabled, - isReadOnly: readOnly, - variant, - token: inputVariant, - inputIcon, - unit, - multiline, - } - - const inputProps = { - ref: ref as Ref, - 'aria-invalid': isError, - type, - disabled, - readOnly, - variant, - onBlur: blurHandler, - onFocus: focusHandler, - ...other, - } - - const textareaProps = { - ...inputProps, - rowsMax, - ref: ref as Ref, - } - - return ( - - {multiline ? ( - - ) : ( - - )} - {(inputIcon || unit) && ( - - {unit && {unit}} - {inputIcon && ( - - {inputIcon} - - )} - - )} - - ) -}) diff --git a/packages/eds-core-react/src/components/TextField/HelperText/HelperText.token.ts b/packages/eds-core-react/src/components/TextField/HelperText/HelperText.token.ts deleted file mode 100644 index 2d1d66656d..0000000000 --- a/packages/eds-core-react/src/components/TextField/HelperText/HelperText.token.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { tokens } from '@equinor/eds-tokens' -import type { Spacing, Typography } from '@equinor/eds-tokens' -import { ColorStateProps } from '../types' - -const { - colors, - spacings: { comfortable }, - typography, -} = tokens - -export type HelperTextProps = { - background: string - typography: Typography - spacings: { - comfortable: Spacing - compact: Spacing - } - default: ColorStateProps - error: ColorStateProps - warning: ColorStateProps - success: ColorStateProps -} - -export const helperText: HelperTextProps = { - background: colors.ui.background__light.hex, - typography: typography.input.helper, - spacings: { - comfortable: { - left: comfortable.small, - right: comfortable.small, - top: comfortable.small, - bottom: '6px', - }, - compact: { - left: comfortable.small, - right: comfortable.small, - top: comfortable.xx_small, - bottom: '6px', - }, - }, - default: { - color: colors.text.static_icons__tertiary.hex, - disabledColor: colors.interactive.disabled__text.hex, - focusColor: colors.text.static_icons__tertiary.hex, - }, - error: { - color: colors.interactive.danger__text.hex, - disabledColor: colors.interactive.disabled__text.hex, - focusColor: colors.interactive.danger__hover.hex, - }, - warning: { - color: colors.interactive.warning__text.hex, - disabledColor: colors.interactive.disabled__text.hex, - focusColor: colors.interactive.warning__hover.hex, - }, - success: { - color: colors.interactive.success__text.hex, - disabledColor: colors.interactive.disabled__text.hex, - focusColor: colors.interactive.success__hover.hex, - }, -} diff --git a/packages/eds-core-react/src/components/TextField/HelperText/HelperText.tsx b/packages/eds-core-react/src/components/TextField/HelperText/HelperText.tsx deleted file mode 100644 index edf0ef0706..0000000000 --- a/packages/eds-core-react/src/components/TextField/HelperText/HelperText.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { ReactNode, forwardRef } from 'react' -import styled, { css } from 'styled-components' -import { typographyTemplate } from '@equinor/eds-utils' -import { helperText as tokens } from './HelperText.token' -import { useTextField } from '../TextField.context' -import { Icon } from '../Icon' -import type { Variants, ColorStateProps } from '../types' -import type { Spacing } from '@equinor/eds-tokens' - -type VariationProps = { - variant: ColorStateProps - isFocused: boolean - isDisabled: boolean -} - -const Variation = ({ variant, isFocused, isDisabled }: VariationProps) => { - if (!variant) { - return `` - } - - const { focusColor, color, disabledColor } = variant - - if (isDisabled) { - return css` - color: ${disabledColor}; - ` - } - - if (isFocused) { - return css` - color: ${focusColor}; - ` - } - - return css` - color: ${color}; - ` -} - -type StyledProps = { - spacings: Spacing -} - -const Container = styled.div` - display: flex; - align-items: flex-start; - margin-top: ${({ spacings }) => spacings.top}; -` -const Text = styled.p` - ${typographyTemplate(tokens.typography)} - margin: 0 0 0 ${({ spacings }) => spacings.left}; - ${Variation} -` - -type TextfieldHelperTextProps = { - /** Helper text */ - helperText?: string - /** Icon */ - icon?: ReactNode - /** Disabled */ - disabled?: boolean - /** Variant */ - variant: Variants -} - -const TextfieldHelperText = forwardRef< - HTMLDivElement, - TextfieldHelperTextProps ->(function TextfieldHelperText( - { helperText, icon, variant = 'default', disabled: isDisabled, ...rest }, - ref, -) { - const helperVariant = tokens[variant] - const spacings = tokens.spacings.comfortable - - const { isFocused } = useTextField() - - const colors = { - color: helperVariant.color, - disabledColor: helperVariant.disabledColor, - focusColor: helperVariant.focusColor, - } - - return ( - - {icon && ( - - {icon} - - )} - - {helperText} - - - ) -}) - -export { TextfieldHelperText as HelperText } diff --git a/packages/eds-core-react/src/components/TextField/HelperText/index.ts b/packages/eds-core-react/src/components/TextField/HelperText/index.ts deleted file mode 100644 index 23c77d008a..0000000000 --- a/packages/eds-core-react/src/components/TextField/HelperText/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { HelperText } from './HelperText' diff --git a/packages/eds-core-react/src/components/TextField/Icon/Icon.tokens.ts b/packages/eds-core-react/src/components/TextField/Icon/Icon.tokens.ts deleted file mode 100644 index 0546d54c30..0000000000 --- a/packages/eds-core-react/src/components/TextField/Icon/Icon.tokens.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { tokens } from '@equinor/eds-tokens' -import type { Spacing } from '@equinor/eds-tokens' -import { ColorStateProps } from '../types' - -const { - colors, - spacings: { comfortable }, -} = tokens - -type InputProps = { - spacings: { - comfortable: Spacing - compact: Spacing - } - default: ColorStateProps - error: ColorStateProps - warning: ColorStateProps - success: ColorStateProps -} - -export const input: InputProps = { - spacings: { - comfortable: { - left: comfortable.small, - right: comfortable.small, - top: '10px', - bottom: '10px', - }, - compact: { - left: comfortable.small, - right: comfortable.small, - top: '10px', - bottom: '10px', - }, - }, - default: { - color: colors.text.static_icons__tertiary.hex, - disabledColor: colors.interactive.disabled__fill.hex, - focusColor: colors.interactive.primary__resting.hex, - }, - error: { - color: colors.interactive.danger__resting.hex, - disabledColor: colors.interactive.disabled__fill.hex, - focusColor: colors.interactive.danger__hover.hex, - }, - warning: { - color: colors.interactive.warning__resting.hex, - disabledColor: colors.interactive.disabled__fill.hex, - focusColor: colors.interactive.warning__hover.hex, - }, - success: { - color: colors.interactive.success__resting.hex, - disabledColor: colors.interactive.disabled__fill.hex, - focusColor: colors.interactive.success__hover.hex, - }, -} diff --git a/packages/eds-core-react/src/components/TextField/Icon/Icon.tsx b/packages/eds-core-react/src/components/TextField/Icon/Icon.tsx deleted file mode 100644 index 476b33b1bf..0000000000 --- a/packages/eds-core-react/src/components/TextField/Icon/Icon.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import { HTMLAttributes, forwardRef } from 'react' -import styled, { css } from 'styled-components' -import { useTextField } from '../TextField.context' -import { input as tokens } from './Icon.tokens' -import type { Variants, ColorStateProps } from '../types' - -type StyledIconProps = { - colors: ColorStateProps - isDisabled?: boolean - isFocused?: boolean - size?: 16 | 24 -} - -const StyledIcon = styled.div( - ({ colors, isDisabled, isFocused, size }) => { - const { focusColor, color, disabledColor } = colors - let fill = color - - if (isDisabled) { - fill = disabledColor - } - if (isFocused) { - fill = focusColor - } - - return css` - &, - svg { - fill: ${fill}; - width: ${size}px; - height: ${size}px; - } - ` - }, -) - -type TextfieldIconProps = { - /** isDisabled */ - isDisabled?: boolean - /** Variant */ - variant?: Variants - /** Colors */ - colors?: ColorStateProps - /** Size */ - size?: 16 | 24 -} & HTMLAttributes - -const InputIcon = forwardRef( - function InputIcon( - { - size = 24, - variant = 'default', - isDisabled = false, - colors = { - color: tokens[variant].color, - disabledColor: tokens[variant].disabledColor, - focusColor: tokens[variant].focusColor, - }, - children, - ...other - }, - ref, - ) { - const { isFocused } = useTextField() - - const iconProps = { - isDisabled, - colors, - isFocused, - size, - } - - return ( - - {children} - - ) - }, -) - -export { InputIcon as Icon } diff --git a/packages/eds-core-react/src/components/TextField/Icon/index.ts b/packages/eds-core-react/src/components/TextField/Icon/index.ts deleted file mode 100644 index d78603a27e..0000000000 --- a/packages/eds-core-react/src/components/TextField/Icon/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Icon } from './Icon' diff --git a/packages/eds-core-react/src/components/TextField/TextField.context.tsx b/packages/eds-core-react/src/components/TextField/TextField.context.tsx deleted file mode 100644 index c31f6c47aa..0000000000 --- a/packages/eds-core-react/src/components/TextField/TextField.context.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useState, useContext, ReactNode, createContext } from 'react' - -export const propsFor = { - variants: ['error', 'warning', 'success', 'default'], -} - -type State = { - isFocused: boolean -} - -type UseTextFieldProps = { - handleFocus: () => void - handleBlur: () => void -} & T - -const initalState: State = { - isFocused: false, -} - -const TextFieldContext = createContext(initalState) - -type ProviderProps = { children: ReactNode } - -export const TextFieldProvider = ({ children }: ProviderProps): JSX.Element => { - const [state, setState] = useState(initalState) - - const handleFocus = () => { - setState((prevState) => ({ ...prevState, isFocused: true })) - } - const handleBlur = () => { - setState((prevState) => ({ ...prevState, isFocused: false })) - } - const value = { - handleFocus, - handleBlur, - isFocused: state.isFocused, - } - return ( - - {children} - - ) -} - -export const useTextField = (): UseTextFieldProps => - useContext(TextFieldContext) as UseTextFieldProps diff --git a/packages/eds-core-react/src/components/TextField/TextField.docs.mdx b/packages/eds-core-react/src/components/TextField/TextField.docs.mdx index 17fd2d42b1..b9615dede5 100644 --- a/packages/eds-core-react/src/components/TextField/TextField.docs.mdx +++ b/packages/eds-core-react/src/components/TextField/TextField.docs.mdx @@ -43,15 +43,15 @@ import { TextField } from '@equinor/eds-core-react' -### Single line +### Types -Single line text fields display only one line of text. +Examples of common used types (`input` type). ### Multiline -Multi-line text fields grow to accommodate multiple lines of text. +Changes the `input` to `textarea` for multiline support. diff --git a/packages/eds-core-react/src/components/TextField/TextField.stories.tsx b/packages/eds-core-react/src/components/TextField/TextField.stories.tsx index f091ac2940..1fb38f1a47 100644 --- a/packages/eds-core-react/src/components/TextField/TextField.stories.tsx +++ b/packages/eds-core-react/src/components/TextField/TextField.stories.tsx @@ -33,9 +33,9 @@ export default { inputIcon: { options: ['error', 'warning', 'success'], mapping: { - error: [], - warning: [], - success: [], + error: [], + warning: [], + success: [], }, control: { type: 'select', @@ -46,9 +46,9 @@ export default { helperIcon: { options: ['error', 'warning', 'success'], mapping: { - error: [], - warning: [], - success: [], + error: [], + warning: [], + success: [], }, control: { type: 'select', @@ -77,25 +77,24 @@ export default { } as ComponentMeta export const Introduction: Story = (args) => ( - + ) +Introduction.bind({}) +Introduction.args = { + unit: 'unit', + meta: 'meta', + id: 'playWithMe', + label: 'Play with me', + helperText: 'helper text', +} + export const Types: Story = () => ( <> = () => ( id="textfield-email" placeholder="Placeholder text" label="Email" - meta="Meta" helperText="Helper Text" /> = () => ( id="textfield-password" placeholder="Placeholder text" label="Password" - meta="Meta" helperText="Helper Text" + inputIcon={} /> ) -Types.storyName = 'Single line' Types.decorators = [ (Story) => { return ( @@ -151,10 +148,7 @@ Types.decorators = [ export const Multiline: Story = () => ( = () => ( export const MultilineRowsMax: Story = () => ( ) -MultilineRowsMax.storyName = 'Multiline with rowsMax' export const MultilineFixedHeight: Story = () => ( = () => ( meta="Meta" helperText="Validation error" variant="error" - helperIcon={} + helperIcon={} /> = () => ( rows={3} helperText="Validation error" variant="error" - helperIcon={} + helperIcon={} /> = () => ( meta="Meta" helperText="Helper/warning text" variant="warning" - helperIcon={} + helperIcon={} /> = () => ( meta="Meta" helperText="Helper text" variant="success" - helperIcon={} + helperIcon={} /> = () => ( rows={3} helperText="Helper text" variant="success" - helperIcon={} + helperIcon={} /> ) @@ -539,7 +531,7 @@ export const Compact: Story = () => { label="Single line" meta="Meta" unit="km/h" - helperIcon={} + helperIcon={} helperText="Helper information text over several lines so that it breaks" /> @@ -602,7 +594,7 @@ export const ValidationWithReactHookForm: Story = () => { invalid ? : undefined } helperText={error?.message} - variant={invalid ? 'error' : 'default'} + variant={invalid ? 'error' : undefined} /> )} /> diff --git a/packages/eds-core-react/src/components/TextField/TextField.tokens.ts b/packages/eds-core-react/src/components/TextField/TextField.tokens.ts deleted file mode 100644 index 15cba14539..0000000000 --- a/packages/eds-core-react/src/components/TextField/TextField.tokens.ts +++ /dev/null @@ -1,128 +0,0 @@ -import { tokens } from '@equinor/eds-tokens' -import type { ComponentToken } from '@equinor/eds-tokens' - -const { - colors, - typography, - spacings: { - comfortable: { small, x_small }, - }, -} = tokens - -export type TextFieldToken = ComponentToken & { - entities?: { - unit?: ComponentToken - } -} -export const textfield: TextFieldToken = { - background: colors.ui.background__light.hex, - border: { - type: 'border', - radius: 0, - width: '1px', - color: colors.text.static_icons__tertiary.hex, - }, - spacings: { - left: small, - right: small, - top: small, - }, - states: { - focus: { - outline: { - width: '2px', - color: colors.interactive.primary__resting.hex, - style: 'solid', - type: 'outline', - offset: '0px', - }, - }, - readOnly: { - background: 'transparent', - boxShadow: 'none', - }, - }, - entities: { - unit: { - typography: { - ...typography.input.label, - color: colors.text.static_icons__tertiary.hex, - }, - states: { - disabled: { - typography: { - color: colors.interactive.disabled__text.hex, - }, - }, - }, - }, - }, - modes: { - compact: { - spacings: { - left: x_small, - right: x_small, - top: x_small, - bottom: x_small, - }, - }, - }, -} - -export const error: TextFieldToken = { - border: { - type: 'border', - radius: 0, - width: '1px', - color: colors.interactive.danger__resting.hex, - }, - states: { - focus: { - outline: { - width: '2px', - color: colors.interactive.danger__hover.hex, - style: 'solid', - type: 'outline', - offset: '0px', - }, - }, - }, -} -export const warning: TextFieldToken = { - border: { - type: 'border', - radius: 0, - width: '1px', - color: colors.interactive.warning__resting.hex, - }, - states: { - focus: { - outline: { - width: '2px', - color: colors.interactive.warning__hover.hex, - style: 'solid', - type: 'outline', - offset: '0px', - }, - }, - }, -} -export const success: TextFieldToken = { - border: { - type: 'border', - radius: 0, - width: '1px', - color: colors.interactive.success__resting.hex, - }, - states: { - focus: { - outline: { - width: '2px', - color: colors.interactive.success__hover.hex, - style: 'solid', - type: 'outline', - offset: '0px', - }, - }, - }, -} diff --git a/packages/eds-core-react/src/components/TextField/TextField.tsx b/packages/eds-core-react/src/components/TextField/TextField.tsx index e98f97fc87..11500b6cb8 100644 --- a/packages/eds-core-react/src/components/TextField/TextField.tsx +++ b/packages/eds-core-react/src/components/TextField/TextField.tsx @@ -3,29 +3,31 @@ import { InputHTMLAttributes, TextareaHTMLAttributes, forwardRef, - Ref, + ForwardedRef, } from 'react' -import styled, { ThemeProvider } from 'styled-components' -import { Field } from './Field' -import { Label } from '../Label' -import { HelperText } from './HelperText' -import { TextFieldProvider } from './TextField.context' -import type { Variants } from './types' -import { textfield as tokens } from './TextField.tokens' -import { useToken, useId } from '@equinor/eds-utils' -import { useEds } from '../EdsProvider' +import { useId } from '@equinor/eds-utils' +import { InputWrapper } from '../InputWrapper' +import { Input } from '../Input' +import { Textarea } from '../Textarea' +import type { Variants } from '../types' -const Container = styled.div` - min-width: 100px; - width: 100%; -` +type FieldProps = SharedTextFieldProps & + React.HTMLAttributes +/** Proxy component for working around typescript and element type switching */ +const Field = forwardRef( + function Field(props, ref) { + return props.multiline ? ( +