Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(breadcrumbs) *experimental* #1183

Merged
merged 23 commits into from
Jul 13, 2018
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
60462cd
feat(breadcrumbs): Initial scaffolding for breadcrumbs
Jun 20, 2018
09b4339
feat(breadcrumbs): Checkpoint checkin with breadcrumbs showing up
Jun 21, 2018
f07235a
feat(breadcrumbs): code cleanup and adding graying out to last element
Jun 22, 2018
6225fec
feat(breadcrumbs): Adding themes to experimental area. Using scss to …
Jun 25, 2018
00872d7
add to demo
Jun 25, 2018
1f8d71a
feat(breadcrumbs): Responsive Breadcrumbs, removes the left-most crum…
Jun 26, 2018
99d4d43
Merge branch 'develop' into feature/breadcrumbs
jeremysmartt Jul 9, 2018
e1289b2
Adding sandbox area with routes to test-beds
Jul 9, 2018
2ff7bb2
Using ngAfterContentChecked lifecycle method
Jul 9, 2018
e238436
Adding title to demo
Jul 9, 2018
993ab15
Update documentation Readme with usage
Jul 10, 2018
d00587d
Merge branch 'develop' into feature/breadcrumbs
Jul 10, 2018
d1c0325
Moving tab-select into separate demo directory
Jul 10, 2018
adccda6
Unit Tests for Breadcrumbs
Jul 11, 2018
9ce8512
Unit Tests for Breadcrumbs
Jul 11, 2018
45f2740
chore(breadcrumbs): Update code from code review
Jul 11, 2018
7d0ca24
Merge branch 'develop' into feature/breadcrumbs
Jul 11, 2018
9bd7209
fix(breadcrumb): Fix unit test
Jul 12, 2018
560d290
chore(breadcrumbs): Use Subscription.EMPTY to avoid needing null checks
Jul 12, 2018
95aef11
chore(breadcrumbs): remove console.log
Jul 12, 2018
29f1310
chore(): updates on README
Jul 13, 2018
e2c8e9d
chore(breadcrumbs): remove media usage from breadcrumbs to reduce noise
Jul 13, 2018
fd3799d
fix(breadcrumbs): stop click event on the breadcrumb separator
Jul 13, 2018
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
89 changes: 89 additions & 0 deletions src/platform/experimental/breadcrumbs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# td-breadcrumbs (experimental)

`td-breadcrumbs` element generates breadcrumbs for navigation. Handles Responsive by removing breadcrumbs from the beginning of the list as allowable space decreases.

## API Summary

#### Inputs

+ separatorIcon?: string
+ Sets the icon url shown between breadcrumbs. Defaults to right chevron.

#### Methods

+ count: number
+ The total count of individual breadcrumbs (read only)

#### Attributes

+ hiddenBreadcrumbs: TdBreadcrumbComponent[]
+ Array of currently hidden breadcrumbs (responsive)

# td-breadcrumb

`td-breadcrumb` element generates an individual breadcrumb component.

## API Summary

#### Methods

+ displayCrumb: boolean
+ Whether to display the individual breadcrumb or not
+ width: number
+ The current width of the individual breadcrumb (read only)

## Setup

Import the [CovalentBreadcrumbsModule] in your NgModule:

```typescript
import { CovalentBreadcrumbsModule } from '@covalent/experimental/breadcrumbs';
@NgModule({
imports: [
CovalentBreadcrumbsModule,
...
],
...
})
export class MyModule {}
```

## Usage

Basic Example:

```html
<td-breadcrumbs class="pad-left">
<a td-breadcrumb [routerLink]="'/'">Home</a>
<a td-breadcrumb [routerLink]="'/layouts'">Layouts</a>
<a td-breadcrumb [routerLink]="'/layouts2'">Layouts2</a>
<a td-breadcrumb [routerLink]="'/layouts3'">Layouts3</a>
<td-breadcrumb class="tc-grey-500">Manage List</td-breadcrumb>
</td-breadcrumbs>
```

Example with all inputs/outputs:

```html
<td-breadcrumbs #breadcrumbs separatorIcon="motorcycle">
<a td-breadcrumb [routerLink]="'/'">Home</a>
<a td-breadcrumb [routerLink]="'/layouts'">Layouts</a>
<a td-breadcrumb [routerLink]="'/layouts2'">Layouts2</a>
<a td-breadcrumb [routerLink]="'/layouts3'">Layouts3</a>
<td-breadcrumb class="tc-grey-500">Manage List</td-breadcrumb>
</td-breadcrumbs>
<mat-divider></mat-divider>
<div>
Total Breadcrumbs Count: {{breadcrumbs.count}}
</div>
<div>
Hidden Breadcrumbs Count (shrink window to see): {{breadcrumbs.hiddenBreadcrumbs.length}}
</div>
<ng-template let-breadcrumb let-index="index" ngFor [ngForOf]="breadcrumbs.hiddenBreadcrumbs">
<div>
<p>Breadcrumb Number: {{index}}</p>
<p>Breadcrumb Width: {{breadcrumb?.width}}</p>
<mat-divider></mat-divider>
</div>
</ng-template>
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

@mixin td-breadcrumb-theme($theme) {
$foreground: map-get($theme, foreground);

td-breadcrumb {
&:last-of-type {
color: mat-color($foreground, disabled);
cursor: default;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<span *ngIf="displayCrumb" class="td-breadcrumb">
<ng-content></ng-content>
<mat-icon *ngIf="_displayIcon">{{separatorIcon}}</mat-icon>
</span>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
:host {
.td-breadcrumb {
height: 48px;
box-sizing: border-box;
flex-direction: row;
align-items: center;
align-content: center;
max-width: 100%;
justify-content: flex-end;
::ng-deep > * {
margin: 0 10px;
}
}
mat-icon.material-icons.mat-icon {
display: inline-flex;
vertical-align: middle;
}
&.mat-button {
min-width: 0;
padding: 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
Component,
ElementRef,
Renderer2,
HostBinding,
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
} from '@angular/core';

@Component({
selector: 'td-breadcrumb, a[td-breadcrumb]',
styleUrls: ['./breadcrumb.component.scss'],
templateUrl: './breadcrumb.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TdBreadcrumbComponent implements AfterViewInit {

private _displayCrumb: boolean = true;
private _width: number = 0;
// Sets the icon url shown between breadcrumbs. Defaults to right chevron
separatorIcon: string = 'navigate_next';
// Should show the right chevron or not before the label
_displayIcon: boolean = true;

get displayCrumb(): boolean {
return this._displayCrumb;
}

/**
* Whether to display the crumb or not
*/
set displayCrumb(shouldDisplay: boolean) {
this._displayCrumb = shouldDisplay;
this._changeDetectorRef.markForCheck();
}

/**
* Width of the DOM element of the crumb
*/
get width(): number {
return this._width;
}

// Set the display to none on the component, just in case the end user is hiding
// and showing them instead of the component doing itself for reasons like responsive
@HostBinding('style.display')
private get displayBinding(): string {
return this._displayCrumb ? undefined : 'none';
}

constructor(private _elementRef: ElementRef,
private _renderer: Renderer2,
private _changeDetectorRef: ChangeDetectorRef) {
this._renderer.addClass(this._elementRef.nativeElement, 'mat-button');
}

ngAfterViewInit(): void {
// set the width from the actual rendered DOM element
this._width = (<HTMLElement>this._elementRef.nativeElement).getBoundingClientRect().width;
this._changeDetectorRef.markForCheck();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="td-breadcrumbs">
<ng-content></ng-content>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
:host {
.td-breadcrumbs {
white-space: nowrap;
}
}
106 changes: 106 additions & 0 deletions src/platform/experimental/breadcrumbs/breadcrumbs.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {
TestBed,
inject,
async,
ComponentFixture,
} from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import {
Component,
DebugElement,
} from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { By } from '@angular/platform-browser';
import {
CovalentBreadcrumbsModule,
} from './public-api';
import {
TdBreadcrumbsComponent,
} from './breadcrumbs.component';

@Component({
selector: 'fake',
template: `<router-outlet></router-outlet><div>fake</div>`,
})
export class FakeComponent {
}

describe('Component: Breadcrumbs', () => {

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
TdBreadcrumbsTestComponent,
FakeComponent,
],
imports: [
NoopAnimationsModule,
RouterTestingModule.withRoutes([
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason to use mock routes?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just so the test doesn't fail with the routherlink in there

{ path: '', component: FakeComponent },
{ path: 'layouts', component: FakeComponent },
{ path: 'layouts2', component: FakeComponent },
{ path: 'layouts3', component: FakeComponent },
]),
CovalentBreadcrumbsModule,
],
});
TestBed.compileComponents();
}));

it('should render 5 Breadcrumbs',
async(inject([], () => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TdBreadcrumbsTestComponent);
fixture.detectChanges();
fixture.whenStable().then(() => {
let breadcrumbs: TdBreadcrumbsComponent = fixture.debugElement.query(By.directive(TdBreadcrumbsComponent)).componentInstance;
expect(breadcrumbs.count).toBe(5);
});
}),
));

it('should change the separatorIcon',
async(inject([], () => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TdBreadcrumbsTestComponent);
let component: TdBreadcrumbsTestComponent = fixture.debugElement.componentInstance;
component.separatorIcon = 'flight_land';
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(fixture.debugElement.queryAll(By.css('.td-breadcrumb'))[1].nativeElement.innerHTML.indexOf('flight_land')).toBeGreaterThan(-1);
});
}),
));

it('should resize window and hide breadcrumbs',
async(inject([], () => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TdBreadcrumbsTestComponent);
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.debugElement.query(By.directive(TdBreadcrumbsComponent)).nativeElement.parentElement.style.width = '300px';
window.dispatchEvent(new Event('resize'));
fixture.detectChanges();
fixture.whenStable().then(() => {
let breadcrumbs: TdBreadcrumbsComponent = fixture.debugElement.query(By.directive(TdBreadcrumbsComponent)).componentInstance;
expect(breadcrumbs.hiddenBreadcrumbs.length).toBe(2);
});
});
}),
));
});

@Component({
selector: 'td-breadcrumbs-test',
template: `
<div style="width: {{width}}">
<td-breadcrumbs #breadcrumbs class="pad-left" separatorIcon="{{separatorIcon}}">
<a td-breadcrumb [routerLink]="'/'">Home</a>
<a td-breadcrumb [routerLink]="'/layouts'">Layouts</a>
<a td-breadcrumb [routerLink]="'/layouts2'">Layouts2</a>
<a td-breadcrumb [routerLink]="'/layouts3'">Layouts3</a>
<td-breadcrumb class="tc-grey-500">Manage List</td-breadcrumb>
</td-breadcrumbs>
</div>
`,
})
class TdBreadcrumbsTestComponent {
separatorIcon: string = 'motorcycle';
}
Loading