From 1d098411895890cd08d8b9bf185d85142d0897bb Mon Sep 17 00:00:00 2001 From: Andrei Fateev Date: Thu, 11 Jun 2026 17:55:19 +0200 Subject: [PATCH 1/4] base changes --- playwright/cps-accessibility.spec.ts | 10 +-- .../app/api-data/cps-progress-circular.json | 12 ++- .../progress-circular-page.component.html | 25 +++--- .../progress-circular-page.component.scss | 20 ++++- .../progress-circular-page.component.ts | 3 +- .../cps-button/cps-button.component.html | 2 +- .../cps-menu/cps-menu.component.html | 4 +- .../cps-progress-circular.component.html | 5 +- .../cps-progress-circular.component.ts | 86 ++++++++++++++++--- 9 files changed, 129 insertions(+), 38 deletions(-) diff --git a/playwright/cps-accessibility.spec.ts b/playwright/cps-accessibility.spec.ts index b2b7ecfb..707f61fa 100644 --- a/playwright/cps-accessibility.spec.ts +++ b/playwright/cps-accessibility.spec.ts @@ -156,11 +156,11 @@ const components: ComponentEntry[] = [ } }, // { route: '/paginator', name: 'Paginator', selector: 'cps-paginator' }, - // { - // route: '/progress-circular', - // name: 'Progress circular', - // selector: 'cps-progress-circular' - // }, + { + route: '/progress-circular', + name: 'Progress circular', + selector: 'cps-progress-circular' + }, // { // route: '/progress-linear', // name: 'Progress linear', diff --git a/projects/composition/src/app/api-data/cps-progress-circular.json b/projects/composition/src/app/api-data/cps-progress-circular.json index 0ef173e2..50f7d7ac 100644 --- a/projects/composition/src/app/api-data/cps-progress-circular.json +++ b/projects/composition/src/app/api-data/cps-progress-circular.json @@ -10,7 +10,7 @@ "optional": false, "readonly": false, "type": "number | string", - "default": "40", + "default": "2.5rem", "description": "Diameter of the progress bar, of type number denoting pixels or string." }, { @@ -18,7 +18,7 @@ "optional": false, "readonly": false, "type": "number | string", - "default": "4", + "default": "0.25rem", "description": "Thickness of the progress bar, of type number denoting pixels or string." }, { @@ -28,6 +28,14 @@ "type": "string", "default": "calm", "description": "Color of the progress bar." + }, + { + "name": "ariaLabel", + "optional": false, + "readonly": false, + "type": "string", + "default": "Loading", + "description": "Accessible label announced by screen readers to describe what is loading.\nFalls back to \"Loading\" when empty value is provided." } ] } diff --git a/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.html b/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.html index 5304859f..a1bff212 100644 --- a/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.html +++ b/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.html @@ -1,23 +1,26 @@ -
- This is not a page loader!
- If you need to show a spinner while page content is loading, use - Loader component +
+ +
+ This is not a page loader!
+ For a loading spinner, check out the + Loader component +
- + diameter="7.5rem" + strokeWidth="0.5rem"> + + diameter="1.875rem" + strokeWidth="0.1875rem"> + diameter="0.9375rem" + strokeWidth="0.125rem">
diff --git a/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.scss b/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.scss index 646546f9..d5a182c1 100644 --- a/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.scss +++ b/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.scss @@ -1,12 +1,24 @@ .progr-circ-group { align-items: center; - gap: 48px; + gap: 3rem; display: flex; overflow: hidden; } -.progr-circ-page-warning { - margin-bottom: 36px; - font-size: 20px; +.progr-circ-page-info-card { + display: inline-flex; + align-items: center; + gap: 0.75rem; + padding: 1rem 1.25rem; + margin-bottom: 2.25rem; + border-radius: 0.5rem; + border-left: 0.25rem solid var(--cps-color-info); + background: var(--cps-color-info-highlighten); color: var(--cps-color-text-darkest); + font-size: 1rem; + + a { + color: var(--cps-color-info-darken1); + font-weight: 600; + } } diff --git a/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.ts b/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.ts index 596c1f3e..edba8620 100644 --- a/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.ts +++ b/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.ts @@ -1,11 +1,12 @@ import { Component } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { CpsProgressCircularComponent } from 'cps-ui-kit'; +import { CpsIconComponent, CpsProgressCircularComponent } from 'cps-ui-kit'; import { ComponentDocsViewerComponent } from '../../components/component-docs-viewer/component-docs-viewer.component'; import ComponentData from '../../api-data/cps-progress-circular.json'; @Component({ imports: [ + CpsIconComponent, CpsProgressCircularComponent, ComponentDocsViewerComponent, RouterModule diff --git a/projects/cps-ui-kit/src/lib/components/cps-button/cps-button.component.html b/projects/cps-ui-kit/src/lib/components/cps-button/cps-button.component.html index bbfb0140..ba8ebc0f 100644 --- a/projects/cps-ui-kit/src/lib/components/cps-button/cps-button.component.html +++ b/projects/cps-ui-kit/src/lib/components/cps-button/cps-button.component.html @@ -22,7 +22,7 @@ + strokeWidth="0.125rem"> }
diff --git a/projects/cps-ui-kit/src/lib/components/cps-menu/cps-menu.component.html b/projects/cps-ui-kit/src/lib/components/cps-menu/cps-menu.component.html index 6af8d051..27afcac5 100644 --- a/projects/cps-ui-kit/src/lib/components/cps-menu/cps-menu.component.html +++ b/projects/cps-ui-kit/src/lib/components/cps-menu/cps-menu.component.html @@ -132,9 +132,9 @@ } @else { + diameter="1rem"> } diff --git a/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.html b/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.html index 7f7f30a2..4a7c11ce 100644 --- a/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.html +++ b/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.html @@ -1,4 +1,5 @@ + [style.width]="cvtDiameter" + [style.border]="`${cvtStrokeWidth} solid ${cvtColor}`"> diff --git a/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.ts b/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.ts index 253cd434..04980b9c 100644 --- a/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.ts +++ b/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.ts @@ -1,5 +1,15 @@ import { DOCUMENT } from '@angular/common'; -import { Component, Inject, Input, OnInit } from '@angular/core'; +import { + AfterViewInit, + Component, + HostAttributeToken, + Input, + OnInit, + ElementRef, + Renderer2, + inject, + type SimpleChanges +} from '@angular/core'; import { convertSize } from '../../utils/internal/size-utils'; import { getCSSColor } from '../../utils/colors-utils'; @@ -11,20 +21,23 @@ import { getCSSColor } from '../../utils/colors-utils'; imports: [], selector: 'cps-progress-circular', templateUrl: './cps-progress-circular.component.html', - styleUrls: ['./cps-progress-circular.component.scss'] + styleUrls: ['./cps-progress-circular.component.scss'], + host: { + role: 'progressbar' + } }) -export class CpsProgressCircularComponent implements OnInit { +export class CpsProgressCircularComponent implements OnInit, AfterViewInit { /** * Diameter of the progress bar, of type number denoting pixels or string. * @group Props */ - @Input() diameter: number | string = 40; + @Input() diameter: number | string = '2.5rem'; /** * Thickness of the progress bar, of type number denoting pixels or string. * @group Props */ - @Input() strokeWidth: number | string = 4; + @Input() strokeWidth: number | string = '0.25rem'; /** * Color of the progress bar. @@ -32,13 +45,66 @@ export class CpsProgressCircularComponent implements OnInit { */ @Input() color = 'calm'; - // eslint-disable-next-line no-useless-constructor - constructor(@Inject(DOCUMENT) private document: Document) {} + /** + * Accessible label announced by screen readers to describe what is loading. + * Falls back to "Loading" when empty value is provided. + * @group Props + * @default Loading + */ + @Input() ariaLabel = ''; + + private readonly _elementRef = inject(ElementRef); + private readonly _document = inject(DOCUMENT); + private readonly _renderer = inject(Renderer2); + private readonly _staticAriaLabel = inject( + new HostAttributeToken('aria-label'), + { optional: true } + ); + + cvtDiameter = ''; + cvtStrokeWidth = ''; + cvtColor = ''; ngOnInit(): void { - this.diameter = convertSize(this.diameter); - this.strokeWidth = convertSize(this.strokeWidth); + this.cvtDiameter = convertSize(this.diameter); + this.cvtStrokeWidth = convertSize(this.strokeWidth); + this.cvtColor = getCSSColor(this.color, this._document); + this._applyAriaLabel(); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes.ariaLabel) { + this._applyAriaLabel(); + } + if (changes.diameter) { + this.cvtDiameter = convertSize(this.diameter); + } + if (changes.strokeWidth) { + this.cvtStrokeWidth = convertSize(this.strokeWidth); + } + if (changes.color) { + this.cvtColor = getCSSColor(this.color, this._document); + } + } + + ngAfterViewInit(): void { + if (!this._elementRef.nativeElement.getAttribute('aria-label')) { + this._renderer.setAttribute( + this._elementRef.nativeElement, + 'aria-label', + 'Loading' + ); + } + } - this.color = getCSSColor(this.color, this.document); + private _applyAriaLabel(): void { + const label = this.ariaLabel || this._staticAriaLabel; + if (label) { + this._renderer.setAttribute( + this._elementRef.nativeElement, + 'aria-label', + label + ); + } } } From b33aecb1088364a7fb2640e7bf2e1f6a83245b7f Mon Sep 17 00:00:00 2001 From: Andrei Fateev Date: Thu, 11 Jun 2026 18:17:20 +0200 Subject: [PATCH 2/4] update uts --- .../cps-progress-circular.component.spec.ts | 48 +++++++++++++++---- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.spec.ts b/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.spec.ts index 260e5a85..08349c72 100644 --- a/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.spec.ts +++ b/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.spec.ts @@ -20,33 +20,33 @@ describe('CpsProgressCircularComponent', () => { }); it('should have default values', () => { - expect(component.diameter).toBe('40px'); - expect(component.strokeWidth).toBe('4px'); + expect(component.diameter).toBe('2.5rem'); + expect(component.strokeWidth).toBe('0.25rem'); expect(component.color).toBeTruthy(); }); - it('should convert diameter on init', () => { + it('should convert numeric diameter on init', () => { component.diameter = 50; component.ngOnInit(); - expect(component.diameter).toBe('50px'); + expect(component.cvtDiameter).toBe('50px'); }); - it('should keep diameter as string if already string', () => { + it('should keep string diameter as-is on init', () => { component.diameter = '2rem'; component.ngOnInit(); - expect(component.diameter).toBe('2rem'); + expect(component.cvtDiameter).toBe('2rem'); }); - it('should convert strokeWidth on init', () => { + it('should convert numeric strokeWidth on init', () => { component.strokeWidth = 5; component.ngOnInit(); - expect(component.strokeWidth).toBe('5px'); + expect(component.cvtStrokeWidth).toBe('5px'); }); - it('should keep strokeWidth as string if already string', () => { + it('should keep string strokeWidth as-is on init', () => { component.strokeWidth = '0.5rem'; component.ngOnInit(); - expect(component.strokeWidth).toBe('0.5rem'); + expect(component.cvtStrokeWidth).toBe('0.5rem'); }); it('should set custom color', () => { @@ -61,4 +61,32 @@ describe('CpsProgressCircularComponent', () => { ); expect(circle).toBeTruthy(); }); + + describe('Accessibility (aria-label)', () => { + it('should default aria-label to "Loading" when no label is provided', () => { + const host: HTMLElement = fixture.nativeElement; + expect(host.getAttribute('aria-label')).toBe('Loading'); + }); + + it('should set aria-label from the ariaLabel input', () => { + fixture.componentRef.setInput('ariaLabel', 'Saving changes'); + fixture.detectChanges(); + const host: HTMLElement = fixture.nativeElement; + expect(host.getAttribute('aria-label')).toBe('Saving changes'); + }); + + it('should update aria-label when ariaLabel input changes', () => { + fixture.componentRef.setInput('ariaLabel', 'Saving changes'); + fixture.detectChanges(); + fixture.componentRef.setInput('ariaLabel', 'Uploading file'); + fixture.detectChanges(); + const host: HTMLElement = fixture.nativeElement; + expect(host.getAttribute('aria-label')).toBe('Uploading file'); + }); + + it('should have role="progressbar" on the host', () => { + const host: HTMLElement = fixture.nativeElement; + expect(host.getAttribute('role')).toBe('progressbar'); + }); + }); }); From f9f1148cb1e2a3b8f9c853b5a1f815e5818cbb78 Mon Sep 17 00:00:00 2001 From: Andrei Fateev Date: Thu, 11 Jun 2026 18:19:22 +0200 Subject: [PATCH 3/4] update text color --- .../progress-circular-page.component.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.scss b/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.scss index d5a182c1..c2387420 100644 --- a/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.scss +++ b/projects/composition/src/app/pages/progress-circular-page/progress-circular-page.component.scss @@ -18,7 +18,7 @@ font-size: 1rem; a { - color: var(--cps-color-info-darken1); + color: var(--cps-color-info-darken2); font-weight: 600; } } From 8749e8ff95fa69f0e6d0ca0a378a6cdfae0ca94d Mon Sep 17 00:00:00 2001 From: Andrei Fateev Date: Thu, 11 Jun 2026 18:22:22 +0200 Subject: [PATCH 4/4] add OnChanges --- .../cps-progress-circular/cps-progress-circular.component.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.ts b/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.ts index 04980b9c..cf0655a9 100644 --- a/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.ts +++ b/projects/cps-ui-kit/src/lib/components/cps-progress-circular/cps-progress-circular.component.ts @@ -5,6 +5,7 @@ import { HostAttributeToken, Input, OnInit, + OnChanges, ElementRef, Renderer2, inject, @@ -26,7 +27,9 @@ import { getCSSColor } from '../../utils/colors-utils'; role: 'progressbar' } }) -export class CpsProgressCircularComponent implements OnInit, AfterViewInit { +export class CpsProgressCircularComponent + implements OnInit, OnChanges, AfterViewInit +{ /** * Diameter of the progress bar, of type number denoting pixels or string. * @group Props