Skip to content

Commit

Permalink
feat(select): allow setting the theme color (#3928)
Browse files Browse the repository at this point in the history
Fixes #3923.
  • Loading branch information
crisbeto authored and kara committed Apr 21, 2017
1 parent 94a2855 commit 3a29d67
Show file tree
Hide file tree
Showing 8 changed files with 167 additions and 57 deletions.
16 changes: 14 additions & 2 deletions src/demo-app/select/select-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<md-card>
<md-card-subtitle>ngModel</md-card-subtitle>

<md-select placeholder="Drink" [(ngModel)]="currentDrink" [required]="drinksRequired" [disabled]="drinksDisabled"
<md-select placeholder="Drink" [color]="drinksTheme" [(ngModel)]="currentDrink" [required]="drinksRequired" [disabled]="drinksDisabled"
[floatPlaceholder]="floatPlaceholder" #drinkControl="ngModel">
<md-option *ngFor="let drink of drinks" [value]="drink.value" [disabled]="drink.disabled">
{{ drink.viewValue }}
Expand All @@ -22,6 +22,12 @@
<option value="never">Never</option>
</select>
</p>
<p>
<label for="drinks-theme">Theme:</label>
<select [(ngModel)]="drinksTheme" id="drinks-theme">
<option *ngFor="let theme of availableThemes" [value]="theme.value">{{ theme.name }}</option>
</select>
</p>

<button md-button (click)="currentDrink='water-2'">SET VALUE</button>
<button md-button (click)="drinksRequired=!drinksRequired">TOGGLE REQUIRED</button>
Expand All @@ -33,7 +39,7 @@
<md-card-subtitle>Multiple selection</md-card-subtitle>

<md-card-content>
<md-select multiple placeholder="Pokemon" [(ngModel)]="currentPokemon"
<md-select multiple [color]="pokemonTheme" placeholder="Pokemon" [(ngModel)]="currentPokemon"
[required]="pokemonRequired" [disabled]="pokemonDisabled" #pokemonControl="ngModel">
<md-option *ngFor="let creature of pokemon" [value]="creature.value">
{{ creature.viewValue }}
Expand All @@ -43,6 +49,12 @@
<p> Touched: {{ pokemonControl.touched }} </p>
<p> Dirty: {{ pokemonControl.dirty }} </p>
<p> Status: {{ pokemonControl.control?.status }} </p>
<p>
<label for="pokemon-theme">Theme:</label>
<select [(ngModel)]="pokemonTheme" id="pokemon-theme">
<option *ngFor="let theme of availableThemes" [value]="theme.value">{{ theme.name }}</option>
</select>
</p>
<button md-button (click)="setPokemonValue()">SET VALUE</button>
<button md-button (click)="pokemonRequired=!pokemonRequired">TOGGLE REQUIRED</button>
<button md-button (click)="pokemonDisabled=!pokemonDisabled">TOGGLE DISABLED</button>
Expand Down
8 changes: 8 additions & 0 deletions src/demo-app/select/select-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export class SelectDemo {
latestChangeEvent: MdSelectChange;
floatPlaceholder: string = 'auto';
foodControl = new FormControl('pizza-1');
drinksTheme = 'primary';
pokemonTheme = 'primary';

foods = [
{value: 'steak-0', viewValue: 'Steak'},
Expand Down Expand Up @@ -48,6 +50,12 @@ export class SelectDemo {
{value: 'psyduck-6', viewValue: 'Psyduck'},
];

availableThemes = [
{value: 'primary', name: 'Primary' },
{value: 'accent', name: 'Accent' },
{value: 'warn', name: 'Warn' }
];

toggleDisabled() {
this.foodControl.enabled ? this.foodControl.disable() : this.foodControl.enable();
}
Expand Down
21 changes: 15 additions & 6 deletions src/lib/core/option/_option-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,29 @@
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$warn: map-get($theme, warn);

.mat-option {
&:hover:not(.mat-option-disabled), &:focus:not(.mat-option-disabled) {
background: mat-color($background, hover);
}

&.mat-selected {
&.mat-selected.mat-primary, .mat-primary &.mat-selected {
color: mat-color($primary);
}

&.mat-selected.mat-accent, .mat-accent &.mat-selected {
color: mat-color($accent);
}

// In multiple mode there is a checkbox to show that the option is selected.
&:not(.mat-option-multiple) {
background: mat-color($background, hover);
}
&.mat-selected.mat-warn, .mat-warn &.mat-selected {
color: mat-color($warn);
}

// In multiple mode there is a checkbox to show that the option is selected.
&.mat-selected:not(.mat-option-multiple) {
background: mat-color($background, hover);
}

&.mat-active {
Expand All @@ -28,6 +38,5 @@
&.mat-option-disabled {
color: mat-color($foreground, hint-text);
}

}
}
28 changes: 16 additions & 12 deletions src/lib/core/selection/pseudo-checkbox/_pseudo-checkbox-theme.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
@import '../../theming/theming';

@mixin _mat-pseudo-checkbox-inner-content-theme($theme, $pallete-name) {
$pallete: map-get($theme, $pallete-name);
$color: mat-color($pallete, 500);

.mat-pseudo-checkbox-checked.mat-#{$pallete-name},
.mat-pseudo-checkbox-indeterminate.mat-#{$pallete-name},
.mat-#{$pallete-name} .mat-pseudo-checkbox-checked,
.mat-#{$pallete-name} .mat-pseudo-checkbox-indeterminate {
background: $color;
}
}

@mixin mat-pseudo-checkbox-theme($theme) {
$is-dark-theme: map-get($theme, is-dark);
Expand All @@ -14,6 +25,7 @@
$white-30pct-opacity-on-dark: #686868;
$black-26pct-opacity-on-light: #b0b0b0;
$disabled-color: if($is-dark-theme, $white-30pct-opacity-on-dark, $black-26pct-opacity-on-light);
$colored-box-selector: '.mat-pseudo-checkbox-checked, .mat-pseudo-checkbox-indeterminate';

.mat-pseudo-checkbox {
color: mat-color(map-get($theme, foreground), secondary-text);
Expand All @@ -23,19 +35,11 @@
}
}

.mat-pseudo-checkbox-checked, .mat-pseudo-checkbox-indeterminate {
&.mat-primary {
background: mat-color($primary, 500);
}

&.mat-accent {
background: mat-color($accent, 500);
}

&.mat-warn {
background: mat-color($warn, 500);
}
@include _mat-pseudo-checkbox-inner-content-theme($theme, primary);
@include _mat-pseudo-checkbox-inner-content-theme($theme, accent);
@include _mat-pseudo-checkbox-inner-content-theme($theme, warn);

.mat-pseudo-checkbox-checked, .mat-pseudo-checkbox-indeterminate {
&.mat-pseudo-checkbox-disabled {
background: $disabled-color;
}
Expand Down
60 changes: 29 additions & 31 deletions src/lib/select/_select-theme.scss
Original file line number Diff line number Diff line change
@@ -1,46 +1,33 @@
@import '../core/theming/palette';
@import '../core/theming/theming';

@mixin _mat-select-inner-content-theme($palette) {
$color: mat-color($palette);

.mat-select-trigger, .mat-select-arrow {
color: $color;
}

.mat-select-underline {
background-color: $color;
}
}

@mixin mat-select-theme($theme) {
$foreground: map-get($theme, foreground);
$background: map-get($theme, background);
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
$warn: map-get($theme, warn);

.mat-select-trigger {
color: mat-color($foreground, hint-text);

.mat-select:focus:not(.mat-select-disabled) & {
color: mat-color($primary);
}

.mat-select:not(:focus).ng-invalid.ng-touched:not(.mat-select-disabled) & {
color: mat-color($warn);
}
}

.mat-select-underline {
background-color: mat-color($foreground, divider);

.mat-select:focus:not(.mat-select-disabled) & {
background-color: mat-color($primary);
}

.mat-select:not(:focus).ng-invalid.ng-touched:not(.mat-select-disabled) & {
background-color: mat-color($warn);
}
}

.mat-select-arrow {
.mat-select-disabled .mat-select-value,
.mat-select-arrow,
.mat-select-trigger {
color: mat-color($foreground, hint-text);

.mat-select:focus:not(.mat-select-disabled) & {
color: mat-color($primary);
}

.mat-select:not(:focus).ng-invalid.ng-touched:not(.mat-select-disabled) & {
color: mat-color($warn);
}
}

.mat-select-content, .mat-select-panel-done-animating {
Expand All @@ -49,9 +36,20 @@

.mat-select-value {
color: mat-color($foreground, text);
}

.mat-select-disabled & {
color: mat-color($foreground, hint-text);
.mat-select:focus:not(.mat-select-disabled) {
&.mat-primary {
@include _mat-select-inner-content-theme($primary);
}

&.mat-accent {
@include _mat-select-inner-content-theme($accent);
}
}

.mat-select:focus:not(.mat-select-disabled).mat-warn,
.mat-select:not(:focus).ng-invalid.ng-touched:not(.mat-select-disabled) {
@include _mat-select-inner-content-theme($warn);
}
}
2 changes: 1 addition & 1 deletion src/lib/select/select.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
[offsetY]="_offsetY" [offsetX]="_offsetX" (attach)="_setScrollTop()" (detach)="close()">
<div class="mat-select-panel" [@transformPanel]="'showing'" (@transformPanel.done)="_onPanelDone()"
(keydown)="_keyManager.onKeydown($event)" [style.transformOrigin]="_transformOrigin"
[class.mat-select-panel-done-animating]="_panelDoneAnimating">
[class.mat-select-panel-done-animating]="_panelDoneAnimating" [ngClass]="'mat-' + color">
<div class="mat-select-content" [@fadeInContent]="'showing'" (@fadeInContent.done)="_onFadeInDone()">
<ng-content></ng-content>
</div>
Expand Down
66 changes: 65 additions & 1 deletion src/lib/select/select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ describe('MdSelect', () => {
SelectWithPlainTabindex,
SelectEarlyAccessSibling,
BasicSelectInitiallyHidden,
BasicSelectNoPlaceholder
BasicSelectNoPlaceholder,
BasicSelectWithTheming
],
providers: [
{provide: OverlayContainer, useFactory: () => {
Expand Down Expand Up @@ -1709,6 +1710,55 @@ describe('MdSelect', () => {

});

describe('theming', () => {
let fixture: ComponentFixture<BasicSelectWithTheming>;
let testInstance: BasicSelectWithTheming;
let selectElement: HTMLElement;

beforeEach(async(() => {
fixture = TestBed.createComponent(BasicSelectWithTheming);
testInstance = fixture.componentInstance;
fixture.detectChanges();

selectElement = fixture.debugElement.query(By.css('.mat-select')).nativeElement;
}));

it('should default to the primary theme', () => {
expect(fixture.componentInstance.select.color).toBe('primary');
expect(selectElement.classList).toContain('mat-primary');
});

it('should be able to override the theme', () => {
fixture.componentInstance.theme = 'accent';
fixture.detectChanges();

expect(fixture.componentInstance.select.color).toBe('accent');
expect(selectElement.classList).toContain('mat-accent');
expect(selectElement.classList).not.toContain('mat-primary');
});

it('should not be able to set a blank theme', () => {
fixture.componentInstance.theme = '';
fixture.detectChanges();

expect(fixture.componentInstance.select.color).toBe('primary');
expect(selectElement.classList).toContain('mat-primary');
});

it('should pass the theme to the panel', () => {
fixture.componentInstance.theme = 'warn';
fixture.debugElement.query(By.css('.mat-select-trigger')).nativeElement.click();
fixture.detectChanges();

const panel = overlayContainerElement.querySelector('.mat-select-panel');

expect(fixture.componentInstance.select.color).toBe('warn');
expect(selectElement.classList).toContain('mat-warn');
expect(panel.classList).toContain('mat-warn');
});

});

});


Expand Down Expand Up @@ -2040,6 +2090,20 @@ class BasicSelectInitiallyHidden {
})
class BasicSelectNoPlaceholder { }

@Component({
selector: 'basic-select-with-theming',
template: `
<md-select placeholder="Food" [color]="theme">
<md-option value="steak-0">Steak</md-option>
<md-option value="pizza-1">Pizza</md-option>
</md-select>
`
})
class BasicSelectWithTheming {
@ViewChild(MdSelect) select: MdSelect;
theme: string;
}

class FakeViewportRuler {
getViewportRect() {
return {
Expand Down
Loading

0 comments on commit 3a29d67

Please sign in to comment.