Skip to content

Commit

Permalink
Allow expansion indicator positioning.
Browse files Browse the repository at this point in the history
  • Loading branch information
josephperrott committed Nov 2, 2017
1 parent cf11ff2 commit 82e8189
Show file tree
Hide file tree
Showing 9 changed files with 111 additions and 29 deletions.
12 changes: 8 additions & 4 deletions src/demo-app/expansion/expansion-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,20 @@ <h1>matAccordion</h1>
<mat-radio-button value="default">Default</mat-radio-button>
<mat-radio-button value="flat">Flat</mat-radio-button>
</mat-radio-group>
<p>Toggle Position</p>
<mat-radio-group [(ngModel)]="togglePosition">
<mat-radio-button value="before">Before</mat-radio-button>
<mat-radio-button value="after">After</mat-radio-button>
</mat-radio-group>
<p>Accordion Panel(s)</p>
<div>
<mat-checkbox [(ngModel)]="panel1.expanded">Panel 1</mat-checkbox>
<mat-checkbox [(ngModel)]="panel2.expanded">Panel 2</mat-checkbox>
</div>
</div>
<br>
<mat-accordion [displayMode]="displayMode" [multi]="multi"
class="mat-expansion-demo-width">
<mat-accordion [displayMode]="displayMode" [multi]="multi" [togglePosition]="togglePosition"
class="mat-expansion-demo-width">
<mat-expansion-panel #panel1 [hideToggle]="hideToggle">
<mat-expansion-panel-header>Section 1</mat-expansion-panel-header>
<p>This is the content text that makes sense here.</p>
Expand All @@ -65,7 +70,7 @@ <h1>matAccordion</h1>
</mat-action-row>
</mat-expansion-panel>
</mat-accordion>
<!--

<h1>CdkAccordion</h1>
<div>
<p>Accordion Options</p>
Expand Down Expand Up @@ -99,4 +104,3 @@ <h1>CdkAccordion</h1>
<p *ngIf="item3.expanded">I only show if item 3 is expanded</p>
</cdk-accordion-item>
</cdk-accordion>
-->
1 change: 1 addition & 0 deletions src/demo-app/expansion/expansion-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {Component, ViewEncapsulation} from '@angular/core';
})
export class ExpansionDemo {
displayMode: string = 'default';
togglePosition = 'after';
multi = false;
hideToggle = false;
disabled = false;
Expand Down
25 changes: 24 additions & 1 deletion src/lib/expansion/accordion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Directive, Input} from '@angular/core';
import {Directive, Input, SimpleChanges} from '@angular/core';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {CdkAccordion} from '@angular/cdk/accordion';
import {Subject} from 'rxjs/Subject';

/** Workaround for https://github.com/angular/angular/issues/17849 */
export const _CdkAccordion = CdkAccordion;

/** MatAccordion's display modes. */
export type MatAccordionDisplayMode = 'default' | 'flat';


/** MatAccordion's toggle positions. */
export type MatAccordionTogglePosition = 'before' | 'after';

/**
* Directive for a Material Design Accordion.
*/
Expand All @@ -27,6 +32,10 @@ export type MatAccordionDisplayMode = 'default' | 'flat';
}
})
export class MatAccordion extends _CdkAccordion {

/** Stream that emits for changes in `@Input` properties. */
_inputChanges = new Subject<SimpleChanges>();

/** Whether the expansion indicator should be hidden. */
@Input() get hideToggle(): boolean { return this._hideToggle; }
set hideToggle(show: boolean) { this._hideToggle = coerceBooleanProperty(show); }
Expand All @@ -41,4 +50,18 @@ export class MatAccordion extends _CdkAccordion {
* elevation.
*/
@Input() displayMode: MatAccordionDisplayMode = 'default';


/** The positioning of the expansion indicator. */
@Input() togglePosition: MatAccordionTogglePosition = 'after';



ngOnChanges(changes: SimpleChanges) {
this._inputChanges.next(changes);
}

ngOnDestroy() {
this._inputChanges.complete();
}
}
7 changes: 5 additions & 2 deletions src/lib/expansion/expansion-panel-header.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@
<ng-content select="mat-panel-description"></ng-content>
<ng-content></ng-content>
</span>
<span [@indicatorRotate]="_getExpandedState()" *ngIf="_showToggle()"
class="mat-expansion-indicator"></span>
<div class="mat-expansion-indicator-container"
[class.mat-expansion-indicator-container-before]="_placeIndicatorBefore()">
<span class="mat-expansion-indicator" [@indicatorRotate]="_getExpandedState()"
*ngIf="_showIndicator()"></span>
</div>
24 changes: 16 additions & 8 deletions src/lib/expansion/expansion-panel-header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
display: flex;
flex-direction: row;
align-items: center;
padding: 0 24px;
padding: 0 16px;

&:focus,
&:hover {
Expand All @@ -25,6 +25,7 @@
flex: 1;
flex-direction: row;
overflow: hidden;
padding: 0 8px;
}

.mat-expansion-panel-header-title,
Expand All @@ -43,16 +44,23 @@
flex-grow: 2;
}

/**
* Creates the expansion indicator arrow. Done using ::after rather than having
* additional nodes in the template.
*/
.mat-expansion-indicator::after {
.mat-expansion-indicator-container {
// An offset is required to offset the entire expansion indicator against its border width. It is
// calculated as sqrt(2 * (border-width)).
margin-bottom: 2.83px;
padding: 0 8px;
width: 8px;
order: 1;

&.mat-expansion-indicator-container-before {
order: -1;
}
}

.mat-expansion-indicator {
border-style: solid;
border-width: 0 2px 2px 0;
content: '';
display: inline-block;
padding: 3px;
transform: rotate(45deg);
vertical-align: middle;
}
23 changes: 18 additions & 5 deletions src/lib/expansion/expansion-panel-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ import {
ElementRef,
Host,
Input,
Optional,
OnDestroy,
Renderer2,
ViewEncapsulation,
} from '@angular/core';
import {merge} from 'rxjs/observable/merge';
import {Subscription} from 'rxjs/Subscription';
import {EXPANSION_PANEL_ANIMATION_TIMING, MatExpansionPanel} from './expansion-panel';
import {MatAccordion} from './accordion';


/**
Expand Down Expand Up @@ -62,8 +64,8 @@ import {EXPANSION_PANEL_ANIMATION_TIMING, MatExpansionPanel} from './expansion-p
},
animations: [
trigger('indicatorRotate', [
state('collapsed', style({transform: 'rotate(0deg)'})),
state('expanded', style({transform: 'rotate(180deg)'})),
state('collapsed', style({transform: 'rotate(45deg)'})),
state('expanded', style({transform: 'rotate(225deg)'})),
transition('expanded <=> collapsed', animate(EXPANSION_PANEL_ANIMATION_TIMING)),
]),
trigger('expansionHeight', [
Expand All @@ -86,17 +88,24 @@ export class MatExpansionPanelHeader implements OnDestroy {

constructor(
renderer: Renderer2,
@Optional() accordion: MatAccordion,
@Host() public panel: MatExpansionPanel,
private _element: ElementRef,
private _focusMonitor: FocusMonitor,
private _changeDetectorRef: ChangeDetectorRef) {

let changeSubjects = [panel._inputChanges];
if (accordion) {
changeSubjects.push(accordion._inputChanges);
}

// Since the toggle state depends on an @Input on the panel, we
// need to subscribe and trigger change detection manually.
// need to subscribe and trigger change detection manually.
this._parentChangeSubscription = merge(
panel.opened,
panel.closed,
panel._inputChanges.pipe(filter(changes => !!(changes.hideToggle || changes.disabled)))
merge(...changeSubjects).pipe(
filter(changes => !!(changes.hideToggle || changes.disabled || changes.togglePosition)))
)
.subscribe(() => this._changeDetectorRef.markForCheck());

Expand Down Expand Up @@ -132,10 +141,14 @@ export class MatExpansionPanelHeader implements OnDestroy {
}

/** Gets whether the expand indicator should be shown. */
_showToggle(): boolean {
_showIndicator(): boolean {
return !this.panel.hideToggle && !this.panel.disabled;
}

_placeIndicatorBefore(): boolean {
return this.panel.togglePosition === 'before';
}

/** Handle keyup event calling to toggle() if appropriate. */
_keyup(event: KeyboardEvent) {
switch (event.keyCode) {
Expand Down
12 changes: 11 additions & 1 deletion src/lib/expansion/expansion-panel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {CdkAccordionItem} from '@angular/cdk/accordion';
import {UniqueSelectionDispatcher} from '@angular/cdk/collections';
import {CanDisable, mixinDisabled} from '@angular/material/core';
import {Subject} from 'rxjs/Subject';
import {MatAccordion} from './accordion';
import {MatAccordion, MatAccordionTogglePosition} from './accordion';

/** Workaround for https://github.com/angular/angular/issues/17849 */
export const _CdkAccordionItem = CdkAccordionItem;
Expand Down Expand Up @@ -93,6 +93,16 @@ export class MatExpansionPanel extends _MatExpansionPanelMixinBase
/** Whether the toggle indicator should be hidden. */
@Input() hideToggle: boolean = false;

/** The positioning of the expansion indicator. */
@Input()
get togglePosition(): MatAccordionTogglePosition {
return this.accordion ? this.accordion.togglePosition : this._togglePosition;
}
set togglePosition(position: MatAccordionTogglePosition) {
this._togglePosition = position;
}
private _togglePosition: MatAccordionTogglePosition = 'after';

/** Stream that emits for changes in `@Input` properties. */
_inputChanges = new Subject<SimpleChanges>();

Expand Down
5 changes: 3 additions & 2 deletions src/lib/expansion/expansion.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ header to align with Material Design specifications.

By default, the expansion-panel header includes a toggle icon at the end of the
header to indicate the expansion state. This icon can be hidden via the
`hideToggle` property.
`hideToggle` property. The icon's position can also be configured with the `togglePosition`
property.

```html
<mat-expansion-panel>
Expand Down Expand Up @@ -101,7 +102,7 @@ panel can be expanded at a given time:

### Accessibility
The expansion-panel aims to mimic the experience of the native `<details>` and `<summary>` elements.
The expansion panel header has `role="button"` and also the attribute `aria-controls` with the
The expansion panel header has `role="button"` and also the attribute `aria-controls` with the
expansion panel's id as value.

The expansion panel headers are buttons. Users can use the keyboard to activate the expansion panel
Expand Down
31 changes: 25 additions & 6 deletions src/lib/expansion/expansion.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,30 @@ describe('MatExpansionPanel', () => {

const arrow = fixture.debugElement.query(By.css('.mat-expansion-indicator')).nativeElement;

expect(arrow.style.transform).toBe('rotate(0deg)', 'Expected no rotation.');
expect(arrow.style.transform).toBe('rotate(45deg)', 'Expected 45 degree rotation.');

fixture.componentInstance.expanded = true;
fixture.detectChanges();
tick(250);

expect(arrow.style.transform).toBe('rotate(180deg)', 'Expected 180 degree rotation.');
expect(arrow.style.transform).toBe('rotate(225deg)', 'Expected 225 degree rotation.');
}));

it('should update the indicator position when the position is set programmatically',
fakeAsync(() => {
const fixture = TestBed.createComponent(PanelWithContent);

fixture.detectChanges();

const arrowContainer = fixture.debugElement.query(
By.css('.mat-expansion-indicator-container')).nativeElement;

expect(arrowContainer.classList).not.toContain('mat-expansion-indicator-container-before');

fixture.componentInstance.togglePosition = 'before';
fixture.detectChanges();

expect(arrowContainer.classList).toContain('mat-expansion-indicator-container-before');
}));

describe('disabled state', () => {
Expand Down Expand Up @@ -203,10 +220,11 @@ describe('MatExpansionPanel', () => {
@Component({
template: `
<mat-expansion-panel [expanded]="expanded"
[hideToggle]="hideToggle"
[disabled]="disabled"
(opened)="openCallback()"
(closed)="closeCallback()">
[hideToggle]="hideToggle"
[togglePosition]="togglePosition"
[disabled]="disabled"
(opened)="openCallback()"
(closed)="closeCallback()">
<mat-expansion-panel-header>Panel Title</mat-expansion-panel-header>
<p>Some content</p>
<button>I am a button</button>
Expand All @@ -215,6 +233,7 @@ describe('MatExpansionPanel', () => {
class PanelWithContent {
expanded = false;
hideToggle = false;
togglePosition = 'after';
disabled = false;
openCallback = jasmine.createSpy('openCallback');
closeCallback = jasmine.createSpy('closeCallback');
Expand Down

0 comments on commit 82e8189

Please sign in to comment.