From 03ac71523432e2c8d301f13139ecf87ed4cb2e4e Mon Sep 17 00:00:00 2001 From: sivanova Date: Thu, 23 Oct 2025 11:38:05 +0300 Subject: [PATCH 1/3] feat(badge): design enhancement --- .../igniteui-angular/src/lib/badge/README.md | 20 +++- .../src/lib/badge/badge.component.spec.ts | 31 +++++- .../src/lib/badge/badge.component.ts | 18 +++- .../components/badge/_badge-component.scss | 4 + .../styles/components/badge/_badge-theme.scss | 63 ++++++++--- .../core/styles/typography/_bootstrap.scss | 2 +- .../lib/core/styles/typography/_fluent.scss | 2 +- .../lib/core/styles/typography/_indigo.scss | 4 +- .../lib/core/styles/typography/_material.scss | 2 +- src/app/badge/badge.sample.html | 101 +++++++++++++++--- src/app/badge/badge.sample.scss | 46 +++++++- src/app/badge/badge.sample.ts | 10 +- 12 files changed, 259 insertions(+), 44 deletions(-) diff --git a/projects/igniteui-angular/src/lib/badge/README.md b/projects/igniteui-angular/src/lib/badge/README.md index cd6e11958ac..aefde3b2065 100644 --- a/projects/igniteui-angular/src/lib/badge/README.md +++ b/projects/igniteui-angular/src/lib/badge/README.md @@ -1,7 +1,7 @@ # igx-badge The **igx-badge** component is an absolutely positioned element that can be used in tandem with other components such as avatars, navigation menus, or anywhere else in an app where some active indication is required. -With the igx-badge you can display active count or an icon in several different predefined styles. +With the igx-badge you can display active count or an icon in several different predefined styles and sizes. A walkthrough of how to get started can be found [here](https://www.infragistics.com/products/ignite-ui-angular/angular/components/badge.html) # Usage @@ -14,9 +14,12 @@ A walkthrough of how to get started can be found [here](https://www.infragistics |:----------|:-------------:|:------| | `id` | string | Unique identifier of the component. If not provided it will be automatically generated.| | `type` | string | Set the type of the badge to either `primary`, `info`, `success`, `warning`, or `error`. This will change the background color of the badge according to the values set in the default theme. | +| `dot` | boolean | Set whether the badge is displayed as a minimal dot indicator without any content. Default is `false`. | | `position` | string | Set the position of the badge relative to its parent container to either `top-right`, `top-left`, `bottom-right`, or `bottom-left`. | | `value` | string | Set the value to be displayed inside the badge. | | `icon` | string | Set an icon for the badge from the material icons set. Will not be displayed if `value` for the badge is already set. | +| `outlined` | boolean | Set whether the badge should have an outline. Default is `false`. | +| `shape` | string | Set the shape of the badge to either `rounded` or `square`. Default is `rounded`. | # Examples @@ -26,3 +29,18 @@ Using `igx-badge` with the `igx-avatar` component to show active status. ``` + +Using `igx-badge` as a dot indicator for notifications. +```html + + +``` + +Using different badge types. +```html + + + + + +``` \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/badge/badge.component.spec.ts b/projects/igniteui-angular/src/lib/badge/badge.component.spec.ts index d6654f0974e..541c38a0cbd 100644 --- a/projects/igniteui-angular/src/lib/badge/badge.component.spec.ts +++ b/projects/igniteui-angular/src/lib/badge/badge.component.spec.ts @@ -11,7 +11,8 @@ describe('Badge', () => { InitBadgeWithDefaultsComponent, InitBadgeWithIconComponent, IgxBadgeComponent, - InitBadgeWithIconARIAComponent + InitBadgeWithIconARIAComponent, + InitBadgeWithDotComponent ] }).compileComponents(); })); @@ -87,6 +88,26 @@ describe('Badge', () => { const container = fixture.nativeElement.querySelectorAll('.igx-badge')[0]; expect(container.getAttribute('aria-roledescription')).toMatch(expectedDescription); }); + + it('Initializes badge with dot property', () => { + const fixture = TestBed.createComponent(InitBadgeWithDotComponent); + fixture.detectChanges(); + const badge = fixture.componentInstance.badge; + + expect(badge.dot).toBeTruthy(); + expect(fixture.debugElement.query(By.css('.igx-badge--dot'))).toBeTruthy(); + }); + + it('Initializes success badge as dot', () => { + const fixture = TestBed.createComponent(InitBadgeWithDotComponent); + fixture.detectChanges(); + const badge = fixture.componentInstance.badge; + + expect(badge.type).toBe(IgxBadgeType.SUCCESS); + expect(badge.dot).toBeTruthy(); + expect(fixture.debugElement.query(By.css('.igx-badge--dot'))).toBeTruthy(); + expect(fixture.debugElement.query(By.css('.igx-badge--success'))).toBeTruthy(); + }); }); @Component({ @@ -120,3 +141,11 @@ class InitBadgeWithIconComponent { class InitBadgeWithIconARIAComponent { @ViewChild(IgxBadgeComponent, { static: true }) public badge: IgxBadgeComponent; } + +@Component({ + template: ``, + imports: [IgxBadgeComponent] +}) +class InitBadgeWithDotComponent { + @ViewChild(IgxBadgeComponent, { static: true }) public badge: IgxBadgeComponent; +} \ No newline at end of file diff --git a/projects/igniteui-angular/src/lib/badge/badge.component.ts b/projects/igniteui-angular/src/lib/badge/badge.component.ts index fe3990d8b12..c0cfc3a43ad 100644 --- a/projects/igniteui-angular/src/lib/badge/badge.component.ts +++ b/projects/igniteui-angular/src/lib/badge/badge.component.ts @@ -153,7 +153,9 @@ export class IgxBadgeComponent { /** @hidden @internal */ @HostBinding('class.igx-badge--square') public get _squareShape(): boolean { - return this.shape === 'square'; + if (!this.dot) { + return this.shape === 'square'; + } } /** @@ -183,6 +185,20 @@ export class IgxBadgeComponent { @HostBinding('class.igx-badge--outlined') public outlined = false; + /** + * Sets/gets whether the badge is displayed as a dot. + * When true, the badge will be rendered as a minimal 8px indicator without any content. + * Default value is `false`. + * + * @example + * ```html + * + * ``` + */ + @Input({transform: booleanAttribute}) + @HostBinding('class.igx-badge--dot') + public dot = false; + /** * Defines a human-readable, accessor, author-localized description for * the `type` and the `icon` or `value` of the element. diff --git a/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-component.scss b/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-component.scss index 909374bd288..a630908f0f7 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-component.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-component.scss @@ -35,6 +35,10 @@ @extend %igx-badge--error !optional; } + @include m(dot) { + @extend %igx-badge--dot !optional; + } + @include m(outlined) { @extend %igx-badge--outlined !optional; } diff --git a/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss index 2e82deb4545..ee77fac3e47 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss @@ -10,8 +10,11 @@ $variant: map.get($theme, '_meta', 'theme'); %igx-badge-display { - --size: #{rem(22px)}; - --_badge-size: var(--size); + @include sizable(); + + --component-size: var(--ig-size, #{var-get($theme, 'default-size')}); + --badge-size: var(--component-size); + --_badge-size: #{var-get($theme, 'size')}; display: inline-flex; justify-content: center; @@ -25,18 +28,18 @@ overflow: hidden; igx-icon { - --size: var(--igx-icon-size, calc(var(--_badge-size) / 2)); + --size: var(--igx-icon-size, #{sizable(rem(12px), rem(14px), rem(16px))}); + --component-size: var(--badge-size); display: inline-flex; justify-content: center; align-items: center; - font-weight: 400; color: var-get($theme, 'icon-color'); } @if $variant == 'indigo' { igx-icon { - $icon-size: rem(12px); + $icon-size: sizable(rem(8px), rem(10px), rem(12px)); --ig-icon-size: #{$icon-size}; --igx-icon-size: #{$icon-size}; @@ -45,7 +48,7 @@ } %igx-badge--outlined { - box-shadow: inset 0 0 0 rem(if($variant != 'bootstrap', 2px, 1px)) var-get($theme, 'border-color'); + box-shadow: 0 0 0 rem(2px) var-get($theme, 'border-color'); } %igx-badge--square { @@ -54,23 +57,42 @@ %igx-badge-value { white-space: nowrap; - padding-inline: pad-inline(rem(4px)); + padding-inline: pad-inline(rem(4px), rem(6px), if($variant == 'indigo', rem(6px), rem(8px))); } %igx-badge--success { - background: color($color: 'success'); + background: color($color: 'success', $variant: if($variant != 'material', if($variant == 'indigo', 700, 500), 900)); } %igx-badge--info { - background: color($color: 'info'); + background: color($color: 'info', $variant: if($variant != 'material', if($variant == 'fluent', 700, 500), 800)); } %igx-badge--warn { background: color($color: 'warn'); + color: contrast-color($color: 'warn', $variant: 500); + + igx-icon { + color: contrast-color($color: 'warn', $variant: 500); + } } %igx-badge--error { - background: color($color: 'error'); + background: color($color: 'error', $variant: if($variant == 'material', 700, 500)); + color: contrast-color($color: 'error', $variant: if($variant == 'bootstrap', 100, 900)); + } + + %igx-badge--dot { + --_dot-size: #{var-get($theme, 'dot-size')}; + + min-width: var(--_dot-size); + min-height: var(--_dot-size); + padding: 0; + + igx-icon, + > * { + display: none; + } } %igx-badge--hidden { @@ -79,15 +101,28 @@ } /// Adds typography styles for the igx-badge component. -/// Uses the 'caption' category from the typographic scale. +/// Uses 'caption' and 'body-2' categories from the typographic scale. /// @group typography /// @param {Map} $categories [(text: 'caption')] - The categories from the typographic scale used for type styles. -@mixin badge-typography($categories: (text: 'caption')) { +@mixin badge-typography($categories: (text: null), $theme: null) { $text: map.get($categories, 'text'); %igx-badge-display { - @include type-style($text) { - margin: 0; + @if $text { + @include type-style($text); + } @else { + @if $theme == 'indigo' { + @include type-style('button', false) { + font-size: sizable(rem(9px), rem(10px), var(--ig-button-font-size)); + line-height: sizable(rem(12px), rem(14px), var(--ig-button-line-height)); + } + } @else { + font-size: sizable(var(--ig-caption-font-size), var(--ig-body-2-font-size), var(--ig-body-2-font-size)); + font-weight: sizable(var(--ig-caption-font-weight), var(--ig-body-2-font-weight), var(--ig-body-2-font-weight)); + line-height: sizable(var(--ig-caption-line-height), var(--ig-body-2-line-height), var(--ig-body-2-line-height)); + letter-spacing: sizable(var(--ig-caption-letter-spacing), var(--ig-body-2-letter-spacing), var(--ig-body-2-letter-spacing)); + text-transform: sizable(var(--ig-caption-text-transform), var(--ig-body-2-text-transform), var(--ig-body-2-text-transform)); + } } } } diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss index 2bb1b767151..72cd0a11978 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_bootstrap.scss @@ -36,7 +36,7 @@ @use '../components/input/file-input-theme' as *; @mixin typography($type-scale) { - @include badge-typography(); + @include badge-typography($theme: 'bootstrap'); @include banner-typography(); @include bottom-nav-typography(); @include button-typography(); diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss index 2b8a6323070..3f9ab803925 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_fluent.scss @@ -35,7 +35,7 @@ @use '../components/input/file-input-theme' as *; @mixin typography() { - @include badge-typography(); + @include badge-typography($theme: 'fluent'); @include banner-typography($categories: ( message: 'caption' )); diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss index 737346995c4..5f1228dfb6e 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_indigo.scss @@ -35,9 +35,7 @@ @use '../components/input/file-input-theme' as *; @mixin typography($type-scale) { - @include badge-typography($categories: ( - text: 'button', - )); + @include badge-typography($theme: 'indigo'); @include banner-typography(); @include bottom-nav-typography(); @include button-typography(); diff --git a/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss b/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss index bc15191255c..8a6dd101db6 100644 --- a/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss +++ b/projects/igniteui-angular/src/lib/core/styles/typography/_material.scss @@ -35,7 +35,7 @@ @use '../components/input/file-input-theme' as *; @mixin typography() { - @include badge-typography(); + @include badge-typography($theme: 'material'); @include banner-typography(); @include bottom-nav-typography(); @include button-typography(); diff --git a/src/app/badge/badge.sample.html b/src/app/badge/badge.sample.html index 47e674f1fb3..e9632484e62 100644 --- a/src/app/badge/badge.sample.html +++ b/src/app/badge/badge.sample.html @@ -1,21 +1,90 @@

Angular Badge

- -
- - - bluetooth - +
+
+ + With Value +
+
+ + bluetooth + + With Icon +
+
+ + + bluetooth + + On Avatar +
+
+ +

Dot Type Badges

+
+
+ + Primary Dot +
+
+ + Info Dot +
+
+ + Success Dot +
+
+ + Warning Dot +
+
+ + Error Dot +
+
+ + + Dot on Avatar +
+
+ +

Type Variants

+
+
+ + Primary +
+
+ + Info +
+
+ + Success +
+
+ + Warning +
+
+ + Error +
@@ -40,4 +109,4 @@

WC Badge

- + \ No newline at end of file diff --git a/src/app/badge/badge.sample.scss b/src/app/badge/badge.sample.scss index 85f3261b048..8326f5c258f 100644 --- a/src/app/badge/badge.sample.scss +++ b/src/app/badge/badge.sample.scss @@ -1,16 +1,54 @@ +@use '../../../projects/igniteui-angular/src/lib/core/styles/themes/utilities' as *; + .wrapper { display: grid; grid-template-columns: repeat(2, 1fr); + gap: 48px; + padding: 24px; } .badges { - place-items: center; - display: grid; - grid-template-columns: subgrid; + place-items: start; + display: flex; + flex-direction: column; gap: 24px; } -.avatar-sample { +.sample-title { + margin: 16px 0 8px 0; + font-size: 18px; + font-weight: 600; +} + +.badge-examples { + display: flex; + flex-wrap: wrap; + gap: 32px; + align-items: center; + padding: 16px; + background: color($color: 'surface', $variant: 700); +} + +.badge-item { display: flex; + flex-direction: column; align-items: center; + gap: 8px; + position: relative; + + span { + font-size: 14px; + } } + +.avatar-sample { + display: flex; + align-items: center; + position: relative; + + igx-badge { + position: absolute; + top: -4px; + right: -4px; + } +} \ No newline at end of file diff --git a/src/app/badge/badge.sample.ts b/src/app/badge/badge.sample.ts index f8bcb45c90a..0e784d41fea 100644 --- a/src/app/badge/badge.sample.ts +++ b/src/app/badge/badge.sample.ts @@ -3,6 +3,7 @@ import { IgxBadgeComponent, IgxAvatarComponent, IgxIconComponent, + IgSizeDirective } from 'igniteui-angular'; import { defineComponents, @@ -32,11 +33,18 @@ registerIconFromText('bluetooth', bluetooth); styleUrls: ['badge.sample.scss'], templateUrl: 'badge.sample.html', schemas: [CUSTOM_ELEMENTS_SCHEMA], - imports: [IgxBadgeComponent, IgxAvatarComponent, IgxIconComponent] + imports: [IgxBadgeComponent, IgxAvatarComponent, IgxIconComponent, IgSizeDirective] }) export class BadgeSampleComponent { public panelConfig: PropertyPanelConfig = { + size: { + control: { + type: 'button-group', + options: ['small', 'medium', 'large'], + defaultValue: 'medium' + } + }, shape: { control: { type: 'button-group', From 06a5ec4f9426b417de649c8340c08c028f88709a Mon Sep 17 00:00:00 2001 From: Simeon Simeonoff Date: Thu, 23 Oct 2025 14:04:20 +0300 Subject: [PATCH 2/3] refactor(demos): update badge sample --- src/app/badge/badge.sample.html | 2 +- src/app/badge/badge.sample.scss | 30 +++++++++++++++--------------- src/styles/_app-layout.scss | 1 - 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/app/badge/badge.sample.html b/src/app/badge/badge.sample.html index e9632484e62..5240855c490 100644 --- a/src/app/badge/badge.sample.html +++ b/src/app/badge/badge.sample.html @@ -109,4 +109,4 @@

WC Badge

- \ No newline at end of file + diff --git a/src/app/badge/badge.sample.scss b/src/app/badge/badge.sample.scss index 8326f5c258f..126f27254d0 100644 --- a/src/app/badge/badge.sample.scss +++ b/src/app/badge/badge.sample.scss @@ -2,9 +2,8 @@ .wrapper { display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 48px; - padding: 24px; + gap: rem(48px); + padding: rem(24px); } .badges { @@ -14,19 +13,14 @@ gap: 24px; } -.sample-title { - margin: 16px 0 8px 0; - font-size: 18px; - font-weight: 600; -} - .badge-examples { display: flex; flex-wrap: wrap; - gap: 32px; + gap: rem(32px); align-items: center; - padding: 16px; - background: color($color: 'surface', $variant: 700); + padding: rem(16px); + background: color($color: 'gray', $variant: 100); + border-radius: rem(8px); } .badge-item { @@ -46,9 +40,15 @@ align-items: center; position: relative; + igx-avatar { + anchor-name: --avatar; + } + igx-badge { position: absolute; - top: -4px; - right: -4px; + position-anchor: --avatar; + bottom: anchor(--avatar top); + left: anchor(right); + transform: translate(-75%, 75%); } -} \ No newline at end of file +} diff --git a/src/styles/_app-layout.scss b/src/styles/_app-layout.scss index b8f1cb11ada..4626008c618 100644 --- a/src/styles/_app-layout.scss +++ b/src/styles/_app-layout.scss @@ -81,7 +81,6 @@ &-title { @include type-style('h6'); - margin-bottom: 24px; .light { font-weight: 400; From eb7c368f2a3f1176d051ae564252b3fc332e05ff Mon Sep 17 00:00:00 2001 From: sivanova Date: Wed, 29 Oct 2025 15:36:36 +0200 Subject: [PATCH 3/3] fix(badge): indigo warn state --- .../styles/components/badge/_badge-theme.scss | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss b/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss index ee77fac3e47..82402c29970 100644 --- a/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss +++ b/projects/igniteui-angular/src/lib/core/styles/components/badge/_badge-theme.scss @@ -6,7 +6,7 @@ /// @param {Map} $theme - The theme used to style the component. @mixin badge($theme) { @include css-vars($theme); - + $theme-variant: map.get($theme, '_meta', 'variant'); $variant: map.get($theme, '_meta', 'theme'); %igx-badge-display { @@ -70,10 +70,25 @@ %igx-badge--warn { background: color($color: 'warn'); - color: contrast-color($color: 'warn', $variant: 500); - igx-icon { + @if $variant == 'indigo' and $theme-variant == 'light' { + color: color($color: 'gray', $variant: 900); + + igx-icon { + color: color($color: 'gray', $variant: 900); + } + } @else if $variant == 'indigo' and $theme-variant == 'dark' { + color: color($color: 'gray', $variant: 50); + + igx-icon { + color: color($color: 'gray', $variant: 50); + } + } @else { color: contrast-color($color: 'warn', $variant: 500); + + igx-icon { + color: contrast-color($color: 'warn', $variant: 500); + } } }