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

Add ability to brand top left header #281

Merged
merged 17 commits into from
Oct 23, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
</span>
<go-icon
[icon]="buttonIcon"
[iconModifier]="buttonVariant"
iconClass="go-button__icon"
[disabled]="buttonDisabled || isProcessing"
*ngIf="buttonIcon"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<header class="go-header">
<div class="go-header__left"
[ngClass]="{ 'go-header__left--collapsed': isNavCollapsed() }">
[ngClass]="{ 'go-header__left--collapsed': isNavCollapsed(), 'go-header__left--dark': brandColorIsDark }"
[ngStyle]="{'background': brandColor}">
<go-icon-button class="go-header__menu"
buttonIcon="menu"
buttonSize="medium"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ $breakpoint-header-mobile-small: 500px;
width: auto;
}

.go-header__left--dark {
color: $theme-dark-color
}

.go-header__middle {
@include transition(padding);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { GoHeaderComponent } from './go-header.component';
import { GoIconButtonModule } from '../go-icon-button/go-icon-button.module';
import { GoConfigService } from '../../go-config.service';

describe('GoHeaderComponent', () => {
let component: GoHeaderComponent;
Expand All @@ -12,6 +13,9 @@ describe('GoHeaderComponent', () => {
declarations: [ GoHeaderComponent ],
imports: [
GoIconButtonModule
],
providers: [
GoConfigService
]
})
.compileComponents();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,50 @@
import { Component, ElementRef, Input, ViewChild } from '@angular/core';
import { GoSideNavService } from '../go-side-nav/go-side-nav/go-side-nav.service';
import { Component, ElementRef, Input, OnChanges, ViewChild } from '@angular/core';
import { fromEvent, Observable, Subscription } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { debounceTime, distinctUntilKeyChanged, distinctUntilChanged } from 'rxjs/operators';
import { GoConfigInterface } from '../../go-config.model';
import { GoConfigService } from '../../go-config.service';
import { GoSideNavService } from '../go-side-nav/go-side-nav/go-side-nav.service';

@Component({
selector: 'go-header',
templateUrl: './go-header.component.html',
styleUrls: ['./go-header.component.scss']
})
export class GoHeaderComponent {
export class GoHeaderComponent implements OnChanges {

@Input() altText: string = '';
@Input() logo: string = '';

@ViewChild('middleSection') middleSection: ElementRef;

public brandColor: string;
public brandColorIsDark: boolean;

private minWidthBreakpoint: number = 768;
private resizeObservable: Observable<Event> = fromEvent(window, 'resize');
private resizeSubsciption: Subscription;
private resizeSubscription: Subscription;

constructor(public sideNavService: GoSideNavService) {
constructor (
public sideNavService: GoSideNavService,
private configService: GoConfigService
) {
this.setMobileNav();
this.setupResizeSubscription();
}

ngOnChanges(): void {
this.configService.config
.pipe(distinctUntilChanged())
.subscribe((value: GoConfigInterface) => {
if (value.headerBrandingEnabled) {
this.handleBrandColorChange(value);
} else {
this.brandColor = '';
this.brandColorIsDark = false;
}
});
}

isNavCollapsed(): boolean {
return window.innerWidth <= this.minWidthBreakpoint ? true : !this.sideNavService.navOpen;
}
Expand All @@ -37,7 +58,7 @@ export class GoHeaderComponent {
}

private setupResizeSubscription(): void {
this.resizeSubsciption = this.resizeObservable
this.resizeSubscription = this.resizeObservable
.pipe(debounceTime(250))
.subscribe(event => {
this.setMobileNav();
Expand All @@ -49,4 +70,11 @@ export class GoHeaderComponent {
this.sideNavService.navOpen = false;
}
}

private handleBrandColorChange(value: GoConfigInterface): void {
const baseDarkHex: string = '#313536';
this.brandColor = value.brandColor;

this.brandColorIsDark = !this.configService.contrastIsAccessible(this.brandColor, baseDarkHex);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
.go-icon-button {
@include transition(background-color);

background: $theme-light-bg;
background: transparent;
border: 0;
border-radius: $global-radius--round;
color: $base-dark;
color: inherit;
cursor: pointer;
display: inline-flex;
outline: none;
Expand All @@ -18,7 +18,7 @@
}

&:hover, &:focus {
background: $theme-light-bg-hover;
background: $base-light-secondary;
}

&:active {
Expand Down
1 change: 1 addition & 0 deletions projects/go-lib/src/lib/go-config.model.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export interface GoConfigInterface {
brandColor: string;
headerBrandingEnabled: boolean;
}
57 changes: 57 additions & 0 deletions projects/go-lib/src/lib/go-config.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { TestBed } from '@angular/core/testing';
import { skip } from 'rxjs/operators';
import { GoConfigInterface } from './go-config.model';
import { GoConfigService } from './go-config.service';


describe('GoConfigService', () => {
let service: GoConfigService;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
providers: [GoConfigService]
});

service = TestBed.get(GoConfigService);
});

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

describe('setBrandColor', () => {
it('sets config.brandColor to a new color', () => {
spyOn(service, 'setBrandColor').and.callThrough();
service.config
.pipe(skip(1))
.subscribe((updatedConfig: GoConfigInterface) => {
expect(updatedConfig.brandColor).toBe('#f6f6f6');
});
service.setBrandColor('#f6f6f6');
});
});

describe('toggleHeaderBrandingEnabled', () => {
it('toggles config.headerBrandingEnabled', () => {
spyOn(service, 'setBrandColor').and.callThrough();
service.config
.pipe(skip(1))
.subscribe((updatedConfig: GoConfigInterface) => {
expect(updatedConfig.headerBrandingEnabled).toBe(true);
});
service.toggleHeaderBrandingEnabled();
});
});

describe('contrastIsAccessible', () => {
it('returns false when given a color combo that is not accessible', () => {
expect(service.contrastIsAccessible('#ffffff', '#bababa')).toBeFalsy();
});

it('returns true when given a color combo that is accessible', () => {
expect(service.contrastIsAccessible('#ffffff', '#000000')).toBeTruthy();
});
});
});

52 changes: 51 additions & 1 deletion projects/go-lib/src/lib/go-config.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@ import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { GoConfigInterface } from './go-config.model';

interface RGB {
r: number;
g: number;
b: number;
}

@Injectable()
export class GoConfigService {
config: BehaviorSubject<GoConfigInterface> = new BehaviorSubject<GoConfigInterface> ({
brandColor: '#65B360'
brandColor: '#65B360',
headerBrandingEnabled: false
});

public setBrandColor(color: string): void {
Expand All @@ -14,4 +21,47 @@ export class GoConfigService {
config.brandColor = color;
this.config.next(config);
}

public toggleHeaderBrandingEnabled(): void {
// we have to copy the config here or it won't regester a change in components
const config: GoConfigInterface = Object.assign({}, this.config.getValue());
config.headerBrandingEnabled = !config.headerBrandingEnabled;
this.config.next(config);
}

public contrastIsAccessible(backgroundHex: string, foregroundHex: string): boolean {
const backgroundRgb: RGB = this.hexToRgb(backgroundHex);
const foregroundRgb: RGB = this.hexToRgb(foregroundHex);

const contrast: number = this.contrast(backgroundRgb, foregroundRgb);

return contrast > 4.5;
jaredami marked this conversation as resolved.
Show resolved Hide resolved
}

private hexToRgb(hex: string): RGB {
const result: RegExpExecArray = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);

return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}

private luminance(r: number, g: number, b: number): number {
const a: number[] = [r, g, b].map((v: number) => {
v /= 255;
return v <= 0.03928
? v / 12.92
: Math.pow( (v + 0.055) / 1.055, 2.4 );
});
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}

private contrast(rgb1: RGB, rgb2: RGB): number {
const luminance1: number = this.luminance(rgb1['r'], rgb1['g'], rgb1['b']) + 0.05;
const luminance2: number = this.luminance(rgb2['r'], rgb2['g'], rgb2['b']) + 0.05;

return luminance1 / luminance2;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,26 @@ <h1 class="go-heading-5">Branding</h1>
</ng-container>
<ng-container go-card-content>
<p class="go-body-copy">
The <code class="code-block--inline">GoConfigurationService</code> can be used to brand a few select pieces of our design system. Currently
the only things that can be branded are the active indicators on the sidenav and accordions. The
<code class="code-block--inline">public setBrandColor(color: string): void</code> method can be used
to update the branding color by passing in either a hex number or the name of the color that we want
to update the design system current brand to.
The <code class="code-block--inline">GoConfigurationService</code> can be used to brand a few select
pieces of our design system. Currently the only things that can be branded are the active indicators
on the sidenav and accordions as well as the left portion of the header.
</p>

<h1 class="go-heading-5">Update Branding Color</h1>
<div class="go-container">
<div class="go-column--100">
<div class="go-column">
<p class="go-body-copy">
The <code class="code-block--inline">public setBrandColor(color: string): void</code> method can be used
to update the branding color by passing in a hex code.
</p>
</div>
</div>

<div class="go-column--50">
<div class="go-column">
<go-input
[control]="formControl"
[control]="inputControl"
label="Brand Color">
</go-input>
</div>
Expand All @@ -42,6 +50,35 @@ <h1 class="go-heading-5">Branding</h1>
</code>
</div>
</div>

<h1 class="go-heading-5">Enable Header Branding</h1>
<div class="go-container">
<div class="go-column--100">
<div class="go-column">
<p class="go-body-copy">
The <code class="code-block--inline">public toggleHeaderBrandingEnabled(): void</code> method can
be used to enable/disable header branding.
</p>
</div>
</div>

<div class="go-column--50">
<div class="go-column">
<go-switch-toggle
[control]="toggleControl"
label="Toggle Header Branding">
</go-switch-toggle>
</div>
</div>

<div class="go-column go-column--50">
<code [highlight]="toggleExample">
</code>

<code [highlight]="toggleHeaderEnabledExample">
</code>
</div>
</div>
</ng-container>
</go-card>
</section>
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import { FormControl } from '@angular/forms';
})
export class ConfigurationDocsComponent implements OnInit {
pageTitle: string = 'Configuration';
formControl: FormControl;
inputControl: FormControl;
toggleControl: FormControl = new FormControl(false);

updateColorExample: string = `
updateColor(): void {
Expand All @@ -29,13 +30,31 @@ export class ConfigurationDocsComponent implements OnInit {
</go-button>
`;

toggleExample: string = `
<go-switch-toggle
[control]="toggleControl"
label="Header Branding">
</go-switch-toggle>
`;

toggleHeaderEnabledExample: string = `
ngOnInit(): void {
this.toggleControl.valueChanges.subscribe(() => {
this.goConfigService.toggleHeaderBrandingEnabled();
});
}
`;

constructor(private goConfigService: GoConfigService) { }

ngOnInit(): void {
this.formControl = new FormControl(this.goConfigService.config.getValue().brandColor);
this.inputControl = new FormControl(this.goConfigService.config.getValue().brandColor);
this.toggleControl.valueChanges.subscribe(() => {
this.goConfigService.toggleHeaderBrandingEnabled();
});
}

updateColor(): void {
this.goConfigService.setBrandColor(this.formControl.value);
this.goConfigService.setBrandColor(this.inputControl.value);
}
}
4 changes: 4 additions & 0 deletions projects/go-tester/src/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
<app-search-test></app-search-test>
</go-search>
<ng-container go-header-right>
<go-switch-toggle
[control]="toggleControl"
label="Header Branding"
></go-switch-toggle>
<go-badge badgeData="1231" [displayData]="false">
<go-icon-button buttonIcon="add_circle" (handleClick)="openOffCanvas()"></go-icon-button>
</go-badge>
Expand Down
Loading