Skip to content

Commit d6163bb

Browse files
Merge branch 'main' into FW-634
2 parents 4769761 + 0dd0646 commit d6163bb

File tree

8 files changed

+83
-20
lines changed

8 files changed

+83
-20
lines changed

.github/workflows/actions/build-core/action.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ runs:
44
using: 'composite'
55
steps:
66
- uses: actions/checkout@v2
7+
with:
8+
# Checkout the latest commit in this branch
9+
ref: ${{ github.event.pull_request.head.sha }}
710
- uses: actions/setup-node@v1
811
with:
912
node-version: 15.x

.github/workflows/build.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ jobs:
99
runs-on: ubuntu-latest
1010
steps:
1111
- uses: actions/checkout@v2
12+
- uses: actions/checkout@v2
13+
with:
14+
# Checkout the latest commit in this branch
15+
ref: ${{ github.event.pull_request.head.sha }}
1216
- uses: ./.github/workflows/actions/build-core
1317

1418
test-core-clean-build:

angular/src/directives/control-value-accessors/value-accessor.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,17 @@ export class ValueAccessor implements ControlValueAccessor, AfterViewInit, OnDes
110110

111111
export const setIonicClasses = (element: ElementRef): void => {
112112
raf(() => {
113-
const input = element.nativeElement as HTMLElement;
113+
const input = element.nativeElement as HTMLInputElement;
114+
const hasValue = input.value != null && input.value.toString().length > 0;
114115
const classes = getClasses(input);
115116
setClasses(input, classes);
116-
117117
const item = input.closest('ion-item');
118118
if (item) {
119-
setClasses(item, classes);
119+
if (hasValue) {
120+
setClasses(item, [...classes, 'item-has-value']);
121+
} else {
122+
setClasses(item, classes);
123+
}
120124
}
121125
});
122126
};
@@ -127,21 +131,18 @@ const getClasses = (element: HTMLElement) => {
127131
for (let i = 0; i < classList.length; i++) {
128132
const item = classList.item(i);
129133
if (item !== null && startsWith(item, 'ng-')) {
130-
classes.push(`ion-${item.substr(3)}`);
134+
classes.push(`ion-${item.substring(3)}`);
131135
}
132136
}
133137
return classes;
134138
};
135139

136140
const setClasses = (element: HTMLElement, classes: string[]) => {
137141
const classList = element.classList;
138-
['ion-valid', 'ion-invalid', 'ion-touched', 'ion-untouched', 'ion-dirty', 'ion-pristine'].forEach((c) =>
139-
classList.remove(c)
140-
);
141-
142-
classes.forEach((c) => classList.add(c));
142+
classList.remove('ion-valid', 'ion-invalid', 'ion-touched', 'ion-untouched', 'ion-dirty', 'ion-pristine');
143+
classList.add(...classes);
143144
};
144145

145146
const startsWith = (input: string, search: string): boolean => {
146-
return input.substr(0, search.length) === search;
147+
return input.substring(0, search.length) === search;
147148
};

angular/test/test-app/e2e/src/modal.spec.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ describe('Modals', () => {
4343

4444
});
4545

46-
4746
describe('Modals: Inline', () => {
4847
beforeEach(() => {
4948
cy.visit('/modal-inline');
@@ -77,3 +76,29 @@ describe('Modals: Inline', () => {
7776
cy.get('ion-modal').children('.ion-page').should('not.exist');
7877
})
7978
});
79+
80+
describe('when in a modal', () => {
81+
82+
beforeEach(() => {
83+
cy.visit('/modals');
84+
cy.get('#action-button').click();
85+
cy.get('#close-modal').click();
86+
cy.get('#action-button').click();
87+
});
88+
89+
it('should render ion-item item-has-value class when control value is set', () => {
90+
cy.get('[formControlName="select"]').invoke('attr', 'value', 0);
91+
cy.get('#inputWithFloatingLabel').should('have.class', 'item-has-value');
92+
});
93+
94+
it('should not render ion-item item-has-value class when control value is undefined', () => {
95+
cy.get('[formControlName="select"]').invoke('attr', 'value', undefined);
96+
cy.get('#inputWithFloatingLabel').should('not.have.class', 'item-has-value');
97+
});
98+
99+
it('should not render ion-item item-has-value class when control value is null', () => {
100+
cy.get('[formControlName="select"]').invoke('attr', 'value', null);
101+
cy.get('#inputWithFloatingLabel').should('not.have.class', 'item-has-value');
102+
});
103+
104+
});

angular/test/test-app/src/app/modal-example/modal-example.component.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,14 @@ <h3>{{valueFromParams}}</h3>
2222
<ion-button (click)="push()" class="push-page">Push page</ion-button>
2323
<ion-button (click)="pop()" class="pop-page">Pop page</ion-button>
2424
</p>
25+
26+
<form [formGroup]="form">
27+
<ion-item id="inputWithFloatingLabel">
28+
<ion-label color="primary" position="floating">Floating Label</ion-label>
29+
<ion-select multiple="false" formControlName="select">
30+
<ion-select-option [value]="0">Option 0</ion-select-option>
31+
<ion-select-option [value]="1">Option 1</ion-select-option>
32+
</ion-select>
33+
</ion-item>
34+
</form>
2535
</ion-content>

angular/test/test-app/src/app/modal-example/modal-example.component.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Component, Input, NgZone, OnInit, Optional } from '@angular/core';
2+
import { FormControl, FormGroup } from '@angular/forms';
23
import { ModalController, NavParams, IonNav, ViewWillLeave, ViewDidEnter, ViewDidLeave } from '@ionic/angular';
34

45
@Component({
@@ -9,6 +10,10 @@ export class ModalExampleComponent implements OnInit, ViewWillLeave, ViewDidEnte
910

1011
@Input() value: string;
1112

13+
form = new FormGroup({
14+
select: new FormControl([])
15+
});
16+
1217
valueFromParams: string;
1318
onInit = 0;
1419
willEnter = 0;

core/src/components/datetime/datetime.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { ComponentInterface, EventEmitter } from '@stencil/core';
22
import { Component, Element, Event, Host, Method, Prop, State, Watch, h, writeTask } from '@stencil/core';
3+
import { printIonWarning } from '@utils/logging';
34
import { caretDownSharp, caretUpSharp, chevronBack, chevronDown, chevronForward } from 'ionicons/icons';
45

56
import { getIonMode } from '../../global/ionic-global';
@@ -298,15 +299,20 @@ export class Datetime implements ComponentInterface {
298299
* This allows us to update the current value's date/time display without
299300
* refocusing or shifting the user's display (leaves the user in place).
300301
*/
301-
const { month, day, year, hour, minute } = parseDate(this.value);
302-
this.activePartsClone = {
303-
...this.activeParts,
304-
month,
305-
day,
306-
year,
307-
hour,
308-
minute,
309-
};
302+
const valueDateParts = parseDate(this.value);
303+
if (valueDateParts) {
304+
const { month, day, year, hour, minute } = valueDateParts;
305+
this.activePartsClone = {
306+
...this.activeParts,
307+
month,
308+
day,
309+
year,
310+
hour,
311+
minute,
312+
};
313+
} else {
314+
printIonWarning(`Unable to parse date string: ${this.value}. Please provide a valid ISO 8601 datetime string.`);
315+
}
310316
}
311317

312318
this.emitStyle();

core/src/utils/logging/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/**
2+
* Logs a warning to the console with an Ionic prefix
3+
* to indicate the library that is warning the developer.
4+
*
5+
* @param message - The string message to be logged to the console.
6+
*/
7+
export const printIonWarning = (message: string) => {
8+
return console.warn(`[Ionic Warning]: ${message}`);
9+
};

0 commit comments

Comments
 (0)