Skip to content

Commit

Permalink
feat(Breadcrumbs)!: remove top-level sub-component (#1680)
Browse files Browse the repository at this point in the history
- merge BreadcrumbsItem into Breadcrumbs directly
  • Loading branch information
booc0mtaco committed Jul 19, 2023
1 parent f46eca7 commit 669081d
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 246 deletions.
133 changes: 133 additions & 0 deletions src/components/Breadcrumbs/Breadcrumbs.module.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import '../../design-tokens/mixins.css';

/*------------------------------------*\
# BREADCRUMBS
\*------------------------------------*/
Expand All @@ -15,3 +17,134 @@
list-style: none;
display: flex;
}

/*------------------------------------*\
# BREADCRUMBS ITEM
\*------------------------------------*/

/**
* Breadcrumbs list item.
*/
.breadcrumbs__item {
font: var(--eds-theme-typography-body-xs);
max-width: 100%;
/* Required for the Menu to absolutely position relative to this container. */
position: relative;

/* Hides all breadcrumbs except the last breadcrumb in smaller breakpoints. */
display: none;
flex-shrink: 0;
&:last-of-type {
display: flex;
/* Truncate last breadcrumb in smaller breakpoints */
flex: 1 0 0%;
}

min-width: 0;

align-items: center;

@media all and (min-width: $eds-bp-md) {
/* Display breadcrumbs in larger breakpoints. */
display: flex;
&:last-of-type {
/* Truncate last breadcrumb in smaller breakpoints */
flex: 0 0 auto;
}
}
}

/**
* Back variant of the breadcrumbs list item.
*/
.breadcrumbs__item-back {
display: flex;
margin-right: var(--eds-size-1-and-half);
@media all and (min-width: $eds-bp-md) {
/* Hidden for larger breakpoints. */
display: none;
}
}
.breadcrumbs__item-back > .breadcrumbs__separator {
/* Hide the separator for the back variant. */
display: none;
}

/**
* Ellipsis variant of the breadcrumbs list item.
*/
.breadcrumbs__ellipsis {
min-height: unset;
min-width: unset;

border: none;
border-radius: unset;
background-color: transparent;
}
.breadcrumbs__ellipsis:hover {
background-color: transparent;
}

/**
* Breadcrumbs link.
*/
.breadcrumbs__link {
color: var(--eds-theme-color-text-neutral-subtle);
text-decoration: none;

&:last-of-type {
/* Truncate last link with ellipsis. */
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

&:hover,
&:focus-visible {
text-decoration: underline;
}

&:focus-visible {
@mixin focus;
}

@supports not selector(:focus-visible) {
&:hover,
&:focus {
text-decoration: underline;
}

&:focus {
@mixin focus;
}
}
}

/**
* Breadcrumbs Icon - a separator between breadcrumb links.
*/
.breadcrumbs__separator {
color: var(--eds-theme-color-icon-neutral-subtle);
margin-left: var(--eds-size-1);
margin-right: var(--eds-size-1);
cursor: default;
}

/**
* Last breadcrumbs item icon.
*/
.breadcrumbs__item:last-child .breadcrumbs__separator.breadcrumbs__separator {
/* A separator shouldn't be displayed after last link. */
display: none;
}

/**
* Breadcrumbs Back Icon.
*/
.breadcrumbs__back-icon {
color: var(--eds-theme-color-icon-neutral-subtle);
/* Transform over height due to icon being placed inside <a>. */
transform: scale(1.5);
position: relative;
bottom: 0.125rem;
}
113 changes: 110 additions & 3 deletions src/components/Breadcrumbs/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import clsx from 'clsx';
import { debounce } from 'lodash';
import React, { createContext, useContext, type ReactNode } from 'react';
import { flattenReactChildren } from '../../util/flattenReactChildren';
import BreadcrumbsItem from '../BreadcrumbsItem';
// import BreadcrumbsItem from '../BreadcrumbsItem';
import Icon from '../Icon';
import Menu from '../Menu';
import styles from './Breadcrumbs.module.css';

type Separators = '|' | '>' | '/';

type Props = {
/**
* aria-label for `nav` element to describe Breadcrumbs navigation to screen readers
Expand All @@ -27,11 +30,11 @@ type Props = {
* Custom string separator between individual breadcrumbs
* Defaults to '/'
*/
separator?: '|' | '>' | '/';
separator?: Separators;
};

type Context = {
separator?: '|' | '>' | '/';
separator?: Separators;
};

const BreadcrumbsContext = createContext<Context>({});
Expand Down Expand Up @@ -180,4 +183,108 @@ const CustomSeparatorBreadcrumbsItem = (
return <BreadcrumbsItem separator={separator} {...props} />;
};

type BreadcrumbItemProps = {
/**
* CSS class names that can be appended to the component.
*/
className?: string;
/**
* URL for the breadcrumbs item.
* Required since breadcrumbs should reroute user.
* Null case is used for the collapsed variant, which uses Menu Items which has hrefs.
*/
href: string | null;
/**
* URLs for the collapsed breadcrumbs variant.
* Should be <Menu.Item href={href}>{text}</Menu.Item>.
*/
menuItems?: React.ReactNode[];
/**
* Custom string separator after current breadcrumb item.
* Defaults to '/'
*/
separator?: Separators;
/**
* Breadcrumbs item text.
*/
text?: string;
/**
* Behavior variations for the breadcrumbs item.
* - **back** - results in a left facing icon, usually denoting the second last breadcrumb item in a mobile breakpoint.
* - **collapsed** - results in an ellipsis, where interaction spawns a Menu containing more links.
*/
variant?: 'back' | 'collapsed';
};

/**
* `import {BreadcrumbsItem} from "@chanzuckerberg/eds";`
*
* A single breadcrumb subcomponent, to be used in the Breadcrumbs component.
*/
export const BreadcrumbsItem = ({
className,
href,
menuItems,
separator = '/',
text,
variant,
...other
}: BreadcrumbItemProps) => {
const componentClassName = clsx(
styles['breadcrumbs__item'],
variant === 'back' && styles['breadcrumbs__item-back'],
className,
);

const ellipsisButtonClassName = clsx(
styles['breadcrumbs__link'],
styles['breadcrumbs__ellipsis'],
);

const getInteractionElement = () => {
if (variant === 'collapsed') {
/* The collapsed variant is a button with ellipsis. Interaction spawns a Menu containing the collapsed breadcrumb links. */
return (
<Menu>
<Menu.PlainButton
aria-label="Show more breadcrumbs"
className={ellipsisButtonClassName}
>
...
</Menu.PlainButton>
<Menu.Items>{menuItems}</Menu.Items>
</Menu>
);
} else if (variant === 'back') {
/* The back variant is a left pointing icon that usually links to the second last breadcrumb href. */
return (
<a className={styles['breadcrumbs__link']} href={href as string}>
<Icon
className={styles['breadcrumbs__back-icon']}
name="chevron-left"
purpose="informative"
title={text as string}
/>
</a>
);
} else {
/* The default variant displays the prop text and links the prop href. */
return (
<a className={styles['breadcrumbs__link']} href={href as string}>
{text}
</a>
);
}
};

return (
<li className={componentClassName} {...other}>
{getInteractionElement()}
<span aria-hidden className={styles['breadcrumbs__separator']}>
{separator}
</span>
</li>
);
};

Breadcrumbs.Item = CustomSeparatorBreadcrumbsItem;
Loading

0 comments on commit 669081d

Please sign in to comment.