Skip to content

Commit

Permalink
feat(core): migrate breadcrumbs component (#8402)
Browse files Browse the repository at this point in the history
* feat(core): migrate breadcrumbs component

* fix(core): fix flickering of the overflow layout items

* fix(docs): remove deprecated example

* fix(core): dispose subscription on destroy

* fix(core): add complete subscriptions on destroy

* fix(fn): unsubscribe from observable

* fix(core): fix unit tests

* fix(core): add deprecation message
  • Loading branch information
N1XUS authored Jul 25, 2022
1 parent 6ed1ebc commit 0448fc6
Show file tree
Hide file tree
Showing 33 changed files with 856 additions and 509 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,3 @@
<fd-breadcrumb-href-example></fd-breadcrumb-href-example>
</component-example>
<code-example [exampleFiles]="breadcrumbHrefHtml"></code-example>

<fd-docs-section-title id="responsive" componentName="breadcrumb"> Responsive Breadcrumbs </fd-docs-section-title>
<description>
The breadcrumb will automatically refer to its parent element to know whether to show breadcrumbs or to collapse
them into the overflow menu. You can provide an HTMLElement to the [containerElement] input to set your own
container element. Note that the responsiveness feature will not function properly if the containerElement or parent
element have a set fixed width.
</description>
<component-example>
<fd-breadcrumb-responsive-example></fd-breadcrumb-responsive-example>
</component-example>
<code-example [exampleFiles]="breadcrumbResponsiveHtml"></code-example>
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Component } from '@angular/core';

import breadcrumbHrefExample from '!./examples/breadcrumb-href-example.component.html?raw';
import breadcrumbResponsiveExample from '!./examples/breadcrumb-responsive-example.component.html?raw';
import breadcrumbRouterLinkExample from '!./examples/breadcrumb-routerLink-example.component.html?raw';
import { ExampleFile } from '../../../documentation/core-helpers/code-example/example-file';

Expand All @@ -25,12 +24,4 @@ export class BreadcrumbDocsComponent {
fileName: 'fd-breadcrumb-href-example'
}
];

breadcrumbResponsiveHtml: ExampleFile[] = [
{
language: 'html',
code: breadcrumbResponsiveExample,
fileName: 'fd-breadcrumb-responsive-example'
}
];
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ApiComponent } from '../../../documentation/core-helpers/api/api.compon
import { API_FILES } from '../../api-files';
import {
BreadcrumbHrefExampleComponent,
BreadcrumbResponsiveExampleComponent,
BreadcrumbRouterLinkExampleComponent
} from './examples/breadcrumb-examples.component';
import { BreadcrumbHeaderComponent } from './breadcrumb-header/breadcrumb-header.component';
Expand All @@ -31,8 +30,7 @@ const routes: Routes = [
BreadcrumbDocsComponent,
BreadcrumbHeaderComponent,
BreadcrumbHrefExampleComponent,
BreadcrumbRouterLinkExampleComponent,
BreadcrumbResponsiveExampleComponent
BreadcrumbRouterLinkExampleComponent
],
providers: [moduleDeprecationsProvider(DeprecatedBreadcrumbsCompactDirective)]
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,3 @@ export class BreadcrumbRouterLinkExampleComponent {
]
})
export class BreadcrumbHrefExampleComponent {}

@Component({
selector: 'fd-breadcrumb-responsive-example',
templateUrl: './breadcrumb-responsive-example.component.html'
})
export class BreadcrumbResponsiveExampleComponent {}

This file was deleted.

2 changes: 1 addition & 1 deletion e2e/wdio/core/pages/dynamic-page.po.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class DynamicPagePo extends CoreBaseComponentPo {
flexibleColumn = '.fd-flexible-column-layout__column ';
article = '.fd-dynamic-page-section-example';
breadcrumbLink = '.fd-dynamic-page__breadcrumb-wrapper a';
currentBreadcrumbLink = '.fd-dynamic-page__breadcrumb-wrapper fd-breadcrumb-item:last-child span';
currentBreadcrumbLink = '.fd-dynamic-page__breadcrumb-wrapper .fd-overflow-layout__item--last span';

open(): void {
super.open(this.url);
Expand Down
82 changes: 56 additions & 26 deletions libs/core/src/lib/breadcrumb/breadcrumb-item.component.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { DomPortal } from '@angular/cdk/portal';
import {
AfterViewInit,
ChangeDetectorRef,
ChangeDetectionStrategy,
Component,
ContentChild,
ElementRef,
forwardRef,
Renderer2
ViewEncapsulation
} from '@angular/core';
import { LinkComponent } from '@fundamental-ngx/core/link';
import { DomPortal } from '@angular/cdk/portal';

/**
* Breadcrumb item directive. Must have child breadcrumb link directives.
Expand All @@ -21,54 +21,84 @@ import { DomPortal } from '@angular/cdk/portal';
*/
@Component({
selector: 'fd-breadcrumb-item',
template: '<div style="display: inline"><ng-content></ng-content></div>',
template: '<ng-content></ng-content>',
host: {
class: 'fd-breadcrumb__item'
}
},
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class BreadcrumbItemComponent implements AfterViewInit {
/** @hidden */
get elementRef(): ElementRef<HTMLElement> {
return this._elementRef;
}

/** @hidden */
@ContentChild(forwardRef(() => LinkComponent))
breadcrumbLink: LinkComponent;

/** @hidden */
get width(): number {
return this._elementRef.nativeElement.getBoundingClientRect().width;
}

/** In case there is no link in Item and breadcrumb item is non-interactive, we move whole item content to menu item title */
breadcrumbItemPortal: DomPortal<Element>;

/** When breadcrumb item has link in it, we are moving link content to menu item title */
linkContentPortal: DomPortal;

constructor(
private _elementRef: ElementRef<HTMLElement>,
private renderer2: Renderer2,
private _cdR: ChangeDetectorRef
) {}
/**
* Breadcrumb item dom portal.
*/
portal: DomPortal;

/** @hidden */
private _attached = false;

/** @hidden */
constructor(public readonly elementRef: ElementRef<HTMLElement>) {}

/** @hidden */
get needsClickProxy(): boolean {
get _needsClickProxy(): boolean {
return (
!!this.breadcrumbLink?.elementRef().nativeElement.getAttribute('href') || !!this.breadcrumbLink.routerLink
);
}

show = (): void => this.renderer2.setStyle(this._elementRef.nativeElement, 'display', 'inline-block');
hide = (): void => this.renderer2.setStyle(this._elementRef.nativeElement, 'display', 'none');

/** @hidden */
ngAfterViewInit(): void {
if (this.breadcrumbLink) {
this._attach();
}

/**
* Sets breadcrumb item dom portal.
*/
setPortal(): void {
if (!this.portal) {
this.portal = new DomPortal(this.elementRef);
}
}

/** @hidden */
_detach(): void {
if (!this._attached) {
return;
}

if (this.linkContentPortal?.isAttached) {
this.linkContentPortal?.detach();
}

if (this.breadcrumbItemPortal?.isAttached) {
this.breadcrumbItemPortal?.detach();
}

this._attached = false;
}

/** @hidden */
_attach(): void {
if (this._attached) {
return;
}

if (this.breadcrumbLink && this.breadcrumbLink.contentSpan) {
this.linkContentPortal = new DomPortal<HTMLElement>(this.breadcrumbLink.contentSpan.nativeElement);
}

this.breadcrumbItemPortal = new DomPortal(this.elementRef.nativeElement.firstElementChild as Element);
this._cdR.detectChanges();
this._attached = true;
}
}
114 changes: 68 additions & 46 deletions libs/core/src/lib/breadcrumb/breadcrumb.component.html
Original file line number Diff line number Diff line change
@@ -1,52 +1,74 @@
<fd-menu #menu [closeOnEscapeKey]="true" [focusAutoCapture]="true" [placement]="_placement$ | async">
<ng-container *ngFor="let breadcrumbItem of _collapsedBreadcrumbItems">
<li fd-menu-item [disabled]="breadcrumbItem.breadcrumbLink ? breadcrumbItem.breadcrumbLink.disabled : false">
<fd-overflow-layout
[reverseHiddenItems]="reverse"
showMorePosition="left"
[enableKeyboardNavigation]="false"
(visibleItemsCount)="_onVisibleItemsCountChange($event)"
(hiddenItemsCount)="_onHiddenItemsCountChange($event)"
>
<ng-container *ngFor="let breadcrumb of _items">
<div
*fdOverflowItemRef="breadcrumb; let hidden"
fdOverflowLayoutItem
(hiddenChange)="_onHiddenChange($event, breadcrumb)"
>
<ng-template [cdkPortalOutlet]="breadcrumb.portal"></ng-template>
</div>
</ng-container>
<ng-container *fdOverflowExpand="let breadcrumbs; items: _items">
<fd-menu #menu [closeOnEscapeKey]="true" [focusAutoCapture]="true" [placement]="_placement$ | async">
<ng-container *ngFor="let breadcrumbItem of breadcrumbs">
<li
fd-menu-item
[disabled]="
breadcrumbItem.item.breadcrumbLink ? breadcrumbItem.item.breadcrumbLink.disabled : false
"
>
<a
fd-menu-interactive
(click)="itemClicked(breadcrumbItem.item, $event)"
(keydown.enter)="itemClicked(breadcrumbItem.item, $event)"
(keydown.space)="itemClicked(breadcrumbItem.item, $event)"
>
<ng-container *ngIf="breadcrumbItem?.item.breadcrumbLink; else portalContent">
<ng-container *ngIf="breadcrumbItem.item.breadcrumbLink._prefixIconName">
<fd-menu-addon
position="before"
[glyph]="breadcrumbItem.item.breadcrumbLink._prefixIconName"
></fd-menu-addon>
</ng-container>
<span fd-menu-title>
<ng-container [cdkPortalOutlet]="breadcrumbItem.item.linkContentPortal"></ng-container>
</span>
<ng-container *ngIf="breadcrumbItem.item.breadcrumbLink._postfixIconName">
<fd-menu-addon
[glyph]="breadcrumbItem.item.breadcrumbLink._postfixIconName"
></fd-menu-addon>
</ng-container>
</ng-container>
<ng-template #portalContent>
<span fd-menu-title>
<ng-container
[cdkPortalOutlet]="breadcrumbItem.item.breadcrumbItemPortal"
></ng-container>
</span>
</ng-template>
</a>
</li>
</ng-container>
</fd-menu>
<span class="fd-breadcrumb__item" *ngIf="breadcrumbs.length > 0" [fdMenuTrigger]="menu">
<a
fd-menu-interactive
(click)="itemClicked(breadcrumbItem, $event)"
(keydown.enter)="itemClicked(breadcrumbItem, $event)"
(keydown.space)="itemClicked(breadcrumbItem, $event)"
fd-link
tabindex="0"
class="fd-breadcrumb__collapsed"
(keydown.enter)="_keyDownHandle($event)"
(keydown.space)="_keyDownHandle($event)"
>
<ng-container *ngIf="breadcrumbItem?.breadcrumbLink; else portalContent">
<ng-container *ngIf="breadcrumbItem.breadcrumbLink._prefixIconName">
<fd-menu-addon
position="before"
[glyph]="breadcrumbItem.breadcrumbLink._prefixIconName"
></fd-menu-addon>
</ng-container>
<span fd-menu-title>
<ng-container [cdkPortalOutlet]="breadcrumbItem.linkContentPortal"></ng-container>
</span>
<ng-container *ngIf="breadcrumbItem.breadcrumbLink._postfixIconName">
<fd-menu-addon [glyph]="breadcrumbItem.breadcrumbLink._postfixIconName"></fd-menu-addon>
</ng-container>
</ng-container>
<ng-template #portalContent>
<span fd-menu-title>
<ng-container [cdkPortalOutlet]="breadcrumbItem.breadcrumbItemPortal"></ng-container>
</span>
</ng-template>
<fd-icon glyph="slim-arrow-down"></fd-icon>
</a>
</li>
</span>
</ng-container>
</fd-menu>

<span
class="fd-breadcrumb__item"
*ngIf="_collapsedBreadcrumbItems.length"
[fdMenuTrigger]="menu"
#overflowBreadcrumbsContainer
>
<a
fd-link
tabindex="0"
class="fd-breadcrumb__collapsed"
(keydown.enter)="_keyDownHandle($event)"
(keydown.space)="_keyDownHandle($event)"
>
...
<fd-icon glyph="slim-arrow-down"></fd-icon>
</a>
</span>
</fd-overflow-layout>

<ng-content></ng-content>
10 changes: 9 additions & 1 deletion libs/core/src/lib/breadcrumb/breadcrumb.component.scss
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
@import '~fundamental-styles/dist/breadcrumb';

.fd-breadcrumb {
display: inline-block;
display: flex;
white-space: nowrap;

.fd-breadcrumb__collapsed {
cursor: pointer;
}
}

.fd-breadcrumb__item:last-child::after {
content: '/';
}

.fd-overflow-layout__item--last .fd-breadcrumb__item::after {
content: none;
}
Loading

0 comments on commit 0448fc6

Please sign in to comment.