Skip to content

Commit

Permalink
Fix(web-react): Mandatory href for anchors #DS-661
Browse files Browse the repository at this point in the history
  • Loading branch information
curdaj authored and literat committed Nov 22, 2024
1 parent 1b669be commit f86a929
Show file tree
Hide file tree
Showing 57 changed files with 496 additions and 360 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Link } from '../Link';
import { useBreadcrumbsStyleProps } from './useBreadcrumbsStyleProps';

const defaultProps = {
href: '',
iconNameEnd: 'chevron-right',
iconNameStart: 'chevron-left',
isCurrent: false,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import React, { Fragment } from 'react';
import { ActionLinkColors } from '../../../constants';
import { UNDERLINED_OPTIONS } from '../../../types';
import { Icon } from '../../Icon';
import { Link } from '../../Link';
import Breadcrumbs from '../Breadcrumbs';
Expand Down Expand Up @@ -29,9 +31,9 @@ const BreadcrumbsCustom = () => {
const isLastItem = index === items.length - 1;

const linkParams = {
underlined: isLastItem ? 'hover' : 'always',
underlined: isLastItem ? UNDERLINED_OPTIONS.HOVER : UNDERLINED_OPTIONS.ALWAYS,
'aria-current': isLastItem ? 'page' : undefined,
color: isLastItem ? 'secondary' : 'primary',
color: isLastItem ? ActionLinkColors.SECONDARY : ActionLinkColors.PRIMARY,
};

return (
Expand Down
4 changes: 2 additions & 2 deletions packages/web-react/src/components/ButtonLink/ButtonLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import classNames from 'classnames';
import React, { ElementType, ForwardedRef, forwardRef } from 'react';
import { useStyleProps } from '../../hooks';
import { SpiritButtonLinkProps } from '../../types';
import { PolymorphicForwardRef, SpiritButtonLinkProps } from '../../types';
import { Spinner } from '../Spinner';
import { useButtonLinkAriaProps } from './useButtonLinkAriaProps';
import { useButtonLinkStyleProps } from './useButtonLinkStyleProps';
Expand Down Expand Up @@ -49,6 +49,6 @@ const _ButtonLink = <T extends ElementType = 'a', C = void, S = void>(
);
};

const ButtonLink = forwardRef<HTMLAnchorElement, SpiritButtonLinkProps<ElementType>>(_ButtonLink);
const ButtonLink = (forwardRef as PolymorphicForwardRef)(_ButtonLink);

export default ButtonLink;
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import { restPropsTest } from '../../../../tests/providerTests/restPropsTest';
import { stylePropsTest } from '../../../../tests/providerTests/stylePropsTest';
import ButtonLink from '../ButtonLink';

const buttonLinkDefaultProps = {
href: '#',
};

describe('ButtonLink', () => {
classNamePrefixProviderTest(ButtonLink, 'Button');

Expand All @@ -28,45 +32,45 @@ describe('ButtonLink', () => {
restPropsTest(ButtonLink, 'a');

it('should have default classname', () => {
const { container } = render(<ButtonLink />);
const { container } = render(<ButtonLink {...buttonLinkDefaultProps} />);

const element = container.querySelector('a') as HTMLElement;
expect(element).toHaveClass('Button--primary');
});

it('should have disabled classname', () => {
const { container } = render(<ButtonLink isDisabled />);
const { container } = render(<ButtonLink isDisabled {...buttonLinkDefaultProps} />);

const element = container.querySelector('a') as HTMLElement;
expect(element).toHaveClass('Button');
expect(element).toHaveClass('Button--disabled');
});

it('should have block classname', () => {
const { container } = render(<ButtonLink isBlock />);
const { container } = render(<ButtonLink isBlock {...buttonLinkDefaultProps} />);

const element = container.querySelector('a') as HTMLElement;
expect(element).toHaveClass('Button');
expect(element).toHaveClass('Button--block');
});

it('should have size classname', () => {
const { container } = render(<ButtonLink size="medium" />);
const { container } = render(<ButtonLink size="medium" {...buttonLinkDefaultProps} />);

const element = container.querySelector('a') as HTMLElement;
expect(element).toHaveClass('Button');
expect(element).toHaveClass('Button--medium');
});

it('should render text children', () => {
const dom = render(<ButtonLink>Hello World</ButtonLink>);
const dom = render(<ButtonLink {...buttonLinkDefaultProps}>Hello World</ButtonLink>);

const element = dom.container.querySelector('a') as HTMLElement;
expect(element.textContent).toBe('Hello World');
});

it('should not have default type attribute', () => {
const { container } = render(<ButtonLink />);
const { container } = render(<ButtonLink {...buttonLinkDefaultProps} />);

const element = container.querySelector('a') as HTMLElement;
expect(element).not.toHaveAttribute('type');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ const ButtonLinkDemoFactory = ({ ...props }: ButtonBaseProps) => {
<DocsSection key={size} title={`Size ${size}`}>
{colors.map((color) => (
<div key={color}>
<ButtonLink size={size} color={color} elementType="a" {...props}>
<ButtonLink href="#" size={size} color={color} elementType="a" {...props}>
{`Button ${color}`}
</ButtonLink>{' '}
<ButtonLink size={size} color={color} {...props}>
<ButtonLink href="#" size={size} color={color} {...props}>
<Icon name="link" marginRight="space-400" />
Menu
</ButtonLink>{' '}
<ButtonLink size={size} color={color} isSymmetrical {...props}>
<ButtonLink href="#" size={size} color={color} isSymmetrical {...props}>
<Icon name="link" />
</ButtonLink>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const meta: Meta<typeof ButtonLink> = {
args: {
children: 'Click me',
color: ActionButtonColors.PRIMARY,
href: '#',
isBlock: false,
isDisabled: false,
isLoading: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const CollapseMultipleTriggers = () => {
<Button onClick={toggleHandler} color="secondary">
Secondary trigger
</Button>{' '}
<ButtonLink onClick={toggleHandler} aria-expanded={isOpen} color="tertiary">
<ButtonLink href="#" onClick={toggleHandler} aria-expanded={isOpen} color="tertiary">
Tertiary trigger
</ButtonLink>
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const CollapseVisibilityBreakpointDesktop = () => {

return (
<>
<ButtonLink onClick={toggleHandlerDesktop} size="medium" UNSAFE_className="d-desktop-none">
<ButtonLink href="#" onClick={toggleHandlerDesktop} size="medium" UNSAFE_className="d-desktop-none">
Collapse trigger
</ButtonLink>
<Collapse id="collapse-visibility-breakpoint-desktop-id" isOpen={isOpenDesktop} collapsibleToBreakpoint="desktop">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const CollapseVisibilityBreakpointTablet = () => {

return (
<>
<ButtonLink onClick={toggleHandlerTablet} size="medium" UNSAFE_className="d-tablet-none">
<ButtonLink href="#" onClick={toggleHandlerTablet} size="medium" UNSAFE_className="d-tablet-none">
Collapse trigger
</ButtonLink>
<Collapse id="collapse-visibility-breakpoint-tablet-id" isOpen={isOpenTablet} collapsibleToBreakpoint="tablet">
Expand Down
10 changes: 5 additions & 5 deletions packages/web-react/src/components/Footer/demo/FooterDefault.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,31 +138,31 @@ const FooterDefault = () => {
{/* Flex with social media links */}
<Flex elementType="ul" alignmentX="center" alignmentY="center">
<li>
<ButtonLink size="medium" color="tertiary" isSymmetrical>
<ButtonLink href="#" size="medium" color="tertiary" isSymmetrical>
<VisuallyHidden>Facebook</VisuallyHidden>
<Icon name="logo-facebook" />
</ButtonLink>
</li>
<li>
<ButtonLink size="medium" color="tertiary" isSymmetrical>
<ButtonLink href="#" size="medium" color="tertiary" isSymmetrical>
<VisuallyHidden>X</VisuallyHidden>
<Icon name="logo-x" />
</ButtonLink>
</li>
<li>
<ButtonLink size="medium" color="tertiary" isSymmetrical>
<ButtonLink href="#" size="medium" color="tertiary" isSymmetrical>
<VisuallyHidden>YouTube</VisuallyHidden>
<Icon name="logo-youtube" />
</ButtonLink>
</li>
<li>
<ButtonLink size="medium" color="tertiary" isSymmetrical>
<ButtonLink href="#" size="medium" color="tertiary" isSymmetrical>
<VisuallyHidden>Google</VisuallyHidden>
<Icon name="logo-google" />
</ButtonLink>
</li>
<li>
<ButtonLink size="medium" color="tertiary" isSymmetrical>
<ButtonLink href="#" size="medium" color="tertiary" isSymmetrical>
<VisuallyHidden>LinkedIn</VisuallyHidden>
<Icon name="logo-linkedin" />
</ButtonLink>
Expand Down
7 changes: 4 additions & 3 deletions packages/web-react/src/components/Header/HeaderDialogLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import classNames from 'classnames';
import React, { ElementType, forwardRef } from 'react';
import { useStyleProps } from '../../hooks';
import { PolymorphicRef, SpiritDialogHeaderLinkProps } from '../../types';
import { PolymorphicForwardRef, PolymorphicRef, SpiritDialogHeaderLinkProps } from '../../types';
import { useHeaderStyleProps } from './useHeaderStyleProps';

/* We need an exception for components exported with forwardRef */
Expand All @@ -12,14 +12,15 @@ const _HeaderDialogLink = <E extends ElementType = 'a'>(
props: SpiritDialogHeaderLinkProps<E>,
ref: PolymorphicRef<E>,
): JSX.Element => {
const { elementType: ElementTag = 'a', children, isCurrent, ...restProps } = props;
const { elementType: ElementTag = 'a' as ElementType, children, isCurrent, ...restProps } = props;
const { classProps } = useHeaderStyleProps({ isCurrentLink: isCurrent });
const { styleProps, props: otherProps } = useStyleProps(restProps);

return (
<ElementTag
{...otherProps}
className={classNames(classProps.headerDialogLink, styleProps.className)}
href={restProps.href}
style={styleProps.style}
ref={ref}
>
Expand All @@ -28,6 +29,6 @@ const _HeaderDialogLink = <E extends ElementType = 'a'>(
);
};

const HeaderDialogLink = forwardRef<HTMLAnchorElement, SpiritDialogHeaderLinkProps<ElementType>>(_HeaderDialogLink);
const HeaderDialogLink = (forwardRef as PolymorphicForwardRef)(_HeaderDialogLink);

export default HeaderDialogLink;
7 changes: 4 additions & 3 deletions packages/web-react/src/components/Header/HeaderLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import classNames from 'classnames';
import React, { ElementType, forwardRef } from 'react';
import { useStyleProps } from '../../hooks';
import { PolymorphicRef, SpiritHeaderLinkProps } from '../../types';
import { PolymorphicForwardRef, PolymorphicRef, SpiritHeaderLinkProps } from '../../types';
import { useHeaderStyleProps } from './useHeaderStyleProps';

/* We need an exception for components exported with forwardRef */
Expand All @@ -12,14 +12,15 @@ const _HeaderLink = <E extends ElementType = 'a'>(
props: SpiritHeaderLinkProps<E>,
ref: PolymorphicRef<E>,
): JSX.Element => {
const { elementType: ElementTag = 'a', children, isCurrent, ...restProps } = props;
const { elementType: ElementTag = 'a' as ElementType, children, isCurrent, ...restProps } = props;
const { classProps } = useHeaderStyleProps({ isCurrentLink: isCurrent });
const { styleProps, props: otherProps } = useStyleProps(restProps);

return (
<ElementTag
{...otherProps}
className={classNames(classProps.headerLink, styleProps.className)}
href={restProps.href}
style={styleProps.style}
ref={ref}
>
Expand All @@ -28,6 +29,6 @@ const _HeaderLink = <E extends ElementType = 'a'>(
);
};

const HeaderLink = forwardRef<HTMLAnchorElement, SpiritHeaderLinkProps<ElementType>>(_HeaderLink);
const HeaderLink = (forwardRef as PolymorphicForwardRef)(_HeaderLink);

export default HeaderLink;
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,19 @@ describe('HeaderDialogLink', () => {
restPropsTest((props) => <HeaderDialogLink {...props} />, 'a');

it('should render text children', () => {
const dom = render(<HeaderDialogLink id="test">Hello World</HeaderDialogLink>);
const dom = render(
<HeaderDialogLink href="#" id="test">
Hello World
</HeaderDialogLink>,
);

const element = dom.container.querySelector('a') as HTMLElement;
expect(element.textContent).toBe('Hello World');
});

it('should render button element', () => {
const dom = render(
<HeaderDialogLink id="test" elementType="button">
<HeaderDialogLink href="#" id="test" elementType="button">
Hello World
</HeaderDialogLink>,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,19 @@ describe('HeaderLink', () => {
restPropsTest((props) => <HeaderLink {...props} />, 'a');

it('should render text children', () => {
const dom = render(<HeaderLink id="test">Hello World</HeaderLink>);
const dom = render(
<HeaderLink href="#" id="test">
Hello World
</HeaderLink>,
);

const element = dom.container.querySelector('a') as HTMLElement;
expect(element.textContent).toBe('Hello World');
});

it('should render button element', () => {
const dom = render(
<HeaderLink id="test" elementType="button">
<HeaderLink href="#" id="test" elementType="button">
Hello World
</HeaderLink>,
);
Expand Down
24 changes: 14 additions & 10 deletions packages/web-react/src/components/Header/demo/HeaderWithActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,21 @@ const HeaderWithActions = () => {
<HeaderDesktopActions aria-label="Main navigation">
<HeaderNav>
<HeaderNavItem>
<HeaderLink isCurrent>Job offers</HeaderLink>
<HeaderLink href="#" isCurrent>
Job offers
</HeaderLink>
</HeaderNavItem>
<HeaderNavItem>
<HeaderLink>Part-time jobs</HeaderLink>
<HeaderLink href="#">Part-time jobs</HeaderLink>
</HeaderNavItem>
<HeaderNavItem>
<HeaderLink>Inspiration</HeaderLink>
<HeaderLink href="#">Inspiration</HeaderLink>
</HeaderNavItem>
<HeaderNavItem>
<HeaderLink>Replies</HeaderLink>
<HeaderLink href="#">Replies</HeaderLink>
</HeaderNavItem>
<HeaderNavItem>
<HeaderLink>Employers</HeaderLink>
<HeaderLink href="#">Employers</HeaderLink>
</HeaderNavItem>
</HeaderNav>
</HeaderDesktopActions>
Expand All @@ -62,19 +64,21 @@ const HeaderWithActions = () => {
<HeaderDialogActions color="primary" aria-label="Main navigation">
<HeaderDialogNav>
<HeaderDialogNavItem>
<HeaderDialogLink isCurrent>Job offers</HeaderDialogLink>
<HeaderDialogLink href="#" isCurrent>
Job offers
</HeaderDialogLink>
</HeaderDialogNavItem>
<HeaderDialogNavItem>
<HeaderDialogLink>Part-time jobs</HeaderDialogLink>
<HeaderDialogLink href="#">Part-time jobs</HeaderDialogLink>
</HeaderDialogNavItem>
<HeaderDialogNavItem>
<HeaderDialogLink>Inspiration</HeaderDialogLink>
<HeaderDialogLink href="#">Inspiration</HeaderDialogLink>
</HeaderDialogNavItem>
<HeaderDialogNavItem>
<HeaderDialogLink>Replies</HeaderDialogLink>
<HeaderDialogLink href="#">Replies</HeaderDialogLink>
</HeaderDialogNavItem>
<HeaderDialogNavItem>
<HeaderDialogLink>Employers</HeaderDialogLink>
<HeaderDialogLink href="#">Employers</HeaderDialogLink>
</HeaderDialogNavItem>
</HeaderDialogNav>
</HeaderDialogActions>
Expand Down
Loading

0 comments on commit f86a929

Please sign in to comment.