Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improving Button DOM Node customization #1435

Merged
merged 36 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
55110f9
initial push
xman343 Jul 24, 2023
0da3b89
labelProps
xman343 Jul 24, 2023
6b469d3
changeset added
xman343 Jul 24, 2023
a9c998d
unit tests created
xman343 Jul 24, 2023
0f10d2f
Merge branch 'dev' into xander/button-xProps
xman343 Jul 25, 2023
fddf698
removed 'iui-button-label' class
xman343 Jul 25, 2023
30791ab
Merge branch 'dev' into xander/button-xProps
xman343 Jul 26, 2023
4437780
IconButton iconProps
xman343 Jul 26, 2023
d63ab52
Merge branch 'dev' into xander/button-xProps
xman343 Jul 27, 2023
a8fc108
Merge branch 'dev' into xander/button-xProps
xman343 Jul 27, 2023
6663deb
Merge branch 'dev' into xander/button-xProps
xman343 Jul 28, 2023
3d712d2
Merge branch 'dev' into xander/button-xProps
xman343 Jul 28, 2023
33f4f7f
iconProps changeset
xman343 Jul 28, 2023
9f48023
Merge branch 'dev' into xander/button-xProps
xman343 Jul 28, 2023
496ef0f
Merge branch 'dev' into xander/button-xProps
xman343 Jul 31, 2023
0888efc
splitButton props
xman343 Jul 31, 2023
e8ff747
changeset update
xman343 Aug 1, 2023
c33e2ac
Merge branch 'dev' into xander/button-xProps
xman343 Aug 1, 2023
6b7854a
Merge branch 'dev' into xander/button-xProps
xman343 Aug 1, 2023
d2057b3
Merge branch 'dev' into xander/button-xProps
xman343 Aug 1, 2023
6539563
Merge branch 'dev' into xander/button-xProps
xman343 Aug 2, 2023
0cac6d6
Merge branch 'dev' into xander/button-xProps
xman343 Aug 2, 2023
4c75238
IconButton Omit updated
xman343 Aug 2, 2023
021e51a
merge fix
xman343 Aug 2, 2023
eada77b
Merge branch 'dev' into xander/button-xProps
xman343 Aug 2, 2023
a76b74b
SplitButton wrapperProps
xman343 Aug 7, 2023
34b218e
Merge branch 'dev' into xander/button-xProps
xman343 Aug 7, 2023
e76eade
Merge branch 'dev' into xander/button-xProps
xman343 Aug 9, 2023
8639336
unnecessary classnames removed
xman343 Aug 10, 2023
03c5d58
className removed from wrapper
xman343 Aug 10, 2023
1aefdf6
Update packages/itwinui-react/src/core/Buttons/SplitButton/SplitButto…
xman343 Aug 10, 2023
3f0bb33
Update packages/itwinui-react/src/core/Buttons/SplitButton/SplitButto…
xman343 Aug 10, 2023
f21b64a
Update packages/itwinui-react/src/core/Buttons/SplitButton/SplitButto…
xman343 Aug 10, 2023
b7dcaec
wrapperRef update
xman343 Aug 10, 2023
970f245
Merge branch 'xander/button-xProps' of https://github.com/iTwin/iTwin…
xman343 Aug 10, 2023
e9a5c3e
splitbutton changeset
xman343 Aug 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/plenty-eels-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@itwin/itwinui-react': minor
---

Improving customization of Button with new props: labelProps, startIconProps, and endIconProps.
Improving customization of IconButton with iconProps.
5 changes: 5 additions & 0 deletions .changeset/slimy-ladybugs-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@itwin/itwinui-react': major
---

Improving customization of SplitButton with wrapperProps and menuButtonProps. ClassName now passed to the button instead of the component wrapper.
33 changes: 33 additions & 0 deletions packages/itwinui-react/src/core/Buttons/Button/Button.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,36 @@ it('should support polymorphic `as` prop', () => {
expect(button).toHaveTextContent('label');
expect(button.href).toEqual('https://example.com/');
});

it('should render [x]Props correctly', () => {
const { container } = render(
<Button
startIcon={<SvgPlaceholder />}
endIcon={<SvgPlaceholder />}
startIconProps={{ className: 'start-icon-class', style: { width: 80 } }}
endIconProps={{ className: 'end-icon-class', style: { width: 80 } }}
labelProps={{ className: 'label-class', style: { width: 80 } }}
>
label
</Button>,
);

// Test for Button startIcon
const startIconElement = container.querySelector(
'.iui-button-icon.start-icon-class',
) as HTMLElement;
expect(startIconElement).toBeTruthy();
expect(startIconElement.style.width).toBe('80px');

// Test for Button endIcon
const endIconElement = container.querySelector(
'.iui-button-icon.end-icon-class',
) as HTMLElement;
expect(endIconElement).toBeTruthy();
expect(endIconElement.style.width).toBe('80px');

// Test for Button Label
const labelElement = container.querySelector('.label-class') as HTMLElement;
expect(labelElement).toBeTruthy();
expect(labelElement.style.width).toBe('80px');
});
31 changes: 28 additions & 3 deletions packages/itwinui-react/src/core/Buttons/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ export type ButtonProps = {
* Icon shown after the main button content.
*/
endIcon?: JSX.Element;
/**
* Passes props to the button label.
*/
labelProps?: React.ComponentProps<'span'>;
/**
* Passes props to the start icon.
*/
startIconProps?: React.ComponentProps<'span'>;
/**
* Passes props to the end icon.
*/
endIconProps?: React.ComponentProps<'span'>;
} & Pick<React.ComponentProps<typeof ButtonBase>, 'htmlDisabled'>;

/**
Expand All @@ -46,6 +58,9 @@ export const Button = React.forwardRef((props, ref) => {
styleType = 'default',
startIcon,
endIcon,
labelProps,
startIconProps,
endIconProps,
...rest
} = props;

Expand All @@ -58,15 +73,25 @@ export const Button = React.forwardRef((props, ref) => {
{...rest}
>
{startIcon && (
<Box as='span' className='iui-button-icon' aria-hidden>
<Box
as='span'
aria-hidden
{...startIconProps}
className={cx('iui-button-icon', startIconProps?.className)}
>
{startIcon}
</Box>
)}

{children && <span>{children}</span>}
{children && <span {...labelProps}>{children}</span>}

{endIcon && (
<Box as='span' className='iui-button-icon' aria-hidden>
<Box
as='span'
aria-hidden
{...endIconProps}
className={cx('iui-button-icon', endIconProps?.className)}
>
{endIcon}
</Box>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,19 @@ it('should support polymorphic `as` prop', () => {
const button = container.querySelector('a.iui-button') as HTMLAnchorElement;
expect(button.href).toEqual('https://example.com/');
});

it('should pass props to IconButton parts', () => {
const { container } = render(
<IconButton
iconProps={{ className: 'custom-icon-class', style: { width: 80 } }}
>
<SvgMore />
</IconButton>,
);

const icon = container.querySelector(
'.iui-button-icon.custom-icon-class',
) as HTMLElement;
expect(icon).toBeTruthy;
expect(icon.style.width).toBe('80px');
});
17 changes: 15 additions & 2 deletions packages/itwinui-react/src/core/Buttons/IconButton/IconButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@ export type IconButtonProps = {
* Name of the button, shown in a tooltip and exposed to assistive technologies.
*/
label?: React.ReactNode;
} & Omit<ButtonProps, 'startIcon' | 'endIcon'>;
/**
* Passes props to IconButton icon.
*/
iconProps?: React.ComponentProps<'span'>;
} & Omit<
ButtonProps,
'startIcon' | 'endIcon' | 'startIconProps' | 'endIconProps' | 'labelProps'
>;

/**
* Icon button
Expand All @@ -35,6 +42,7 @@ export const IconButton = React.forwardRef((props, ref) => {
size,
className,
label,
iconProps,
...rest
} = props;

Expand All @@ -49,7 +57,12 @@ export const IconButton = React.forwardRef((props, ref) => {
aria-pressed={isActive}
{...rest}
>
<Box as='span' className='iui-button-icon' aria-hidden>
<Box
as='span'
aria-hidden
{...iconProps}
className={cx('iui-button-icon', iconProps?.className)}
>
{children}
</Box>
{label ? <VisuallyHidden>{label}</VisuallyHidden> : null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,37 @@ it('should support polymorphic `as` prop', async () => {
await userEvent.click(dropdownButton);
expect(document.querySelector('.iui-menu')).toBeVisible();
});

it('passes custom props to subcomponents', () => {
const { container } = render(
<SplitButton
wrapperProps={{
className: 'custom-wrapper-class',
style: { fontSize: 12 },
}}
menuButtonProps={{
className: 'custom-menu-button-class',
style: { fontSize: 14 },
}}
menuItems={(close) => [
<MenuItem key={0} onClick={close}>
Test0
</MenuItem>,
]}
>
Example
</SplitButton>,
);

const wrapperElement = container.querySelector(
'.custom-wrapper-class',
) as HTMLElement;
expect(wrapperElement).toBeTruthy();
expect(wrapperElement.style.fontSize).toBe('12px');

const menuButtonElement = container.querySelector(
'.iui-button.custom-menu-button-class',
) as HTMLElement;
expect(menuButtonElement).toBeTruthy();
expect(menuButtonElement.style.fontSize).toBe('14px');
});
41 changes: 29 additions & 12 deletions packages/itwinui-react/src/core/Buttons/SplitButton/SplitButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ export type SplitButtonProps = ButtonProps & {
* Content of primary button.
*/
children: React.ReactNode;
/**
* Passes props to SplitButton wrapper.
*/
wrapperProps?: React.ComponentProps<'div'>;
/**
* Passes props to SplitButton menu button.
*/
menuButtonProps?: React.ComponentProps<typeof IconButton>;
};

/**
Expand All @@ -53,32 +61,36 @@ export const SplitButton = React.forwardRef((props, forwardedRef) => {
styleType = 'default',
size,
children,
style,
title,
wrapperProps,
menuButtonProps,
...rest
} = props;

const [isMenuOpen, setIsMenuOpen] = React.useState(false);

const [menuWidth, setMenuWidth] = React.useState(0);
const ref = React.useRef<HTMLDivElement>(null);
const wrapperRef = React.useRef<HTMLDivElement>(null);

React.useEffect(() => {
if (ref.current) {
setMenuWidth(ref.current.offsetWidth);
if (wrapperRef.current) {
setMenuWidth(wrapperRef.current.offsetWidth);
}
}, [children, size]);

return (
<Box
className={cx(className, 'iui-button-split', {
'iui-disabled': props.disabled,
})}
style={style}
title={title}
ref={ref}
{...wrapperProps}
className={cx(
'iui-button-split',
{
'iui-disabled': props.disabled,
},
wrapperProps?.className,
)}
ref={wrapperRef}
>
<Button
className={className}
styleType={styleType}
size={size}
onClick={onClick}
Expand All @@ -94,7 +106,12 @@ export const SplitButton = React.forwardRef((props, forwardedRef) => {
onShow={React.useCallback(() => setIsMenuOpen(true), [])}
onHide={React.useCallback(() => setIsMenuOpen(false), [])}
>
<IconButton styleType={styleType} size={size} disabled={props.disabled}>
<IconButton
styleType={styleType}
size={size}
disabled={props.disabled}
{...menuButtonProps}
>
{isMenuOpen ? <SvgCaretUpSmall /> : <SvgCaretDownSmall />}
</IconButton>
</DropdownMenu>
Expand Down