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

fix: improved badge typing for prop combinations wrt button & a element types #3078

Merged
merged 4 commits into from Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/warm-poets-teach.md
@@ -0,0 +1,7 @@
---
'@twilio-paste/badge': patch
'@twilio-paste/popover': patch
'@twilio-paste/core': patch
---

[Badge, Popover] Improved typings for restricted Badge prop combinations when create button or linked badges
40 changes: 0 additions & 40 deletions packages/paste-core/components/badge/__tests__/index.spec.tsx
Expand Up @@ -179,26 +179,6 @@ describe('Badge', () => {
expect(getByRole('button')).toBeInTheDocument();
});

it('should show an error if as is "button" but href is provided', () => {
expect(() =>
render(
<Badge as="button" variant="neutral" href="#">
test
</Badge>
)
).toThrow();
});

it('should show an error if as is "button" and onClick is not given', () => {
expect(() =>
render(
<Badge as="button" variant="neutral">
test
</Badge>
)
).toThrow();
});

it('should render badge as button with correct styles', () => {
render(
<Badge as="button" onClick={() => null} variant="success">
Expand Down Expand Up @@ -301,26 +281,6 @@ describe('Badge', () => {
expect(getByRole('link')).toBeInTheDocument();
});

it('should show an error if as is "a" but onClick is provided', () => {
expect(() =>
render(
<Badge as="a" variant="neutral" onClick={() => {}}>
test
</Badge>
)
).toThrow();
});

it('should show an error if as is "a" and href is not given', () => {
expect(() =>
render(
<Badge as="a" variant="neutral">
test
</Badge>
)
).toThrow();
});

it('should render badge as anchor with correct styles', () => {
render(
<Badge href="#test" as="a" variant="success">
Expand Down
3 changes: 2 additions & 1 deletion packages/paste-core/components/badge/package.json
Expand Up @@ -60,6 +60,7 @@
"@twilio-paste/uid-library": "^1.0.0",
"prop-types": "^15.7.2",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"react-dom": "^18.0.0",
"typescript": "^4.9.4"
}
}
18 changes: 1 addition & 17 deletions packages/paste-core/components/badge/src/index.tsx
Expand Up @@ -8,24 +8,8 @@ import type {BadgeProps, BadgeVariants} from './types';
import {useResizeChildIcons} from './hooks';
import {badgeVariantStyles, getBadgeAnchorStyles, getBadgeButtonStyles} from './styles';

const handlePropValidation = ({as, href, onClick}: Partial<BadgeProps>): void => {
if (as === 'a') {
if (href === null || href === undefined)
throw new Error('[Paste: Badge] Badge is being used as an anchor (`as="a"`). Provide an href.');
if (onClick != null)
throw new Error('[Paste: Badge] An onClick was provided. To use as a button, use `as="button"`.');
}
if (as === 'button') {
if (onClick === null || onClick === undefined)
throw new Error('[Paste: Badge] Badge is being used as an button (`as="button"`). Provide an onClick.');
if (href === null) throw new Error('[Paste: Badge] An href was provided. To use as an anchor, use `as="a"`.');
}
};

export const Badge = React.forwardRef<HTMLElement, BadgeProps>(
({as, href, variant, children, element = 'BADGE', ...props}, ref) => {
handlePropValidation({as, href, ...props});

const resizedChildren = useResizeChildIcons(children);

let badgeStyles = badgeVariantStyles[variant];
Expand Down Expand Up @@ -98,4 +82,4 @@ Badge.propTypes = {
onClick: PropTypes.func,
};

export type {BadgeProps} from './types';
export * from './types';
31 changes: 21 additions & 10 deletions packages/paste-core/components/badge/src/types.ts
Expand Up @@ -16,13 +16,24 @@ export type BadgeVariants =
| 'default'
| 'info';

type BadgeBaseProps = Pick<BoxProps, 'element'> &
React.HTMLAttributes<HTMLSpanElement> &
React.HTMLAttributes<HTMLButtonElement> &
React.HTMLAttributes<HTMLAnchorElement> & {
children: NonNullable<React.ReactNode>;
variant: BadgeVariants;
as: 'span' | 'button' | 'a';
href?: string;
};
export type BadgeProps = BadgeBaseProps;
export type BadgeBaseProps = Pick<BoxProps, 'element'> & {
children: NonNullable<React.ReactNode>;
variant: BadgeVariants;
};
export type BadgeSpanProps = React.HTMLAttributes<HTMLSpanElement> & {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the improved typings

as: 'span';
href?: never;
onClick?: never;
};
export type BadgeButtonProps = React.HTMLAttributes<HTMLButtonElement> & {
as: 'button';
onClick: React.HTMLAttributes<HTMLButtonElement>['onClick'];
href?: never;
};
export type BadgeAnchorProps = React.HTMLAttributes<HTMLAnchorElement> & {
as: 'a';
href: string;
onClick?: never;
};

export type BadgeProps = BadgeBaseProps & (BadgeSpanProps | BadgeButtonProps | BadgeAnchorProps);
3 changes: 2 additions & 1 deletion packages/paste-core/components/popover/package.json
Expand Up @@ -72,6 +72,7 @@
"@twilio-paste/uid-library": "^1.0.0",
"prop-types": "^15.7.2",
"react": "^18.0.0",
"react-dom": "^18.0.0"
"react-dom": "^18.0.0",
"typescript": "^4.9.4"
}
}
9 changes: 6 additions & 3 deletions packages/paste-core/components/popover/src/types.ts
@@ -1,13 +1,16 @@
import type {BadgeProps} from '@twilio-paste/badge';
import type {BadgeBaseProps, BadgeButtonProps} from '@twilio-paste/badge';
import type {ButtonProps} from '@twilio-paste/button';
import type {BoxProps} from '@twilio-paste/box';

export type ButtonBadgeProps = BadgeProps;
export type ButtonBadgeProps = BadgeBaseProps &
Omit<BadgeButtonProps, 'onClick'> & {
onClick?: BadgeButtonProps['onClick'];
};

type PopoverButtonBaseProps = Pick<BoxProps, 'element'> & {
id?: string;
toggle?: () => void;
};

export type PopoverButtonProps = PopoverButtonBaseProps & Omit<ButtonProps, 'as'>;
export type PopoverBadgeButtonProps = PopoverButtonBaseProps & Omit<BadgeProps, 'as'>;
export type PopoverBadgeButtonProps = PopoverButtonBaseProps & Omit<ButtonBadgeProps, 'as'>;
2 changes: 2 additions & 0 deletions yarn.lock
Expand Up @@ -11013,6 +11013,7 @@ __metadata:
prop-types: ^15.7.2
react: ^18.0.0
react-dom: ^18.0.0
typescript: ^4.9.4
peerDependencies:
"@twilio-paste/anchor": ^10.0.0
"@twilio-paste/animation-library": ^1.0.0
Expand Down Expand Up @@ -13140,6 +13141,7 @@ __metadata:
prop-types: ^15.7.2
react: ^18.0.0
react-dom: ^18.0.0
typescript: ^4.9.4
peerDependencies:
"@twilio-paste/anchor": ^10.0.0
"@twilio-paste/animation-library": ^1.0.0
Expand Down