From 015e68283d3b49ed0e399ac25ea144214d77085d Mon Sep 17 00:00:00 2001 From: Chaitanya Deorukhkar Date: Thu, 28 Mar 2024 11:11:49 +0530 Subject: [PATCH] feat: Redesign all `Input` components (#2030) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changes ### TextInput - Redesigned UI - Add `leadingIcon` prop - ⚠️ Deprecate `icon` prop in favour of `leadingIcon` which will be removed in the next major version - Add `trailingIcon` prop - Add `trailingLinkButton` prop - Add `size` prop ### TextArea - Redesigned UI - Add `size` prop ### PasswordInput - Redesigned UI - Adds `size` prop ### OTPInput - Redesigned UI - Add `size` prop ### SelectInput - Redesigned UI - Add `size` prop ### Autocomplete - Redesigned UI - Add `size` prop ### Radio - Add `size` prop ### Checkbox - Add `size` prop --- .changeset/heavy-badgers-care.md | 42 + packages/blade/jest.web.config.js | 1 + .../components/Checkbox/Checkbox.stories.tsx | 7 + .../src/components/Checkbox/Checkbox.tsx | 23 +- .../Checkbox/CheckboxGroup.stories.tsx | 6 + .../Checkbox/CheckboxGroup/CheckboxGroup.tsx | 4 +- .../Checkbox/CheckboxIcon/CheckboxIcon.tsx | 16 +- .../Checkbox.native.test.tsx.snap | 2 +- .../__snapshots__/Checkbox.web.test.tsx.snap | 4 +- .../CheckboxGroup.native.test.tsx.snap | 8 +- .../CheckboxGroup.ssr.test.tsx.snap | 90 +- .../CheckboxGroup.web.test.tsx.snap | 160 +- .../src/components/Checkbox/checkboxTokens.ts | 16 +- .../Dropdown/__tests__/Dropdown.web.test.tsx | 22 + .../Dropdown.native.test.tsx.snap | 713 +++--- .../__snapshots__/Dropdown.ssr.test.tsx.snap | 254 +- .../__snapshots__/Dropdown.web.test.tsx.snap | 1368 ++++++++++- .../docs/DropdownWithAutoComplete.stories.tsx | 9 + .../docs/DropdownWithSelect.stories.tsx | 9 + .../Dropdown/docs/autoCompleteStories.tsx | 70 + .../src/components/Dropdown/docs/stories.ts | 65 + .../FileUpload.ssr.test.tsx.snap | 4 +- .../FileUpload.web.test.tsx.snap | 4 +- .../CharacterCounter/CharacterCounter.tsx | 10 +- .../blade/src/components/Form/FormHint.tsx | 43 +- .../blade/src/components/Form/FormLabel.tsx | 39 +- .../Form/Selector/SelectorSupportText.tsx | 9 +- .../Form/Selector/SelectorTitle.tsx | 2 +- .../blade/src/components/Form/formTokens.ts | 67 + .../AnimatedBaseInputWrapper.native.tsx | 99 +- .../AnimatedBaseInputWrapper.web.tsx | 109 +- .../Input/BaseInput/BaseInput.stories.tsx | 61 +- .../components/Input/BaseInput/BaseInput.tsx | 248 +- .../BaseInputAnimatedBorder.native.tsx | 74 - .../BaseInput/BaseInputAnimatedBorder.web.tsx | 80 - .../BaseInput/BaseInputTagSlot.native.tsx | 10 +- .../Input/BaseInput/BaseInputTagSlot.web.tsx | 11 +- .../Input/BaseInput/BaseInputVisuals.tsx | 127 +- .../Input/BaseInput/BaseInputWrapper.tsx | 6 +- .../BaseInput/StyledBaseInput.native.tsx | 30 +- .../Input/BaseInput/StyledBaseInput.web.tsx | 12 +- .../__tests__/BaseInput.native.test.tsx | 26 + .../__tests__/BaseInput.web.test.tsx | 26 + .../BaseInput.native.test.tsx.snap | 1643 ++++++++++--- .../__snapshots__/BaseInput.ssr.test.tsx.snap | 128 +- .../__snapshots__/BaseInput.web.test.tsx.snap | 1450 ++++++++++-- .../Input/BaseInput/baseInputConfig.ts | 23 - .../Input/BaseInput/baseInputStyles.ts | 166 +- .../Input/BaseInput/baseInputTokens.ts | 87 + .../getBaseInputBorderStyles.native.ts | 15 + .../BaseInput/getBaseInputBorderStyles.web.ts | 37 + .../src/components/Input/BaseInput/types.ts | 4 + .../BaseDropdownInputTrigger.tsx | 7 +- .../SelectInput.stories.tsx | 5 + .../__tests__/AutoComplete.web.test.tsx | 76 + .../AutoComplete.native.test.tsx.snap | 690 +++--- .../AutoComplete.ssr.test.tsx.snap | 244 +- .../AutoComplete.web.test.tsx.snap | 1683 ++++++++++--- .../Input/DropdownInputTriggers/types.ts | 1 + .../Input/OTPInput/OTPInput.stories.tsx | 129 +- .../components/Input/OTPInput/OTPInput.tsx | 7 +- .../__tests__/OTPInput.native.test.tsx | 6 + .../OTPInput/__tests__/OTPInput.web.test.tsx | 6 + .../OTPInput.native.test.tsx.snap | 2097 +++++++++++++---- .../__snapshots__/OTPInput.ssr.test.tsx.snap | 351 +-- .../__snapshots__/OTPInput.web.test.tsx.snap | 975 ++++++-- .../PasswordInput/PasswordInput.stories.tsx | 30 +- .../Input/PasswordInput/PasswordInput.tsx | 3 + .../__tests__/PasswordInput.native.test.tsx | 6 + .../__tests__/PasswordInput.web.test.tsx | 13 + .../PasswordInput.native.test.tsx.snap | 843 +++++-- .../PasswordInput.ssr.test.tsx.snap | 206 +- .../PasswordInput.web.test.tsx.snap | 720 +++++- .../Input/TextArea/TextArea.stories.tsx | 36 + .../components/Input/TextArea/TextArea.tsx | 6 +- .../TextArea/__tests__/TextArea.web.test.tsx | 8 + .../TextArea.native.test.tsx.snap | 163 +- .../__snapshots__/TextArea.ssr.test.tsx.snap | 130 +- .../__snapshots__/TextArea.web.test.tsx.snap | 693 +++++- .../Input/TextInput/TextInput.stories.tsx | 112 +- .../components/Input/TextInput/TextInput.tsx | 25 +- .../__tests__/TextInput.web.test.tsx | 21 +- .../TextInput.native.test.tsx.snap | 656 +++--- .../__snapshots__/TextInput.ssr.test.tsx.snap | 250 +- .../__snapshots__/TextInput.web.test.tsx.snap | 1078 +++++++-- .../src/components/Radio/Radio.stories.tsx | 6 + packages/blade/src/components/Radio/Radio.tsx | 16 +- .../Radio/RadioGroup/RadioGroup.tsx | 4 +- .../__snapshots__/Radio.ssr.test.tsx.snap | 76 +- .../__snapshots__/Radio.web.test.tsx.snap | 4 +- .../RadioGroup.native.test.tsx.snap | 8 +- .../RadioGroup.ssr.test.tsx.snap | 78 +- .../RadioGroup.web.test.tsx.snap | 142 +- .../blade/src/components/Radio/radioTokens.ts | 17 +- .../src/components/Tag/AnimatedTag.web.tsx | 2 + packages/blade/src/components/Tag/Tag.tsx | 15 +- .../__snapshots__/Tag.native.test.tsx.snap | 4 +- .../__snapshots__/Tag.ssr.test.tsx.snap | 7 +- .../__snapshots__/Tag.web.test.tsx.snap | 5 +- .../blade/src/components/Tag/getTagsGroup.tsx | 2 + packages/blade/src/components/Tag/types.ts | 2 + .../components/Typography/Heading/Heading.tsx | 4 +- .../Typography/Heading/getHeadingStyles.ts | 18 + .../src/components/Typography/Text/Text.tsx | 13 +- .../Text/__tests__/Text.native.test.tsx | 6 +- .../Text/__tests__/Text.web.test.tsx | 6 +- packages/blade/src/tokens/global/size.ts | 2 + .../getFocusRingStyles.native.ts | 4 +- .../getFocusRingStyles.web.ts | 9 +- .../src/utils/getFocusRingStyles/types.ts | 8 + 110 files changed, 14508 insertions(+), 4765 deletions(-) create mode 100644 .changeset/heavy-badgers-care.md create mode 100644 packages/blade/src/components/Form/formTokens.ts delete mode 100644 packages/blade/src/components/Input/BaseInput/BaseInputAnimatedBorder.native.tsx delete mode 100644 packages/blade/src/components/Input/BaseInput/BaseInputAnimatedBorder.web.tsx delete mode 100644 packages/blade/src/components/Input/BaseInput/baseInputConfig.ts create mode 100644 packages/blade/src/components/Input/BaseInput/baseInputTokens.ts create mode 100644 packages/blade/src/components/Input/BaseInput/getBaseInputBorderStyles.native.ts create mode 100644 packages/blade/src/components/Input/BaseInput/getBaseInputBorderStyles.web.ts create mode 100644 packages/blade/src/components/Typography/Heading/getHeadingStyles.ts create mode 100644 packages/blade/src/utils/getFocusRingStyles/types.ts diff --git a/.changeset/heavy-badgers-care.md b/.changeset/heavy-badgers-care.md new file mode 100644 index 00000000000..edad15836e9 --- /dev/null +++ b/.changeset/heavy-badgers-care.md @@ -0,0 +1,42 @@ +--- +"@razorpay/blade": minor +--- + +feat: Redesign all `Input` components + +> Note: No breaking changes to the existing API. The Input components will continue to work as before but with an updated design. + +## Changes +### TextInput +- Redesigned UI +- Add `leadingIcon` prop +- ⚠️ Deprecate `icon` prop in favour of `leadingIcon` which will be removed in the next major version +- Add `trailingIcon` prop +- Add `trailingLinkButton` prop +- Add `size` prop + +### TextArea +- Redesigned UI +- Add `size` prop + +### PasswordInput +- Redesigned UI +- Adds `size` prop + +### OTPInput +- Redesigned UI +- Add `size` prop + +### SelectInput +- Redesigned UI +- Add `size` prop + +### Autocomplete +- Redesigned UI +- Add `size` prop + +### Radio +- Add `size` prop + +### Checkbox +- Add `size` prop diff --git a/packages/blade/jest.web.config.js b/packages/blade/jest.web.config.js index 35b701638a4..6836cfb0f74 100644 --- a/packages/blade/jest.web.config.js +++ b/packages/blade/jest.web.config.js @@ -2,6 +2,7 @@ const ignores = ['/node_modules/']; const baseConfig = { testPathIgnorePatterns: [...ignores, 'native.test'], + testTimeout: 10000, coverageThreshold: { global: { branches: 75, diff --git a/packages/blade/src/components/Checkbox/Checkbox.stories.tsx b/packages/blade/src/components/Checkbox/Checkbox.stories.tsx index 3e8d1837c6f..66d86b4f518 100644 --- a/packages/blade/src/components/Checkbox/Checkbox.stories.tsx +++ b/packages/blade/src/components/Checkbox/Checkbox.stories.tsx @@ -105,6 +105,13 @@ Small.args = { size: 'small', }; +export const Large = CheckboxTemplate.bind({}); +Large.storyName = 'Large'; +Large.args = { + size: 'large', + helpText: 'Checkbox help text', +}; + export const Indeterminate = CheckboxTemplate.bind({}); Indeterminate.storyName = 'Indeterminate'; Indeterminate.args = { diff --git a/packages/blade/src/components/Checkbox/Checkbox.tsx b/packages/blade/src/components/Checkbox/Checkbox.tsx index a27ffee2595..5abe8b90094 100644 --- a/packages/blade/src/components/Checkbox/Checkbox.tsx +++ b/packages/blade/src/components/Checkbox/Checkbox.tsx @@ -1,9 +1,10 @@ +/* eslint-disable @typescript-eslint/restrict-plus-operands */ /* eslint-disable @typescript-eslint/no-shadow */ import React from 'react'; import { useCheckboxGroupContext } from './CheckboxGroup/CheckboxGroupContext'; import { CheckboxIcon } from './CheckboxIcon'; import { useCheckbox } from './useCheckbox'; -import { checkboxHoverTokens } from './checkboxTokens'; +import { checkboxHoverTokens, checkboxSizes } from './checkboxTokens'; import isEmpty from '~utils/lodashButBetter/isEmpty'; import isUndefined from '~utils/lodashButBetter/isUndefined'; import { metaAttribute, MetaConstants } from '~utils/metaAttribute'; @@ -18,6 +19,7 @@ import { SelectorInput } from '~components/Form/Selector/SelectorInput'; import type { BladeElementRef, TestID } from '~utils/types'; import { assignWithoutSideEffects } from '~utils/assignWithoutSideEffects'; import { throwBladeError } from '~utils/logger'; +import { makeSize, useTheme } from '~utils'; type OnChange = ({ isChecked, @@ -101,7 +103,7 @@ type CheckboxProps = { * * @default "medium" */ - size?: 'small' | 'medium'; + size?: 'small' | 'medium' | 'large'; /** * Sets the tab-index property on checkbox element * @@ -184,7 +186,12 @@ const _Checkbox: React.ForwardRefRenderFunction // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion const _isChecked = isChecked ?? groupProps?.state?.isChecked(value!); const _size = groupProps.size ?? size; - const isSmall = _size === 'small'; + const { theme } = useTheme(); + const formHintSize = { + small: 'medium', + medium: 'medium', + large: 'large', + } as const; // only show error when the self validation is set to error // Since we don't want to show errorText inside the group @@ -213,6 +220,9 @@ const _Checkbox: React.ForwardRefRenderFunction onChange: handleChange, }); + // Checkbox icon's size & margin + margin-left of SelectorTitle which is 2 + const helpTextLeftSpacing = makeSize(checkboxSizes.icon[size].width + theme.spacing[3]); + return ( ) : null} {showSupportingText ? ( - - {helpText} + + + {helpText} + ) : null} {label} @@ -164,6 +165,7 @@ const CheckboxGroup = ({ })} { +const CheckedIcon = ({ color, size }: { color: string; size: 'small' | 'medium' | 'large' }) => { const width = makeSpace(svgSize[size].width); const height = makeSpace(svgSize[size].height); @@ -42,7 +46,13 @@ const CheckedIcon = ({ color, size }: { color: string; size: 'small' | 'medium' ); }; -const IndeterminateIcon = ({ color, size }: { color: string; size: 'small' | 'medium' }) => { +const IndeterminateIcon = ({ + color, + size, +}: { + color: string; + size: 'small' | 'medium' | 'large'; +}) => { const width = makeSpace(svgSize[size].width); const height = makeSpace(svgSize[size].height); @@ -67,7 +77,7 @@ export type CheckboxIconProps = { isNegative?: boolean; isChecked?: boolean; isIndeterminate?: boolean; - size: 'small' | 'medium'; + size: 'small' | 'medium' | 'large'; }; const CheckboxIcon = ({ diff --git a/packages/blade/src/components/Checkbox/__tests__/__snapshots__/Checkbox.native.test.tsx.snap b/packages/blade/src/components/Checkbox/__tests__/__snapshots__/Checkbox.native.test.tsx.snap index a83517d11db..949b840c06c 100644 --- a/packages/blade/src/components/Checkbox/__tests__/__snapshots__/Checkbox.native.test.tsx.snap +++ b/packages/blade/src/components/Checkbox/__tests__/__snapshots__/Checkbox.native.test.tsx.snap @@ -348,7 +348,7 @@ exports[` should set error state with validationState 1`] = ` } > should set error state with validationState 1`] = ` should set error state with validationState 1`] = `
should render with label 1`] = ` style={ [ { - "marginBottom": 8, + "marginBottom": 12, }, ] } @@ -357,7 +357,7 @@ exports[` should render with label 1`] = ` style={ [ { - "marginBottom": 8, + "marginBottom": 12, }, ] } @@ -883,7 +883,7 @@ exports[` should render with label 2`] = ` style={ [ { - "marginBottom": 8, + "marginBottom": 12, }, ] } @@ -1050,7 +1050,7 @@ exports[` should render with label 2`] = ` style={ [ { - "marginBottom": 8, + "marginBottom": 12, }, ] } diff --git a/packages/blade/src/components/Checkbox/__tests__/__snapshots__/CheckboxGroup.ssr.test.tsx.snap b/packages/blade/src/components/Checkbox/__tests__/__snapshots__/CheckboxGroup.ssr.test.tsx.snap index c49d1cb640f..067464c4a21 100644 --- a/packages/blade/src/components/Checkbox/__tests__/__snapshots__/CheckboxGroup.ssr.test.tsx.snap +++ b/packages/blade/src/components/Checkbox/__tests__/__snapshots__/CheckboxGroup.ssr.test.tsx.snap @@ -1,9 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` should render errorText when validationState is set to error 1`] = `"

Select fruits

,Invalid selection

Invalid selection
"`; +exports[` should render errorText when validationState is set to error 1`] = `"

Select fruits

,Invalid selection

Invalid selection
"`; exports[` should render errorText when validationState is set to error 2`] = ` -.c9.c9.c9.c9.c9 { +.c10.c10.c10.c10.c10 { position: relative; display: -webkit-box; display: -webkit-flex; @@ -30,7 +30,7 @@ exports[` should render errorText when validationState is set t border-color: hsla(4,74%,49%,1); } -.c10.c10.c10.c10.c10 { +.c11.c11.c11.c11.c11 { -webkit-animation: gYwSSW-450290765 150ms cubic-bezier(0.17,0,1,1); animation: gYwSSW-450290765 150ms cubic-bezier(0.17,0,1,1); } @@ -68,7 +68,11 @@ exports[` should render errorText when validationState is set t gap: 0px; } -.c7.c7.c7.c7.c7 { +.c6.c6.c6.c6.c6 { + margin-bottom: 8px; +} + +.c8.c8.c8.c8.c8 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -78,19 +82,19 @@ exports[` should render errorText when validationState is set t flex-direction: row; } -.c11.c11.c11.c11.c11 { +.c12.c12.c12.c12.c12 { margin-left: 4px; } -.c13.c13.c13.c13.c13 { +.c14.c14.c14.c14.c14 { margin-bottom: 0px; } -.c14.c14.c14.c14.c14 { +.c15.c15.c15.c15.c15 { margin-top: 4px; } -.c15.c15.c15.c15.c15 { +.c16.c16.c16.c16.c16 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -104,7 +108,7 @@ exports[` should render errorText when validationState is set t align-items: center; } -.c16.c16.c16.c16.c16 { +.c17.c17.c17.c17.c17 { margin-right: 4px; } @@ -147,7 +151,7 @@ exports[` should render errorText when validationState is set t padding: 0; } -.c12.c12.c12.c12.c12 { +.c13.c13.c13.c13.c13 { color: hsla(211,26%,34%,1); font-family: "Inter","Inter Fallback Arial",Arial; font-size: 0.875rem; @@ -164,7 +168,7 @@ exports[` should render errorText when validationState is set t padding: 0; } -.c17.c17.c17.c17.c17 { +.c18.c18.c18.c18.c18 { color: hsla(4,74%,49%,1); font-family: "Inter","Inter Fallback Arial",Arial; font-size: 0.6875rem; @@ -198,7 +202,7 @@ exports[` should render errorText when validationState is set t word-wrap: normal; } -.c6.c6.c6.c6.c6 { +.c7.c7.c7.c7.c7 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; @@ -207,7 +211,7 @@ exports[` should render errorText when validationState is set t margin-bottom: 2px; } -.c8.c8.c8.c8.c8 { +.c9.c9.c9.c9.c9 { border: 0; -webkit-clip: rect(0 0 0 0); clip: rect(0 0 0 0); @@ -224,7 +228,7 @@ exports[` should render errorText when validationState is set t word-wrap: normal; } -.c8.c8.c8.c8.c8:focus-visible + div { +.c9.c9.c9.c9.c9:focus-visible + div { outline: 4px solid hsla(227,100%,59%,0.18); outline-offset: 1px; -webkit-transition-property: outline-width; @@ -251,7 +255,7 @@ exports[` should render errorText when validationState is set t
should render errorText when validationState is set t data-blade-component="base-box" >
should render errorText when validationState is set t data-blade-component="checkbox" >