Merge pull request #3961 from dpalou/MOBILE-3947
MOBILE-3947 ui: Display Cancel/Done buttons in datetimemain
commit
275089850e
|
@ -1211,4 +1211,13 @@ class behat_app extends behat_app_helper {
|
||||||
$this->resize_app_window($width, $height);
|
$this->resize_app_window($width, $height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until Toast disappears.
|
||||||
|
*
|
||||||
|
* @When I wait toast to dismiss in the app
|
||||||
|
*/
|
||||||
|
public function i_wait_toast_to_dismiss_in_the_app() {
|
||||||
|
$this->runtime_js('waitToastDismiss()');
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,13 @@
|
||||||
<!-- Date. -->
|
<!-- Date. -->
|
||||||
<ion-item class="ion-text-wrap">
|
<ion-item class="ion-text-wrap">
|
||||||
<ion-label position="stacked">
|
<ion-label position="stacked">
|
||||||
<p class="item-heading" [core-mark-required]="true">{{ 'core.date' | translate }}</p>
|
<label class="item-heading" [core-mark-required]="true" for="timestart-button">{{ 'core.date' | translate }}</label>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-datetime-button datetime="timestart" />
|
<ion-datetime-button datetime="timestart" id="timestart-button" />
|
||||||
<ion-modal [keepContentsMounted]="true">
|
<ion-modal [keepContentsMounted]="true">
|
||||||
<ng-template>
|
<ng-template>
|
||||||
<ion-datetime id="timestart" formControlName="timestart" presentation="date-time" [max]="maxDate" [min]="minDate">
|
<ion-datetime id="timestart" formControlName="timestart" presentation="date-time" [max]="maxDate" [min]="minDate"
|
||||||
|
[showDefaultButtons]="true">
|
||||||
<span slot="title">{{'core.date' | translate}}</span>
|
<span slot="title">{{'core.date' | translate}}</span>
|
||||||
</ion-datetime>
|
</ion-datetime>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -146,16 +147,16 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-radio [value]="1">
|
<ion-radio [value]="1">
|
||||||
<p>{{ 'addon.calendar.durationuntil' | translate }}</p>
|
<p><label for="timedurationuntil-button">{{ 'addon.calendar.durationuntil' | translate }}</label></p>
|
||||||
</ion-radio>
|
</ion-radio>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item *ngIf="form.controls.duration.value === 1">
|
<ion-item *ngIf="form.controls.duration.value === 1">
|
||||||
<ion-label position="stacked" />
|
<ion-label position="stacked" />
|
||||||
<ion-datetime-button datetime="timedurationuntil" />
|
<ion-datetime-button datetime="timedurationuntil" id="timedurationuntil-button" />
|
||||||
<ion-modal [keepContentsMounted]="true">
|
<ion-modal [keepContentsMounted]="true">
|
||||||
<ng-template>
|
<ng-template>
|
||||||
<ion-datetime id="timedurationuntil" formControlName="timedurationuntil" [max]="maxDate" [min]="minDate"
|
<ion-datetime id="timedurationuntil" formControlName="timedurationuntil" [max]="maxDate" [min]="minDate"
|
||||||
presentation="date-time">
|
presentation="date-time" [showDefaultButtons]="true">
|
||||||
<span slot="title">{{'addon.calendar.durationuntil' | translate}}</span>
|
<span slot="title">{{'addon.calendar.durationuntil' | translate}}</span>
|
||||||
</ion-datetime>
|
</ion-datetime>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
@ -163,11 +164,11 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item>
|
<ion-item>
|
||||||
<ion-radio [value]="2">
|
<ion-radio [value]="2">
|
||||||
<p id="durationinminutes">{{ 'addon.calendar.durationminutes' | translate }}</p>
|
<p><label for="timedurationminutes">{{ 'addon.calendar.durationminutes' | translate }}</label></p>
|
||||||
</ion-radio>
|
</ion-radio>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item *ngIf="form.controls.duration.value === 2">
|
<ion-item *ngIf="form.controls.duration.value === 2">
|
||||||
<ion-input type="number" name="timedurationminutes" labelPlacement="start" aria-labelledby="durationinminutes"
|
<ion-input type="number" name="timedurationminutes" labelPlacement="start" id="timedurationminutes"
|
||||||
[placeholder]="'addon.calendar.durationminutes' | translate" formControlName="timedurationminutes" />
|
[placeholder]="'addon.calendar.durationminutes' | translate" formControlName="timedurationminutes" />
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ion-radio-group>
|
</ion-radio-group>
|
||||||
|
|
|
@ -26,8 +26,9 @@ Feature: Test creation of calendar events in app
|
||||||
When I press "More" in the app
|
When I press "More" in the app
|
||||||
And I press "Calendar" in the app
|
And I press "Calendar" in the app
|
||||||
And I press "New event" in the app
|
And I press "New event" in the app
|
||||||
|
Then I should find "## now ##%b %e, %Y##" in the app
|
||||||
# Flaky step, sometimes it fails due to minute change when checking.
|
# Flaky step, sometimes it fails due to minute change when checking.
|
||||||
Then the field "Date" matches value "## now ##%Y-%m-%dT%H:%M##" in the app
|
And I should find "## now ##%l:%M %p##" in the app
|
||||||
And I should not be able to press "Save" in the app
|
And I should not be able to press "Save" in the app
|
||||||
|
|
||||||
# Check that student can only create User events.
|
# Check that student can only create User events.
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<ion-modal [keepContentsMounted]="true">
|
<ion-modal [keepContentsMounted]="true">
|
||||||
<ng-template>
|
<ng-template>
|
||||||
<ion-datetime id="datetime" [formControlName]="'f_'+field.id" [max]="maxDate" [min]="minDate"
|
<ion-datetime id="datetime" [formControlName]="'f_'+field.id" [max]="maxDate" [min]="minDate"
|
||||||
[disabled]="searchMode && !searchFields['f_'+field.id+'_z']" presentation="date" />
|
[disabled]="searchMode && !searchFields['f_'+field.id+'_z']" presentation="date" [showDefaultButtons]="true" />
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</ion-modal>
|
</ion-modal>
|
||||||
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
|
<core-input-errors *ngIf="error && editMode" [control]="form.controls['f_'+field.id]" [errorText]="error" />
|
||||||
|
|
|
@ -13,16 +13,22 @@
|
||||||
<!-- Edit. -->
|
<!-- Edit. -->
|
||||||
<ion-item *ngIf="edit && field && field.shortname && form" class="ion-text-wrap" [formGroup]="form">
|
<ion-item *ngIf="edit && field && field.shortname && form" class="ion-text-wrap" [formGroup]="form">
|
||||||
<ion-label position="stacked">
|
<ion-label position="stacked">
|
||||||
<span [core-mark-required]="required">
|
<label [core-mark-required]="required" for="profile-field-datetime-{{field.shortname}}-button">
|
||||||
<core-format-text [text]="field.name" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId"
|
<core-format-text [text]="field.name" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId"
|
||||||
[courseId]="courseId" [wsNotFiltered]="true" />
|
[courseId]="courseId" [wsNotFiltered]="true" />
|
||||||
</span>
|
</label>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
|
|
||||||
<ion-datetime-button datetime="datetime" />
|
<ion-datetime-button datetime="profile-field-datetime-{{field.shortname}}" id="profile-field-datetime-{{field.shortname}}-button">
|
||||||
|
<ng-container *ngIf="control?.value === undefined">
|
||||||
|
<span slot="date-target">{{ 'core.choosedots' | translate }}</span>
|
||||||
|
<span slot="time-target">{{ 'core.choosedots' | translate }}</span>
|
||||||
|
</ng-container>
|
||||||
|
</ion-datetime-button>
|
||||||
<ion-modal [keepContentsMounted]="true">
|
<ion-modal [keepContentsMounted]="true">
|
||||||
<ng-template>
|
<ng-template>
|
||||||
<ion-datetime id="datetime" [formControlName]="modelName" [presentation]="ionDateTimePresentation" [max]="max" [min]="min">
|
<ion-datetime id="profile-field-datetime-{{field.shortname}}" [formControlName]="modelName"
|
||||||
|
[presentation]="ionDateTimePresentation" [max]="max" [min]="min" [showDefaultButtons]="true">
|
||||||
<span slot="title">
|
<span slot="title">
|
||||||
<core-format-text [text]="field.name" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId"
|
<core-format-text [text]="field.name" [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId"
|
||||||
[courseId]="courseId" [wsNotFiltered]="true" />
|
[courseId]="courseId" [wsNotFiltered]="true" />
|
||||||
|
|
|
@ -86,7 +86,7 @@ export class AddonUserProfileFieldDatetimeComponent extends CoreUserProfileField
|
||||||
*/
|
*/
|
||||||
protected createFormControl(field: AuthEmailSignupProfileField): FormControl<string | undefined> {
|
protected createFormControl(field: AuthEmailSignupProfileField): FormControl<string | undefined> {
|
||||||
const formData = {
|
const formData = {
|
||||||
value: field.defaultdata != '0' ? field.defaultdata : undefined,
|
value: field.defaultdata && field.defaultdata !== '0' ? field.defaultdata : undefined,
|
||||||
disabled: this.disabled,
|
disabled: this.disabled,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -23,19 +23,19 @@ Feature: Set a new reminder on course
|
||||||
# Default set
|
# Default set
|
||||||
When I press "Set a reminder for \"Course 1\" (Course end date)" in the app
|
When I press "Set a reminder for \"Course 1\" (Course end date)" in the app
|
||||||
Then I should find "Reminder set for " in the app
|
Then I should find "Reminder set for " in the app
|
||||||
And I should find "Reminder set for" in the app
|
|
||||||
|
|
||||||
# Set from list
|
# Set from list
|
||||||
When I press "Set a reminder for \"Course 1\" (Course end date)" in the app
|
When I wait toast to dismiss in the app
|
||||||
|
And I press "Set a reminder for \"Course 1\" (Course end date)" in the app
|
||||||
Then I should find "Set a reminder" in the app
|
Then I should find "Set a reminder" in the app
|
||||||
And "At the time of the event" should be selected in the app
|
And "At the time of the event" should be selected in the app
|
||||||
But "12 hours before" should not be selected in the app
|
But "12 hours before" should not be selected in the app
|
||||||
When I press "12 hours before" in the app
|
When I press "12 hours before" in the app
|
||||||
Then I should find "Reminder set for " in the app
|
Then I should find "Reminder set for " in the app
|
||||||
And I should find "Reminder set for" in the app
|
|
||||||
|
|
||||||
# Custom set
|
# Custom set
|
||||||
When I press "Set a reminder for \"Course 1\" (Course end date)" in the app
|
When I wait toast to dismiss in the app
|
||||||
|
And I press "Set a reminder for \"Course 1\" (Course end date)" in the app
|
||||||
Then I should find "Set a reminder" in the app
|
Then I should find "Set a reminder" in the app
|
||||||
And "At the time of the event" should not be selected in the app
|
And "At the time of the event" should not be selected in the app
|
||||||
But "12 hours before" should be selected in the app
|
But "12 hours before" should be selected in the app
|
||||||
|
@ -46,10 +46,10 @@ Feature: Set a new reminder on course
|
||||||
| Units | hours |
|
| Units | hours |
|
||||||
And I press "Set reminder" in the app
|
And I press "Set reminder" in the app
|
||||||
Then I should find "Reminder set for " in the app
|
Then I should find "Reminder set for " in the app
|
||||||
And I should find "Reminder set for" in the app
|
|
||||||
|
|
||||||
# Remove
|
# Remove
|
||||||
When I press "Set a reminder for \"Course 1\" (Course end date)" in the app
|
When I wait toast to dismiss in the app
|
||||||
|
And I press "Set a reminder for \"Course 1\" (Course end date)" in the app
|
||||||
Then "2 hours before" should be selected in the app
|
Then "2 hours before" should be selected in the app
|
||||||
When I press "Delete reminder" in the app
|
When I press "Delete reminder" in the app
|
||||||
Then I should find "Reminder deleted" in the app
|
Then I should find "Reminder deleted" in the app
|
||||||
|
|
|
@ -376,6 +376,11 @@ export class TestingBehatDomUtilsService {
|
||||||
|
|
||||||
containers = containers
|
containers = containers
|
||||||
.filter(container => {
|
.filter(container => {
|
||||||
|
if (!this.isElementVisible(container)) {
|
||||||
|
// Ignore containers not visible.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (container.tagName === 'ION-ALERT') {
|
if (container.tagName === 'ION-ALERT') {
|
||||||
// For some reason, in Behat sometimes alerts aren't removed from DOM, the close animation doesn't finish.
|
// For some reason, in Behat sometimes alerts aren't removed from DOM, the close animation doesn't finish.
|
||||||
// Filter alerts with pointer-events none since that style is set before the close animation starts.
|
// Filter alerts with pointer-events none since that style is set before the close animation starts.
|
||||||
|
@ -453,7 +458,16 @@ export class TestingBehatDomUtilsService {
|
||||||
const inputId = label.getAttribute('for');
|
const inputId = label.getAttribute('for');
|
||||||
|
|
||||||
if (inputId) {
|
if (inputId) {
|
||||||
return document.getElementById(inputId) || undefined;
|
const element = document.getElementById(inputId) || undefined;
|
||||||
|
if (element?.tagName !== 'ION-DATETIME-BUTTON') {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search the ion-datetime associated with the button.
|
||||||
|
const datetimeId = (<HTMLIonDatetimeButtonElement> element).datetime;
|
||||||
|
const datetime = document.querySelector<HTMLElement>(`ion-datetime#${datetimeId}`);
|
||||||
|
|
||||||
|
return datetime || undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
input = this.getShadowDOMHost(label) || undefined;
|
input = this.getShadowDOMHost(label) || undefined;
|
||||||
|
@ -477,6 +491,19 @@ export class TestingBehatDomUtilsService {
|
||||||
locator: TestingBehatElementLocator,
|
locator: TestingBehatElementLocator,
|
||||||
options: TestingBehatFindOptions = {},
|
options: TestingBehatFindOptions = {},
|
||||||
): HTMLElement | undefined {
|
): HTMLElement | undefined {
|
||||||
|
// Remove extra spaces.
|
||||||
|
const treatedText = locator.text.trim().replace(/\s\s+/g, ' ');
|
||||||
|
if (treatedText !== locator.text) {
|
||||||
|
const element = this.findElementsBasedOnText({
|
||||||
|
...locator,
|
||||||
|
text: treatedText,
|
||||||
|
}, options)[0];
|
||||||
|
|
||||||
|
if (element) {
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return this.findElementsBasedOnText(locator, options)[0];
|
return this.findElementsBasedOnText(locator, options)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { CoreCustomURLSchemes, CoreCustomURLSchemesProvider } from '@services/ur
|
||||||
import { ONBOARDING_DONE } from '@features/login/constants';
|
import { ONBOARDING_DONE } from '@features/login/constants';
|
||||||
import { CoreConfig } from '@services/config';
|
import { CoreConfig } from '@services/config';
|
||||||
import { EnvironmentConfig } from '@/types/config';
|
import { EnvironmentConfig } from '@/types/config';
|
||||||
import { LocalNotifications, makeSingleton, NgZone } from '@singletons';
|
import { LocalNotifications, makeSingleton, NgZone, ToastController } from '@singletons';
|
||||||
import { CoreNetwork, CoreNetworkService } from '@services/network';
|
import { CoreNetwork, CoreNetworkService } from '@services/network';
|
||||||
import { CorePushNotifications, CorePushNotificationsProvider } from '@features/pushnotifications/services/pushnotifications';
|
import { CorePushNotifications, CorePushNotificationsProvider } from '@features/pushnotifications/services/pushnotifications';
|
||||||
import { CoreCronDelegate, CoreCronDelegateService } from '@services/cron';
|
import { CoreCronDelegate, CoreCronDelegateService } from '@services/cron';
|
||||||
|
@ -33,6 +33,7 @@ import { Swiper } from 'swiper';
|
||||||
import { LocalNotificationsMock } from '@features/emulator/services/local-notifications';
|
import { LocalNotificationsMock } from '@features/emulator/services/local-notifications';
|
||||||
import { GetClosureArgs } from '@/core/utils/types';
|
import { GetClosureArgs } from '@/core/utils/types';
|
||||||
import { CoreIframeComponent } from '@components/iframe/iframe';
|
import { CoreIframeComponent } from '@components/iframe/iframe';
|
||||||
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Behat runtime servive with public API.
|
* Behat runtime servive with public API.
|
||||||
|
@ -745,6 +746,15 @@ export class TestingBehatRuntimeService {
|
||||||
return 'OK';
|
return 'OK';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for toast to be dismissed in the app.
|
||||||
|
*
|
||||||
|
* @returns Promise resolved when toast has been dismissed.
|
||||||
|
*/
|
||||||
|
async waitToastDismiss(): Promise<void> {
|
||||||
|
await CoreUtils.ignoreErrors(ToastController.dismiss());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TestingBehatRuntime = makeSingleton(TestingBehatRuntimeService);
|
export const TestingBehatRuntime = makeSingleton(TestingBehatRuntimeService);
|
||||||
|
|
|
@ -2015,6 +2015,11 @@ ion-item.item-label-stacked ion-datetime-button {
|
||||||
align-self: self-end;
|
align-self: self-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ion-datetime-button p {
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
// Table App styles
|
// Table App styles
|
||||||
table.core-table {
|
table.core-table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
|
|
Loading…
Reference in New Issue