Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

Commit

Permalink
Contrib > Added resizing functionality to flyout (#1539) (#1566)
Browse files Browse the repository at this point in the history
  • Loading branch information
Blackbaud-SteveBrush authored Mar 29, 2018
1 parent b31f631 commit 9b601b7
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 10 deletions.
5 changes: 4 additions & 1 deletion src/demos/flyout/flyout-demo.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,10 @@ export class SkyFlyoutDemoComponent {
}],
ariaDescribedBy: 'my-describedby-id',
ariaLabelledBy: 'my-labelledby-id',
ariaRole: 'modal'
ariaRole: 'modal',
defaultWidth: 500,
maxWidth: 1000,
minWidth: 200
});

this.flyout.closed.subscribe(() => {
Expand Down
13 changes: 12 additions & 1 deletion src/modules/flyout/flyout.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,18 @@
[attr.aria-labelledby]="config?.ariaLabelledBy"
[ngClass]="{ 'sky-flyout-hidden': !isOpen && !isOpening }"
(@flyoutState.done)="animationDone($event)"
[@flyoutState]="getAnimationState()">
[@flyoutState]="getAnimationState()"
[style.width.px]="flyoutWidth">

<button
type="button"
class="sky-flyout-resize-handle"
aria-hidden="true"
role="separator"
tabindex="-1"
(mousedown)="onMouseDown($event)">
</button>

<div class="sky-flyout-header" #flyoutHeader>
<div class="sky-flyout-header-content"></div>
<div class="sky-flyout-header-buttons">
Expand Down
31 changes: 26 additions & 5 deletions src/modules/flyout/flyout.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,39 @@
background-color: #fff;
border-left: 6px solid $sky-highlight-color-info;
z-index: $sky-flyout-z-index;
overflow: auto;
width: 100%;

&:focus {
outline: none;
}

@media (min-width: $sky-screen-sm-min) {
width: 50%;
min-width: 320px;
@media (max-width: $sky-screen-sm-min) {
min-width: 100%;
max-width: 100%;
}

&.sky-flyout-hidden {
visibility: hidden;
}
}

.sky-flyout-resize-handle {
height: 100%;
width: 14px;
position: absolute;
left: -10px;
cursor: ew-resize;
padding: 0;
border: 0;
background: transparent;
display: block;
top: 0;
bottom: 0;

@media (max-width: $sky-screen-sm-min) {
cursor: initial;
}
}

.sky-flyout-header {
@include sky-border(dark, bottom);
width: 100%;
Expand All @@ -48,3 +64,8 @@
padding-right: 50px;
}
}

.sky-flyout-content {
overflow-y: auto;
height: calc(100% - 50px);
}
82 changes: 79 additions & 3 deletions src/modules/flyout/flyout.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,21 @@ describe('Flyout component', () => {
tick();
}

function makeEvent(eventType: string, evtObj: any) {
let evt = document.createEvent('MouseEvents');
evt.initMouseEvent(eventType, false, false, window, 0, 0, 0, evtObj.clientX,
0, false, false, false, false, 0, undefined);
document.dispatchEvent(evt);
}

function getFlyoutElement(): HTMLElement {
return document.querySelector('.sky-flyout') as HTMLElement;
return document.querySelector('.sky-flyout') as HTMLElement;
}

function getFlyoutHandleElement(): HTMLElement {
return document.querySelector('.sky-flyout-resize-handle') as HTMLElement;
}

function getFlyoutHeaderElement(): HTMLElement {
return document.querySelector('.sky-flyout-header') as HTMLElement;
}
Expand Down Expand Up @@ -154,16 +165,18 @@ describe('Flyout component', () => {
expect(flyoutContentElement).toHaveText('Sally');
}));

it('should accept configuration options for aria-labelledBy, aria-describedby and role',
it('should accept configuration options for aria-labelledBy, aria-describedby, role, and width',
fakeAsync(() => {
const expectedLabel = 'customlabelledby';
const expectedDescribed = 'customdescribedby';
const expectedRole = 'customrole';
const expectedDefault = 500;

openFlyout({
ariaLabelledBy: expectedLabel,
ariaDescribedBy: expectedDescribed,
ariaRole: expectedRole
ariaRole: expectedRole,
defaultWidth: expectedDefault
});

const flyoutElement = getFlyoutElement();
Expand All @@ -174,6 +187,8 @@ describe('Flyout component', () => {
.toBe(expectedDescribed);
expect(flyoutElement.getAttribute('role'))
.toBe(expectedRole);
expect(flyoutElement.style.width)
.toBe(expectedDefault + 'px');
})
);

Expand All @@ -193,4 +208,65 @@ describe('Flyout component', () => {
expect(headerElement.classList.contains('sky-flyout-help-shim')).toBeTruthy();
})
);

it('should resize when handle is dragged', fakeAsync(() => {
openFlyout({});
const flyoutElement = getFlyoutElement();
const handleElement = getFlyoutHandleElement();

expect(flyoutElement.style.width).toBe('500px');

let evt = document.createEvent('MouseEvents');
evt.initMouseEvent('mousedown', false, false, window, 0, 0, 0, 1000,
0, false, false, false, false, 0, undefined);

handleElement.dispatchEvent(evt);
makeEvent('mousemove', { clientX: 1100 });
fixture.detectChanges();
expect(flyoutElement.style.width).toBe('400px');
makeEvent('mousemove', { clientX: 1000 });
fixture.detectChanges();
expect(flyoutElement.style.width).toBe('500px');
makeEvent('mouseup', {});
})
);

it('should respect minimum and maximum when resizing', fakeAsync(() => {
openFlyout({ maxWidth: 1000, minWidth: 200});
const flyoutElement = getFlyoutElement();
const handleElement = getFlyoutHandleElement();

expect(flyoutElement.style.width).toBe('500px');
let evt = document.createEvent('MouseEvents');
evt.initMouseEvent('mousedown', false, false, window, 0, 0, 0, 1000,
0, false, false, false, false, 0, undefined);
handleElement.dispatchEvent(evt);
makeEvent('mousemove', { clientX: 500 });
fixture.detectChanges();
expect(flyoutElement.style.width).toBe('1000px');
makeEvent('mousemove', { clientX: 200 });
fixture.detectChanges();
expect(flyoutElement.style.width).toBe('1000px');
makeEvent('mousemove', { clientX: 1300 });
fixture.detectChanges();
expect(flyoutElement.style.width).toBe('200px');
makeEvent('mousemove', { clientX: 1400 });
fixture.detectChanges();
expect(flyoutElement.style.width).toBe('200px');
makeEvent('mouseup', {});
})
);

it('should not resize when handle is not clicked',
fakeAsync(() => {

openFlyout({});
const flyoutElement = getFlyoutElement();

expect(flyoutElement.style.width).toBe('500px');
makeEvent('mousemove', { clientX: 1100 });
fixture.detectChanges();
expect(flyoutElement.style.width).toBe('500px');
})
);
});
41 changes: 41 additions & 0 deletions src/modules/flyout/flyout.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
ChangeDetectorRef,
ComponentFactoryResolver,
ElementRef,
HostListener,
Injector,
OnDestroy,
OnInit,
Expand Down Expand Up @@ -60,6 +61,10 @@ export class SkyFlyoutComponent implements OnDestroy, OnInit {
public isOpen = false;
public isOpening = false;

public flyoutWidth = 0;
public isDragging = false;
private xCoord = 0;

public get messageStream(): Subject<SkyFlyoutMessage> {
return this._messageStream;
}
Expand Down Expand Up @@ -113,6 +118,9 @@ export class SkyFlyoutComponent implements OnDestroy, OnInit {
}

this.config = Object.assign({ providers: [] }, config);
this.config.defaultWidth = this.config.defaultWidth || 500;
this.config.minWidth = this.config.minWidth || 320;
this.config.maxWidth = this.config.maxWidth || this.config.defaultWidth;

const factory = this.resolver.resolveComponentFactory(component);
const providers = ReflectiveInjector.resolve(this.config.providers);
Expand All @@ -126,6 +134,8 @@ export class SkyFlyoutComponent implements OnDestroy, OnInit {
type: SkyFlyoutMessageType.Open
});

this.flyoutWidth = this.config.defaultWidth;

return this.flyoutInstance;
}

Expand All @@ -145,6 +155,37 @@ export class SkyFlyoutComponent implements OnDestroy, OnInit {
}
}

public onMouseDown(event: MouseEvent) {
this.isDragging = true;
this.xCoord = event.clientX;
event.preventDefault();
event.stopPropagation();
}

@HostListener('document:mousemove', ['$event'])
public onMouseMove(event: MouseEvent) {
if (!this.isDragging) {
return;
}

const offsetX = event.clientX - this.xCoord;
let width = this.flyoutWidth;

width -= offsetX;

if (width < this.config.minWidth || width > this.config.maxWidth) {
return;
}

this.flyoutWidth = width;
this.xCoord = event.clientX;
}

@HostListener('document:mouseup', ['$event'])
public onHandleRelease(event: MouseEvent) {
this.isDragging = false;
}

private open() {
if (!this.isOpen) {
this.isOpen = false;
Expand Down
3 changes: 3 additions & 0 deletions src/modules/flyout/types/flyout-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ export interface SkyFlyoutConfig {
ariaDescribedBy?: string;
ariaLabelledBy?: string;
ariaRole?: string;
defaultWidth?: number;
minWidth?: number;
maxWidth?: number;
}

0 comments on commit 9b601b7

Please sign in to comment.