Skip to content

Commit

Permalink
feat(blade): Changes in components for X migration (#2448)
Browse files Browse the repository at this point in the history
* chore: add styled props to iconbutton

* chore: button group changes

* chore: expose drawer ref

* chore: expose event from radio group onchange

* chore: fix tests

* chore: add onClick to sidenavlink

* chore: add onClick to sidenav collapsible click

* Create young-dragons-poke.md
  • Loading branch information
anuraghazra authored Jan 6, 2025
1 parent db5c906 commit e00172b
Show file tree
Hide file tree
Showing 20 changed files with 139 additions and 100 deletions.
22 changes: 22 additions & 0 deletions .changeset/young-dragons-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
"@razorpay/blade": minor
---

feat(blade): Changes in components for X migration

**Drawer:**
- Changed the drawer's showOverlay behaviour to not mandate the overlay on level2 stacking
- Exposed ref

**RadioGroup:**
- Exposed event in onChange

**ButtonGroup:**
- Added styled props
- Added support for Tooltip inside ButtonGroup

**IconButton:**
- Added styledProps

**SideNavLink:**
- Added onClick
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { BladeCommonEvents } from '~components/types';
import type { Platform } from '~utils';
import type { SubtleOrIntense } from '~tokens/theme/theme';
import { makeAnalyticsAttribute } from '~utils/makeAnalyticsAttribute';
import type { StyledPropsBlade } from '~components/Box/styledProps';

type IconButtonProps = {
/**
Expand Down Expand Up @@ -46,6 +47,7 @@ type IconButtonProps = {
_tabIndex?: number;
} & DataAnalyticsAttribute &
BladeCommonEvents &
StyledPropsBlade &
Platform.Select<{
web: {
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
Expand Down Expand Up @@ -108,6 +110,7 @@ const _IconButton: React.ForwardRefRenderFunction<BladeElementRef, IconButtonPro
onTouchEnd={onTouchEnd}
onTouchStart={onTouchStart}
{...makeAnalyticsAttribute(rest)}
{...rest}
/>
);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { getFocusRingStyles } from '~utils/getFocusRingStyles';
import { throwBladeError } from '~utils/logger';
import getIn from '~utils/lodashButBetter/get';
import { makeAnalyticsAttribute } from '~utils/makeAnalyticsAttribute';
import { useStyledProps } from '~components/Box/styledProps';

type StyledButtonProps = {
emphasis: SubtleOrIntense;
Expand All @@ -23,7 +24,7 @@ type StyledButtonProps = {
const StyledButton = styled.button<StyledButtonProps>((props) => {
const { theme, emphasis } = props;
const motionToken = theme.motion;

const styledPropsCSSObject = useStyledProps(props);
const emphasisColor = emphasis === 'intense' ? 'gray' : 'staticWhite';

if (__DEV__) {
Expand Down Expand Up @@ -74,6 +75,7 @@ const StyledButton = styled.button<StyledButtonProps>((props) => {
'&:active': {
color: theme.colors.interactive.icon[emphasisColor].subtle,
},
...styledPropsCSSObject,
};
});

Expand Down Expand Up @@ -121,6 +123,7 @@ const StyledIconButton = React.forwardRef<HTMLButtonElement, StyledIconButtonPro
{...makeAccessible({ label: accessibilityLabel })}
{...metaAttribute({ name: MetaConstants.IconButton, testID })}
{...makeAnalyticsAttribute(rest)}
{...rest}
>
<Icon size={size} color={isDisabled ? 'interactive.icon.gray.disabled' : 'currentColor'} />
</StyledButton>
Expand Down
4 changes: 3 additions & 1 deletion packages/blade/src/components/Button/IconButton/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { IconComponent } from '~components/Icons';
import type { DataAnalyticsAttribute, RemoveUndefinedFromUnion, TestID } from '~utils/types';
import type { BladeCommonEvents } from '~components/types';
import type { SubtleOrIntense } from '~tokens/theme/theme';
import type { StyledPropsBlade } from '~components/Box/styledProps';

export type StyledIconButtonProps = {
icon: IconComponent;
Expand All @@ -15,4 +16,5 @@ export type StyledIconButtonProps = {
onClick?: IconButtonProps['onClick'];
} & TestID &
BladeCommonEvents &
DataAnalyticsAttribute;
DataAnalyticsAttribute &
StyledPropsBlade;
30 changes: 7 additions & 23 deletions packages/blade/src/components/ButtonGroup/ButtonGroup.web.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { ReactElement } from 'react';
import React from 'react';
import styled from 'styled-components';
import type { ButtonGroupProps } from './types';
Expand All @@ -13,10 +12,9 @@ import type { DotNotationToken } from '~utils/lodashButBetter/get';
import getIn from '~utils/lodashButBetter/get';
import { getBackgroundColorToken } from '~components/Button/BaseButton/BaseButton';
import type { Theme } from '~components/BladeProvider';
import { throwBladeError } from '~utils/logger';
import { isValidAllowedChildren } from '~utils/isValidAllowedChildren';
import type { BladeElementRef } from '~utils/types';
import { makeAnalyticsAttribute } from '~utils/makeAnalyticsAttribute';
import { useVerifyAllowedChildren } from '~utils/useVerifyAllowedChildren';

const getDividerColorToken = ({
color,
Expand Down Expand Up @@ -74,6 +72,12 @@ const _ButtonGroup = (
isFullWidth,
};

useVerifyAllowedChildren({
allowedComponents: ['Button', 'Dropdown', 'Tooltip', 'Popover'],
componentName: 'ButtonGroup',
children,
});

return (
<ButtonGroupProvider value={contextValue}>
<StyledButtonGroup
Expand All @@ -88,26 +92,6 @@ const _ButtonGroup = (
role="group"
>
{React.Children.map(children, (child, index) => {
if (__DEV__) {
// throw error if child is not a button or dropdown with button trigger
/* eslint-disable no-restricted-properties */
if (
!isValidAllowedChildren(child, 'Button') &&
!(
isValidAllowedChildren(child, 'Dropdown') &&
(child as ReactElement).props.children.some((c: ReactElement) =>
isValidAllowedChildren(c, 'DropdownButton'),
)
)
) {
throwBladeError({
moduleName: 'ButtonGroup',
message: `Only "Button" or "Dropdown" component with Button trigger are allowed as children.`,
});
}
/* eslint-enable no-restricted-properties */
}

return (
<>
{child}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { Button } from '~components/Button/Button';
import { ChevronDownIcon, PlusIcon } from '~components/Icons';
import { Dropdown, DropdownButton, DropdownOverlay } from '~components/Dropdown';
import { ActionList, ActionListItem } from '~components/ActionList';
import { AutoComplete } from '~components/Input/DropdownInputTriggers';

beforeAll(() => jest.spyOn(console, 'error').mockImplementation());
afterAll(() => jest.restoreAllMocks());
Expand Down Expand Up @@ -168,40 +167,7 @@ describe('<ButtonGroup />', () => {
</ButtonGroup>,
),
).toThrowError(
'[Blade: ButtonGroup]: Only "Button" or "Dropdown" component with Button trigger are allowed as children.',
);
});

it('should throw error with invalid dropdown children', () => {
expect(() =>
renderWithTheme(
<ButtonGroup>
<Button icon={PlusIcon}>Payout</Button>
<Dropdown selectionType="single">
<AutoComplete
label="City"
placeholder="Select your City"
name="action"
onChange={({ name, values }) => {
console.log({ name, values });
}}
onInputValueChange={({ name, value }) => {
console.log({ name, value });
}}
/>
<DropdownOverlay>
<ActionList>
<ActionListItem title="Mumbai" value="mumbai" />
<ActionListItem title="Pune" value="pune" />
<ActionListItem title="Bangalore" value="bangalore" />
<ActionListItem title="Mysore" value="mysore" />
</ActionList>
</DropdownOverlay>
</Dropdown>
</ButtonGroup>,
),
).toThrowError(
'[Blade: ButtonGroup]: Only "Button" or "Dropdown" component with Button trigger are allowed as children.',
'[Blade: ButtonGroup]: Only `Button, Dropdown, Tooltip, Popover` components are accepted in `ButtonGroup` children',
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getStyledPropsArgTypes } from '~components/Box/BaseBox/storybookArgType
import { RefreshIcon, ShareIcon, DownloadIcon, ChevronDownIcon, PlusIcon } from '~components/Icons';
import { Dropdown, DropdownButton, DropdownOverlay } from '~components/Dropdown';
import { ActionList, ActionListItem } from '~components/ActionList';
import { Tooltip } from '~components/Tooltip';

const Page = (): React.ReactElement => {
return (
Expand Down Expand Up @@ -76,7 +77,9 @@ const ButtonGroupDropdownTemplate: StoryFn<typeof ButtonGroupComponent> = (args)
return (
<Box display="flex" alignItems="center" justifyContent="center">
<ButtonGroupComponent {...args}>
<Button icon={PlusIcon}>Payout</Button>
<Tooltip content="Create a new payout">
<Button icon={PlusIcon}>Payout</Button>
</Tooltip>
<Dropdown>
<DropdownButton icon={ChevronDownIcon} />
<DropdownOverlay defaultPlacement="bottom-end">
Expand Down
4 changes: 3 additions & 1 deletion packages/blade/src/components/ButtonGroup/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { StyledPropsBlade } from '~components/Box/styledProps';
import type { ButtonProps } from '~components/Button/Button';
import type { DataAnalyticsAttribute } from '~utils/types';

Expand Down Expand Up @@ -38,7 +39,8 @@ type ButtonGroupProps = {
* Test ID for automation
*/
testID?: string;
} & DataAnalyticsAttribute;
} & DataAnalyticsAttribute &
StyledPropsBlade;

type StyledButtonGroupProps = Pick<
ButtonGroupProps,
Expand Down
36 changes: 21 additions & 15 deletions packages/blade/src/components/Drawer/Drawer.web.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { metaAttribute, MetaConstants } from '~utils/metaAttribute';
import { useId } from '~utils/useId';
import { useVerifyAllowedChildren } from '~utils/useVerifyAllowedChildren';
import { makeAnalyticsAttribute } from '~utils/makeAnalyticsAttribute';
import type { BladeElementRef } from '~utils/types';
import { mergeRefs } from '~utils/useMergeRefs';

const SHOW_DRAWER = 'show-drawer';

Expand Down Expand Up @@ -65,18 +67,21 @@ const DrawerOverlay = styled(FloatingOverlay)(({ theme }) => {
};
});

const _Drawer = ({
isOpen,
onDismiss,
zIndex = componentZIndices.drawer,
children,
accessibilityLabel,
showOverlay = true,
initialFocusRef,
isLazy = true,
testID,
...rest
}: DrawerProps): React.ReactElement => {
const _Drawer: React.ForwardRefRenderFunction<BladeElementRef, DrawerProps> = (
{
isOpen,
onDismiss,
zIndex = componentZIndices.drawer,
children,
accessibilityLabel,
showOverlay = true,
initialFocusRef,
isLazy = true,
testID,
...rest
},
ref,
) => {
const closeButtonRef = React.useRef<HTMLDivElement>(null);
const [zIndexState, setZIndexState] = React.useState<number>(zIndex);

Expand Down Expand Up @@ -155,7 +160,7 @@ const _Drawer = ({
{...makeAnalyticsAttribute(rest)}
zIndex={zIndexState}
>
{showOverlay || stackingLevel === 2 ? (
{showOverlay ? (
<DrawerOverlay
onClick={() => {
onDismiss();
Expand Down Expand Up @@ -184,7 +189,7 @@ const _Drawer = ({
height="100%"
display="flex"
flexDirection="column"
ref={refs.setFloating}
ref={mergeRefs(ref, refs.setFloating)}
onKeyDown={(event) => {
if (event?.key === 'Escape' || event?.code === 'Escape') {
onDismiss();
Expand Down Expand Up @@ -238,7 +243,8 @@ const _Drawer = ({
*
*
*/
const Drawer = assignWithoutSideEffects(_Drawer, {
const Drawer = assignWithoutSideEffects(React.forwardRef(_Drawer), {
displayName: 'Drawer',
componentId: drawerComponentIds.Drawer,
});

Expand Down
4 changes: 2 additions & 2 deletions packages/blade/src/components/Radio/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ const _Radio: React.ForwardRefRenderFunction<BladeElementRef, RadioProps> = (
const isReactNative = getPlatformType() === 'react-native';
const _size = groupProps.size ?? size;

const handleChange: OnChange = ({ isChecked, value }) => {
const handleChange: OnChange = ({ isChecked, value, event }) => {
if (isChecked) {
groupProps?.state?.setValue(value!);
groupProps?.state?.setValue(value!, event);
} else {
groupProps?.state?.removeValue();
}
Expand Down
10 changes: 9 additions & 1 deletion packages/blade/src/components/Radio/RadioGroup/RadioGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,15 @@ type RadioGroupProps = {
/**
* The callback invoked when any of the radio's state changes
*/
onChange?: ({ name, value }: { name: string | undefined; value: string }) => void;
onChange?: ({
name,
value,
event,
}: {
name: string | undefined;
value: string;
event: React.ChangeEvent<HTMLInputElement>;
}) => void;
/**
* The name of the input field in a radio
* (Useful for form submission).
Expand Down
10 changes: 6 additions & 4 deletions packages/blade/src/components/Radio/RadioGroup/useRadioGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type UseRadioGroupProps = Pick<

export type State = {
value: string;
setValue(value: string): void;
setValue(value: string, event: React.ChangeEvent<Element>): void;
removeValue(): void;
isChecked(value: string): boolean;
};
Expand Down Expand Up @@ -52,18 +52,20 @@ const useRadioGroup = ({
const [checkedValue, setValue] = useControllableState({
value,
defaultValue,
onChange: (v: string) => onChange?.({ value: v, name: fallbackName }),
onChange: (v, event) => {
onChange?.({ value: v, name: fallbackName, event });
},
});

const state = React.useMemo<State>(() => {
return {
value: checkedValue,
setValue(v: string): void {
setValue(v, event): void {
if (isDisabled) {
return;
}

setValue(() => v);
setValue(() => v, false, event);
},
removeValue(): void {
if (isDisabled) {
Expand Down
2 changes: 1 addition & 1 deletion packages/blade/src/components/Radio/useRadio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export type OnChange = ({
value,
}: {
isChecked: boolean;
event?: React.ChangeEvent;
event: React.ChangeEvent;
value?: string;
}) => void;

Expand Down
Loading

0 comments on commit e00172b

Please sign in to comment.