Skip to content
This repository has been archived by the owner on Apr 14, 2023. It is now read-only.

Commit

Permalink
Merge branch 'master' of github.com:pjlamb12/ngx-plug-n-play
Browse files Browse the repository at this point in the history
  • Loading branch information
pjlamb12 committed Aug 31, 2019
2 parents b98d2cc + 78813ac commit 6bbf655
Show file tree
Hide file tree
Showing 17 changed files with 425 additions and 69 deletions.
198 changes: 132 additions & 66 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion projects/ngx-plug-n-play-lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"version": "1.1.1",
"peerDependencies": {
"@angular/common": ">7.2.0",
"@angular/core": ">7.2.0"
"@angular/core": ">7.2.0",
"@angular/animations": ">7.2.0"
},
"author": {
"email": "[email protected]",
Expand Down
28 changes: 28 additions & 0 deletions projects/ngx-plug-n-play-lib/src/lib/accordion/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Accordion Module

The `AccordionModule` contains one component, the `AccordionComponent`. It uses content projection to provide a trigger to open and close the accordion, and the body of the accordion. The parent component, the one implementing the accordion, thus determines all the styles for the component.

Here's an example of use:

```html
<pnp-accordion>
<ng-container accordion-header>
<p class="accordion-trigger">Trigger</p>
</ng-container>
<div accordion-body>
<p>
Lorem, ipsum dolor
</p>
</div>
</pnp-accordion>
```

When the `.accordion-trigger` is clicked, an Angular animation is triggered to either scroll up or scroll down to show the `.accordion-body`.

The `AccordionComponent` has one `Input`:

- `triggerSelector: string` &mdash; This is the CSS class selector for the accordion trigger. It defaults to `.accordion-trigger`. If no projected content element has the same class, then a class inside the `AccordionComponent` is used as the trigger, `.accordion-header-container`.

The component has one `Output`:

- `isCollapsedUpdated: EventEmitter<boolean>`: &mdash; The `Output` emits true or false, letting the parent component know if the accordion is open or not. That way the parent component can change its display in some way depending on the status. It doesn't need to change the visibility of the `[accordion-body]`, but maybe an icon or something like that.
10 changes: 10 additions & 0 deletions projects/ngx-plug-n-play-lib/src/lib/accordion/accordion.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AccordionComponent } from './accordion/accordion.component';

@NgModule({
declarations: [AccordionComponent],
exports: [AccordionComponent],
imports: [CommonModule],
})
export class AccordionModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.accordion-body-container {
overflow: hidden;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<div class="accordion-header-container" [class.accordion-open]="!isCollapsed">
<ng-content select="[accordion-header]"></ng-content>
</div>
<div class="accordion-body-container" [@collapse]="isCollapsed ? 'collapsed' : 'open'">
<ng-content select="[accordion-body]"></ng-content>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { AccordionComponent } from './accordion.component';
import { Component, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed, async, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

@Component({
selector: 'app-test-host',
template: `
<pnp-accordion>
<ng-container accordion-header>
<p class="accordion-trigger">Trigger Accordion</p>
</ng-container>
<div accordion-body>
<p>
Lorem, ipsum dolor sit.
</p>
</div>
</pnp-accordion>
`,
})
class TestHostComponent {
@ViewChild(AccordionComponent) accordion: AccordionComponent;
}

describe('AccordionComponent', () => {
let testHostComponent: TestHostComponent;
let component: AccordionComponent;
let fixture: ComponentFixture<TestHostComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [BrowserAnimationsModule],
declarations: [TestHostComponent, AccordionComponent],
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(TestHostComponent);
testHostComponent = fixture.componentInstance;
component = testHostComponent.accordion;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should have the .accordion-trigger element as the trigger', () => {
const classList: DOMTokenList = component.trigger.classList;

expect(classList.contains('accordion-trigger')).toBeTruthy();
});

it('should call the function that will emit the new accordion value', () => {
spyOn(component, 'updateCollapsedStatus');
component.toggleAccordionVisibility();

expect(component.updateCollapsedStatus).toHaveBeenCalled();
});

it('should emit the new accordion value', () => {
spyOn(component.isCollapsedUpdated, 'emit');
component.updateCollapsedStatus();

expect(component.isCollapsedUpdated.emit).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Component, OnInit, Output, EventEmitter, Input, ElementRef } from '@angular/core';
import { trigger } from '@angular/animations';
import { collapseAnimation } from '../../shared/animations/collapse-animation';
import { fromEvent, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Component({
selector: 'pnp-accordion',
templateUrl: './accordion.component.html',
styleUrls: ['./accordion.component.css'],
animations: [trigger('collapse', collapseAnimation())],
})
export class AccordionComponent implements OnInit {
@Output() isCollapsedUpdated: EventEmitter<boolean> = new EventEmitter<boolean>();
@Input() triggerSelector: string = '.accordion-trigger';
public isCollapsed: boolean = true;
public trigger;
private destroy$: Subject<boolean> = new Subject<boolean>();

constructor(private _elRef: ElementRef) {}

ngOnInit() {
this.updateCollapsedStatus();
this.trigger = this._elRef.nativeElement.querySelector(this.triggerSelector)
? this._elRef.nativeElement.querySelector(this.triggerSelector)
: this._elRef.nativeElement.querySelector('.accordion-header-container');

fromEvent(this.trigger, 'click')
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.toggleAccordionVisibility();
});
}

ngOnDestroy() {
this.destroy$.next(true);
this.destroy$.complete();
}

updateCollapsedStatus() {
this.isCollapsedUpdated.emit(this.isCollapsed);
}

toggleAccordionVisibility() {
this.isCollapsed = !this.isCollapsed;
this.updateCollapsedStatus();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { state, style, animate, transition } from '@angular/animations';

export function collapseAnimation(
slideDownDuration: string = '500ms',
slideUpDuration: string = '500ms',
startingOpacity: string = '1',
endingOpacity: string = '1',
) {
return [
state(
'collapsed',
style({
height: '0',
overflow: 'hidden',
opacity: startingOpacity,
}),
),
state(
'open',
style({
overflow: 'hidden',
opacity: endingOpacity,
}),
),
transition('collapsed=>open', animate(slideDownDuration)),
transition('open=>collapsed', animate(slideUpDuration)),
];
}
1 change: 1 addition & 0 deletions projects/ngx-plug-n-play-lib/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './lib/typeahead/typeahead.module';
export * from './lib/alert-toaster/alert-toaster.service';
export * from './lib/alert-toaster/alert-toaster.module';

export * from './lib/accordion/accordion.module';
export * from './lib/dropdown/dropdown.module';
export * from './lib/dropdown/dropdown.util';
export * from './lib/dropdown/dropdown-selected-item.interface';
41 changes: 41 additions & 0 deletions src/app/accordion-demo/accordion-demo.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<h1>Accordion Component Demo</h1>
<pnp-accordion>
<ng-container accordion-header>
<button class="btn btn-primary accordion-trigger mb-2">Open</button>
</ng-container>
<div class="card" accordion-body>
<p class="card-body">Here is the content of the accordion dropdown.</p>
</div>
</pnp-accordion>

<p>
The <code>AccordionModule</code> contains one component, the <code>AccordionComponent</code>. It uses content
projection to provide a trigger to open and close the accordion, and the body of the accordion. The parent
component, the one implementing the accordion, thus determines all the styles for the component.
</p>

<p>
When the <code>.accordion-trigger</code> is clicked, an Angular animation is triggered to either scroll up or scroll
down to show the <code>.accordion-body</code>.
</p>

<p>The <code>AccordionComponent</code> has one <code>Input</code>:</p>

<ul>
<li>
<code>triggerSelector: string</code> &mdash; This is the CSS class selector for the accordion trigger. It
defaults to <code>.accordion-trigger</code>. If no projected content element has the same class, then a class
inside the <code>AccordionComponent</code> is used as the trigger, <code>.accordion-header-container</code>.
</li>
</ul>

<p>The component has one <code>Output</code>:</p>

<ul>
<li>
<code>isCollapsedUpdated: EventEmitter&lt;boolean&gt;</code>: &mdash; The <code>Output</code> emits true or
false, letting the parent component know if the accordion is open or not. That way the parent component can
change its display in some way depending on the status. It doesn't need to change the visibility of the
<code>[accordion-body]</code>, but maybe an icon or something like that.
</li>
</ul>
Empty file.
24 changes: 24 additions & 0 deletions src/app/accordion-demo/accordion-demo.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { AccordionDemoComponent } from './accordion-demo.component';

describe('AccordionDemoComponent', () => {
let component: AccordionDemoComponent;
let fixture: ComponentFixture<AccordionDemoComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AccordionDemoComponent],
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(AccordionDemoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
12 changes: 12 additions & 0 deletions src/app/accordion-demo/accordion-demo.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';

@Component({
selector: 'pnp-accordion-demo',
templateUrl: './accordion-demo.component.html',
styleUrls: ['./accordion-demo.component.scss'],
})
export class AccordionDemoComponent implements OnInit {
constructor() {}

ngOnInit() {}
}
2 changes: 2 additions & 0 deletions src/app/app-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { RouterModule, Routes } from '@angular/router';
import { AlertsToasterDemoComponent } from './alerts-toaster-demo/alerts-toaster-demo.component';
import { HomeComponent } from './home/home.component';
import { TypeaheadDemoComponent } from './typeahead-demo/typeahead-demo.component';
import { AccordionDemoComponent } from './accordion-demo/accordion-demo.component';
import { DropdownDemoComponent } from './dropdown-demo/dropdown-demo.component';

const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'typeahead-demo', component: TypeaheadDemoComponent },
{ path: 'alerts-toaster-demo', component: AlertsToasterDemoComponent },
{ path: 'accordion-demo', component: AccordionDemoComponent },
{ path: 'dropdown-demo', component: DropdownDemoComponent },
{ path: '**', redirectTo: '', pathMatch: 'full' },
];
Expand Down
19 changes: 17 additions & 2 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,39 @@
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { AlertToasterModule, TypeaheadModule, DropdownModule } from '../../projects/ngx-plug-n-play-lib/src/public-api';
import {
AlertToasterModule,
TypeaheadModule,
DropdownModule,
AccordionModule,
} from '../../projects/ngx-plug-n-play-lib/src/public-api';
import { AlertsToasterDemoComponent } from './alerts-toaster-demo/alerts-toaster-demo.component';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { MainNavComponent } from './main-nav/main-nav.component';
import { TypeaheadDemoComponent } from './typeahead-demo/typeahead-demo.component';
import { DropdownDemoComponent } from './dropdown-demo/dropdown-demo.component';
import { AccordionDemoComponent } from './accordion-demo/accordion-demo.component';

@NgModule({
imports: [BrowserModule, FormsModule, AppRoutingModule, TypeaheadModule, AlertToasterModule, DropdownModule],
imports: [
BrowserModule,
FormsModule,
AppRoutingModule,
TypeaheadModule,
AlertToasterModule,
DropdownModule,
AccordionModule,
],
declarations: [
AppComponent,
MainNavComponent,
HomeComponent,
TypeaheadDemoComponent,
AlertsToasterDemoComponent,
DropdownDemoComponent,
AccordionDemoComponent,
],
bootstrap: [AppComponent],
})
Expand Down
3 changes: 3 additions & 0 deletions src/app/main-nav/main-nav.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
<a class="nav-link" [routerLink]="['/dropdown-demo']">Dropdown Demo</a>
</li>
<li class="nav-item" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
<a class="nav-link" [routerLink]="['/accordion-demo']">Accordion Demo</a>
</li>
</ul>
</div>
</nav>

0 comments on commit 6bbf655

Please sign in to comment.