From eea25d091d7eb319d6ec1de8b793881d3a10949b Mon Sep 17 00:00:00 2001 From: Sean Perkins Date: Tue, 5 Apr 2022 12:44:45 -0400 Subject: [PATCH] fix(angular): item styling when control has value (#24932) Resolves #23809 --- .../control-value-accessors/value-accessor.ts | 21 ++++++++------- angular/test/test-app/e2e/src/modal.spec.ts | 27 ++++++++++++++++++- .../modal-example.component.html | 10 +++++++ .../modal-example/modal-example.component.ts | 5 ++++ 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/angular/src/directives/control-value-accessors/value-accessor.ts b/angular/src/directives/control-value-accessors/value-accessor.ts index daa4a72685..615d736828 100644 --- a/angular/src/directives/control-value-accessors/value-accessor.ts +++ b/angular/src/directives/control-value-accessors/value-accessor.ts @@ -110,13 +110,17 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes export const setIonicClasses = (element: ElementRef): void => { raf(() => { - const input = element.nativeElement as HTMLElement; + const input = element.nativeElement as HTMLInputElement; + const hasValue = input.value != null && input.value.toString().length > 0; const classes = getClasses(input); setClasses(input, classes); - const item = input.closest('ion-item'); if (item) { - setClasses(item, classes); + if (hasValue) { + setClasses(item, [...classes, 'item-has-value']); + } else { + setClasses(item, classes); + } } }); }; @@ -127,7 +131,7 @@ const getClasses = (element: HTMLElement) => { for (let i = 0; i < classList.length; i++) { const item = classList.item(i); if (item !== null && startsWith(item, 'ng-')) { - classes.push(`ion-${item.substr(3)}`); + classes.push(`ion-${item.substring(3)}`); } } return classes; @@ -135,13 +139,10 @@ const getClasses = (element: HTMLElement) => { const setClasses = (element: HTMLElement, classes: string[]) => { const classList = element.classList; - ['ion-valid', 'ion-invalid', 'ion-touched', 'ion-untouched', 'ion-dirty', 'ion-pristine'].forEach((c) => - classList.remove(c) - ); - - classes.forEach((c) => classList.add(c)); + classList.remove('ion-valid', 'ion-invalid', 'ion-touched', 'ion-untouched', 'ion-dirty', 'ion-pristine'); + classList.add(...classes); }; const startsWith = (input: string, search: string): boolean => { - return input.substr(0, search.length) === search; + return input.substring(0, search.length) === search; }; diff --git a/angular/test/test-app/e2e/src/modal.spec.ts b/angular/test/test-app/e2e/src/modal.spec.ts index b2854cc05c..cb937fcca6 100644 --- a/angular/test/test-app/e2e/src/modal.spec.ts +++ b/angular/test/test-app/e2e/src/modal.spec.ts @@ -43,7 +43,6 @@ describe('Modals', () => { }); - describe('Modals: Inline', () => { beforeEach(() => { cy.visit('/modal-inline'); @@ -77,3 +76,29 @@ describe('Modals: Inline', () => { cy.get('ion-modal').children('.ion-page').should('not.exist'); }) }); + +describe('when in a modal', () => { + + beforeEach(() => { + cy.visit('/modals'); + cy.get('#action-button').click(); + cy.get('#close-modal').click(); + cy.get('#action-button').click(); + }); + + it('should render ion-item item-has-value class when control value is set', () => { + cy.get('[formControlName="select"]').invoke('attr', 'value', 0); + cy.get('#inputWithFloatingLabel').should('have.class', 'item-has-value'); + }); + + it('should not render ion-item item-has-value class when control value is undefined', () => { + cy.get('[formControlName="select"]').invoke('attr', 'value', undefined); + cy.get('#inputWithFloatingLabel').should('not.have.class', 'item-has-value'); + }); + + it('should not render ion-item item-has-value class when control value is null', () => { + cy.get('[formControlName="select"]').invoke('attr', 'value', null); + cy.get('#inputWithFloatingLabel').should('not.have.class', 'item-has-value'); + }); + +}); diff --git a/angular/test/test-app/src/app/modal-example/modal-example.component.html b/angular/test/test-app/src/app/modal-example/modal-example.component.html index 6562262ede..513f7ad889 100644 --- a/angular/test/test-app/src/app/modal-example/modal-example.component.html +++ b/angular/test/test-app/src/app/modal-example/modal-example.component.html @@ -22,4 +22,14 @@ Push page Pop page

+ +
+ + Floating Label + + Option 0 + Option 1 + + +
diff --git a/angular/test/test-app/src/app/modal-example/modal-example.component.ts b/angular/test/test-app/src/app/modal-example/modal-example.component.ts index 363e0db61e..a9640244c8 100644 --- a/angular/test/test-app/src/app/modal-example/modal-example.component.ts +++ b/angular/test/test-app/src/app/modal-example/modal-example.component.ts @@ -1,4 +1,5 @@ import { Component, Input, NgZone, OnInit, Optional } from '@angular/core'; +import { FormControl, FormGroup } from '@angular/forms'; import { ModalController, NavParams, IonNav, ViewWillLeave, ViewDidEnter, ViewDidLeave } from '@ionic/angular'; @Component({ @@ -9,6 +10,10 @@ export class ModalExampleComponent implements OnInit, ViewWillLeave, ViewDidEnte @Input() value: string; + form = new FormGroup({ + select: new FormControl([]) + }); + valueFromParams: string; onInit = 0; willEnter = 0;