diff --git a/common/changes/office-ui-fabric-react/breadcrumboverflowindex_2018-04-19-00-05.json b/common/changes/office-ui-fabric-react/breadcrumboverflowindex_2018-04-19-00-05.json new file mode 100644 index 0000000000000..588dbbacc331e --- /dev/null +++ b/common/changes/office-ui-fabric-react/breadcrumboverflowindex_2018-04-19-00-05.json @@ -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" +} \ No newline at end of file diff --git a/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.base.tsx b/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.base.tsx index 76befb4c56397..68c13c333df4b 100644 --- a/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.base.tsx +++ b/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.base.tsx @@ -29,13 +29,16 @@ const nullFunction = (): null => null; export class Breadcrumb extends BaseComponent { public static defaultProps: IBreadcrumbProps = { items: [], - maxDisplayedItems: 999 + maxDisplayedItems: 999, + overflowIndex: 0 }; protected focusZone = createRef(); constructor(props: IBreadcrumbProps) { super(props); + + this._validateProps(props); } /** @@ -48,11 +51,13 @@ export class Breadcrumb extends BaseComponent { } 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 ( @@ -64,10 +69,17 @@ export class Breadcrumb extends BaseComponent { ); } + 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]; @@ -82,7 +94,8 @@ export class Breadcrumb extends BaseComponent { ariaLabel, dividerAs: Divider = Icon, onRenderItem = this._onRenderItem, - overflowAriaLabel + overflowAriaLabel, + overflowIndex } = data.props; const { renderedOverflowItems, renderedItems } = data; @@ -99,6 +112,41 @@ export class Breadcrumb extends BaseComponent { // knows not to render on that item const lastItemIndex = renderedItems.length - 1; + const itemElements: JSX.Element[] = renderedItems.map( + (item, index) => ( +
  • + { onRenderItem(item, this._onRenderItem) } + { index !== lastItemIndex && } +
  • + )); + + if (renderedOverflowItems && renderedOverflowItems.length !== 0) { + itemElements.splice(overflowIndex!, 0, ( +
  • + + +
  • + ) + ); + } + return (
    { >
      - { renderedOverflowItems && renderedOverflowItems.length !== 0 && ( -
    1. - - -
    2. - ) } - { renderedItems.map( - (item, index) => ( -
    3. - { onRenderItem(item, this._onRenderItem) } - { index !== lastItemIndex && } -
    4. - )) } + {itemElements}
    @@ -179,4 +198,15 @@ export class Breadcrumb extends BaseComponent { 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'); + } + } } diff --git a/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.test.tsx b/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.test.tsx index 0e529c524801c..fc31314c41a7b 100644 --- a/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.test.tsx +++ b/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.test.tsx @@ -48,6 +48,18 @@ describe('Breadcrumb', () => { tree = component.toJSON(); expect(tree).toMatchSnapshot(); + + // With overflow and overflowIndex + component = renderer.create( + + ); + + tree = component.toJSON(); + expect(tree).toMatchSnapshot(); }); it('can call the callback when an item is clicked', () => { diff --git a/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.types.ts b/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.types.ts index 05c0cb7225cd1..419097fbbc444 100644 --- a/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.types.ts +++ b/packages/office-ui-fabric-react/src/components/Breadcrumb/Breadcrumb.types.ts @@ -41,6 +41,7 @@ export interface IBreadcrumbProps extends React.Props { maxDisplayedItems?: number; /** Method to call when trying to render an item. */ + onRenderItem?: IRenderFunction; /** @@ -58,6 +59,11 @@ export interface IBreadcrumbProps extends React.Props { * 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 { diff --git a/packages/office-ui-fabric-react/src/components/Breadcrumb/__snapshots__/Breadcrumb.test.tsx.snap b/packages/office-ui-fabric-react/src/components/Breadcrumb/__snapshots__/Breadcrumb.test.tsx.snap index 52d3ff04150f1..575f97c0d6720 100644 --- a/packages/office-ui-fabric-react/src/components/Breadcrumb/__snapshots__/Breadcrumb.test.tsx.snap +++ b/packages/office-ui-fabric-react/src/components/Breadcrumb/__snapshots__/Breadcrumb.test.tsx.snap @@ -477,3 +477,214 @@ exports[`Breadcrumb renders breadcumb correctly 3`] = ` `; + +exports[`Breadcrumb renders breadcumb correctly 4`] = ` +
    +
    +
    +
    +
      +
    1. + +
      + TestText1 +
      +
      + +
    2. +
    3. + + +
    4. +
    5. + +
      + TestText4 +
      +
      +
    6. +
    +
    +
    +
    +
    +`; diff --git a/packages/office-ui-fabric-react/src/components/Breadcrumb/examples/Breadcrumb.Basic.Example.tsx b/packages/office-ui-fabric-react/src/components/Breadcrumb/examples/Breadcrumb.Basic.Example.tsx index 25cb0874589e0..c3fc03ce3ca12 100644 --- a/packages/office-ui-fabric-react/src/components/Breadcrumb/examples/Breadcrumb.Basic.Example.tsx +++ b/packages/office-ui-fabric-react/src/components/Breadcrumb/examples/Breadcrumb.Basic.Example.tsx @@ -58,6 +58,18 @@ export class BreadcrumbBasicExample extends React.Component { maxDisplayedItems={ 3 } ariaLabel={ 'Website breadcrumb' } /> + + + ); }