Skip to content

Commit

Permalink
feat: add TableEditableCell component for Table (#2226)
Browse files Browse the repository at this point in the history
  • Loading branch information
chaitanyadeorukhkar authored Jul 2, 2024
1 parent 872e309 commit 03837f9
Show file tree
Hide file tree
Showing 54 changed files with 4,987 additions and 1,289 deletions.
5 changes: 5 additions & 0 deletions .changeset/five-brooms-beg.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@razorpay/blade": minor
---

feat: add `TableEditableCell` component for Table
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ exports[`<Dropdown /> should render dropdown 1`] = `
<View
currentInteraction="default"
data-blade-component="base-box"
isTableInputCell={false}
style={
[
{},
Expand All @@ -230,13 +231,14 @@ exports[`<Dropdown /> should render dropdown 1`] = `
<View
currentInteraction="default"
isDropdownTrigger={true}
isTableInputCell={false}
onClick={[Function]}
setShowAllTagsWithAnimation={[Function]}
size="medium"
style={
[
{
"backgroundColor": "yellow",
"backgroundColor": "hsla(0, 0%, 100%, 1)",
"borderColor": "hsla(214, 28%, 84%, 1)",
"borderRadius": 4,
"borderStyle": "solid",
Expand Down Expand Up @@ -379,7 +381,7 @@ exports[`<Dropdown /> should render dropdown 1`] = `
onStartShouldSetResponder={[Function]}
style={
{
"backgroundColor": "transparent",
"backgroundColor": "hsla(0, 0%, 100%, 0)",
"color": "hsla(211, 20%, 52%, 0.32)",
"flexBasis": 0,
"flexGrow": 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<Dropdown /> should render dropdown and make it visible on click 1`] = `"<div id="root"><div data-blade-component="dropdown" class="BaseBox-bmPWx"><div data-blade-component="base-box" class="BaseBox-bmPWx kUMlmJ"><div data-blade-component="base-box" class="BaseBox-bmPWx jjDQyK"><div data-blade-component="visually-hidden" class="VisuallyHiddenweb__StyledVisuallyHidden-g3hls3-0 Fuolv"><input tabindex="-1" aria-hidden="true" value=""/></div><div data-blade-component="select-input" class="BaseBox-bmPWx"><div data-blade-component="base-box" class="BaseBox-bmPWx hqusWt"><div data-blade-component="base-box" class="BaseBox-bmPWx jaAmIu"><label for="dropdown-undefined-trigger-undefined-input-undefined" style="width:auto;flex-shrink:0;margin-right:0px" id="dropdown-undefined-label" data-blade-component="form-label"><div data-blade-component="base-box" class="BaseBox-bmPWx fZlcZT"><div data-blade-component="base-box" class="BaseBox-bmPWx ikPXAQ"><p class="StyledBaseText-dVBfTO kCEKIT" data-blade-component="text">Fruits</p><div data-blade-component="visually-hidden" class="VisuallyHiddenweb__StyledVisuallyHidden-g3hls3-0 Fuolv"><p class="StyledBaseText-dVBfTO fAQkza" data-blade-component="text"></p></div></div></div></label></div><div data-blade-component="base-box" class="BaseBox-bmPWx BaseInput__FocusRingWrapper-sc-177qd3e-0 iIoIGQ"><div data-blade-component="base-box" class="BaseBox-bmPWx AnimatedBaseInputWrapperweb__StyledBaseInputWrapper-e1vobd-0 AnimatedBaseInputWrapperweb__StyledAnimatedBaseInputWrapper-e1vobd-1 jzHgrG hZVkky"><div class="BaseBox-bmPWx BaseInputTagSlotweb__TagSlotContainer-sc-1bt1h3t-0 eRtEHm dUNBKx tags-slot" data-blade-component="base-box"><div data-blade-component="base-box" class="BaseBox-bmPWx fhrYRb"><button type="button" autoComplete="off" id="dropdown-undefined-trigger-undefined-input-undefined" value="" placeholder="Select Option" data-blade-component="styled-base-input" aria-required="false" aria-disabled="false" aria-invalid="false" aria-describedby="" aria-haspopup="listbox" aria-expanded="false" aria-controls="dropdown-undefined-actionlist" role="combobox" class="StyledBaseInputweb__StyledBaseNativeButton-hsusrk-1 jLxJaB"><p class="StyledBaseText-dVBfTO kpQZgj" data-blade-component="text">Select Option</p></button></div></div><div data-blade-component="base-box" class="BaseBox-bmPWx hGaCmA"><div data-blade-component="base-box" class="BaseBox-bmPWx fTNRoG"><div data-blade-component="base-box" class="BaseBox-bmPWx dxquVw"><svg aria-hidden="true" data-blade-component="icon" height="16px" viewBox="0 0 24 24" width="16px" fill="none" class="Svgweb__StyledSvg-vcmjs8-0 lmEfDF"><path d="M5.29289 8.29289C5.68342 7.90237 6.31658 7.90237 6.70711 8.29289L12 13.5858L17.2929 8.29289C17.6834 7.90237 18.3166 7.90237 18.7071 8.29289C19.0976 8.68342 19.0976 9.31658 18.7071 9.70711L12.7071 15.7071C12.3166 16.0976 11.6834 16.0976 11.2929 15.7071L5.29289 9.70711C4.90237 9.31658 4.90237 8.68342 5.29289 8.29289Z" clip-rule="evenodd" fill="hsla(211, 22%, 56%, 1)" fill-rule="evenodd" data-blade-component="svg-path"></path></svg><svg aria-hidden="true" data-blade-component="icon" height="16px" viewBox="0 0 24 24" width="16px" fill="none" class="Svgweb__StyledSvg-vcmjs8-0 fDSHXQ"><path d="M11.2929 8.29289C11.6834 7.90237 12.3166 7.90237 12.7071 8.29289L18.7071 14.2929C19.0976 14.6834 19.0976 15.3166 18.7071 15.7071C18.3166 16.0976 17.6834 16.0976 17.2929 15.7071L12 10.4142L6.70711 15.7071C6.31658 16.0976 5.68342 16.0976 5.29289 15.7071C4.90237 15.3166 4.90237 14.6834 5.29289 14.2929L11.2929 8.29289Z" clip-rule="evenodd" fill="hsla(211, 22%, 56%, 1)" fill-rule="evenodd" data-blade-component="svg-path"></path></svg></div></div></div></div></div></div><div data-blade-component="base-box" class="BaseBox-bmPWx iARoiz"><div data-blade-component="base-box" class="BaseBox-bmPWx qIbA"></div></div></div></div></div></div></div>"`;
exports[`<Dropdown /> should render dropdown and make it visible on click 1`] = `"<div id="root"><div data-blade-component="dropdown" class="BaseBox-bmPWx"><div data-blade-component="base-box" class="BaseBox-bmPWx kUMlmJ"><div data-blade-component="base-box" class="BaseBox-bmPWx jjDQyK"><div data-blade-component="visually-hidden" class="VisuallyHiddenweb__StyledVisuallyHidden-g3hls3-0 Fuolv"><input tabindex="-1" aria-hidden="true" value=""/></div><div data-blade-component="select-input" class="BaseBox-bmPWx"><div data-blade-component="base-box" class="BaseBox-bmPWx hqusWt"><div data-blade-component="base-box" class="BaseBox-bmPWx jaAmIu"><label for="dropdown-undefined-trigger-undefined-input-undefined" style="width:auto;flex-shrink:0;margin-right:0px" id="dropdown-undefined-label" data-blade-component="form-label"><div data-blade-component="base-box" class="BaseBox-bmPWx fZlcZT"><div data-blade-component="base-box" class="BaseBox-bmPWx ikPXAQ"><p class="StyledBaseText-dVBfTO kCEKIT" data-blade-component="text">Fruits</p><div data-blade-component="visually-hidden" class="VisuallyHiddenweb__StyledVisuallyHidden-g3hls3-0 Fuolv"><p class="StyledBaseText-dVBfTO fAQkza" data-blade-component="text"></p></div></div></div></label></div><div data-blade-component="base-box" class="BaseBox-bmPWx BaseInput__FocusRingWrapper-sc-177qd3e-0 iIoIGQ"><div data-blade-component="base-box" class="BaseBox-bmPWx AnimatedBaseInputWrapperweb__StyledBaseInputWrapper-e1vobd-0 AnimatedBaseInputWrapperweb__StyledAnimatedBaseInputWrapper-e1vobd-1 jzHgrG hZVkky"><div class="BaseBox-bmPWx BaseInputTagSlotweb__TagSlotContainer-sc-1bt1h3t-0 eRtEHm dUNBKx tags-slot" data-blade-component="base-box"><div data-blade-component="base-box" class="BaseBox-bmPWx fhrYRb"><button type="button" autoComplete="off" id="dropdown-undefined-trigger-undefined-input-undefined" value="" placeholder="Select Option" data-blade-component="styled-base-input" aria-required="false" aria-disabled="false" aria-invalid="false" aria-describedby="" aria-haspopup="listbox" aria-expanded="false" aria-controls="dropdown-undefined-actionlist" role="combobox" class="StyledBaseInputweb__StyledBaseNativeButton-hsusrk-1 gPRLwp"><p class="StyledBaseText-dVBfTO kpQZgj" data-blade-component="text">Select Option</p></button></div></div><div data-blade-component="base-box" class="BaseBox-bmPWx hGaCmA"><div data-blade-component="base-box" class="BaseBox-bmPWx fTNRoG"><div data-blade-component="base-box" class="BaseBox-bmPWx dxquVw"><svg aria-hidden="true" data-blade-component="icon" height="16px" viewBox="0 0 24 24" width="16px" fill="none" class="Svgweb__StyledSvg-vcmjs8-0 lmEfDF"><path d="M5.29289 8.29289C5.68342 7.90237 6.31658 7.90237 6.70711 8.29289L12 13.5858L17.2929 8.29289C17.6834 7.90237 18.3166 7.90237 18.7071 8.29289C19.0976 8.68342 19.0976 9.31658 18.7071 9.70711L12.7071 15.7071C12.3166 16.0976 11.6834 16.0976 11.2929 15.7071L5.29289 9.70711C4.90237 9.31658 4.90237 8.68342 5.29289 8.29289Z" clip-rule="evenodd" fill="hsla(211, 22%, 56%, 1)" fill-rule="evenodd" data-blade-component="svg-path"></path></svg><svg aria-hidden="true" data-blade-component="icon" height="16px" viewBox="0 0 24 24" width="16px" fill="none" class="Svgweb__StyledSvg-vcmjs8-0 fDSHXQ"><path d="M11.2929 8.29289C11.6834 7.90237 12.3166 7.90237 12.7071 8.29289L18.7071 14.2929C19.0976 14.6834 19.0976 15.3166 18.7071 15.7071C18.3166 16.0976 17.6834 16.0976 17.2929 15.7071L12 10.4142L6.70711 15.7071C6.31658 16.0976 5.68342 16.0976 5.29289 15.7071C4.90237 15.3166 4.90237 14.6834 5.29289 14.2929L11.2929 8.29289Z" clip-rule="evenodd" fill="hsla(211, 22%, 56%, 1)" fill-rule="evenodd" data-blade-component="svg-path"></path></svg></div></div></div></div></div></div><div data-blade-component="base-box" class="BaseBox-bmPWx iARoiz"><div data-blade-component="base-box" class="BaseBox-bmPWx qIbA"></div></div></div></div></div></div></div>"`;

exports[`<Dropdown /> should render dropdown and make it visible on click 2`] = `
.c0.c0.c0.c0.c0 {
Expand Down Expand Up @@ -248,7 +248,7 @@ exports[`<Dropdown /> should render dropdown and make it visible on click 2`] =
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
background-color: transparent;
background-color: hsla(0,0%,100%,0);
padding-top: 8px;
padding-bottom: 8px;
padding-left: 0px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ exports[`<Dropdown /> should render dropdown and make it visible on click 1`] =
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
background-color: transparent;
background-color: hsla(0,0%,100%,0);
padding-top: 8px;
padding-bottom: 8px;
padding-left: 0px;
Expand Down Expand Up @@ -920,7 +920,7 @@ exports[`<Dropdown /> should render dropdown with large size select input 1`] =
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
background-color: transparent;
background-color: hsla(0,0%,100%,0);
padding-top: 12px;
padding-bottom: 12px;
padding-left: 0px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const StyledBaseInputWrapper = styled(Animated.View)<BaseInputWrapperProps>((pro
validationState: props.validationState,
isTextArea: props.isTextArea,
isDropdownTrigger: props.isDropdownTrigger,
isTableInputCell: props.isTableInputCell,
}),
backgroundColor: 'yellow',
}));

const _AnimatedBaseInputWrapper: React.ForwardRefRenderFunction<
Expand Down Expand Up @@ -132,7 +132,7 @@ const _AnimatedBaseInputWrapper: React.ForwardRefRenderFunction<

const animatedBorderAndBackgroundStyle = useAnimatedStyle(
() => ({
borderWidth: theme.border.width.thin,
borderWidth: rest.isTableInputCell ? theme.border.width.none : theme.border.width.thin,
borderRadius: theme.border.radius.medium,
borderStyle: 'solid',
backgroundColor: withTiming(backgroundColor, motionConfig),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const StyledBaseInputWrapper = styled(BaseBox)<
| 'isTextArea'
| 'isDropdownTrigger'
| 'maxTagRows'
| 'isTableInputCell'
>
>((props) => ({
...getInputBackgroundAndBorderStyles({
Expand All @@ -30,6 +31,7 @@ const StyledBaseInputWrapper = styled(BaseBox)<
validationState: props.validationState,
isTextArea: props.isTextArea,
isDropdownTrigger: props.isDropdownTrigger,
isTableInputCell: props.isTableInputCell,
}),
'&:hover': {
...getInputBackgroundAndBorderStyles({
Expand All @@ -40,6 +42,7 @@ const StyledBaseInputWrapper = styled(BaseBox)<
validationState: props.validationState,
isDropdownTrigger: props.isDropdownTrigger,
isTextArea: props.isTextArea,
isTableInputCell: props.isTableInputCell,
}),
transitionProperty: 'background-color',
transitionDuration: castWebType(makeMotionTime(props.theme.motion.duration.xquick)),
Expand All @@ -53,6 +56,7 @@ const StyledBaseInputWrapper = styled(BaseBox)<
validationState: props.validationState,
isDropdownTrigger: props.isDropdownTrigger,
isTextArea: props.isTextArea,
isTableInputCell: props.isTableInputCell,
}),
},
}));
Expand Down Expand Up @@ -147,6 +151,7 @@ to {
setShowAllTagsWithAnimation?.(false);
}
}}
isTableInputCell={rest.isTableInputCell}
/>
);
};
Expand Down
66 changes: 43 additions & 23 deletions packages/blade/src/components/Input/BaseInput/BaseInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,11 @@ type BaseInputCommonProps = FormInputLabelProps &
* @default text
**/
valueComponentType?: 'text' | 'heading';
/**
* Whether to render the input as a table cell
* @default true
**/
isTableInputCell?: boolean;
} & TestID &
Platform.Select<{
native: {
Expand Down Expand Up @@ -718,29 +723,35 @@ const getDescribedByElementId = ({

const FocusRingWrapper = styled(BaseBox)<{
currentInteraction: ActionStates;
}>(({ theme, currentInteraction }) => ({
borderRadius: makeBorderSize(theme.border.radius.medium),
isTableInputCell: NonNullable<BaseInputProps['isTableInputCell']>;
}>(({ theme, currentInteraction, isTableInputCell }) => ({
borderRadius: makeBorderSize(
isTableInputCell ? theme.border.radius.none : theme.border.radius.medium,
),
width: '100%',
'&:focus-within': {
...getFocusRingStyles({
theme,
}),
transitionDuration: castWebType(
makeMotionTime(
getIn(
theme.motion.duration,
baseInputBorderBackgroundMotion[currentInteraction === 'focus' ? 'enter' : 'exit']
.duration,
'&:focus-within': !isTableInputCell
? {
...getFocusRingStyles({
theme,
}),
transitionDuration: castWebType(
makeMotionTime(
getIn(
theme.motion.duration,
baseInputBorderBackgroundMotion[currentInteraction === 'focus' ? 'enter' : 'exit']
.duration,
),
),
),
),
),
transitionTimingFunction: castWebType(
getIn(
theme.motion.easing,
baseInputBorderBackgroundMotion[currentInteraction === 'focus' ? 'enter' : 'exit'].easing,
),
),
},
transitionTimingFunction: castWebType(
getIn(
theme.motion.easing,
baseInputBorderBackgroundMotion[currentInteraction === 'focus' ? 'enter' : 'exit']
.easing,
),
),
}
: {},
}));

const _BaseInput: React.ForwardRefRenderFunction<BladeElementRef, BaseInputProps> = (
Expand Down Expand Up @@ -807,6 +818,7 @@ const _BaseInput: React.ForwardRefRenderFunction<BladeElementRef, BaseInputProps
size = 'medium',
trailingButton,
valueComponentType = 'text',
isTableInputCell = false,
...styledProps
},
ref,
Expand Down Expand Up @@ -927,7 +939,10 @@ const _BaseInput: React.ForwardRefRenderFunction<BladeElementRef, BaseInputProps
{trailingHeaderSlot?.(value ?? inputValue)}
</BaseBox>
)}
<FocusRingWrapper currentInteraction={currentInteraction}>
<FocusRingWrapper
currentInteraction={currentInteraction}
isTableInputCell={isTableInputCell}
>
<BaseInputWrapper
isDropdownTrigger={isDropdownTrigger}
isTextArea={isTextArea}
Expand All @@ -951,6 +966,7 @@ const _BaseInput: React.ForwardRefRenderFunction<BladeElementRef, BaseInputProps
inputRef.current?.focus();
}
}}
isTableInputCell={isTableInputCell}
>
<BaseInputVisuals
size={size}
Expand Down Expand Up @@ -1026,6 +1042,7 @@ const _BaseInput: React.ForwardRefRenderFunction<BladeElementRef, BaseInputProps
isDropdownTrigger={isDropdownTrigger}
$size={size}
valueComponentType={valueComponentType}
isTableInputCell={isTableInputCell}
{...metaAttribute({ name: MetaConstants.StyledBaseInput })}
/>
</BaseInputTagSlot>
Expand All @@ -1037,12 +1054,15 @@ const _BaseInput: React.ForwardRefRenderFunction<BladeElementRef, BaseInputProps
validationState={validationState}
trailingButton={trailingButton}
size={size}
errorText={errorText}
successText={successText}
isTableInputCell={isTableInputCell}
/>
</BaseInputWrapper>
</FocusRingWrapper>
</BaseBox>

{!hideFormHint && (
{hideFormHint || isTableInputCell ? null : (
<BaseBox
marginLeft={makeSize(
isLabelLeftPositioned && !hideLabelText ? formHintLeftLabelMarginLeft[size] : 0,
Expand Down
78 changes: 72 additions & 6 deletions packages/blade/src/components/Input/BaseInput/BaseInputVisuals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import type { BaseBoxProps, SpacingValueType } from '~components/Box/BaseBox';
import type { IconColors } from '~components/Icons';
import { isValidAllowedChildren } from '~utils/isValidAllowedChildren';
import { throwBladeError } from '~utils/logger';
import { Tooltip } from '~components/Tooltip';
import { Box } from '~components/Box';

type InputVisuals = Pick<
BaseInputProps,
Expand All @@ -21,6 +23,9 @@ type InputVisuals = Pick<
| 'validationState'
| 'size'
| 'trailingButton'
| 'isTableInputCell'
| 'errorText'
| 'successText'
> & {
size: NonNullable<BaseInputProps['size']>;
};
Expand Down Expand Up @@ -171,6 +176,55 @@ export const getInputVisualsToBeRendered = ({
hasTrailingButton: Boolean(trailingButton),
});

const getTooltipContent = ({
validationState,
errorText,
successText,
}: {
validationState: BaseInputProps['validationState'];
errorText: BaseInputProps['errorText'];
successText: BaseInputProps['errorText'];
}): string => {
if (validationState === 'error' && errorText) {
return errorText;
}

if (validationState === 'success' && successText) {
return successText;
}

return '';
};

const ValidationIconTooltip = ({
children,
validationState,
errorText,
successText,
isTableInputCell,
}: {
children: ReactElement;
validationState: BaseInputProps['validationState'];
errorText: BaseInputProps['errorText'];
successText: BaseInputProps['errorText'];
isTableInputCell: BaseInputProps['isTableInputCell'];
}) => {
if (
(isTableInputCell && validationState === 'error' && errorText) ||
(validationState === 'success' && successText)
) {
return (
<Tooltip content={getTooltipContent({ validationState, errorText, successText })}>
<Box display="flex" justifyContent="center" alignItems="center">
{children}
</Box>
</Tooltip>
);
}

return children;
};

export const BaseInputVisuals = ({
leadingIcon: LeadingIcon,
prefix,
Expand All @@ -181,6 +235,9 @@ export const BaseInputVisuals = ({
isDisabled,
validationState = 'none',
size,
isTableInputCell,
errorText,
successText,
trailingButton: TrailingButton,
}: InputVisuals): ReactElement | null => {
const {
Expand Down Expand Up @@ -290,14 +347,23 @@ export const BaseInputVisuals = ({
{TrailingIcon ? (
<BaseBox
display="flex"
justifyContent="center"
alignItems="center"
{...getTrailingIconStyles({ hasTrailingIcon, hasTrailingButton })}
>
<TrailingIcon
size={iconSize[size]}
color={
isDisabled ? 'interactive.icon.gray.disabled' : trailingIconColor[validationState]
}
/>
<ValidationIconTooltip
isTableInputCell={isTableInputCell}
errorText={errorText}
successText={successText}
validationState={validationState}
>
<TrailingIcon
size={iconSize[size]}
color={
isDisabled ? 'interactive.icon.gray.disabled' : trailingIconColor[validationState]
}
/>
</ValidationIconTooltip>
</BaseBox>
) : null}
{TrailingButton ? (
Expand Down
Loading

0 comments on commit 03837f9

Please sign in to comment.