Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"changes": [
{
"packageName": "office-ui-fabric-react",
"comment": "Add the option of collapsing breadcrumb overflow items into a position other than the first one",
"type": "minor"
}
],
"packageName": "office-ui-fabric-react",
"email": "samuelmtimbo@gmail.com"
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ const nullFunction = (): null => null;
export class Breadcrumb extends BaseComponent<IBreadcrumbProps, any> {
public static defaultProps: IBreadcrumbProps = {
items: [],
maxDisplayedItems: 999
maxDisplayedItems: 999,
overflowIndex: 0
};

protected focusZone = createRef<FocusZone>();

constructor(props: IBreadcrumbProps) {
super(props);

this._validateProps(props);
}

/**
Expand All @@ -48,11 +51,13 @@ export class Breadcrumb extends BaseComponent<IBreadcrumbProps, any> {
}

public render() {
const { onReduceData = this._onReduceData, maxDisplayedItems, items } = this.props;
const { onReduceData = this._onReduceData, overflowIndex, maxDisplayedItems, items } = this.props;
const renderedItems = [...items];
const renderedOverflowItems = renderedItems.splice(overflowIndex!, renderedItems.length - maxDisplayedItems!);
const breadCrumbData: IBreadCrumbData = {
props: this.props,
renderedItems: items.slice(-maxDisplayedItems!),
renderedOverflowItems: items.slice(0, -maxDisplayedItems!)
renderedItems,
renderedOverflowItems
};

return (
Expand All @@ -64,10 +69,17 @@ export class Breadcrumb extends BaseComponent<IBreadcrumbProps, any> {
);
}

public componentWillReceiveProps(nextProps: IBreadcrumbProps): void {
this._validateProps(nextProps);
}

private _onReduceData = (data: IBreadCrumbData): IBreadCrumbData | undefined => {
let { renderedItems, renderedOverflowItems } = data;
const movedItem = renderedItems[0];
renderedItems = renderedItems.slice(1);
const { overflowIndex, items } = data.props;

const movedItem = renderedItems[overflowIndex!];
renderedItems = [...renderedItems];
renderedItems.splice(overflowIndex!, 1);

renderedOverflowItems = [...renderedOverflowItems, movedItem];

Expand All @@ -82,7 +94,8 @@ export class Breadcrumb extends BaseComponent<IBreadcrumbProps, any> {
ariaLabel,
dividerAs: Divider = Icon,
onRenderItem = this._onRenderItem,
overflowAriaLabel
overflowAriaLabel,
overflowIndex
} = data.props;
const { renderedOverflowItems, renderedItems } = data;

Expand All @@ -99,6 +112,41 @@ export class Breadcrumb extends BaseComponent<IBreadcrumbProps, any> {
// knows not to render on that item
const lastItemIndex = renderedItems.length - 1;

const itemElements: JSX.Element[] = renderedItems.map(
(item, index) => (
<li className={ css('ms-Breadcrumb-listItem', styles.listItem) } key={ item.key || String(index) }>
{ onRenderItem(item, this._onRenderItem) }
{ index !== lastItemIndex && <Divider
className={ css('ms-Breadcrumb-chevron', styles.chevron) }
iconName={ getRTL() ? 'ChevronLeft' : 'ChevronRight' }
/> }
</li>
));

if (renderedOverflowItems && renderedOverflowItems.length !== 0) {
itemElements.splice(overflowIndex!, 0, (
<li className={ css('ms-Breadcrumb-overflow', styles.overflow) } key={ OVERFLOW_KEY }>
<IconButton
className={ css('ms-Breadcrumb-overflowButton', styles.overflowButton) }
iconProps={ { iconName: 'More' } }
role='button'
aria-haspopup='true'
ariaLabel={ overflowAriaLabel }
onRenderMenuIcon={ nullFunction }
menuProps={ {
items: contextualItems,
directionalHint: DirectionalHint.bottomLeftEdge
} }
/>
<Divider
className={ css('ms-Breadcrumb-chevron', styles.chevron) }
iconName={ getRTL() ? 'ChevronLeft' : 'ChevronRight' }
/>
</li>
)
);
}

return (
<div
className={ css('ms-Breadcrumb', className, styles.root) }
Expand All @@ -107,36 +155,7 @@ export class Breadcrumb extends BaseComponent<IBreadcrumbProps, any> {
>
<FocusZone componentRef={ this.focusZone } direction={ FocusZoneDirection.horizontal } >
<ol className={ css('ms-Breadcrumb-list', styles.list) }>
{ renderedOverflowItems && renderedOverflowItems.length !== 0 && (
<li className={ css('ms-Breadcrumb-overflow', styles.overflow) } key={ OVERFLOW_KEY }>
<IconButton
className={ css('ms-Breadcrumb-overflowButton', styles.overflowButton) }
iconProps={ { iconName: 'More' } }
role='button'
aria-haspopup='true'
ariaLabel={ overflowAriaLabel }
onRenderMenuIcon={ nullFunction }
menuProps={ {
items: contextualItems,
directionalHint: DirectionalHint.bottomLeftEdge
} }
/>
<Divider
className={ css('ms-Breadcrumb-chevron', styles.chevron) }
iconName={ getRTL() ? 'ChevronLeft' : 'ChevronRight' }
/>
</li>
) }
{ renderedItems.map(
(item, index) => (
<li className={ css('ms-Breadcrumb-listItem', styles.listItem) } key={ item.key || String(index) }>
{ onRenderItem(item, this._onRenderItem) }
{ index !== lastItemIndex && <Divider
className={ css('ms-Breadcrumb-chevron', styles.chevron) }
iconName={ getRTL() ? 'ChevronLeft' : 'ChevronRight' }
/> }
</li>
)) }
{itemElements}
</ol>
</FocusZone>
</div>
Expand Down Expand Up @@ -179,4 +198,15 @@ export class Breadcrumb extends BaseComponent<IBreadcrumbProps, any> {
item.onClick(ev, item);
}
}

/**
* Validate incoming props
* @param props Props to validate
*/
private _validateProps(props: IBreadcrumbProps): void {
const { items, overflowIndex } = props;
if (overflowIndex! < 0 || overflowIndex! > items.length - 1) {
throw new Error('Breadcrumb: overflowIndex out of range');
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ describe('Breadcrumb', () => {

tree = component.toJSON();
expect(tree).toMatchSnapshot();

// With overflow and overflowIndex
component = renderer.create(
<Breadcrumb
items={ items }
maxDisplayedItems={ 2 }
overflowIndex={ 1 }
/>
);

tree = component.toJSON();
expect(tree).toMatchSnapshot();
});

it('can call the callback when an item is clicked', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export interface IBreadcrumbProps extends React.Props<Breadcrumb> {
maxDisplayedItems?: number;

/** Method to call when trying to render an item. */

onRenderItem?: IRenderFunction<IBreadcrumbItem>;

/**
Expand All @@ -58,6 +59,11 @@ export interface IBreadcrumbProps extends React.Props<Breadcrumb> {
* Optional name to use for aria label on overflow button.
*/
overflowAriaLabel?: string;

/**
* Optional index where overflow items will be collapsed. Defaults to 0.
*/
overflowIndex?: number;
}

export interface IBreadcrumbItem {
Expand Down
Loading