diff --git a/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/etalons/scheduler__appointment__main-form__with-labelMode-static (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/etalons/scheduler__appointment__main-form__with-labelMode-static (fluent.blue.light).png new file mode 100644 index 000000000000..436343b8a71e Binary files /dev/null and b/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/etalons/scheduler__appointment__main-form__with-labelMode-static (fluent.blue.light).png differ diff --git a/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/etalons/scheduler__appointment__recurrence-form__with-labelMode-static (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/etalons/scheduler__appointment__recurrence-form__with-labelMode-static (fluent.blue.light).png new file mode 100644 index 000000000000..3892cd4d9df8 Binary files /dev/null and b/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/etalons/scheduler__appointment__recurrence-form__with-labelMode-static (fluent.blue.light).png differ diff --git a/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/form.visual.ts b/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/form.visual.ts index 99681154d827..0d61f2799deb 100644 --- a/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/form.visual.ts +++ b/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/form.visual.ts @@ -337,3 +337,35 @@ test.meta({ browserSize: [1500, 1500] })('Recurrence settings button should have currentDate: new Date(2021, 2, 25), }); }); + +test.meta({ browserSize: [1500, 1500] })('appointment form with labelMode=static', async (t) => { + const { takeScreenshot, compareResults } = createScreenshotsComparer(t); + + const scheduler = new Scheduler(SCHEDULER_SELECTOR); + const appointmentPopup = await scheduler.openAppointmentPopup(t, undefined, false); + + await testScreenshot( + t, + takeScreenshot, + 'scheduler__appointment__main-form__with-labelMode-static.png', + { element: appointmentPopup.contentElement }, + ); + + await t + .expect(compareResults.isValid()) + .ok(compareResults.errorMessages()); +}).before(async () => { + await createWidget('dxScheduler', { + dataSource: [], + views: ['week'], + currentView: 'week', + currentDate: new Date(2021, 2, 25), + resources: getResources(true), + editing: { + allowUpdating: true, + form: { + labelMode: 'static', + }, + }, + }); +}); diff --git a/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/recurrence-form.visual.ts b/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/recurrence-form.visual.ts index e6754d3c33b0..8652011eb263 100644 --- a/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/recurrence-form.visual.ts +++ b/e2e/testcafe-devextreme/tests/scheduler/common/appointmentForm/recurrence-form.visual.ts @@ -145,3 +145,71 @@ test.meta({ browserSize: [450, 1000] })('recurrence form on mobile screen', asyn }, }, })); + +test.meta({ browserSize: [1500, 1500] })('recurrence form with labelMode=static', async (t) => { + const { takeScreenshot, compareResults } = createScreenshotsComparer(t); + + const appointment = { + text: 'Readonly Recurrent Appointment', + startDate: new Date('2024-01-01T10:00:00'), + endDate: new Date('2024-01-01T11:00:00'), + recurrenceRule: 'FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=10', + }; + + const scheduler = new Scheduler(SCHEDULER_SELECTOR); + const appointmentPopup = await scheduler.openAppointmentPopup(t, appointment, true); + + await appointmentPopup.openRecurrenceSettings(t); + + await testScreenshot( + t, + takeScreenshot, + 'scheduler__appointment__recurrence-form__with-labelMode-static.png', + { element: appointmentPopup.contentElement }, + ); + + await t + .expect(compareResults.isValid()) + .ok(compareResults.errorMessages()); +}).before(async () => { + await createWidget('dxScheduler', { + dataSource: [], + views: ['week'], + currentView: 'week', + currentDate: new Date(2021, 2, 25), + editing: { + allowUpdating: true, + popup: { + width: 420, + height: 500, + }, + form: { + iconsShowMode: 'both', + labelMode: 'static', + items: [ + 'mainGroup', + { + name: 'recurrenceGroup', + items: [ + 'recurrenceStartDateGroup', + 'recurrenceRuleGroup', + { + name: 'recurrenceEndGroup', + items: [ + 'recurrenceEndIcon', + { + name: 'recurrenceEndEditor', + label: { + visible: true, + location: 'top', + }, + }, + ], + }, + ], + }, + ], + }, + }, + }); +}); diff --git a/packages/devextreme-scss/scss/widgets/base/scheduler/_index.scss b/packages/devextreme-scss/scss/widgets/base/scheduler/_index.scss index f9b961f6ee2a..24b04b1d6359 100644 --- a/packages/devextreme-scss/scss/widgets/base/scheduler/_index.scss +++ b/packages/devextreme-scss/scss/widgets/base/scheduler/_index.scss @@ -507,21 +507,19 @@ $scheduler-appointment-form-label-padding: 20px; align-items: start; } + .dx-scheduler-form-top-label-offset { + margin-top: $scheduler-appointment-popup-icon-margin-top; + } + .dx-scheduler-form-icon { display: flex; align-items: center; - margin-top: $scheduler-appointment-popup-icon-margin-top; > .dx-field-item-content { line-height: 1; } } - .dx-scheduler-form-date-range-group .dx-scheduler-form-icon, - .dx-scheduler-form-recurrence-end-group .dx-scheduler-form-icon { - margin-top: 0; - } - /* Editors alignment */ .dx-scheduler-form-all-day-switch.dx-field-item.dx-label-h-align { align-items: center; diff --git a/packages/devextreme-scss/scss/widgets/generic/scheduler/_index.scss b/packages/devextreme-scss/scss/widgets/generic/scheduler/_index.scss index b204799a3646..4bef8ff8be1f 100644 --- a/packages/devextreme-scss/scss/widgets/generic/scheduler/_index.scss +++ b/packages/devextreme-scss/scss/widgets/generic/scheduler/_index.scss @@ -277,6 +277,10 @@ $generic-scheduler-agenda-group-header-padding: $generic-scheduler-agenda-time-c font-size: $generic-scheduler-appointment-popup-icon-size; } } + + &.dx-scheduler-form-inner-label-offset { + margin-top: $generic-scheduler-appointment-popup-icon-inner-label-margin-top; + } } .dx-scheduler-form-icon-sized-gap { diff --git a/packages/devextreme-scss/scss/widgets/generic/scheduler/_sizes.scss b/packages/devextreme-scss/scss/widgets/generic/scheduler/_sizes.scss index deac8c0cc6dc..97a54afea79d 100644 --- a/packages/devextreme-scss/scss/widgets/generic/scheduler/_sizes.scss +++ b/packages/devextreme-scss/scss/widgets/generic/scheduler/_sizes.scss @@ -33,6 +33,7 @@ $generic-scheduler-appointment-popup-icon-size: null !default; $generic-scheduler-appointment-popup-icon-container-height: null !default; $generic-scheduler-appointment-popup-icon-padding-right: null !default; $generic-scheduler-appointment-popup-icon-margin-top: null !default; +$generic-scheduler-appointment-popup-icon-inner-label-margin-top: null !default; $generic-scheduler-appointment-popup-item-padding-horizontal: null !default; $generic-scheduler-appointment-popup-item-padding-top: null !default; $generic-scheduler-appointment-popup-all-day-item-height: null !default; @@ -99,6 +100,7 @@ $generic-scheduler-group-header-agenda-font-size: 14px !default; $generic-scheduler-appointment-popup-icon-container-height: 36px !default; $generic-scheduler-appointment-popup-icon-padding-right: 5px !default; $generic-scheduler-appointment-popup-icon-margin-top: 22px !default; + $generic-scheduler-appointment-popup-icon-inner-label-margin-top: null !default; $generic-scheduler-appointment-popup-item-padding-horizontal: 5px !default; $generic-scheduler-appointment-popup-item-padding-top: 20px !default; $generic-scheduler-appointment-popup-all-day-item-height: 36px !default; @@ -167,6 +169,7 @@ $generic-scheduler-group-header-agenda-font-size: 14px !default; $generic-scheduler-appointment-popup-icon-container-height: 26px !default; $generic-scheduler-appointment-popup-icon-padding-right: 5px !default; $generic-scheduler-appointment-popup-icon-margin-top: 18px !default; + $generic-scheduler-appointment-popup-icon-inner-label-margin-top: 4.5px !default; $generic-scheduler-appointment-popup-item-padding-horizontal: 5px !default; $generic-scheduler-appointment-popup-item-padding-top: 10px !default; $generic-scheduler-appointment-popup-all-day-item-height: 24px !default; diff --git a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts index 816df5c92e08..88f091c34ee5 100644 --- a/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts +++ b/packages/devextreme/js/__internal/scheduler/appointment_popup/m_form.ts @@ -34,9 +34,14 @@ const CLASSES = { icon: 'dx-icon', hidden: 'dx-hidden', fieldItemContent: 'dx-field-item-content', + formItem: 'dx-item', + labelTop: 'dx-field-item-label-location-top', + label: 'dx-label', groupWithIcon: 'dx-scheduler-form-group-with-icon', formIcon: 'dx-scheduler-form-icon', + formIconTopLabelOffset: 'dx-scheduler-form-top-label-offset', + formIconInnerLabelOffset: 'dx-scheduler-form-inner-label-offset', defaultResourceIcon: 'dx-scheduler-default-resources-icon', mainGroup: 'dx-scheduler-form-main-group', @@ -307,6 +312,8 @@ export class AppointmentForm { this._$mainGroup = $formElement.find(`.${CLASSES.mainGroup}`); this._$recurrenceGroup = $formElement.find(`.${CLASSES.recurrenceGroup}`); + this.alignIconsWithEditors(); + onContentReady?.call(this, e); }, } as FormProperties; @@ -858,6 +865,33 @@ export class AppointmentForm { } } + private alignIconsWithEditors(): void { + const $groups = this.dxForm.$element().find(`.${CLASSES.groupWithIcon}`); + + $groups.toArray().forEach((groupElement) => { + const iconElement = groupElement.querySelector(`.${CLASSES.formIcon}`); + + const itemElements = groupElement.querySelectorAll(`.${CLASSES.formItem}`); + const firstSimpleItemElement = Array.from(itemElements) + .find((itemElement) => { + const isGroup = itemElement.querySelector(`.${CLASSES.formItem}`) !== null; + const isIcon = itemElement.querySelector(`.${CLASSES.formIcon}`) !== null; + + return !isGroup && !isIcon; + }); + + if (!firstSimpleItemElement || !iconElement) { + return; + } + + const hasTopLabel = firstSimpleItemElement.querySelector(`.${CLASSES.labelTop}`) !== null; + const hasInnerLabel = !hasTopLabel && firstSimpleItemElement.querySelector(`.${CLASSES.label}`) !== null; + + iconElement.classList.toggle(CLASSES.formIconTopLabelOffset, hasTopLabel); + iconElement.classList.toggle(CLASSES.formIconInnerLabelOffset, hasInnerLabel); + }); + } + showMainGroup(): void { this._popup.updateToolbarForMainGroup();