Skip to content

Commit

Permalink
feat(Breadcrumbs): migrate to Tailwind
Browse files Browse the repository at this point in the history
Update packages/orbit-components/src/Breadcrumbs/index.tsx

Co-authored-by: Daniel Sil <daniel.sil@kiwi.com>
  • Loading branch information
mainframev and DSil committed Oct 2, 2023
1 parent 332f58e commit c2c7935
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 117 deletions.
120 changes: 63 additions & 57 deletions packages/orbit-components/src/Breadcrumbs/BreadcrumbsItem/index.tsx
@@ -1,63 +1,48 @@
"use client";

import * as React from "react";
import styled, { css } from "styled-components";
import cx from "clsx";

import defaultTheme from "../../defaultTheme";
import ChevronForward from "../../icons/ChevronForward";
import type { Props } from "./types";
import ChevronForward from "../../icons/ChevronForward";

const StyledBreadcrumbsItem = styled.li`
display: flex;
align-items: center;
`;

const StyledBreadcrumbsItemAnchor = styled(({ component, children, isClickable, ...props }) => {
const Component = isClickable ? component : "div";
return <Component {...props}>{children}</Component>;
})`
${({ theme, $active, isClickable }) => css`
font-weight: ${$active ? theme.orbit.fontWeightBold : theme.orbit.fontWeightMedium};
color: ${theme.orbit.paletteInkDark};
text-decoration: ${isClickable && !$active ? "underline" : "none"};
${isClickable &&
css`
transition: color ${theme.orbit.durationFast} ease-in-out;
cursor: pointer;
border: none;
padding: 0;
outline-offset: 1px;
&:hover {
text-decoration: none;
color: ${theme.orbit.paletteProductNormalHover};
}
`};
`}
`;
const Component = ({
href,
onClick,
component: Comp,
children,
...props
}: Props &
Pick<
React.HTMLAttributes<HTMLDivElement | HTMLAnchorElement | HTMLButtonElement>,
"itemScope" | "itemType" | "itemProp" | "itemID" | "className"
>) => {
const isClickable = onClick != null || href != null;
if (!isClickable) return <div {...props}>{children}</div>;

StyledBreadcrumbsItemAnchor.defaultProps = {
theme: defaultTheme,
};
if (Comp != null)
return (
<Comp {...props} onClick={onClick} href={href}>
{children}
</Comp>
);

const StyledBreadcrumbsItemIcon = styled(ChevronForward)`
${({ theme }) => css`
margin: 0 ${theme.orbit.spaceXXSmall};
`}
`;
if (href == null)
return (
<button type="button" {...props} onClick={onClick}>
{children}
</button>
);

StyledBreadcrumbsItemIcon.defaultProps = {
theme: defaultTheme,
return (
<a href={href} onClick={onClick} {...props}>
{children}
</a>
);
};

const DefaultComponent = (props: Props) =>
// @ts-expect-error TODO
// eslint-disable-next-line jsx-a11y/anchor-has-content
props.onClick && !props.href ? <button {...props} type="button" /> : <a {...props} />;

const BreadcrumbsItem = ({
active,
active = false,
children,
dataTest,
onClick,
Expand All @@ -68,32 +53,53 @@ const BreadcrumbsItem = ({
...props
}: Props) => {
return (
<StyledBreadcrumbsItem
<li
data-test={dataTest}
aria-current={active ? "page" : undefined}
itemProp="itemListElement"
itemScope
itemType="http://schema.org/ListItem"
className="flex items-center"
>
<StyledBreadcrumbsItemAnchor
isClickable={href || onClick}
<Component
href={href}
component={component || DefaultComponent}
$active={active}
component={component}
onClick={onClick}
itemScope
itemType="http://schema.org/WebPage"
itemProp="item"
itemID={id || href}
itemID={id ?? href}
{...props}
className={cx(
"text-ink-dark",
active ? "font-bold" : "font-medium",
(onClick != null || href != null) && [
active ? "no-underline" : "underline",
"cursor-pointer",
"border-none",
"p-0",
"outline-offset-1",
"duration-fast transition-colors ease-in-out",
"hover:no-underline",
"hover:text-product-normal-hover",
],
)}
>
<span itemProp="name">{children}</span>
</StyledBreadcrumbsItemAnchor>
</Component>

<meta itemProp="position" content={String(contentKey)} />

{!active && (
<StyledBreadcrumbsItemIcon ariaHidden reverseOnRtl size="small" color="tertiary" />
<ChevronForward
ariaHidden
reverseOnRtl
size="small"
color="tertiary"
className="mx-xxs my-0"
/>
)}
</StyledBreadcrumbsItem>
</li>
);
};

Expand Down
Expand Up @@ -12,5 +12,5 @@ export interface Props extends Common.Globals {
readonly href?: string;
readonly id?: string;
readonly contentKey?: number;
readonly onClick?: Common.Event<React.SyntheticEvent<HTMLLinkElement>>;
readonly onClick?: Common.Event<React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>>;
}
2 changes: 1 addition & 1 deletion packages/orbit-components/src/Breadcrumbs/README.md
Expand Up @@ -6,7 +6,7 @@ To implement the Button component into your project you'll need to add the impor
import Breadcrumbs, { BreadcrumbsItem } from "@kiwicom/orbit-components/lib/Breadcrumbs";
```

After adding import in your project you can use it simply like:
After adding import to your project you can use it simply like:

```jsx
<Breadcrumbs>
Expand Down
92 changes: 34 additions & 58 deletions packages/orbit-components/src/Breadcrumbs/index.tsx
@@ -1,78 +1,55 @@
"use client";

import * as React from "react";
import styled, { css } from "styled-components";
import cx from "clsx";

import type * as Common from "../common/types";
import defaultTheme from "../defaultTheme";
import ChevronBackward from "../icons/ChevronBackward";
import getSpacingToken from "../common/getSpacingToken";
import { right } from "../utils/rtl";
import TextLink from "../TextLink";
import Hide from "../Hide";
import type { Props } from "./types";
import type { Props as BreadcrumbsItemProps } from "./BreadcrumbsItem/types";
import ChevronBackward from "../icons/ChevronBackward";
import Hide from "../Hide";
import TextLink from "../TextLink";
import spaceAfterClasses from "../common/tailwind/spaceAfter";

const StyledBreadcrumbs = styled.nav<{ spaceAfter?: Common.SpaceAfterSizes }>`
${({ theme }) => css`
font-family: ${theme.orbit.fontFamily};
font-size: ${theme.orbit.fontSizeTextSmall};
margin-bottom: ${getSpacingToken};
`}
`;

StyledBreadcrumbs.defaultProps = {
theme: defaultTheme,
};

const StyledBreadcrumbsList = styled.ol`
display: flex;
flex-wrap: wrap;
list-style: none;
margin: 0;
padding: 0;
`;

const StyledBackButtonWrapper = styled.span`
${({ theme }) => css`
margin-${right}: ${theme.orbit.spaceSmall};
`};
`;

StyledBackButtonWrapper.defaultProps = {
theme: defaultTheme,
};

const Breadcrumbs = (props: Props) => {
const childEls = React.Children.toArray(
props.children,
) as React.ReactElement<BreadcrumbsItemProps>[];
const Breadcrumbs = ({
children,
dataTest,
onGoBack,
goBackTitle = "Back",
spaceAfter,
backHref,
id,
}: Props) => {
const childEls = React.Children.toArray(children) as React.ReactElement<BreadcrumbsItemProps>[];

const { children, dataTest, onGoBack, goBackTitle = "Back", spaceAfter, backHref, id } = props;
return (
<>
<Hide on={["smallMobile", "mediumMobile"]}>
<StyledBreadcrumbs
<nav
className={cx(
"font-base font-text text-small",
spaceAfter && spaceAfterClasses[spaceAfter],
)}
aria-label="Breadcrumb"
id={id}
data-test={dataTest}
spaceAfter={spaceAfter}
>
<StyledBreadcrumbsList itemScope itemType="http://schema.org/BreadcrumbList">
<ol
className="m-0 flex list-none flex-wrap p-0"
itemScope
itemType="http://schema.org/BreadcrumbList"
>
{React.Children.map(childEls, (item, key) => {
if (React.isValidElement(item)) {
return React.cloneElement<BreadcrumbsItemProps>(item, {
active: key === React.Children.count(children) - 1,
contentKey: key + 1,
});
}
return null;
if (!React.isValidElement(item)) return null;
return React.cloneElement<BreadcrumbsItemProps>(item, {
active: key === React.Children.count(children) - 1,
contentKey: key + 1,
});
})}
</StyledBreadcrumbsList>
</StyledBreadcrumbs>
</ol>
</nav>
</Hide>
<Hide on={["largeMobile", "tablet", "desktop", "largeDesktop"]}>
{onGoBack || backHref ? (
{onGoBack || Boolean(backHref) ? (
<TextLink
standAlone
type="secondary"
Expand All @@ -90,6 +67,5 @@ const Breadcrumbs = (props: Props) => {
);
};

export default Breadcrumbs;

export { default as BreadcrumbsItem } from "./BreadcrumbsItem";
export default Breadcrumbs;

0 comments on commit c2c7935

Please sign in to comment.