Skip to content

Commit

Permalink
feat: custom components for button and link (#814)
Browse files Browse the repository at this point in the history
* feat(button): allow to use custom component, router issue

* feat(link): allow to use custom component, add tests
  • Loading branch information
reme3d2y committed Aug 27, 2021
1 parent 39bf4c3 commit a623dd0
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 13 deletions.
28 changes: 28 additions & 0 deletions packages/button/src/Component.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,34 @@ describe('Button', () => {
});
});

describe('Custom component', () => {
it('should use custom component', () => {
const cb = jest.fn();
cb.mockReturnValue(null);

render(<Button Component={cb} dataTestId={dataTestId} />);

expect(cb).toBeCalled();

const props = cb.mock.calls[0][0];
expect(props['data-test-id']).toBe(dataTestId);
});

it('should pass `to` instead `href` to custom component', () => {
const cb = jest.fn();
cb.mockReturnValue(null);

render(<Button Component={cb} href='test' />);

expect(cb).toBeCalled();

const props = cb.mock.calls[0][0];

expect(props.href).toBeFalsy();
expect(props.to).toBe('test');
});
});

it('should unmount without errors', () => {
const { unmount } = render(
<Button leftAddons={<span>Left</span>} rightAddons={<span>Right</span>}>
Expand Down
24 changes: 16 additions & 8 deletions packages/button/src/Component.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {
AnchorHTMLAttributes,
ButtonHTMLAttributes,
ElementType,
useEffect,
useRef,
useState,
Expand Down Expand Up @@ -54,7 +55,12 @@ export type ComponentProps = {
/**
* Выводит ссылку в виде кнопки
*/
href?: AnchorHTMLAttributes<HTMLAnchorElement>['href'];
href?: string;

/**
* Позволяет использовать кастомный компонент для кнопки (например Link из роутера)
*/
Component?: ElementType;

/**
* Идентификатор для систем автоматизированного тестирования
Expand Down Expand Up @@ -103,6 +109,7 @@ export const Button = React.forwardRef<HTMLAnchorElement | HTMLButtonElement, Bu
loading = false,
nowrap = false,
colors = 'default',
Component = href ? 'a' : 'button',
...restProps
},
ref,
Expand Down Expand Up @@ -175,16 +182,19 @@ export const Button = React.forwardRef<HTMLAnchorElement | HTMLButtonElement, Bu
if (href) {
const { target } = restProps as AnchorHTMLAttributes<HTMLAnchorElement>;

// Для совместимости с react-router-dom, меняем href на to
const hrefProps = { [typeof Component === 'string' ? 'href' : 'to']: href };

return (
<a
<Component
rel={target === '_blank' ? 'noreferrer noopener' : undefined}
{...componentProps}
{...(restProps as AnchorHTMLAttributes<HTMLAnchorElement>)}
href={href}
{...hrefProps}
ref={mergeRefs([buttonRef, ref])}
>
{buttonChildren}
</a>
</Component>
);
}

Expand All @@ -193,17 +203,15 @@ export const Button = React.forwardRef<HTMLAnchorElement | HTMLButtonElement, Bu
>;

return (
// eslint-disable-next-line react/button-has-type
<button
<Component
{...componentProps}
{...restButtonProps}
// eslint-disable-next-line react/button-has-type
type={type}
disabled={disabled || showLoader}
ref={mergeRefs([buttonRef, ref])}
>
{buttonChildren}
</button>
</Component>
);
},
);
Expand Down
20 changes: 20 additions & 0 deletions packages/link/src/Component.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,26 @@ describe('Attributes tests', () => {
});
});

describe('Custom component', () => {
it('should use custom component and replace `href` to `to`', () => {
const cb = jest.fn();
cb.mockReturnValue(null);

render(
<Link Component={cb} href='test'>
Link
</Link>,
);

expect(cb).toBeCalled();

const props = cb.mock.calls[0][0];

expect(props.href).toBeFalsy();
expect(props.to).toBe('test');
});
});

describe('Render tests', () => {
it('should unmount without errors', () => {
const { unmount } = render(<Link href=''>Link</Link>);
Expand Down
19 changes: 14 additions & 5 deletions packages/link/src/Component.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { AnchorHTMLAttributes, forwardRef, ReactNode, useRef } from 'react';
import React, { AnchorHTMLAttributes, ElementType, forwardRef, ReactNode, useRef } from 'react';
import cn from 'classnames';
import mergeRefs from 'react-merge-refs';
import { useFocus } from '@alfalab/hooks';
Expand Down Expand Up @@ -33,12 +33,17 @@ export type LinkProps = NativeProps & {
/**
* Слот слева
*/
leftAddons?: React.ReactNode;
leftAddons?: ReactNode;

/**
* Слот справа
*/
rightAddons?: React.ReactNode;
rightAddons?: ReactNode;

/**
* Позволяет использовать кастомный компонент для кнопки (например Link из роутера)
*/
Component?: ElementType;

/**
* Дополнительный класс (native prop)
Expand Down Expand Up @@ -77,6 +82,8 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
dataTestId,
children,
colors = 'default',
href,
Component = 'a',
...restProps
},
ref,
Expand All @@ -98,10 +105,12 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
),
'data-test-id': dataTestId,
rel: restProps.target === '_blank' ? 'noreferrer noopener' : undefined,
// Для совместимости с react-router-dom, меняем href на to
[typeof Component === 'string' ? 'href' : 'to']: href,
};

return (
<a {...componentProps} {...restProps} ref={mergeRefs([linkRef, ref])}>
<Component {...componentProps} {...restProps} ref={mergeRefs([linkRef, ref])}>
{leftAddons || rightAddons ? (
<React.Fragment>
{leftAddons && <span className={styles.addons}>{leftAddons}</span>}
Expand All @@ -115,7 +124,7 @@ export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
) : (
<span className={styles.text}>{children}</span>
)}
</a>
</Component>
);
},
);
Expand Down

0 comments on commit a623dd0

Please sign in to comment.