Skip to content

fix(input-group): label displacement in CSS grid and flex when changing height - #17872

Merged
simeonoff merged 6 commits into
masterfrom
apetrov/input-label-displacement
Oct 28, 2026
Merged

simeonoff merged 6 commits into
masterfrom
apetrov/input-label-displacement

Conversation

@adrianptrv

Copy link
Copy Markdown
Contributor

Closes #17704

The issue could be seen in all themes.

Type of Change (check all that apply):

  • Bug fix

Component(s) / Area(s) Affected:

Input Group

How Has This Been Tested?

  • Manual testing

Screenshots / Recordings

Before:
Screenshot 2026-10-26 at 10 24 20

After:
Screenshot 2026-10-26 at 10 24 00

Checklist:

  • All relevant tags have been applied to this PR

@adrianptrv
adrianptrv requested a review from simeonoff October 26, 2026 12:29
Copilot AI balanced review requested due to automatic review settings October 26, 2026 12:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@adrianptrv adrianptrv added the ❌ status: awaiting-test PRs awaiting manual verification label Oct 26, 2026
@simeonoff

Copy link
Copy Markdown
Member

The fix works for the case in the issue. One gap: the :not([class^='igx-'] > *, [class*=' igx-'] > *) exclusion also skips input groups whose parent is one of our own elements. A form laid out directly in igx-card-content (or an expansion panel body, or stepper content) still has the label jump:

<igx-card>
  <igx-card-content class="form-row"> <!-- display: grid; grid-template-columns: 1fr 1fr -->
    <igx-input-group><label igxLabel>Username</label><input igxInput /></igx-input-group>
    <igx-input-group>
      <label igxLabel>Email</label><input igxInput />
      @if (invalid) { <igx-hint>Please enter a valid email address</igx-hint> }
    </igx-input-group>
  </igx-card-content>
</igx-card>

With the hint shown, the Username label sits at 19px and the Email label at 6px, the same as on master.

I assume the exclusion is there because of the grid cell editors. They set --_bundle-row: 1 and height: 100% on the bundle, so they need their single row to stretch to the cell height. With start everywhere, the string and number editors shrink from 50px to 20px and stick to the top of the cell.

Instead of deciding by the parent's class, we can apply start everywhere and opt out only the cell editors, next to where they already override the bundle row:

--- a/projects/igniteui-angular/input-group/src/input-group/themes/_base.scss
+++ b/projects/igniteui-angular/input-group/src/input-group/themes/_base.scss
@@ -74,9 +74,7 @@
         color-scheme: var(--ig-theme-variant, light);
         min-height: var-get($theme, 'size');
 
-        &:not([class^='igx-'] > *, [class*=' igx-'] > *) {
-            align-content: start;
-        }
+        align-content: start;
 
--- a/projects/igniteui-angular/grids/themes/_base.scss
+++ b/projects/igniteui-angular/grids/themes/_base.scss
@@ -74,6 +74,9 @@
         > igx-input-group {
 	        --_bundle-row: 1;
 
+            // The editor fills the cell, so its single row has to stretch.
+            align-content: stretch;
+
             input,
             .igx-input-group__input {

.igx-grid__td is shared by all grids, so tree, hierarchical and pivot are covered. With this change, the editors (string, number, date) and the filtering row measure the same as on your branch, and the card case is fixed. The input-group, combo, select, pickers, query builder and grid editing/filtering suites pass.

Please also add a spec. This one fails on master for both cases and on the current branch for the card case:

describe('IgxInputGroup label alignment in a stretched row (#17704)', () => {
    const labelTops = (fix) => fix.debugElement.queryAll(By.css('igx-input-group'))
        .map(({ nativeElement: g }) => Math.round(g.querySelector('label').getBoundingClientRect().top - g.getBoundingClientRect().top));

    for (const container of ['div', 'card'] as const) {
        it(`keeps the neighbour's label in place when a hint appears (${container} container)`, async () => {
            const fix = TestBed.createComponent(InputGroupFormRowComponent);
            fix.componentInstance.container = container;
            fix.detectChanges();
            await fix.whenStable();
            const [userBefore] = labelTops(fix);

            fix.componentInstance.showHint = true;
            fix.detectChanges();
            await fix.whenStable();
            const [userAfter, emailAfter] = labelTops(fix);

            expect(userAfter).toBe(userBefore);
            expect(userAfter).toBe(emailAfter);
        });
    }
});

@Component({
    template: `
        <ng-template #fields>
            <igx-input-group>
                <label igxLabel>Username</label>
                <input igxInput value="john" />
            </igx-input-group>
            <igx-input-group>
                <label igxLabel>Email</label>
                <input igxInput value="not-an-email" />
                @if (showHint) {
                    <igx-hint>Please enter a valid email address</igx-hint>
                }
            </igx-input-group>
        </ng-template>
        @if (container === 'div') {
            <div class="form-row"><ng-container *ngTemplateOutlet="fields" /></div>
        } @else {
            <igx-card>
                <igx-card-content class="form-row"><ng-container *ngTemplateOutlet="fields" /></igx-card-content>
            </igx-card>
        }`,
    styles: `.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; width: 600px; }`,
    changeDetection: ChangeDetectionStrategy.Eager,
    imports: [IgxInputGroupComponent, IgxInputDirective, IgxLabelDirective, IgxHintDirective, IgxCardComponent, IgxCardContentDirective, NgTemplateOutlet]
})
class InputGroupFormRowComponent {
    public container: 'div' | 'card' = 'div';
    public showHint = false;
}

A check in grid-cell-editing.spec.ts that the editor's bundle height equals the cell height would also protect the grid opt-out.

If the scoping was for something other than the cell editors, let me know and I'll test that case too.

@simeonoff simeonoff left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment.

@adrianptrv

Copy link
Copy Markdown
Contributor Author

See comment.

About the comment, yes, the scoping was specifically for the grid cell editors. I agree that the approach of opting out the cell editors fits the fix better. I’ve made the suggested changes and added an additional check in grid-cell-editing.spec.ts.

@simeonoff
simeonoff merged commit e33aa6c into master Oct 28, 2026
7 checks passed
@simeonoff
simeonoff deleted the apetrov/input-label-displacement branch October 28, 2026 19:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

input-group ❌ status: awaiting-test PRs awaiting manual verification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

IgxInputGroup: validation hint on one input group shifts the label position of the adjacent input group in a CSS grid layout

3 participants