Skip to content

Commit

Permalink
feat(components/colorpicker): add labelText input (#1968) (#1979)
Browse files Browse the repository at this point in the history
  • Loading branch information
blackbaud-sky-build-user authored Feb 1, 2024
1 parent 464aa5c commit f27b176
Show file tree
Hide file tree
Showing 15 changed files with 129 additions and 91 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
<form novalidate [formGroup]="formGroup" (ngSubmit)="submit()">
<div class="sky-margin-stacked-lg">
<label class="sky-control-label" [for]="colorInput.id">
What is your favorite color?
</label>
<sky-colorpicker
labelText="What is your favorite color?"
(selectedColorChanged)="onSelectedColorChanged($event)"
#colorPicker
>
<input
formControlName="favoriteColor"
skyId
type="text"
[presetColors]="swatches"
[skyColorpickerInput]="colorPicker"
#colorInput="skyId"
/>
</sky-colorpicker>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ import {
ReactiveFormsModule,
} from '@angular/forms';
import { SkyColorpickerModule, SkyColorpickerOutput } from '@skyux/colorpicker';
import { SkyIdModule } from '@skyux/core';

@Component({
standalone: true,
selector: 'app-demo',
templateUrl: './demo.component.html',
imports: [ReactiveFormsModule, SkyColorpickerModule, SkyIdModule],
imports: [ReactiveFormsModule, SkyColorpickerModule],
})
export class DemoComponent {
protected formGroup: FormGroup;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
<form class="sky-margin-stacked-xxl" novalidate [formGroup]="formGroup">
<div class="sky-margin-stacked-lg">
<label class="sky-control-label" [for]="colorInput.id">
What is your favorite color?
</label>
<sky-colorpicker
labelText="What is your favorite color?"
[messageStream]="colorpickerController"
[showResetButton]="showResetButton"
#colorPicker
>
<input
formControlName="favoriteColor"
skyId
type="text"
[skyColorpickerInput]="colorPicker"
#colorInput="skyId"
/>
</sky-colorpicker>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,14 @@ import {
SkyColorpickerMessageType,
SkyColorpickerModule,
} from '@skyux/colorpicker';
import { SkyIdModule } from '@skyux/core';

import { Subject } from 'rxjs';

@Component({
standalone: true,
selector: 'app-demo',
templateUrl: './demo.component.html',
imports: [
FormsModule,
ReactiveFormsModule,
SkyColorpickerModule,
SkyIdModule,
],
imports: [FormsModule, ReactiveFormsModule, SkyColorpickerModule],
})
export class DemoComponent {
protected colorpickerController = new Subject<SkyColorpickerMessage>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
<form novalidate [formGroup]="reactiveForm" (ngSubmit)="submit()">
<div class="sky-form-group">
<label class="sky-control-label" [for]="colorInput.id">
What is your favorite color?
</label>
<sky-colorpicker
(selectedColorChanged)="onSelectedColorChanged($event)"
pickerButtonIcon="calendar"
pickerButtonIconType="skyux"
labelText="What is your favorite color?"
#colorPicker
>
<input
formControlName="favoriteColor"
skyId
type="text"
[presetColors]="swatches"
[skyColorpickerInput]="colorPicker"
#colorInput="skyId"
/>
</sky-colorpicker>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { SkyColorpickerModule } from '@skyux/colorpicker';
import { SkyIdModule } from '@skyux/core';

import { ColorpickerRoutingModule } from './colorpicker-routing.module';
import { ColorpickerComponent } from './colorpicker.component';
Expand All @@ -13,7 +12,6 @@ import { ColorpickerComponent } from './colorpicker.component';
CommonModule,
ReactiveFormsModule,
SkyColorpickerModule,
SkyIdModule,
ColorpickerRoutingModule,
],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
OnInit,
Renderer2,
forwardRef,
inject,
} from '@angular/core';
import {
ControlValueAccessor,
Expand All @@ -21,8 +22,9 @@ import {
} from '@angular/forms';
import { SkyLibResourcesService } from '@skyux/i18n';

import { Subscription } from 'rxjs';
import { Subject, Subscription, takeUntil } from 'rxjs';

import { SkyColorpickerInputService } from './colorpicker-input.service';
import { SkyColorpickerComponent } from './colorpicker.component';
import { SkyColorpickerService } from './colorpicker.service';
import { SkyColorpickerOutput } from './types/colorpicker-output';
Expand Down Expand Up @@ -81,6 +83,18 @@ export class SkyColorpickerInputDirective
return this.#_initialColor || SKY_COLORPICKER_DEFAULT_COLOR;
}

/**
* The ID should only be settable when `labelText` is undefined.
* When `labelText` is set, the ID is defined by `SkyColorpickerComponent`.
* @internal
*/
@Input()
public set id(value: string | undefined) {
if (!this.#labelText && value) {
this.#setInputId(value);
}
}

/**
* This property is deprecated and does not affect the colorpicker.
* We recommend against using it.
Expand Down Expand Up @@ -132,16 +146,20 @@ export class SkyColorpickerInputDirective
}

#modelValue: SkyColorpickerOutput | undefined;
#pickerChangedSubscription: Subscription | undefined;
#elementRef: ElementRef;
#renderer: Renderer2;
#svc: SkyColorpickerService;
#resourcesSvc: SkyLibResourcesService;
#injector: Injector;
#inputIdSubscription: Subscription | undefined;
#labelText: string | undefined;

#_disabled: boolean | undefined;
#_initialColor: string | undefined;

readonly #colorpickerInputSvc = inject(SkyColorpickerInputService);
readonly #ngUnsubscribe = new Subject<void>();

constructor(
elementRef: ElementRef,
renderer: Renderer2,
Expand Down Expand Up @@ -178,18 +196,32 @@ export class SkyColorpickerInputDirective
this.skyColorpickerInput.initialColor = this.initialColor;
this.skyColorpickerInput.returnFormat = this.returnFormat;

this.#pickerChangedSubscription =
this.skyColorpickerInput.selectedColorChanged.subscribe(
(newColor: SkyColorpickerOutput) => {
/* istanbul ignore else */
if (newColor) {
this.#modelValue = this.#formatter(newColor);

// Write the new value to the reactive form control, which will update the template model
this.writeValue(newColor);
}
},
);
this.skyColorpickerInput.selectedColorChanged
.pipe(takeUntil(this.#ngUnsubscribe))
.subscribe((newColor: SkyColorpickerOutput) => {
/* istanbul ignore else */
if (newColor) {
this.#modelValue = this.#formatter(newColor);

// Write the new value to the reactive form control, which will update the template model
this.writeValue(newColor);
}
});

this.#colorpickerInputSvc.labelText
.pipe(takeUntil(this.#ngUnsubscribe))
.subscribe((labelText) => {
this.#labelText = labelText;
this.#inputIdSubscription?.unsubscribe();

if (labelText) {
this.#inputIdSubscription = this.#colorpickerInputSvc.inputId
.pipe(takeUntil(this.#ngUnsubscribe))
.subscribe((inputId) => {
this.#setInputId(inputId);
});
}
});

this.skyColorpickerInput.updatePickerValues(this.initialColor);

Expand Down Expand Up @@ -217,9 +249,8 @@ export class SkyColorpickerInputDirective
}

public ngOnDestroy(): void {
if (this.#pickerChangedSubscription) {
this.#pickerChangedSubscription.unsubscribe();
}
this.#ngUnsubscribe.next();
this.#ngUnsubscribe.complete();
}

public setColorPickerDefaults(): void {
Expand Down Expand Up @@ -341,4 +372,8 @@ export class SkyColorpickerInputDirective
// TODO: Need to implement the async `getString` method in a breaking change.
return this.#resourcesSvc.getStringForLocale({ locale: 'en-US' }, key);
}

#setInputId(id: string): void {
this.#renderer.setAttribute(this.#elementRef.nativeElement, 'id', id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Injectable } from '@angular/core';

import { ReplaySubject } from 'rxjs';

/**
* @internal
*/
@Injectable()
export class SkyColorpickerInputService {
public inputId = new ReplaySubject<string>(1);
public labelText = new ReplaySubject<string | undefined>(1);
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<label *ngIf="labelText" class="sky-control-label" [for]="inputId">{{
labelText
}}</label>

<div
class="sky-input-group"
[ngClass]="{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,27 @@ describe('Colorpicker Component', () => {
).toBeNull();
}));

it('should add a label if labelText is provided', () => {
const labelText = 'Label Text';
component.labelText = labelText;

fixture.detectChanges();

const label = fixture.nativeElement.querySelector('.sky-control-label');

expect(label).toBeVisible();
expect(label.textContent).toBe(labelText);
});

it('should allow setting ID if labelText is not set', () => {
const id = 'test-id';
component.id = id;

fixture.detectChanges();

expect(fixture.nativeElement.querySelector(`#${id}`)).toExist();
});

it('should add icon overlay', fakeAsync(() => {
const icon = getColorpickerIcon();
expect(icon).toBeNull();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
SkyAffixService,
SkyAffixer,
SkyCoreAdapterService,
SkyIdService,
SkyOverlayInstance,
SkyOverlayService,
} from '@skyux/core';
Expand All @@ -29,6 +30,7 @@ import { Subject, fromEvent } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { SliderDimension, SliderPosition } from './colorpicker-classes';
import { SkyColorpickerInputService } from './colorpicker-input.service';
import { SkyColorpickerService } from './colorpicker.service';
import { SkyColorpickerChangeAxis } from './types/colorpicker-axis';
import { SkyColorpickerChangeColor } from './types/colorpicker-color';
Expand All @@ -50,7 +52,7 @@ let componentIdIndex = 0;
selector: 'sky-colorpicker',
templateUrl: './colorpicker.component.html',
styleUrls: ['./colorpicker.component.scss'],
providers: [SkyColorpickerService],
providers: [SkyColorpickerInputService, SkyColorpickerService],
encapsulation: ViewEncapsulation.None,
})
export class SkyColorpickerComponent implements OnInit, OnDestroy {
Expand Down Expand Up @@ -87,6 +89,20 @@ export class SkyColorpickerComponent implements OnInit, OnDestroy {
@Input()
public labelledBy: string | undefined;

/**
* The text to display as the colorpicker's label. Use this instead of a `label` element when the label is text-only.
* Specifying `labelText` also enables automatic error message handling for standard colorpicker errors.
*/
@Input()
public set labelText(value: string | undefined) {
this.#_labelText = value;
this.#colorpickerInputSvc.labelText.next(value);
}

public get labelText(): string | undefined {
return this.#_labelText;
}

/**
* Fires when users select a color in the colorpicker.
*/
Expand Down Expand Up @@ -265,6 +281,7 @@ export class SkyColorpickerComponent implements OnInit, OnDestroy {
return this.#_colorpickerRef;
}

protected inputId: string | undefined;
protected colorpickerId: string;
protected isOpen = false;
protected triggerButtonId: string;
Expand Down Expand Up @@ -297,14 +314,18 @@ export class SkyColorpickerComponent implements OnInit, OnDestroy {
#affixSvc: SkyAffixService;
#changeDetector: ChangeDetectorRef;
#coreAdapter: SkyCoreAdapterService;
readonly #environmentInjector = inject(EnvironmentInjector);
#overlaySvc: SkyOverlayService;
#svc: SkyColorpickerService;
#themeSvc: SkyThemeService | undefined;

readonly #environmentInjector = inject(EnvironmentInjector);
readonly #colorpickerInputSvc = inject(SkyColorpickerInputService);
readonly #idSvc = inject(SkyIdService);

#_backgroundColorForDisplay: string | undefined;
#_colorpickerRef: ElementRef | undefined;
#_disabled = false;
#_labelText: string | undefined;

constructor(
affixSvc: SkyAffixService,
Expand All @@ -324,6 +345,8 @@ export class SkyColorpickerComponent implements OnInit, OnDestroy {
componentIdIndex++;

this.#idIndex = componentIdIndex;
this.inputId = this.#idSvc.generateId();
this.#colorpickerInputSvc.inputId.next(this.inputId);
this.skyColorpickerRedId = `sky-colorpicker-red-${this.#idIndex}`;
this.skyColorpickerHexId = `sky-colorpicker-hex--${this.#idIndex}`;
this.skyColorpickerRedId = `sky-colorpicker-red--${this.#idIndex}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
[pickerButtonIconType]="pickerButtonIconType"
[label]="label"
[labelledBy]="labelledBy"
[labelText]="labelText"
[messageStream]="colorpickerController"
#colorPickerTest
>
<input
[id]="id"
[allowTransparency]="selectedTransparency"
[alphaChannel]="selectedHexType"
[disabled]="disabled"
Expand Down
Loading

0 comments on commit f27b176

Please sign in to comment.