From 849440f561a973c0bceb428ab935290b9058f40e Mon Sep 17 00:00:00 2001 From: Williams Tardif <32278060+WilliamsTardif@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:09:38 -0400 Subject: [PATCH 1/9] feat(textInput): new adornment added --- .../password-creation-input.tsx | 5 +- .../password-input/password-input.tsx | 5 +- .../components/text-input/text-input.test.tsx | 50 ++ .../text-input/text-input.test.tsx.snap | 762 ++++++++++++++++-- .../src/components/text-input/text-input.tsx | 138 +++- packages/storybook/stories/text-input.mdx | 11 +- .../storybook/stories/text-input.stories.tsx | 16 +- 7 files changed, 878 insertions(+), 109 deletions(-) diff --git a/packages/react/src/components/password-creation-input/password-creation-input.tsx b/packages/react/src/components/password-creation-input/password-creation-input.tsx index 6f0219ddc3..86bcb8e333 100644 --- a/packages/react/src/components/password-creation-input/password-creation-input.tsx +++ b/packages/react/src/components/password-creation-input/password-creation-input.tsx @@ -3,7 +3,6 @@ import styled from 'styled-components'; import { useDeviceContext } from '../device-context-provider/device-context-provider'; import { IconButton } from '../buttons/icon-button'; import { FieldContainer } from '../field-container/field-container'; -import { Input } from '../text-input/text-input'; import { useTranslation } from '../../i18n/use-translation'; import { Tooltip } from '../tooltip/tooltip'; import { getPasswordStrength } from './password-strength'; @@ -12,6 +11,7 @@ import { getDefaultValidationConditions, ValidationCondition } from './validatio import { v4 as uuid } from '../../utils/uuid'; import { PasswordStrengthContainer } from './password-strength-container'; import { useDataAttributes } from '../../hooks/use-data-attributes'; +import { inputsStyle } from '../text-input/styles/inputs'; const StyledUl = styled.ul` font-size: 0.75rem; @@ -26,7 +26,8 @@ const PasswordInputContainer = styled.div` position: relative; `; -const StyledInput = styled(Input)` +const StyledInput = styled.input<{ isMobile: boolean }>` + ${({ theme, isMobile }) => inputsStyle({ theme, isMobile, isFocusable: false })}; padding-right: var(--size-2x); `; diff --git a/packages/react/src/components/password-input/password-input.tsx b/packages/react/src/components/password-input/password-input.tsx index 483adb9c03..995f9eefdb 100644 --- a/packages/react/src/components/password-input/password-input.tsx +++ b/packages/react/src/components/password-input/password-input.tsx @@ -13,8 +13,8 @@ import { v4 as uuid } from '../../utils/uuid'; import { useDataAttributes } from '../../hooks/use-data-attributes'; import { useDeviceContext } from '../device-context-provider/device-context-provider'; import { FieldContainer } from '../field-container/field-container'; -import { Input } from '../text-input/text-input'; import { Tooltip } from '../tooltip/tooltip'; +import { inputsStyle } from '../text-input/styles/inputs'; interface PasswordInputProps { id?: string; @@ -37,7 +37,8 @@ const PasswordInputContainer = styled.div` position: relative; `; -const StyledInput = styled(Input)` +const StyledInput = styled.input<{ isMobile: boolean }>` + ${({ theme, isMobile }) => inputsStyle({ theme, isMobile, isFocusable: false })}; padding-right: var(--size-2x); `; diff --git a/packages/react/src/components/text-input/text-input.test.tsx b/packages/react/src/components/text-input/text-input.test.tsx index fd47712801..64b60f299a 100644 --- a/packages/react/src/components/text-input/text-input.test.tsx +++ b/packages/react/src/components/text-input/text-input.test.tsx @@ -4,6 +4,7 @@ import { doNothing } from '../../test-utils/callbacks'; import { getByTestId } from '../../test-utils/enzyme-selectors'; import { mountWithTheme, renderWithTheme } from '../../test-utils/renderer'; import { TextInput } from './text-input'; +import { Icon } from '../icon/icon'; describe('TextInput', () => { const initialProps = { @@ -186,6 +187,55 @@ describe('TextInput', () => { expect(tree).toMatchSnapshot(); }); + test('Matches the snapshot adornment text', () => { + const tree = renderWithTheme( + , + ); + + expect(tree).toMatchSnapshot(); + }); + + test('Matches the snapshot adornment icon', () => { + const tree = renderWithTheme( + } + />, + ); + + expect(tree).toMatchSnapshot(); + }); + + test('matches the snapshot (Normal - Adornment at end)', () => { + const tree = renderWithTheme( + , + ); + + expect(tree).toMatchSnapshot(); + }); + test('Matches the snapshot [disabled = true]', () => { const tree = renderWithTheme( Telephone - + > + + `; @@ -167,7 +198,7 @@ input + .c1 { margin-bottom: var(--spacing-half); } -.c3 { +.c4 { background: #FFFFFF; border: 1px solid #60666E; border-radius: var(--border-radius); @@ -185,56 +216,81 @@ input + .c1 { outline: none; padding: 0 var(--spacing-1x); width: 100%; + border: 0; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + min-height: 100%; + text-align: left; } -.c3::-webkit-input-placeholder { +.c4::-webkit-input-placeholder { color: #60666E; } -.c3::-moz-placeholder { +.c4::-moz-placeholder { color: #60666E; } -.c3:-ms-input-placeholder { +.c4:-ms-input-placeholder { color: #60666E; } -.c3::placeholder { +.c4::placeholder { color: #60666E; } -.c3:disabled { +.c4:disabled { background-color: #F1F2F2; border-color: #B7BBC2; color: #B7BBC2; } -.c3:disabled, -.c3:disabled::-webkit-input-placeholder { +.c4:disabled, +.c4:disabled::-webkit-input-placeholder { color: #B7BBC2; } -.c3:disabled, -.c3:disabled::-moz-placeholder { +.c4:disabled, +.c4:disabled::-moz-placeholder { color: #B7BBC2; } -.c3:disabled, -.c3:disabled:-ms-input-placeholder { +.c4:disabled, +.c4:disabled:-ms-input-placeholder { color: #B7BBC2; } -.c3:disabled, -.c3:disabled::placeholder { +.c4:disabled, +.c4:disabled::placeholder { color: #B7BBC2; } +.c4:focus, +.c4:disabled { + border: 0; + box-shadow: none; +} + +.c3 { + background: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-sizing: border-box; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + height: var(--size-2x); + border-color: #CD2C23; +} + .c3 { outline: 2px solid transparent; outline-offset: -2px; } -.c3:focus { +.c3:focus-within { box-shadow: 0 0 0 2px #006296; outline: 2px solid #84C6EA; outline-offset: -2px; @@ -251,16 +307,20 @@ input + .c1 { > Telephone (required) - + > + + `; @@ -299,7 +359,524 @@ input + .c1 { margin-bottom: var(--spacing-half); } +.c4 { + background: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-sizing: border-box; + color: #1B1C1E; + font-family: inherit; + font-size: 0.875rem; + -webkit-letter-spacing: 0.015rem; + -moz-letter-spacing: 0.015rem; + -ms-letter-spacing: 0.015rem; + letter-spacing: 0.015rem; + line-height: 1.5rem; + margin: 0; + min-height: var(--size-2x); + outline: none; + padding: 0 var(--spacing-1x); + width: 100%; + border: 0; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + min-height: 100%; + text-align: left; +} + +.c4::-webkit-input-placeholder { + color: #60666E; +} + +.c4::-moz-placeholder { + color: #60666E; +} + +.c4:-ms-input-placeholder { + color: #60666E; +} + +.c4::placeholder { + color: #60666E; +} + +.c4:disabled { + background-color: #F1F2F2; + border-color: #B7BBC2; + color: #B7BBC2; +} + +.c4:disabled, +.c4:disabled::-webkit-input-placeholder { + color: #B7BBC2; +} + +.c4:disabled, +.c4:disabled::-moz-placeholder { + color: #B7BBC2; +} + +.c4:disabled, +.c4:disabled:-ms-input-placeholder { + color: #B7BBC2; +} + +.c4:disabled, +.c4:disabled::placeholder { + color: #B7BBC2; +} + +.c4:focus, +.c4:disabled { + border: 0; + box-shadow: none; +} + +.c3 { + background: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-sizing: border-box; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + height: var(--size-2x); + border-color: #CD2C23; +} + +.c3 { + outline: 2px solid transparent; + outline-offset: -2px; +} + +.c3:focus-within { + box-shadow: 0 0 0 2px #006296; + outline: 2px solid #84C6EA; + outline-offset: -2px; +} + +
+ +
+ +
+
+`; + +exports[`TextInput Matches the snapshot adornment icon 1`] = ` +.c2 { + color: #1B1C1E; + display: block; + font-size: 0.75rem; + font-weight: var(--font-normal); + -webkit-letter-spacing: 0.02rem; + -moz-letter-spacing: 0.02rem; + -ms-letter-spacing: 0.02rem; + letter-spacing: 0.02rem; + line-height: 1.25rem; + margin: 0; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; +} + +input + .c1 { + margin-left: var(--spacing-half); +} + +.c0 { + margin: 0 0 var(--spacing-3x); +} + +.c0 input, +.c0 select, +.c0 textarea { + border-color: #60666E; +} + +.c0 > :nth-child(1) { + margin-bottom: var(--spacing-half); +} + +.c5 { + background: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-sizing: border-box; + color: #1B1C1E; + font-family: inherit; + font-size: 0.875rem; + -webkit-letter-spacing: 0.015rem; + -moz-letter-spacing: 0.015rem; + -ms-letter-spacing: 0.015rem; + letter-spacing: 0.015rem; + line-height: 1.5rem; + margin: 0; + min-height: var(--size-2x); + outline: none; + padding: 0 var(--spacing-1x); + width: 100%; + border: 0; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + min-height: 100%; + text-align: left; +} + +.c5::-webkit-input-placeholder { + color: #60666E; +} + +.c5::-moz-placeholder { + color: #60666E; +} + +.c5:-ms-input-placeholder { + color: #60666E; +} + +.c5::placeholder { + color: #60666E; +} + +.c5:disabled { + background-color: #F1F2F2; + border-color: #B7BBC2; + color: #B7BBC2; +} + +.c5:disabled, +.c5:disabled::-webkit-input-placeholder { + color: #B7BBC2; +} + +.c5:disabled, +.c5:disabled::-moz-placeholder { + color: #B7BBC2; +} + +.c5:disabled, +.c5:disabled:-ms-input-placeholder { + color: #B7BBC2; +} + +.c5:disabled, +.c5:disabled::placeholder { + color: #B7BBC2; +} + +.c5:focus, +.c5:disabled { + border: 0; + box-shadow: none; +} + +.c4 { + -webkit-align-self: center; + -ms-flex-item-align: center; + align-self: center; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding-left: var(--spacing-1x); +} + +.c3 { + background: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-sizing: border-box; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + height: var(--size-2x); + border-color: #CD2C23; +} + +.c3 { + outline: 2px solid transparent; + outline-offset: -2px; +} + +.c3:focus-within { + box-shadow: 0 0 0 2px #006296; + outline: 2px solid #84C6EA; + outline-offset: -2px; +} + +
+ +
+ + + + +
+
+`; + +exports[`TextInput Matches the snapshot adornment text 1`] = ` +.c2 { + color: #1B1C1E; + display: block; + font-size: 0.75rem; + font-weight: var(--font-normal); + -webkit-letter-spacing: 0.02rem; + -moz-letter-spacing: 0.02rem; + -ms-letter-spacing: 0.02rem; + letter-spacing: 0.02rem; + line-height: 1.25rem; + margin: 0; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; +} + +input + .c1 { + margin-left: var(--spacing-half); +} + +.c0 { + margin: 0 0 var(--spacing-3x); +} + +.c0 input, +.c0 select, +.c0 textarea { + border-color: #60666E; +} + +.c0 > :nth-child(1) { + margin-bottom: var(--spacing-half); +} + +.c5 { + background: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-sizing: border-box; + color: #1B1C1E; + font-family: inherit; + font-size: 0.875rem; + -webkit-letter-spacing: 0.015rem; + -moz-letter-spacing: 0.015rem; + -ms-letter-spacing: 0.015rem; + letter-spacing: 0.015rem; + line-height: 1.5rem; + margin: 0; + min-height: var(--size-2x); + outline: none; + padding: 0 var(--spacing-1x); + width: 100%; + border: 0; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + min-height: 100%; + text-align: left; +} + +.c5::-webkit-input-placeholder { + color: #60666E; +} + +.c5::-moz-placeholder { + color: #60666E; +} + +.c5:-ms-input-placeholder { + color: #60666E; +} + +.c5::placeholder { + color: #60666E; +} + +.c5:disabled { + background-color: #F1F2F2; + border-color: #B7BBC2; + color: #B7BBC2; +} + +.c5:disabled, +.c5:disabled::-webkit-input-placeholder { + color: #B7BBC2; +} + +.c5:disabled, +.c5:disabled::-moz-placeholder { + color: #B7BBC2; +} + +.c5:disabled, +.c5:disabled:-ms-input-placeholder { + color: #B7BBC2; +} + +.c5:disabled, +.c5:disabled::placeholder { + color: #B7BBC2; +} + +.c5:focus, +.c5:disabled { + border: 0; + box-shadow: none; +} + +.c4 { + -webkit-align-self: center; + -ms-flex-item-align: center; + align-self: center; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding-left: var(--spacing-1x); +} + .c3 { + background: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-sizing: border-box; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + height: var(--size-2x); + border-color: #CD2C23; +} + +.c3 { + outline: 2px solid transparent; + outline-offset: -2px; +} + +.c3:focus-within { + box-shadow: 0 0 0 2px #006296; + outline: 2px solid #84C6EA; + outline-offset: -2px; +} + +
+ +
+ + # + + +
+
+`; + +exports[`TextInput matches the snapshot (Normal - Adornment at end) 1`] = ` +.c2 { + color: #1B1C1E; + display: block; + font-size: 0.75rem; + font-weight: var(--font-normal); + -webkit-letter-spacing: 0.02rem; + -moz-letter-spacing: 0.02rem; + -ms-letter-spacing: 0.02rem; + letter-spacing: 0.02rem; + line-height: 1.25rem; + margin: 0; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; +} + +input + .c1 { + margin-left: var(--spacing-half); +} + +.c0 { + margin: 0 0 var(--spacing-3x); +} + +.c0 input, +.c0 select, +.c0 textarea { + border-color: #60666E; +} + +.c0 > :nth-child(1) { + margin-bottom: var(--spacing-half); +} + +.c4 { background: #FFFFFF; border: 1px solid #60666E; border-radius: var(--border-radius); @@ -317,56 +894,92 @@ input + .c1 { outline: none; padding: 0 var(--spacing-1x); width: 100%; + border: 0; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + min-height: 100%; + text-align: right; } -.c3::-webkit-input-placeholder { +.c4::-webkit-input-placeholder { color: #60666E; } -.c3::-moz-placeholder { +.c4::-moz-placeholder { color: #60666E; } -.c3:-ms-input-placeholder { +.c4:-ms-input-placeholder { color: #60666E; } -.c3::placeholder { +.c4::placeholder { color: #60666E; } -.c3:disabled { +.c4:disabled { background-color: #F1F2F2; border-color: #B7BBC2; color: #B7BBC2; } -.c3:disabled, -.c3:disabled::-webkit-input-placeholder { +.c4:disabled, +.c4:disabled::-webkit-input-placeholder { color: #B7BBC2; } -.c3:disabled, -.c3:disabled::-moz-placeholder { +.c4:disabled, +.c4:disabled::-moz-placeholder { color: #B7BBC2; } -.c3:disabled, -.c3:disabled:-ms-input-placeholder { +.c4:disabled, +.c4:disabled:-ms-input-placeholder { color: #B7BBC2; } -.c3:disabled, -.c3:disabled::placeholder { +.c4:disabled, +.c4:disabled::placeholder { color: #B7BBC2; } +.c4:focus, +.c4:disabled { + border: 0; + box-shadow: none; +} + +.c5 { + -webkit-align-self: center; + -ms-flex-item-align: center; + align-self: center; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding-right: var(--spacing-1x); +} + +.c3 { + background: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-sizing: border-box; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + height: var(--size-2x); + border-color: #CD2C23; +} + .c3 { outline: 2px solid transparent; outline-offset: -2px; } -.c3:focus { +.c3:focus-within { box-shadow: 0 0 0 2px #006296; outline: 2px solid #84C6EA; outline-offset: -2px; @@ -383,14 +996,23 @@ input + .c1 { > Telephone - + > + + + # + + `; diff --git a/packages/react/src/components/text-input/text-input.tsx b/packages/react/src/components/text-input/text-input.tsx index 731ee6cc6b..2f7c21295a 100644 --- a/packages/react/src/components/text-input/text-input.tsx +++ b/packages/react/src/components/text-input/text-input.tsx @@ -8,12 +8,14 @@ import { KeyboardEvent, MouseEvent, ReactElement, + ReactNode, Ref, useCallback, - useEffect, + useEffect, useImperativeHandle, + useMemo, useRef, useState, } from 'react'; -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; import { useDataAttributes } from '../../hooks/use-data-attributes'; import { useTranslation } from '../../i18n/use-translation'; import { useDeviceContext } from '../device-context-provider/device-context-provider'; @@ -22,15 +24,67 @@ import { TooltipProps } from '../tooltip/tooltip'; import { inputsStyle } from './styles/inputs'; import { useAriaConditionalIds } from '../../hooks/use-aria-conditional-ids'; import { useId } from '../../hooks/use-id'; +import { focus } from '../../utils/css-state'; -export const Input = styled.input<{ isMobile: boolean; }>` - ${({ theme, isMobile }) => inputsStyle({ theme, isMobile })} +interface StyledInputProps { + isMobile: boolean; + $textAlign?: 'left' | 'right'; +} + +const StyleInput = styled.input` + ${({ theme, isMobile }) => inputsStyle({ theme, isMobile, isFocusable: false })}; + border: 0; + flex: 1 1 auto; + min-height: 100%; + text-align: ${({ $textAlign }) => $textAlign}; + &:focus, + &:disabled { + border: 0; + box-shadow: none; + } +`; + +const Adornment = styled.span<{ $position: 'start' | 'end' }>` + align-self: center; + display: flex; + padding-left: ${({ $position }) => ($position === 'start' ? 'var(--spacing-1x)' : undefined)}; + padding-right: ${({ $position }) => ($position === 'end' ? 'var(--spacing-1x)' : undefined)}; +`; + +interface StyledWrapperProps { + $disabled?: boolean; + $invalid?: boolean; +} + +const Wrapper = styled.div` + background: ${({ theme }) => theme.component['numeric-input-background-color']}; + border: 1px solid ${({ theme }) => theme.component['numeric-input-border-color']}; + border-radius: var(--border-radius); + box-sizing: border-box; + display: flex; + height: var(--size-2x); + + ${({ theme }) => focus({ theme }, { focusType: 'focus-within' })}; + + ${({ $invalid, theme }) => $invalid && css` + border-color: ${theme.component['numeric-input-error-border-color']}; +`}; + ${({ $disabled, theme }) => $disabled && css` + background-color: ${theme.component['numeric-input-disabled-background-color']}; + border-color: ${theme.component['numeric-input-disabled-border-color']}; + + ${Adornment} { + color: ${theme.component['numeric-input-disabled-adornment-text-color']}; + } + `}; `; type PartialInputProps = Pick, HTMLInputElement>, 'inputMode' | 'name' | 'value' | 'autoComplete'>; interface TextInputProps extends PartialInputProps { + adornment?: ReactNode; + adornmentPosition?: 'start' | 'end'; ariaDescribedBy?: string; ariaInvalid?: boolean; className?: string; @@ -63,6 +117,8 @@ interface TextInputProps extends PartialInputProps { } export const TextInput = forwardRef(({ + adornment, + adornmentPosition = 'start', ariaDescribedBy, ariaInvalid, className, @@ -96,6 +152,7 @@ export const TextInput = forwardRef(({ const [{ validity }, setValidity] = useState({ validity: valid ?? true }); const fieldId = useId(providedId); const dataAttributes = useDataAttributes(otherProps); + const inputRef = useRef(null); const processedAriaDescribedBy = useAriaConditionalIds([ { id: ariaDescribedBy }, @@ -103,6 +160,11 @@ export const TextInput = forwardRef(({ { id: `${fieldId}_hint`, include: !!hint }, ]); + useImperativeHandle(ref, () => inputRef.current as HTMLInputElement); + const handleAdornmentClick = (): void => { + inputRef.current?.focus(); + }; + const handleBlur: (event: FocusEvent) => void = useCallback((event) => { if (valid === undefined) { setValidity({ validity: event.currentTarget.checkValidity() }); @@ -131,6 +193,17 @@ export const TextInput = forwardRef(({ } }, [onFocus]); + const adornmentContent = useMemo(() => ( + adornment ? ( + + {adornment} + + ) : null + ), [adornment, adornmentPosition]); + useEffect(() => { if (valid !== undefined) { setValidity({ validity: valid }); @@ -150,32 +223,37 @@ export const TextInput = forwardRef(({ hint={hint} data-testid="field-container" > - + + {(adornment && adornmentPosition === 'start') && adornmentContent} + + {(adornment && adornmentPosition === 'end') && adornmentContent} + ); }); diff --git a/packages/storybook/stories/text-input.mdx b/packages/storybook/stories/text-input.mdx index c59edce886..3c48ea6015 100644 --- a/packages/storybook/stories/text-input.mdx +++ b/packages/storybook/stories/text-input.mdx @@ -11,25 +11,28 @@ import * as TextInputStories from './text-input.stories'; 4. [Properties](#properties) ## Definition -Text input allows users to enter a single line of text into a form. +Text input allows users to enter a single line of text into a form. ## Usage -### When to use +### When to use - Use to enter a short text (usually one or two words) [1]. - E.g. Name, Last Name, Role, Organization, etc. - Use for input that needs unique information that cannot be predicted with a preset of options [2] - Use to input data that can be entered more quickly in a free-hand format versus a more complex control [2]. -### When not to use +### When not to use - Do not use for numeric inputs, date input, passwords or long text strings. - For long text strings, consider using the text area component. - Do not use if a user can only enter an option from a predefined list as it is likely to result in an error. Consider using a control component such as a dropdown, select, or radio button group instead [2]. -## Variants +## Variants ### Default +### With icon as adornment + + ## Properties diff --git a/packages/storybook/stories/text-input.stories.tsx b/packages/storybook/stories/text-input.stories.tsx index 236cf54b16..09164b90c9 100644 --- a/packages/storybook/stories/text-input.stories.tsx +++ b/packages/storybook/stories/text-input.stories.tsx @@ -1,4 +1,4 @@ -import { TextInput } from '@equisoft/design-elements-react'; +import {Icon, TextInput} from '@equisoft/design-elements-react'; import { Meta, StoryObj } from '@storybook/react'; import { rawCodeParameters } from './utils/parameters'; @@ -7,8 +7,12 @@ const TextInputMeta: Meta = { component: TextInput, args: { type: 'text', + adornment: '%', }, argTypes: { + adornment: { + control: { disable: true }, + }, onBlur: { control: { disable: true }, }, @@ -45,6 +49,16 @@ export const Default: Story = { }, }; +export const IconAsAdornment: Story = { + ...TextInputMeta, + args: { + label: 'First Name', + validationErrorMessage: 'Error message', + adornment: , + hint: 'Hint', + }, +}; + export const EventCallbacks: Story = { ...TextInputMeta, parameters: rawCodeParameters, From a495961a17b200e646092b9e126581a01cc05756 Mon Sep 17 00:00:00 2001 From: Williams Tardif <32278060+WilliamsTardif@users.noreply.github.com> Date: Mon, 22 Jul 2024 11:53:51 -0400 Subject: [PATCH 2/9] fix(textInput): clean the code --- .../src/components/text-input/text-input.tsx | 34 ++++++++++--------- .../tokens/component/text-input-tokens.ts | 5 ++- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/packages/react/src/components/text-input/text-input.tsx b/packages/react/src/components/text-input/text-input.tsx index 2f7c21295a..f70a1bbbc6 100644 --- a/packages/react/src/components/text-input/text-input.tsx +++ b/packages/react/src/components/text-input/text-input.tsx @@ -11,8 +11,10 @@ import { ReactNode, Ref, useCallback, - useEffect, useImperativeHandle, - useMemo, useRef, + useEffect, + useImperativeHandle, + useMemo, + useRef, useState, } from 'react'; import styled, { css } from 'styled-components'; @@ -53,12 +55,12 @@ const Adornment = styled.span<{ $position: 'start' | 'end' }>` interface StyledWrapperProps { $disabled?: boolean; - $invalid?: boolean; + $valid?: boolean; } const Wrapper = styled.div` - background: ${({ theme }) => theme.component['numeric-input-background-color']}; - border: 1px solid ${({ theme }) => theme.component['numeric-input-border-color']}; + background: ${({ theme }) => theme.component['text-input-background-color']}; + border: 1px solid ${({ theme }) => theme.component['text-input-border-color']}; border-radius: var(--border-radius); box-sizing: border-box; display: flex; @@ -66,15 +68,15 @@ const Wrapper = styled.div` ${({ theme }) => focus({ theme }, { focusType: 'focus-within' })}; - ${({ $invalid, theme }) => $invalid && css` - border-color: ${theme.component['numeric-input-error-border-color']}; + ${({ $valid, theme }) => !$valid && css` + border-color: ${theme.component['text-input-error-border-color']}; `}; ${({ $disabled, theme }) => $disabled && css` - background-color: ${theme.component['numeric-input-disabled-background-color']}; - border-color: ${theme.component['numeric-input-disabled-border-color']}; + background-color: ${theme.component['text-input-disabled-background-color']}; + border-color: ${theme.component['text-input-disabled-border-color']}; ${Adornment} { - color: ${theme.component['numeric-input-disabled-adornment-text-color']}; + color: ${theme.component['text-input-disabled-adornment-text-color']}; } `}; `; @@ -160,11 +162,6 @@ export const TextInput = forwardRef(({ { id: `${fieldId}_hint`, include: !!hint }, ]); - useImperativeHandle(ref, () => inputRef.current as HTMLInputElement); - const handleAdornmentClick = (): void => { - inputRef.current?.focus(); - }; - const handleBlur: (event: FocusEvent) => void = useCallback((event) => { if (valid === undefined) { setValidity({ validity: event.currentTarget.checkValidity() }); @@ -193,6 +190,11 @@ export const TextInput = forwardRef(({ } }, [onFocus]); + useImperativeHandle(ref, () => inputRef.current as HTMLInputElement); + const handleAdornmentClick = (): void => { + inputRef.current?.focus(); + }; + const adornmentContent = useMemo(() => ( adornment ? ( - + {(adornment && adornmentPosition === 'start') && adornmentContent} Date: Mon, 22 Jul 2024 16:34:56 -0400 Subject: [PATCH 3/9] fix(TextInput): fixing tests --- .../money-input/money-input.test.tsx.snap | 222 +++++++---- .../password-input.test.tsx.snap | 354 ++++++++---------- .../text-input/text-input.test.tsx.snap | 6 - 3 files changed, 309 insertions(+), 273 deletions(-) diff --git a/packages/react/src/components/money-input/money-input.test.tsx.snap b/packages/react/src/components/money-input/money-input.test.tsx.snap index 8f48aa6938..6430252aa9 100644 --- a/packages/react/src/components/money-input/money-input.test.tsx.snap +++ b/packages/react/src/components/money-input/money-input.test.tsx.snap @@ -15,7 +15,7 @@ exports[`CurrencyInput Component matches snapshot (en-CA) 1`] = ` margin-bottom: var(--spacing-half); } -.c2 { +.c3 { background: #FFFFFF; border: 1px solid #60666E; border-radius: var(--border-radius); @@ -33,56 +33,80 @@ exports[`CurrencyInput Component matches snapshot (en-CA) 1`] = ` outline: none; padding: 0 var(--spacing-1x); width: 100%; + border: 0; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + min-height: 100%; + text-align: left; } -.c2::-webkit-input-placeholder { +.c3::-webkit-input-placeholder { color: #60666E; } -.c2::-moz-placeholder { +.c3::-moz-placeholder { color: #60666E; } -.c2:-ms-input-placeholder { +.c3:-ms-input-placeholder { color: #60666E; } -.c2::placeholder { +.c3::placeholder { color: #60666E; } -.c2:disabled { +.c3:disabled { background-color: #F1F2F2; border-color: #B7BBC2; color: #B7BBC2; } -.c2:disabled, -.c2:disabled::-webkit-input-placeholder { +.c3:disabled, +.c3:disabled::-webkit-input-placeholder { color: #B7BBC2; } -.c2:disabled, -.c2:disabled::-moz-placeholder { +.c3:disabled, +.c3:disabled::-moz-placeholder { color: #B7BBC2; } -.c2:disabled, -.c2:disabled:-ms-input-placeholder { +.c3:disabled, +.c3:disabled:-ms-input-placeholder { color: #B7BBC2; } -.c2:disabled, -.c2:disabled::placeholder { +.c3:disabled, +.c3:disabled::placeholder { color: #B7BBC2; } +.c3:focus, +.c3:disabled { + border: 0; + box-shadow: none; +} + +.c2 { + background: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-sizing: border-box; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + height: var(--size-2x); +} + .c2 { outline: 2px solid transparent; outline-offset: -2px; } -.c2:focus { +.c2:focus-within { box-shadow: 0 0 0 2px #006296; outline: 2px solid #84C6EA; outline-offset: -2px; @@ -100,15 +124,19 @@ exports[`CurrencyInput Component matches snapshot (en-CA) 1`] = ` class="c1" data-testid="field-container" > - + > + + `; @@ -128,7 +156,7 @@ exports[`CurrencyInput Component matches snapshot (en-US) 1`] = ` margin-bottom: var(--spacing-half); } -.c2 { +.c3 { background: #FFFFFF; border: 1px solid #60666E; border-radius: var(--border-radius); @@ -146,56 +174,80 @@ exports[`CurrencyInput Component matches snapshot (en-US) 1`] = ` outline: none; padding: 0 var(--spacing-1x); width: 100%; + border: 0; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + min-height: 100%; + text-align: left; } -.c2::-webkit-input-placeholder { +.c3::-webkit-input-placeholder { color: #60666E; } -.c2::-moz-placeholder { +.c3::-moz-placeholder { color: #60666E; } -.c2:-ms-input-placeholder { +.c3:-ms-input-placeholder { color: #60666E; } -.c2::placeholder { +.c3::placeholder { color: #60666E; } -.c2:disabled { +.c3:disabled { background-color: #F1F2F2; border-color: #B7BBC2; color: #B7BBC2; } -.c2:disabled, -.c2:disabled::-webkit-input-placeholder { +.c3:disabled, +.c3:disabled::-webkit-input-placeholder { color: #B7BBC2; } -.c2:disabled, -.c2:disabled::-moz-placeholder { +.c3:disabled, +.c3:disabled::-moz-placeholder { color: #B7BBC2; } -.c2:disabled, -.c2:disabled:-ms-input-placeholder { +.c3:disabled, +.c3:disabled:-ms-input-placeholder { color: #B7BBC2; } -.c2:disabled, -.c2:disabled::placeholder { +.c3:disabled, +.c3:disabled::placeholder { color: #B7BBC2; } +.c3:focus, +.c3:disabled { + border: 0; + box-shadow: none; +} + +.c2 { + background: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-sizing: border-box; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + height: var(--size-2x); +} + .c2 { outline: 2px solid transparent; outline-offset: -2px; } -.c2:focus { +.c2:focus-within { box-shadow: 0 0 0 2px #006296; outline: 2px solid #84C6EA; outline-offset: -2px; @@ -213,15 +265,19 @@ exports[`CurrencyInput Component matches snapshot (en-US) 1`] = ` class="c1" data-testid="field-container" > - + > + + `; @@ -241,7 +297,7 @@ exports[`CurrencyInput Component matches snapshot (fr-CA) 1`] = ` margin-bottom: var(--spacing-half); } -.c2 { +.c3 { background: #FFFFFF; border: 1px solid #60666E; border-radius: var(--border-radius); @@ -259,56 +315,80 @@ exports[`CurrencyInput Component matches snapshot (fr-CA) 1`] = ` outline: none; padding: 0 var(--spacing-1x); width: 100%; + border: 0; + -webkit-flex: 1 1 auto; + -ms-flex: 1 1 auto; + flex: 1 1 auto; + min-height: 100%; + text-align: left; } -.c2::-webkit-input-placeholder { +.c3::-webkit-input-placeholder { color: #60666E; } -.c2::-moz-placeholder { +.c3::-moz-placeholder { color: #60666E; } -.c2:-ms-input-placeholder { +.c3:-ms-input-placeholder { color: #60666E; } -.c2::placeholder { +.c3::placeholder { color: #60666E; } -.c2:disabled { +.c3:disabled { background-color: #F1F2F2; border-color: #B7BBC2; color: #B7BBC2; } -.c2:disabled, -.c2:disabled::-webkit-input-placeholder { +.c3:disabled, +.c3:disabled::-webkit-input-placeholder { color: #B7BBC2; } -.c2:disabled, -.c2:disabled::-moz-placeholder { +.c3:disabled, +.c3:disabled::-moz-placeholder { color: #B7BBC2; } -.c2:disabled, -.c2:disabled:-ms-input-placeholder { +.c3:disabled, +.c3:disabled:-ms-input-placeholder { color: #B7BBC2; } -.c2:disabled, -.c2:disabled::placeholder { +.c3:disabled, +.c3:disabled::placeholder { color: #B7BBC2; } +.c3:focus, +.c3:disabled { + border: 0; + box-shadow: none; +} + +.c2 { + background: #FFFFFF; + border: 1px solid #60666E; + border-radius: var(--border-radius); + box-sizing: border-box; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + height: var(--size-2x); +} + .c2 { outline: 2px solid transparent; outline-offset: -2px; } -.c2:focus { +.c2:focus-within { box-shadow: 0 0 0 2px #006296; outline: 2px solid #84C6EA; outline-offset: -2px; @@ -326,15 +406,19 @@ exports[`CurrencyInput Component matches snapshot (fr-CA) 1`] = ` class="c1" data-testid="field-container" > - + > + + `; diff --git a/packages/react/src/components/password-input/password-input.test.tsx.snap b/packages/react/src/components/password-input/password-input.test.tsx.snap index 459d17d005..51e243f824 100644 --- a/packages/react/src/components/password-input/password-input.test.tsx.snap +++ b/packages/react/src/components/password-input/password-input.test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` -.c6 { +.c5 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -41,23 +41,24 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` user-select: none; } -.c6 { +.c5 { outline: 2px solid transparent; outline-offset: -2px; } -.c6:focus { +.c5:focus { box-shadow: 0 0 0 2px #006296; outline: 2px solid #84C6EA; outline-offset: -2px; } -.c6 > svg { +.c5 > svg { + color: inherit; height: var(--size-1x); width: var(--size-1x); } -.c7 { +.c6 { background-color: transparent; border-color: transparent; color: #60666E; @@ -66,43 +67,42 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` width: var(--size-1halfx); } -.c7 { +.c6 { outline: 2px solid transparent; outline-offset: -2px; } -.c7:focus { +.c6:focus { box-shadow: 0 0 0 2px #006296; outline: 2px solid #84C6EA; outline-offset: -2px; } -.c7:hover, -.c7[aria-expanded='true'] { +.c6:hover, +.c6[aria-expanded='true'] { background-color: rgb(0 0 0 / 0.15); border-color: transparent; color: #000000; } -.c7[aria-disabled='true'] { +.c6:disabled { background-color: transparent; border-color: transparent; color: #B7BBC2; - cursor: not-allowed; } -.c7 > svg { +.c6 > svg { height: var(--size-1x); width: var(--size-1x); } -.c10 { +.c9 { height: 1.25rem; position: absolute; width: 1rem; } -.c10::before { +.c9::before { border-style: solid; content: ''; display: block; @@ -111,7 +111,7 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` width: 0; } -.c10::after { +.c9::after { border-style: solid; content: ''; display: block; @@ -121,7 +121,7 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` width: 0; } -.c8 { +.c7 { background-color: #60666E; border: 1px solid #FFFFFF; border-radius: var(--border-radius-half); @@ -147,7 +147,7 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` z-index: 1000; } -.c8[data-popper-placement*="bottom"] > .c9 { +.c7[data-popper-placement*="bottom"] > .c8 { height: 1rem; left: 0; margin-top: -0.375rem; @@ -155,19 +155,19 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` width: 1rem; } -.c8[data-popper-placement*="bottom"] > .c9::before { +.c7[data-popper-placement*="bottom"] > .c8::before { border-color: transparent transparent #FFFFFF transparent; border-width: 0 0.5rem 0.5rem; position: absolute; top: -2px; } -.c8[data-popper-placement*="bottom"] > .c9::after { +.c7[data-popper-placement*="bottom"] > .c8::after { border-color: transparent transparent #60666E transparent; border-width: 0 0.5rem 0.5rem; } -.c8[data-popper-placement*="top"] > .c9 { +.c7[data-popper-placement*="top"] > .c8 { bottom: 0; height: 0; left: 0; @@ -175,58 +175,58 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` width: 1rem; } -.c8[data-popper-placement*="top"] > .c9::before { +.c7[data-popper-placement*="top"] > .c8::before { border-color: #FFFFFF transparent transparent transparent; border-width: 0.5rem 0.5rem 0; position: absolute; top: 0; } -.c8[data-popper-placement*="top"] > .c9::after { +.c7[data-popper-placement*="top"] > .c8::after { border-color: #60666E transparent transparent transparent; border-width: 0.5rem 0.5rem 0; top: -0.1rem; } -.c8[data-popper-placement*="right"] > .c9 { +.c7[data-popper-placement*="right"] > .c8 { height: 1rem; left: 0; margin-left: -0.8rem; width: 1rem; } -.c8[data-popper-placement*="right"] > .c9::before { +.c7[data-popper-placement*="right"] > .c8::before { border-color: transparent #FFFFFF transparent transparent; border-width: 0.5rem 0.5rem 0.5rem 0; } -.c8[data-popper-placement*="right"] > .c9::after { +.c7[data-popper-placement*="right"] > .c8::after { border-color: transparent #60666E transparent transparent; border-width: 0.5rem 0.5rem 0.5rem 0; left: 0.375rem; top: 0; } -.c8[data-popper-placement*="left"] > .c9 { +.c7[data-popper-placement*="left"] > .c8 { height: 1rem; margin-right: -0.7rem; right: -1px; width: 1rem; } -.c8[data-popper-placement*="left"] > .c9::before { +.c7[data-popper-placement*="left"] > .c8::before { border-color: transparent transparent transparent #FFFFFF; border-width: 0.5rem 0 0.5rem 0.5rem; } -.c8[data-popper-placement*="left"] > .c9::after { +.c7[data-popper-placement*="left"] > .c8::after { border-color: transparent transparent transparent #60666E; border-width: 0.5rem 0 0.5rem 0.5rem; left: 0.17rem; top: 0; } -.c11 { +.c10 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -237,7 +237,7 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` display: flex; } -.c5 { +.c4 { -webkit-align-items: center; -webkit-box-align: center; -ms-flex-align: center; @@ -251,12 +251,12 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` width: fit-content; } -.c5 { +.c4 { outline: 2px solid transparent; outline-offset: -2px; } -.c5:focus { +.c4:focus { box-shadow: 0 0 0 2px #006296; outline: 2px solid #84C6EA; outline-offset: -2px; @@ -276,6 +276,18 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` margin-bottom: var(--spacing-half); } +.c1 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + position: relative; +} + .c2 { background: #FFFFFF; border: 1px solid #60666E; @@ -294,6 +306,7 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` outline: none; padding: 0 var(--spacing-1x); width: 100%; + padding-right: var(--size-2x); } .c2::-webkit-input-placeholder { @@ -338,34 +351,7 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = ` color: #B7BBC2; } -.c2 { - outline: 2px solid transparent; - outline-offset: -2px; -} - -.c2:focus { - box-shadow: 0 0 0 2px #006296; - outline: 2px solid #84C6EA; - outline-offset: -2px; -} - -.c1 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - position: relative; -} - .c3 { - padding-right: var(--size-2x); -} - -.c4 { position: absolute; right: 0.25rem; } @@ -379,7 +365,7 @@ exports[`PasswordInput matches the snapshot (Disabled) 1`] = `