From f42b9657b818da483b839d3b43b4cf55552ef034 Mon Sep 17 00:00:00 2001 From: Denis Date: Thu, 19 Jan 2017 10:01:03 +0200 Subject: [PATCH] Introduce DateTimeFile and InlineDateTimeField. (#8218) * Introduce DateTimeFile and InlineDateTimeField. Fixes #8132 * Correct and provide declarative tests. * Provide a date converter and UI tests. --- .../client/ui/VAbstractCalendarPanel.java | 54 ++- .../client/ui/VAbstractDateFieldCalendar.java | 7 +- .../client/ui/VAbstractPopupCalendar.java | 11 +- .../java/com/vaadin/client/ui/VDateField.java | 4 +- .../vaadin/client/ui/VDateFieldCalendar.java | 2 +- .../client/ui/VDateTimeCalendarPanel.java | 431 ++++++++++++++++++ .../client/ui/VDateTimeFieldCalendar.java | 113 +++++ .../com/vaadin/client/ui/VPopupCalendar.java | 25 +- .../vaadin/client/ui/VPopupTimeCalendar.java | 228 +++++++++ .../AbstractInlineDateFieldConnector.java | 9 +- .../ui/datefield/DateFieldConnector.java | 6 +- .../ui/datefield/DateTimeFieldConnector.java | 87 ++++ .../datefield/InlineDateFieldConnector.java | 9 +- .../InlineDateTimeFieldConnector.java | 79 ++++ .../ui/datefield/PopupDateFieldConnector.java | 6 + .../PopupDateTimeFieldConnector.java | 32 ++ .../ui/datefield/TextualDateConnector.java | 44 +- .../LocalDateTimeToDateConverter.java | 74 +++ .../validator/DateTimeRangeValidator.java | 51 +++ .../com/vaadin/ui/AbstractLocalDateField.java | 4 + .../vaadin/ui/AbstractLocalDateTimeField.java | 118 ++++- .../java/com/vaadin/ui/DateTimeField.java | 139 ++++++ .../com/vaadin/ui/InlineDateTimeField.java | 71 +++ .../ui/declarative/DesignFormatter.java | 4 + .../DesignLocalDateTimeConverter.java | 68 +++ .../LocalDateTimeToDateConverterTest.java | 99 ++++ ...ractLocalDateTimeFieldDeclarativeTest.java | 110 +++++ .../AbstractFieldDeclarativeTest.java | 2 + .../AbstractColorPickerDeclarativeTest.java | 5 +- .../DateTimeFieldDeclarativeTest.java | 63 +++ .../InlineDateTimeFieldDeclarativeTest.java | 71 +++ .../ui/datefield/DateTimeResolution.java | 2 +- .../datefield/InlineDateTimeFieldState.java | 26 ++ .../ui/datefield/LocalDateTimeFieldState.java | 2 +- tests/screenshots | 2 +- .../customelements/DateTimeFieldElement.java | 27 ++ .../InlineDateTimeFieldElement.java | 28 ++ .../datefield/AbstractDateFieldTest.java | 9 +- .../datefield/AbstractDateTimeFieldTest.java | 130 ++++++ .../datefield/AriaDateTimeDisabled.java | 49 ++ .../datefield/CustomDateTimeFormat.java | 59 +++ .../DateFieldDayResolutionOffset.java | 25 +- .../DateTimeFieldChangeResolution.java | 63 +++ .../datefield/DateTimeFieldElementUI.java | 24 + .../datefield/DateTimeFieldFastForward.java | 39 ++ .../datefield/DateTimeFieldIsValid.java | 53 +++ .../datefield/DateTimeFieldKeyboardInput.java | 47 ++ .../datefield/DateTimeFieldReadOnly.java | 45 ++ .../datefield/DateTimeFieldTest.java | 64 +++ .../DisabledInlineDateTimeField.java | 50 ++ .../datefield/InlineDateTimeFieldTest.java | 32 ++ .../datefield/PopupDateTimeFieldStates.java | 68 +++ .../datefield/PopupTimeClosingWithEsc.java | 62 +++ .../datefield/TimePopupSelection.java | 50 ++ .../datefield/AriaDateTimeDisabledTest.java | 44 ++ .../datefield/CustomDateTimeFormatTest.java | 39 ++ .../DateFieldDayResolutionOffsetTest.java | 2 +- .../DateTimeFieldChangeResolutionTest.java | 199 ++++++++ .../datefield/DateTimeFieldElementTest.java | 26 ++ .../DateTimeFieldFastForwardTest.java | 72 +++ .../datefield/DateTimeFieldIsValidTest.java | 66 +++ .../DateTimeFieldKeyboardInputTest.java | 43 ++ .../datefield/DateTimeFieldReadOnlyTest.java | 46 ++ ...stTest.java => DateTimeFieldTestTest.java} | 6 +- .../DisabledInlineDateTimeFieldTest.java | 79 ++++ .../InlineDateTimeFieldTestTest.java | 37 ++ .../datefield/PopupDateFieldStatesTest.java | 12 + .../PopupDateTimeFieldStatesTest.java | 31 ++ .../PopupTimeClosingWithEscTest.java | 63 +++ .../datefield/TimePopupSelectionTest.java | 67 +++ 70 files changed, 3728 insertions(+), 86 deletions(-) create mode 100644 client/src/main/java/com/vaadin/client/ui/VDateTimeCalendarPanel.java create mode 100644 client/src/main/java/com/vaadin/client/ui/VDateTimeFieldCalendar.java create mode 100644 client/src/main/java/com/vaadin/client/ui/VPopupTimeCalendar.java create mode 100644 client/src/main/java/com/vaadin/client/ui/datefield/DateTimeFieldConnector.java create mode 100644 client/src/main/java/com/vaadin/client/ui/datefield/InlineDateTimeFieldConnector.java create mode 100644 client/src/main/java/com/vaadin/client/ui/datefield/PopupDateTimeFieldConnector.java create mode 100644 server/src/main/java/com/vaadin/data/converter/LocalDateTimeToDateConverter.java create mode 100644 server/src/main/java/com/vaadin/data/validator/DateTimeRangeValidator.java create mode 100644 server/src/main/java/com/vaadin/ui/DateTimeField.java create mode 100644 server/src/main/java/com/vaadin/ui/InlineDateTimeField.java create mode 100644 server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateTimeConverter.java create mode 100644 server/src/test/java/com/vaadin/tests/data/converter/LocalDateTimeToDateConverterTest.java create mode 100644 server/src/test/java/com/vaadin/tests/server/component/abstractdatefield/AbstractLocalDateTimeFieldDeclarativeTest.java create mode 100644 server/src/test/java/com/vaadin/tests/server/component/datefield/DateTimeFieldDeclarativeTest.java create mode 100644 server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateTimeFieldDeclarativeTest.java create mode 100644 shared/src/main/java/com/vaadin/shared/ui/datefield/InlineDateTimeFieldState.java create mode 100644 uitest-common/src/main/java/com/vaadin/testbench/customelements/DateTimeFieldElement.java create mode 100644 uitest-common/src/main/java/com/vaadin/testbench/customelements/InlineDateTimeFieldElement.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateTimeFieldTest.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/AriaDateTimeDisabled.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/CustomDateTimeFormat.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldChangeResolution.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldElementUI.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldFastForward.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldIsValid.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldKeyboardInput.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldReadOnly.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldTest.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/DisabledInlineDateTimeField.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/InlineDateTimeFieldTest.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/PopupDateTimeFieldStates.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/PopupTimeClosingWithEsc.java create mode 100644 uitest/src/main/java/com/vaadin/tests/components/datefield/TimePopupSelection.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/AriaDateTimeDisabledTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/CustomDateTimeFormatTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldChangeResolutionTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldElementTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldFastForwardTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldIsValidTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldKeyboardInputTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldReadOnlyTest.java rename uitest/src/test/java/com/vaadin/tests/components/datefield/{AbstractDateFieldTestTest.java => DateTimeFieldTestTest.java} (97%) create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/DisabledInlineDateTimeFieldTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/InlineDateTimeFieldTestTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/PopupDateTimeFieldStatesTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/PopupTimeClosingWithEscTest.java create mode 100644 uitest/src/test/java/com/vaadin/tests/components/datefield/TimePopupSelectionTest.java diff --git a/client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java b/client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java index 601348fea4..8a2ef4d8d5 100644 --- a/client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java +++ b/client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java @@ -421,11 +421,21 @@ public abstract class VAbstractCalendarPanel> this.resolution = resolution; } - private boolean isReadonly() { + /** + * Checks whether the widget is not editable (read-only). + * + * @return {@code true} if the widget is read-only + */ + protected boolean isReadonly() { return parent.isReadonly(); } - private boolean isEnabled() { + /** + * Checks whether the widget is enabled. + * + * @return {@code true} is the widget is enabled + */ + protected boolean isEnabled() { return parent.isEnabled(); } @@ -583,10 +593,26 @@ public abstract class VAbstractCalendarPanel> } - private DateTimeService getDateTimeService() { + /** + * Returns date time service for the widget. + * + * @see #setDateTimeService(DateTimeService) + * + * @return date time service + */ + protected DateTimeService getDateTimeService() { return dateTimeService; } + /** + * Returns the date field which this panel is attached to. + * + * @return the "parent" date field + */ + protected VDateField getDateField() { + return parent; + } + public void setDateTimeService(DateTimeService dateTimeService) { this.dateTimeService = dateTimeService; } @@ -721,7 +747,7 @@ public abstract class VAbstractCalendarPanel> setCellSpacing(0); getFlexCellFormatter().setColSpan(1, 0, 5); getFlexCellFormatter().setStyleName(1, 0, - parent.getStylePrimaryName() + "-calendarpanel-body"); + getDateField().getStylePrimaryName() + "-calendarpanel-body"); days.getFlexCellFormatter().setStyleName(headerRow, weekColumn, "v-week"); @@ -731,7 +757,8 @@ public abstract class VAbstractCalendarPanel> isShowISOWeekNumbers()); days.getRowFormatter().setStyleName(headerRow, - parent.getStylePrimaryName() + "-calendarpanel-weekdays"); + getDateField().getStylePrimaryName() + + "-calendarpanel-weekdays"); if (isShowISOWeekNumbers()) { days.getFlexCellFormatter().setStyleName(headerRow, weekColumn, @@ -739,7 +766,7 @@ public abstract class VAbstractCalendarPanel> days.getFlexCellFormatter().setStyleName(headerRow, firstWeekdayColumn, ""); days.getRowFormatter().addStyleName(headerRow, - parent.getStylePrimaryName() + getDateField().getStylePrimaryName() + "-calendarpanel-weeknumbers"); } else { days.getFlexCellFormatter().setStyleName(headerRow, weekColumn, ""); @@ -792,8 +819,8 @@ public abstract class VAbstractCalendarPanel> Date dayDate = (Date) curr.clone(); Day day = new Day(dayDate); - day.setStyleName( - parent.getStylePrimaryName() + "-calendarpanel-day"); + day.setStyleName(getDateField().getStylePrimaryName() + + "-calendarpanel-day"); if (!isDateInsideRange(dayDate, getResolution(this::isDay))) { day.addStyleDependentName(CN_OUTSIDE_RANGE); @@ -828,7 +855,8 @@ public abstract class VAbstractCalendarPanel> isShowISOWeekNumbers()); if (isShowISOWeekNumbers()) { - final String baseCssClass = parent.getStylePrimaryName() + final String baseCssClass = getDateField() + .getStylePrimaryName() + "-calendarpanel-weeknumber"; String weekCssClass = baseCssClass; @@ -880,7 +908,7 @@ public abstract class VAbstractCalendarPanel> */ protected void doRenderCalendar(boolean updateDate) { super.setStylePrimaryName( - parent.getStylePrimaryName() + "-calendarpanel"); + getDateField().getStylePrimaryName() + "-calendarpanel"); if (focusedDate == null) { Date now = new Date(); @@ -896,7 +924,7 @@ public abstract class VAbstractCalendarPanel> } final boolean needsMonth = !isYear(getResolution()); - boolean needsBody = isDay(getResolution()); + boolean needsBody = isBelowMonth(resolution); buildCalendarHeader(needsMonth); clearCalendarBody(!needsBody); if (needsBody) { @@ -1976,8 +2004,8 @@ public abstract class VAbstractCalendarPanel> } private void setLabel() { - if (parent instanceof VAbstractPopupCalendar) { - ((VAbstractPopupCalendar) parent).setFocusedDate(this); + if (getDateField() instanceof VAbstractPopupCalendar) { + ((VAbstractPopupCalendar) getDateField()).setFocusedDate(this); } } } diff --git a/client/src/main/java/com/vaadin/client/ui/VAbstractDateFieldCalendar.java b/client/src/main/java/com/vaadin/client/ui/VAbstractDateFieldCalendar.java index 4e6aa26020..a5e4733644 100644 --- a/client/src/main/java/com/vaadin/client/ui/VAbstractDateFieldCalendar.java +++ b/client/src/main/java/com/vaadin/client/ui/VAbstractDateFieldCalendar.java @@ -23,14 +23,13 @@ import com.vaadin.client.ui.VAbstractCalendarPanel.SubmitListener; /** * A client side implementation for inline date field. */ -public abstract class VAbstractDateFieldCalendar> +public abstract class VAbstractDateFieldCalendar, R extends Enum> extends VDateField { /** For internal use only. May be removed or replaced in the future. */ - public final VAbstractCalendarPanel calendarPanel; + public final PANEL calendarPanel; - public VAbstractDateFieldCalendar(VAbstractCalendarPanel panel, - R resolution) { + public VAbstractDateFieldCalendar(PANEL panel, R resolution) { super(resolution); calendarPanel = panel; calendarPanel.setParentField(this); diff --git a/client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java b/client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java index 74530835f1..3cac761272 100644 --- a/client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java +++ b/client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java @@ -17,7 +17,6 @@ package com.vaadin.client.ui; import java.util.Date; -import java.util.Locale; import com.google.gwt.aria.client.Id; import com.google.gwt.aria.client.LiveValue; @@ -63,7 +62,7 @@ import com.vaadin.shared.ui.datefield.TextualDateFieldState; * setCalendarPanel(VAbstractCalendarPanel panel) method. * */ -public abstract class VAbstractPopupCalendar> +public abstract class VAbstractPopupCalendar, R extends Enum> extends VAbstractTextualDate implements Field, ClickHandler, CloseHandler, SubPartAware { @@ -71,7 +70,7 @@ public abstract class VAbstractPopupCalendar> public final Button calendarToggle = new Button(); /** For internal use only. May be removed or replaced in the future. */ - public VAbstractCalendarPanel calendar; + public PANEL calendar; /** For internal use only. May be removed or replaced in the future. */ public final VOverlay popup; @@ -100,8 +99,7 @@ public abstract class VAbstractPopupCalendar> private final String CALENDAR_TOGGLE_ID = "popupButton"; - public VAbstractPopupCalendar(VAbstractCalendarPanel calendarPanel, - R resolution) { + public VAbstractPopupCalendar(PANEL calendarPanel, R resolution) { super(resolution); calendarToggle.setText(""); @@ -230,8 +228,7 @@ public abstract class VAbstractPopupCalendar> if (!calendar.isYear(getCurrentResolution())) { getClient().updateVariable(getId(), getResolutionVariable( - calendar.getResolution(calendar::isMonth)) - .toLowerCase(Locale.ENGLISH), + calendar.getResolution(calendar::isMonth)), newDate.getMonth() + 1, false); if (!calendar.isMonth(getCurrentResolution())) { getClient().updateVariable(getId(), diff --git a/client/src/main/java/com/vaadin/client/ui/VDateField.java b/client/src/main/java/com/vaadin/client/ui/VDateField.java index e7a49ef5be..25529fb583 100644 --- a/client/src/main/java/com/vaadin/client/ui/VDateField.java +++ b/client/src/main/java/com/vaadin/client/ui/VDateField.java @@ -230,11 +230,11 @@ public abstract class VDateField> extends FlowPanel * * @see #setCurrentDate(Map) * - * @param dateVaules + * @param dateValues * a map with date values to convert into a date * @return the date based on the dateValues map */ - protected abstract Date getDate(Map dateVaules); + protected abstract Date getDate(Map dateValues); /** * Returns all available resolutions as an array. diff --git a/client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java b/client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java index 8c3c3a805f..ec0f71c72d 100644 --- a/client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java +++ b/client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java @@ -28,7 +28,7 @@ import com.vaadin.shared.ui.datefield.DateResolution; * */ public class VDateFieldCalendar - extends VAbstractDateFieldCalendar { + extends VAbstractDateFieldCalendar { public VDateFieldCalendar() { super(GWT.create(VDateCalendarPanel.class), DateResolution.YEAR); diff --git a/client/src/main/java/com/vaadin/client/ui/VDateTimeCalendarPanel.java b/client/src/main/java/com/vaadin/client/ui/VDateTimeCalendarPanel.java new file mode 100644 index 0000000000..409afd7474 --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/VDateTimeCalendarPanel.java @@ -0,0 +1,431 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui; + +import java.util.Date; + +import com.google.gwt.event.dom.client.ChangeEvent; +import com.google.gwt.event.dom.client.ChangeHandler; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.KeyDownEvent; +import com.google.gwt.event.dom.client.KeyDownHandler; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.ListBox; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.DateTimeService; +import com.vaadin.shared.ui.datefield.DateTimeResolution; + +/** + * @author Vaadin Ltd + * @since 8.0 + */ +public class VDateTimeCalendarPanel + extends VAbstractCalendarPanel { + + private static final String SUBPART_HOUR_SELECT = "h"; + private static final String SUBPART_MINUTE_SELECT = "m"; + private static final String SUBPART_SECS_SELECT = "s"; + private static final String SUBPART_AMPM_SELECT = "ampm"; + + private TimeChangeListener timeChangeListener; + + private VTime time; + + /** + * TimeSelector is a widget consisting of list boxes that modifie the Date + * object that is given for. + * + */ + public class VTime extends FlowPanel implements ChangeHandler { + + private ListBox hours; + + private ListBox mins; + + private ListBox sec; + + private ListBox ampm; + + /** + * Constructor + */ + public VTime() { + super(); + setStyleName(VDateField.CLASSNAME + "-time"); + buildTime(); + } + + private ListBox createListBox() { + ListBox lb = new ListBox(); + lb.setStyleName("v-select"); + lb.addChangeHandler(this); + lb.addBlurHandler(VDateTimeCalendarPanel.this); + lb.addFocusHandler(VDateTimeCalendarPanel.this); + return lb; + } + + /** + * Constructs the ListBoxes and updates their value + * + * @param redraw + * Should new instances of the listboxes be created + */ + private void buildTime() { + clear(); + + hours = createListBox(); + if (getDateTimeService().isTwelveHourClock()) { + hours.addItem("12"); + for (int i = 1; i < 12; i++) { + hours.addItem((i < 10) ? "0" + i : "" + i); + } + } else { + for (int i = 0; i < 24; i++) { + hours.addItem((i < 10) ? "0" + i : "" + i); + } + } + + hours.addChangeHandler(this); + if (getDateTimeService().isTwelveHourClock()) { + ampm = createListBox(); + final String[] ampmText = getDateTimeService().getAmPmStrings(); + ampm.addItem(ampmText[0]); + ampm.addItem(ampmText[1]); + ampm.addChangeHandler(this); + } + + if (getResolution().compareTo(DateTimeResolution.MINUTE) <= 0) { + mins = createListBox(); + for (int i = 0; i < 60; i++) { + mins.addItem((i < 10) ? "0" + i : "" + i); + } + mins.addChangeHandler(this); + } + if (getResolution().compareTo(DateTimeResolution.SECOND) <= 0) { + sec = createListBox(); + for (int i = 0; i < 60; i++) { + sec.addItem((i < 10) ? "0" + i : "" + i); + } + sec.addChangeHandler(this); + } + + final String delimiter = getDateTimeService().getClockDelimeter(); + if (isReadonly()) { + int h = 0; + if (getDate() != null) { + h = getDate().getHours(); + } + if (getDateTimeService().isTwelveHourClock()) { + h -= h < 12 ? 0 : 12; + } + add(new VLabel(h < 10 ? "0" + h : "" + h)); + } else { + add(hours); + } + + if (getResolution().compareTo(DateTimeResolution.MINUTE) <= 0) { + add(new VLabel(delimiter)); + if (isReadonly()) { + final int m = mins.getSelectedIndex(); + add(new VLabel(m < 10 ? "0" + m : "" + m)); + } else { + add(mins); + } + } + if (getResolution().compareTo(DateTimeResolution.SECOND) <= 0) { + add(new VLabel(delimiter)); + if (isReadonly()) { + final int s = sec.getSelectedIndex(); + add(new VLabel(s < 10 ? "0" + s : "" + s)); + } else { + add(sec); + } + } + if (getResolution() == DateTimeResolution.HOUR) { + add(new VLabel(delimiter + "00")); // o'clock + } + if (getDateTimeService().isTwelveHourClock()) { + add(new VLabel(" ")); + if (isReadonly()) { + int i = 0; + if (getDate() != null) { + i = (getDate().getHours() < 12) ? 0 : 1; + } + add(new VLabel(ampm.getItemText(i))); + } else { + add(ampm); + } + } + + if (isReadonly()) { + return; + } + + // Update times + updateTimes(); + + ListBox lastDropDown = getLastDropDown(); + lastDropDown.addKeyDownHandler(new KeyDownHandler() { + @Override + public void onKeyDown(KeyDownEvent event) { + boolean shiftKey = event.getNativeEvent().getShiftKey(); + if (shiftKey) { + return; + } else { + int nativeKeyCode = event.getNativeKeyCode(); + if (nativeKeyCode == KeyCodes.KEY_TAB) { + onTabOut(event); + } + } + } + }); + + } + + private ListBox getLastDropDown() { + int i = getWidgetCount() - 1; + while (i >= 0) { + Widget widget = getWidget(i); + if (widget instanceof ListBox) { + return (ListBox) widget; + } + i--; + } + return null; + } + + /** + * Updates the valus to correspond to the values in value + */ + public void updateTimes() { + if (getDate() == null) { + setDate(new Date()); + } + if (getDateTimeService().isTwelveHourClock()) { + int h = getDate().getHours(); + ampm.setSelectedIndex(h < 12 ? 0 : 1); + h -= ampm.getSelectedIndex() * 12; + hours.setSelectedIndex(h); + } else { + hours.setSelectedIndex(getDate().getHours()); + } + if (getResolution().compareTo(DateTimeResolution.MINUTE) <= 0) { + mins.setSelectedIndex(getDate().getMinutes()); + } + if (getResolution().compareTo(DateTimeResolution.SECOND) <= 0) { + sec.setSelectedIndex(getDate().getSeconds()); + } + if (getDateTimeService().isTwelveHourClock()) { + ampm.setSelectedIndex(getDate().getHours() < 12 ? 0 : 1); + } + + hours.setEnabled(isEnabled()); + if (mins != null) { + mins.setEnabled(isEnabled()); + } + if (sec != null) { + sec.setEnabled(isEnabled()); + } + if (ampm != null) { + ampm.setEnabled(isEnabled()); + } + + } + + private DateTimeService getDateTimeService() { + if (VDateTimeCalendarPanel.this.getDateTimeService() == null) { + setDateTimeService(new DateTimeService()); + } + return VDateTimeCalendarPanel.this.getDateTimeService(); + } + + /* + * (non-Javadoc) VT + * + * @see + * com.google.gwt.event.dom.client.ChangeHandler#onChange(com.google.gwt + * .event.dom.client.ChangeEvent) + */ + @Override + public void onChange(ChangeEvent event) { + /* + * Value from dropdowns gets always set for the value. Like year and + * month when resolution is month or year. + */ + if (event.getSource() == hours) { + int h = hours.getSelectedIndex(); + if (getDateTimeService().isTwelveHourClock()) { + h = h + ampm.getSelectedIndex() * 12; + } + getDate().setHours(h); + if (timeChangeListener != null) { + timeChangeListener.changed(h, getDate().getMinutes(), + getDate().getSeconds(), + DateTimeService.getMilliseconds(getDate())); + } + event.preventDefault(); + event.stopPropagation(); + } else if (event.getSource() == mins) { + final int m = mins.getSelectedIndex(); + getDate().setMinutes(m); + if (timeChangeListener != null) { + timeChangeListener.changed(getDate().getHours(), m, + getDate().getSeconds(), + DateTimeService.getMilliseconds(getDate())); + } + event.preventDefault(); + event.stopPropagation(); + } else if (event.getSource() == sec) { + final int s = sec.getSelectedIndex(); + getDate().setSeconds(s); + if (timeChangeListener != null) { + timeChangeListener.changed(getDate().getHours(), + getDate().getMinutes(), s, + DateTimeService.getMilliseconds(getDate())); + } + event.preventDefault(); + event.stopPropagation(); + } else if (event.getSource() == ampm) { + final int h = hours.getSelectedIndex() + + (ampm.getSelectedIndex() * 12); + getDate().setHours(h); + if (timeChangeListener != null) { + timeChangeListener.changed(h, getDate().getMinutes(), + getDate().getSeconds(), + DateTimeService.getMilliseconds(getDate())); + } + event.preventDefault(); + event.stopPropagation(); + } + } + + } + + /** + * Dispatches an event when the panel when time is changed + */ + public interface TimeChangeListener { + + void changed(int hour, int min, int sec, int msec); + } + + /** + * The time change listener is triggered when the user changes the time. + * + * @param listener + */ + public void setTimeChangeListener(TimeChangeListener listener) { + timeChangeListener = listener; + } + + @Override + public void setDate(Date currentDate) { + doSetDate(currentDate, isTimeSelectorNeeded() && time == null, () -> { + if (isTimeSelectorNeeded()) { + time.updateTimes(); + } + }); + } + + @Override + public void setResolution(DateTimeResolution resolution) { + super.setResolution(resolution); + if (isTimeSelectorNeeded() && time != null) { + // resolution has changed => rebuild time UI + time.buildTime(); + } + } + + @Override + protected boolean acceptDayFocus() { + return getResolution().compareTo(DateTimeResolution.MONTH) < 0; + } + + @Override + protected boolean isDay(DateTimeResolution resolution) { + return DateTimeResolution.DAY.equals(resolution); + } + + @Override + protected boolean isMonth(DateTimeResolution resolution) { + return DateTimeResolution.MONTH.equals(resolution); + } + + @Override + protected boolean isBelowMonth(DateTimeResolution resolution) { + return resolution.compareTo(DateTimeResolution.MONTH) < 0; + } + + @Override + protected void doRenderCalendar(boolean updateDate) { + super.doRenderCalendar(updateDate); + + if (isTimeSelectorNeeded()) { + time = new VTime(); + setWidget(2, 0, time); + getFlexCellFormatter().setColSpan(2, 0, 5); + getFlexCellFormatter().setStyleName(2, 0, + getDateField().getStylePrimaryName() + + "-calendarpanel-time"); + } else if (time != null) { + remove(time); + } + } + + @Override + public String getSubPartName(Element subElement) { + if (time != null) { + if (contains(time.hours, subElement)) { + return SUBPART_HOUR_SELECT; + } else if (contains(time.mins, subElement)) { + return SUBPART_MINUTE_SELECT; + } else if (contains(time.sec, subElement)) { + return SUBPART_SECS_SELECT; + } else if (contains(time.ampm, subElement)) { + return SUBPART_AMPM_SELECT; + + } + } + return super.getSubPartName(subElement); + } + + @Override + public Element getSubPartElement(String subPart) { + if (SUBPART_HOUR_SELECT.equals(subPart)) { + return time.hours.getElement(); + } + if (SUBPART_MINUTE_SELECT.equals(subPart)) { + return time.mins.getElement(); + } + if (SUBPART_SECS_SELECT.equals(subPart)) { + return time.sec.getElement(); + } + if (SUBPART_AMPM_SELECT.equals(subPart)) { + return time.ampm.getElement(); + } + return super.getSubPartElement(subPart); + } + + /** + * Do we need the time selector + * + * @return True if it is required + */ + private boolean isTimeSelectorNeeded() { + return getResolution().compareTo(DateTimeResolution.DAY) < 0; + } +} diff --git a/client/src/main/java/com/vaadin/client/ui/VDateTimeFieldCalendar.java b/client/src/main/java/com/vaadin/client/ui/VDateTimeFieldCalendar.java new file mode 100644 index 0000000000..53becf60d6 --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/VDateTimeFieldCalendar.java @@ -0,0 +1,113 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui; + +import java.util.Date; +import java.util.Map; + +import com.google.gwt.core.client.GWT; +import com.vaadin.shared.ui.datefield.DateTimeResolution; + +/** + * A client side implementation for inline date/time field. + * + * @author Vaadin Ltd + * @since 8.0 + * + */ +public class VDateTimeFieldCalendar extends + VAbstractDateFieldCalendar { + + public VDateTimeFieldCalendar() { + super(GWT.create(VDateTimeCalendarPanel.class), + DateTimeResolution.MINUTE); + } + + @Override + public void updateValueFromPanel() { + // If field is invisible at the beginning, client can still be null when + // this function is called. + if (getClient() == null) { + return; + } + + Date date2 = calendarPanel.getDate(); + Date currentDate = getCurrentDate(); + if (currentDate == null || date2.getTime() != currentDate.getTime()) { + setCurrentDate((Date) date2.clone()); + getClient().updateVariable(getId(), + getResolutionVariable(DateTimeResolution.YEAR), + date2.getYear() + 1900, false); + if (getCurrentResolution().compareTo(DateTimeResolution.YEAR) < 0) { + getClient().updateVariable(getId(), + getResolutionVariable(DateTimeResolution.MONTH), + date2.getMonth() + 1, false); + if (getCurrentResolution() + .compareTo(DateTimeResolution.MONTH) < 0) { + getClient().updateVariable(getId(), + getResolutionVariable(DateTimeResolution.DAY), + date2.getDate(), false); + if (getCurrentResolution() + .compareTo(DateTimeResolution.DAY) < 0) { + getClient().updateVariable(getId(), + getResolutionVariable(DateTimeResolution.HOUR), + date2.getHours(), false); + if (getCurrentResolution() + .compareTo(DateTimeResolution.HOUR) < 0) { + getClient().updateVariable(getId(), + getResolutionVariable( + DateTimeResolution.MINUTE), + date2.getMinutes(), false); + if (getCurrentResolution() + .compareTo(DateTimeResolution.MINUTE) < 0) { + getClient().updateVariable(getId(), + getResolutionVariable( + DateTimeResolution.SECOND), + date2.getSeconds(), false); + } + } + } + } + } + getClient().sendPendingVariableChanges(); + } + } + + @Override + public String resolutionAsString() { + if (getCurrentResolution().compareTo(DateTimeResolution.DAY) >= 0) { + return getResolutionVariable(getCurrentResolution()); + } else { + return "full"; + } + } + + @Override + public boolean isYear(DateTimeResolution resolution) { + return DateTimeResolution.YEAR.equals(resolution); + } + + @Override + protected Date getDate(Map dateValues) { + return VPopupTimeCalendar.makeDate(dateValues); + } + + @Override + protected DateTimeResolution[] doGetResolutions() { + return DateTimeResolution.values(); + } + +} diff --git a/client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java b/client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java index 20425faa8b..b05224f07e 100644 --- a/client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java +++ b/client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java @@ -28,7 +28,8 @@ import com.vaadin.shared.ui.datefield.DateResolution; * @author Vaadin Ltd * */ -public class VPopupCalendar extends VAbstractPopupCalendar { +public class VPopupCalendar + extends VAbstractPopupCalendar { public VPopupCalendar() { super(GWT.create(VDateCalendarPanel.class), DateResolution.YEAR); @@ -50,20 +51,20 @@ public class VPopupCalendar extends VAbstractPopupCalendar { resolution == null ? DateResolution.YEAR : resolution); } - public static Date makeDate(Map dateVaules) { - if (dateVaules.get(DateResolution.YEAR) == -1) { + public static Date makeDate(Map dateValues) { + if (dateValues.get(DateResolution.YEAR) == -1) { return null; } Date date = new Date(2000 - 1900, 0, 1); - int year = dateVaules.get(DateResolution.YEAR); + int year = dateValues.get(DateResolution.YEAR); if (year >= 0) { date.setYear(year - 1900); } - int month = dateVaules.get(DateResolution.MONTH); + int month = dateValues.get(DateResolution.MONTH); if (month >= 0) { date.setMonth(month - 1); } - int day = dateVaules.get(DateResolution.DAY); + int day = dateValues.get(DateResolution.DAY); if (day >= 0) { date.setDate(day); } @@ -101,4 +102,16 @@ public class VPopupCalendar extends VAbstractPopupCalendar { } } + @Override + protected String cleanFormat(String format) { + // Remove unnecessary d & M if resolution is too low + if (getCurrentResolution().compareTo(DateResolution.DAY) > 0) { + format = format.replaceAll("d", ""); + } + if (getCurrentResolution().compareTo(DateResolution.MONTH) > 0) { + format = format.replaceAll("M", ""); + } + return super.cleanFormat(format); + } + } diff --git a/client/src/main/java/com/vaadin/client/ui/VPopupTimeCalendar.java b/client/src/main/java/com/vaadin/client/ui/VPopupTimeCalendar.java new file mode 100644 index 0000000000..af56e1101d --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/VPopupTimeCalendar.java @@ -0,0 +1,228 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui; + +import java.util.Date; +import java.util.Map; + +import com.google.gwt.core.client.GWT; +import com.vaadin.client.LocaleNotLoadedException; +import com.vaadin.client.LocaleService; +import com.vaadin.client.VConsole; +import com.vaadin.shared.ui.datefield.DateTimeResolution; + +/** + * Represents a date-time selection component with a text field and a popup date + * selector. + * + * @author Vaadin Ltd + * + * @since 8.0 + */ +public class VPopupTimeCalendar extends + VAbstractPopupCalendar { + + public VPopupTimeCalendar() { + super(GWT.create(VDateTimeCalendarPanel.class), + DateTimeResolution.MINUTE); + } + + @Override + protected DateTimeResolution[] doGetResolutions() { + return DateTimeResolution.values(); + } + + @Override + public String resolutionAsString() { + if (getCurrentResolution().compareTo(DateTimeResolution.DAY) >= 0) { + return getResolutionVariable(getCurrentResolution()); + } else { + return "full"; + } + } + + @Override + public void setCurrentResolution(DateTimeResolution resolution) { + super.setCurrentResolution( + resolution == null ? DateTimeResolution.MINUTE : resolution); + } + + public static Date makeDate(Map dateValues) { + if (dateValues.get(DateTimeResolution.YEAR) == -1) { + return null; + } + Date date = new Date(2000 - 1900, 0, 1); + int year = dateValues.get(DateTimeResolution.YEAR); + if (year >= 0) { + date.setYear(year - 1900); + } + int month = dateValues.get(DateTimeResolution.MONTH); + if (month >= 0) { + date.setMonth(month - 1); + } + int day = dateValues.get(DateTimeResolution.DAY); + if (day >= 0) { + date.setDate(day); + } + int hour = dateValues.get(DateTimeResolution.HOUR); + if (hour >= 0) { + date.setHours(hour); + } + int minute = dateValues.get(DateTimeResolution.MINUTE); + if (minute >= 0) { + date.setMinutes(minute); + } + int second = dateValues.get(DateTimeResolution.SECOND); + if (second >= 0) { + date.setSeconds(second); + } + return date; + } + + @Override + public boolean isYear(DateTimeResolution resolution) { + return DateTimeResolution.YEAR.equals(resolution); + } + + @Override + protected Date getDate(Map dateValues) { + return makeDate(dateValues); + } + + @Override + protected void updateDateVariables() { + super.updateDateVariables(); + // Update variables + // (only the smallest defining resolution needs to be + // immediate) + Date currentDate = getDate(); + if (getCurrentResolution().compareTo(DateTimeResolution.MONTH) <= 0) { + getClient().updateVariable(getId(), + getResolutionVariable(DateTimeResolution.MONTH), + currentDate != null ? currentDate.getMonth() + 1 : -1, + getCurrentResolution() == DateTimeResolution.MONTH); + } + if (getCurrentResolution().compareTo(DateTimeResolution.DAY) <= 0) { + getClient().updateVariable(getId(), + getResolutionVariable(DateTimeResolution.DAY), + currentDate != null ? currentDate.getDate() : -1, + getCurrentResolution() == DateTimeResolution.DAY); + } + if (getCurrentResolution().compareTo(DateTimeResolution.HOUR) <= 0) { + getClient().updateVariable(getId(), + getResolutionVariable(DateTimeResolution.HOUR), + currentDate != null ? currentDate.getHours() : -1, + getCurrentResolution() == DateTimeResolution.HOUR); + } + if (getCurrentResolution().compareTo(DateTimeResolution.MINUTE) <= 0) { + getClient().updateVariable(getId(), + getResolutionVariable(DateTimeResolution.MINUTE), + currentDate != null ? currentDate.getMinutes() : -1, + getCurrentResolution() == DateTimeResolution.MINUTE); + } + if (getCurrentResolution().compareTo(DateTimeResolution.SECOND) <= 0) { + getClient().updateVariable(getId(), + getResolutionVariable(DateTimeResolution.SECOND), + currentDate != null ? currentDate.getSeconds() : -1, + getCurrentResolution() == DateTimeResolution.SECOND); + } + } + + @Override + @SuppressWarnings("deprecation") + public void updateValue(Date newDate) { + Date currentDate = getCurrentDate(); + super.updateValue(newDate); + if (currentDate == null || newDate.getTime() != currentDate.getTime()) { + if (getCurrentResolution().compareTo(DateTimeResolution.DAY) < 0) { + getClient().updateVariable(getId(), + getResolutionVariable(DateTimeResolution.HOUR), + newDate.getHours(), false); + if (getCurrentResolution() + .compareTo(DateTimeResolution.HOUR) < 0) { + getClient().updateVariable(getId(), + getResolutionVariable(DateTimeResolution.MINUTE), + newDate.getMinutes(), false); + if (getCurrentResolution() + .compareTo(DateTimeResolution.MINUTE) < 0) { + getClient().updateVariable(getId(), + getResolutionVariable( + DateTimeResolution.SECOND), + newDate.getSeconds(), false); + } + } + } + } + } + + @Override + protected String getFormatString() { + if (formatStr == null) { + if (isYear(getCurrentResolution())) { + formatStr = "yyyy"; // force full year + } else { + + try { + String frmString = LocaleService + .getDateFormat(currentLocale); + frmString = cleanFormat(frmString); + // String delim = LocaleService + // .getClockDelimiter(currentLocale); + if (getCurrentResolution() + .compareTo(DateTimeResolution.HOUR) <= 0) { + if (dts.isTwelveHourClock()) { + frmString += " hh"; + } else { + frmString += " HH"; + } + if (getCurrentResolution() + .compareTo(DateTimeResolution.MINUTE) <= 0) { + frmString += ":mm"; + if (getCurrentResolution().compareTo( + DateTimeResolution.SECOND) <= 0) { + frmString += ":ss"; + } + } + if (dts.isTwelveHourClock()) { + frmString += " aaa"; + } + + } + + formatStr = frmString; + } catch (LocaleNotLoadedException e) { + // TODO should die instead? Can the component survive + // without format string? + VConsole.error(e); + } + } + } + return formatStr; + } + + @Override + protected String cleanFormat(String format) { + // Remove unnecessary d & M if resolution is too low + if (getCurrentResolution().compareTo(DateTimeResolution.DAY) > 0) { + format = format.replaceAll("d", ""); + } + if (getCurrentResolution().compareTo(DateTimeResolution.MONTH) > 0) { + format = format.replaceAll("M", ""); + } + return super.cleanFormat(format); + } + +} diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/AbstractInlineDateFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/AbstractInlineDateFieldConnector.java index 5252c40d77..e5bf3e34bb 100644 --- a/client/src/main/java/com/vaadin/client/ui/datefield/AbstractInlineDateFieldConnector.java +++ b/client/src/main/java/com/vaadin/client/ui/datefield/AbstractInlineDateFieldConnector.java @@ -20,6 +20,7 @@ import java.util.Date; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.UIDL; import com.vaadin.client.communication.StateChangeEvent; +import com.vaadin.client.ui.VAbstractCalendarPanel; import com.vaadin.client.ui.VAbstractCalendarPanel.FocusChangeListener; import com.vaadin.client.ui.VAbstractDateFieldCalendar; import com.vaadin.shared.ui.datefield.InlineDateFieldState; @@ -31,8 +32,10 @@ import com.vaadin.shared.ui.datefield.InlineDateFieldState; * * @param * the resolution type which the field is based on (day, month, ...) + * @param + * Subclass of VAbstractCalendarPanel specific for the implementation */ -public abstract class AbstractInlineDateFieldConnector> +public abstract class AbstractInlineDateFieldConnector, R extends Enum> extends AbstractDateFieldConnector { @Override @@ -107,8 +110,8 @@ public abstract class AbstractInlineDateFieldConnector> } @Override - public VAbstractDateFieldCalendar getWidget() { - return (VAbstractDateFieldCalendar) super.getWidget(); + public VAbstractDateFieldCalendar getWidget() { + return (VAbstractDateFieldCalendar) super.getWidget(); } @Override diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java index 35a9420ada..d6b0f65c27 100644 --- a/client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java +++ b/client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java @@ -15,6 +15,7 @@ */ package com.vaadin.client.ui.datefield; +import com.vaadin.client.ui.VDateCalendarPanel; import com.vaadin.client.ui.VPopupCalendar; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.datefield.DateResolution; @@ -26,10 +27,11 @@ import com.vaadin.ui.AbstractLocalDateField; * */ @Connect(AbstractLocalDateField.class) -public class DateFieldConnector extends TextualDateConnector { +public class DateFieldConnector + extends TextualDateConnector { @Override - protected boolean isResolutionAboveMonth() { + protected boolean isResolutionMonthOrHigher() { return getWidget().getCurrentResolution() .compareTo(DateResolution.MONTH) >= 0; } diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/DateTimeFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/DateTimeFieldConnector.java new file mode 100644 index 0000000000..15afa1bbaf --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/datefield/DateTimeFieldConnector.java @@ -0,0 +1,87 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.datefield; + +import java.util.Date; + +import com.vaadin.client.DateTimeService; +import com.vaadin.client.ui.VDateTimeCalendarPanel; +import com.vaadin.client.ui.VDateTimeCalendarPanel.TimeChangeListener; +import com.vaadin.client.ui.VPopupTimeCalendar; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.datefield.DateTimeResolution; +import com.vaadin.shared.ui.datefield.LocalDateTimeFieldState; +import com.vaadin.ui.AbstractLocalDateTimeField; + +/** + * The client-side connector for AbstractLocalDateTimeField. + * + * @author Vaadin Ltd + * @since 8.0 + */ +@Connect(AbstractLocalDateTimeField.class) +public class DateTimeFieldConnector extends + TextualDateConnector { + + @Override + protected boolean isResolutionMonthOrHigher() { + return getWidget().getCurrentResolution() + .compareTo(DateTimeResolution.MONTH) >= 0; + } + + @Override + public VPopupTimeCalendar getWidget() { + return (VPopupTimeCalendar) super.getWidget(); + } + + @Override + public LocalDateTimeFieldState getState() { + return (LocalDateTimeFieldState) super.getState(); + } + + @Override + protected void updateListeners() { + super.updateListeners(); + if (getWidget().getCurrentResolution() + .compareTo(DateTimeResolution.DAY) < 0) { + getWidget().calendar + .setTimeChangeListener(new TimeChangeListener() { + @Override + public void changed(int hour, int min, int sec, + int msec) { + Date d = getWidget().getDate(); + if (d == null) { + // date currently null, use the value from + // calendarPanel + // (~ client time at the init of the widget) + d = (Date) getWidget().calendar.getDate() + .clone(); + } + d.setHours(hour); + d.setMinutes(min); + d.setSeconds(sec); + DateTimeService.setMilliseconds(d, msec); + + // Always update time changes to the server + getWidget().updateValue(d); + + // Update text field + getWidget().buildDate(); + } + }); + } + } +} diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java index 7339191ee9..4fda94b7e2 100644 --- a/client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java +++ b/client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java @@ -15,18 +15,21 @@ */ package com.vaadin.client.ui.datefield; +import com.vaadin.client.ui.VDateCalendarPanel; import com.vaadin.client.ui.VDateFieldCalendar; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.datefield.DateResolution; import com.vaadin.ui.InlineDateField; /** + * The client-side connector for InlineDateField. + * * @author Vaadin Ltd - * + * @since 8.0 */ @Connect(InlineDateField.class) -public class InlineDateFieldConnector - extends AbstractInlineDateFieldConnector { +public class InlineDateFieldConnector extends + AbstractInlineDateFieldConnector { @Override protected boolean isResolutionMonthOrHigher() { diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/InlineDateTimeFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/InlineDateTimeFieldConnector.java new file mode 100644 index 0000000000..194de64135 --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/datefield/InlineDateTimeFieldConnector.java @@ -0,0 +1,79 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.datefield; + +import java.util.Date; + +import com.vaadin.client.DateTimeService; +import com.vaadin.client.ui.VDateTimeCalendarPanel; +import com.vaadin.client.ui.VDateTimeCalendarPanel.TimeChangeListener; +import com.vaadin.client.ui.VDateTimeFieldCalendar; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.datefield.DateTimeResolution; +import com.vaadin.ui.InlineDateTimeField; + +/** + * The client-side connector for InlineDateTimeField. + * + * @author Vaadin Ltd + * @since 8.0 + */ +@Connect(InlineDateTimeField.class) +public class InlineDateTimeFieldConnector extends + AbstractInlineDateFieldConnector { + + @Override + protected boolean isResolutionMonthOrHigher() { + return getWidget().getCurrentResolution() + .compareTo(DateTimeResolution.MONTH) >= 0; + } + + @Override + public VDateTimeFieldCalendar getWidget() { + return (VDateTimeFieldCalendar) super.getWidget(); + } + + @Override + protected void updateListeners() { + super.updateListeners(); + if (getWidget().getCurrentResolution() + .compareTo(DateTimeResolution.DAY) < 0) { + getWidget().calendarPanel + .setTimeChangeListener(new TimeChangeListener() { + @Override + public void changed(int hour, int min, int sec, + int msec) { + Date d = getWidget().getDate(); + if (d == null) { + // date currently null, use the value from + // calendarPanel + // (~ client time at the init of the widget) + d = (Date) getWidget().calendarPanel.getDate() + .clone(); + } + d.setHours(hour); + d.setMinutes(min); + d.setSeconds(sec); + DateTimeService.setMilliseconds(d, msec); + + // Always update time changes to the server + getWidget().calendarPanel.setDate(d); + getWidget().updateValueFromPanel(); + } + }); + } + } +} diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/PopupDateFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/PopupDateFieldConnector.java index bebefe96dd..866723544a 100644 --- a/client/src/main/java/com/vaadin/client/ui/datefield/PopupDateFieldConnector.java +++ b/client/src/main/java/com/vaadin/client/ui/datefield/PopupDateFieldConnector.java @@ -19,6 +19,12 @@ package com.vaadin.client.ui.datefield; import com.vaadin.shared.ui.Connect; import com.vaadin.ui.DateField; +/** + * The client-side connector for DateField. + * + * @author Vaadin Ltd + * @since 8.0 + */ @Connect(DateField.class) public class PopupDateFieldConnector extends DateFieldConnector { diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/PopupDateTimeFieldConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/PopupDateTimeFieldConnector.java new file mode 100644 index 0000000000..eeda4c700e --- /dev/null +++ b/client/src/main/java/com/vaadin/client/ui/datefield/PopupDateTimeFieldConnector.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.client.ui.datefield; + +import com.vaadin.shared.ui.Connect; +import com.vaadin.ui.DateTimeField; + +/** + * The client-side connector for DateTimeField. + * + * @author Vaadin Ltd + * @since 8.0 + * + */ +@Connect(DateTimeField.class) +public class PopupDateTimeFieldConnector extends DateTimeFieldConnector { + +} diff --git a/client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java b/client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java index 2179c7a9ca..0c61f3860e 100644 --- a/client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java +++ b/client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java @@ -24,11 +24,26 @@ import com.google.gwt.user.client.ui.PopupPanel; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.UIDL; import com.vaadin.client.communication.StateChangeEvent; +import com.vaadin.client.ui.VAbstractCalendarPanel; import com.vaadin.client.ui.VAbstractCalendarPanel.FocusChangeListener; import com.vaadin.client.ui.VAbstractPopupCalendar; import com.vaadin.shared.ui.datefield.TextualDateFieldState; -public abstract class TextualDateConnector> +/** + * Abstract date/time field connector which extend + * {@link AbstractTextualDateConnector} functionality with widget that shows + * date/time chooser as a popup panel. + * + * @author Vaadin Ltd + * + * @since 8.0 + * + * @param + * Subclass of VAbstractCalendarPanel specific for the implementation + * @param + * the resolution type which the field is based on (day, month, ...) + */ +public abstract class TextualDateConnector, R extends Enum> extends AbstractTextualDateConnector { @Override @@ -112,17 +127,18 @@ public abstract class TextualDateConnector> * customizing only listeners logic. */ protected void updateListeners() { - if (isResolutionAboveMonth()) { + if (isResolutionMonthOrHigher()) { getWidget().calendar .setFocusChangeListener(new FocusChangeListener() { @Override public void focusChanged(Date date) { - - getWidget().updateValue(date); - getWidget().buildDate(); - Date date2 = getWidget().calendar.getDate(); - date2.setYear(date.getYear()); - date2.setMonth(date.getMonth()); + if (isResolutionMonthOrHigher()) { + getWidget().updateValue(date); + getWidget().buildDate(); + Date date2 = getWidget().calendar.getDate(); + date2.setYear(date.getYear()); + date2.setMonth(date.getMonth()); + } } }); } else { @@ -130,11 +146,17 @@ public abstract class TextualDateConnector> } } - protected abstract boolean isResolutionAboveMonth(); + /** + * Returns {@code true} is the current resolution of the widget is month or + * less specific (e.g. month, year, quarter, etc). + * + * @return {@code true} if the current resolution is above month + */ + protected abstract boolean isResolutionMonthOrHigher(); @Override - public VAbstractPopupCalendar getWidget() { - return (VAbstractPopupCalendar) super.getWidget(); + public VAbstractPopupCalendar getWidget() { + return (VAbstractPopupCalendar) super.getWidget(); } @Override diff --git a/server/src/main/java/com/vaadin/data/converter/LocalDateTimeToDateConverter.java b/server/src/main/java/com/vaadin/data/converter/LocalDateTimeToDateConverter.java new file mode 100644 index 0000000000..e427d956e3 --- /dev/null +++ b/server/src/main/java/com/vaadin/data/converter/LocalDateTimeToDateConverter.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.data.converter; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Date; +import java.util.Objects; + +import com.vaadin.data.Converter; +import com.vaadin.data.Result; +import com.vaadin.data.ValueContext; +import com.vaadin.ui.DateTimeField; +import com.vaadin.ui.InlineDateTimeField; + +/** + * A converter that converts between LocalDateTime and + * Date. This is used when a {@link DateTimeField} or + * {@link InlineDateTimeField} is bound to a {@link Date} property. + * + * @author Vaadin Ltd + */ +public class LocalDateTimeToDateConverter + implements Converter { + + private ZoneOffset zoneOffset; + + /** + * Creates a new converter using the given time zone. + * + * @param zoneOffset + * the time zone offset to use, not null + */ + public LocalDateTimeToDateConverter(ZoneOffset zoneOffset) { + this.zoneOffset = Objects.requireNonNull(zoneOffset, + "Zone offset cannot be null"); + } + + @Override + public Result convertToModel(LocalDateTime localDate, + ValueContext context) { + if (localDate == null) { + return Result.ok(null); + } + + return Result.ok(Date.from(localDate.toInstant(zoneOffset))); + } + + @Override + public LocalDateTime convertToPresentation(Date date, + ValueContext context) { + if (date == null) { + return null; + } + + return Instant.ofEpochMilli(date.getTime()).atZone(zoneOffset) + .toLocalDateTime(); + } + +} diff --git a/server/src/main/java/com/vaadin/data/validator/DateTimeRangeValidator.java b/server/src/main/java/com/vaadin/data/validator/DateTimeRangeValidator.java new file mode 100644 index 0000000000..9096a8cc70 --- /dev/null +++ b/server/src/main/java/com/vaadin/data/validator/DateTimeRangeValidator.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.data.validator; + +import java.time.LocalDateTime; +import java.util.Comparator; + +/** + * Validator for validating that a {@link LocalDateTime} is inside a given + * range. + * + * @author Vaadin Ltd. + * @since 8.0 + */ +public class DateTimeRangeValidator extends RangeValidator { + + /** + * Creates a validator for checking that a {@link LocalDateTime} is within a + * given range. + *

+ * By default the range is inclusive i.e. both minValue and maxValue are + * valid values. Use {@link #setMinValueIncluded(boolean)} or + * {@link #setMaxValueIncluded(boolean)} to change it. + *

+ * + * @param errorMessage + * the message to display in case the value does not validate. + * @param minValue + * The minimum value to accept or null for no limit + * @param maxValue + * The maximum value to accept or null for no limit + */ + public DateTimeRangeValidator(String errorMessage, LocalDateTime minValue, + LocalDateTime maxValue) { + super(errorMessage, Comparator.naturalOrder(), minValue, maxValue); + } + +} diff --git a/server/src/main/java/com/vaadin/ui/AbstractLocalDateField.java b/server/src/main/java/com/vaadin/ui/AbstractLocalDateField.java index ab99fe0f28..7754757429 100644 --- a/server/src/main/java/com/vaadin/ui/AbstractLocalDateField.java +++ b/server/src/main/java/com/vaadin/ui/AbstractLocalDateField.java @@ -27,7 +27,11 @@ import com.vaadin.shared.ui.datefield.AbstractTextualDateFieldState; import com.vaadin.shared.ui.datefield.DateResolution; /** + * Abstract DateField class for {@link LocalDate} type. + * * @author Vaadin Ltd + * + * @since 8.0 * */ public abstract class AbstractLocalDateField diff --git a/server/src/main/java/com/vaadin/ui/AbstractLocalDateTimeField.java b/server/src/main/java/com/vaadin/ui/AbstractLocalDateTimeField.java index 9be77444c6..65da2a2b8b 100644 --- a/server/src/main/java/com/vaadin/ui/AbstractLocalDateTimeField.java +++ b/server/src/main/java/com/vaadin/ui/AbstractLocalDateTimeField.java @@ -15,14 +15,24 @@ */ package com.vaadin.ui; +import java.time.Instant; import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.temporal.ChronoUnit; +import java.util.Date; +import java.util.Map; +import com.vaadin.data.validator.DateTimeRangeValidator; +import com.vaadin.data.validator.RangeValidator; +import com.vaadin.shared.ui.datefield.AbstractTextualDateFieldState; import com.vaadin.shared.ui.datefield.DateTimeResolution; -import com.vaadin.shared.ui.datefield.LocalDateTimeFieldState; /** + * Abstract DateField class for {@link LocalDateTime} type. + * * @author Vaadin Ltd * + * @since 8.0 */ public abstract class AbstractLocalDateTimeField extends AbstractDateField { @@ -45,14 +55,112 @@ public abstract class AbstractLocalDateTimeField super(caption, DateTimeResolution.MINUTE); } + /** + * Constructs a new AbstractLocalDateTimeField with the given + * caption and initial text contents. + * + * @param caption + * the caption String for the editor. + * @param value + * the LocalDateTime value. + */ + public AbstractLocalDateTimeField(String caption, LocalDateTime value) { + super(caption, value, DateTimeResolution.MINUTE); + } + + @Override + protected AbstractTextualDateFieldState getState() { + return (AbstractTextualDateFieldState) super.getState(); + } + + @Override + protected AbstractTextualDateFieldState getState(boolean markAsDirty) { + return (AbstractTextualDateFieldState) super.getState(markAsDirty); + } + @Override - protected LocalDateTimeFieldState getState() { - return (LocalDateTimeFieldState) super.getState(); + protected int getDatePart(LocalDateTime date, + DateTimeResolution resolution) { + LocalDateTime value = date; + if (value == null) { + value = LocalDateTime.of(1, 1, 1, 0, 0); + } + switch (resolution) { + case DAY: + return value.getDayOfMonth(); + case MONTH: + return value.getMonthValue(); + case YEAR: + return value.getYear(); + case HOUR: + return value.getHour(); + case MINUTE: + return value.getMinute(); + case SECOND: + return value.getSecond(); + default: + assert false : "Unexpected resolution argument " + resolution; + return -1; + } } @Override - protected LocalDateTimeFieldState getState(boolean markAsDirty) { - return (LocalDateTimeFieldState) super.getState(markAsDirty); + protected RangeValidator getRangeValidator() { + return new DateTimeRangeValidator(getDateOutOfRangeMessage(), + getDate(getRangeStart(), getResolution()), + getDate(getRangeEnd(), getResolution())); + } + + @Override + protected LocalDateTime buildDate( + Map resolutionValues) { + return LocalDateTime.of(resolutionValues.get(DateTimeResolution.YEAR), + resolutionValues.getOrDefault(DateTimeResolution.MONTH, 1), + resolutionValues.getOrDefault(DateTimeResolution.DAY, 1), + resolutionValues.getOrDefault(DateTimeResolution.HOUR, 0), + resolutionValues.getOrDefault(DateTimeResolution.MINUTE, 0), + resolutionValues.getOrDefault(DateTimeResolution.SECOND, 0)); + } + + @Override + protected LocalDateTime convertFromDate(Date date) { + if (date == null) { + return null; + } + return Instant.ofEpochMilli(date.getTime()).atZone(ZoneOffset.UTC) + .toLocalDateTime(); + } + + @Override + protected Date convertToDate(LocalDateTime date) { + if (date == null) { + return null; + } + return Date.from(date.toInstant(ZoneOffset.UTC)); + } + + private LocalDateTime getDate(LocalDateTime date, + DateTimeResolution forResolution) { + if (date == null) { + return null; + } + switch (forResolution) { + case YEAR: + return date.withDayOfYear(1).toLocalDate().atStartOfDay(); + case MONTH: + return date.withDayOfMonth(1).toLocalDate().atStartOfDay(); + case DAY: + return date.toLocalDate().atStartOfDay(); + case HOUR: + return date.truncatedTo(ChronoUnit.HOURS); + case MINUTE: + return date.truncatedTo(ChronoUnit.MINUTES); + case SECOND: + return date.truncatedTo(ChronoUnit.SECONDS); + default: + assert false : "Unexpected resolution argument " + forResolution; + return null; + } } } diff --git a/server/src/main/java/com/vaadin/ui/DateTimeField.java b/server/src/main/java/com/vaadin/ui/DateTimeField.java new file mode 100644 index 0000000000..cde5633846 --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/DateTimeField.java @@ -0,0 +1,139 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui; + +import java.time.LocalDateTime; + +import com.vaadin.shared.ui.datefield.LocalDateTimeFieldState; + +/** + * A date time entry component, which displays the actual date selector as a + * popup. + * + * @see AbstractLocalDateTimeField + * @see InlineDateTimeField + * @author Vaadin Ltd. + * @since 8.0 + */ +public class DateTimeField extends AbstractLocalDateTimeField { + + /** + * Constructs an empty DateTimeField with no caption. + */ + public DateTimeField() { + super(); + } + + /** + * Constructs a new DateTimeField with the given caption and + * initial text contents. + * + * @param caption + * the caption String for the editor. + * @param value + * the LocalDateTime value. + */ + public DateTimeField(String caption, LocalDateTime value) { + super(caption, value); + } + + /** + * Constructs an empty DateTimeField with caption. + * + * @param caption + * the caption of the datefield. + */ + public DateTimeField(String caption) { + super(caption); + } + + /** + * Returns the current placeholder text. + * + * @see #setPlaceholder(String) + * @return the placeholder text + */ + public String getPlaceholder() { + return getState(false).placeholder; + } + + /** + * Sets the placeholder text. The placeholder is text that is displayed when + * the field would otherwise be empty, to prompt the user for input. + * + * @param placeholder + * the placeholder text to set + */ + public void setPlaceholder(String placeholder) { + getState().placeholder = placeholder; + } + + @Override + protected LocalDateTimeFieldState getState() { + return (LocalDateTimeFieldState) super.getState(); + } + + @Override + protected LocalDateTimeFieldState getState(boolean markAsDirty) { + return (LocalDateTimeFieldState) super.getState(markAsDirty); + } + + /** + * Checks whether the text field is enabled (default) or not. + * + * @see #setTextFieldEnabled(boolean) + * + * @return true if the text field is enabled, false otherwise. + */ + public boolean isTextFieldEnabled() { + return getState(false).textFieldEnabled; + } + + /** + * Enables or disables the text field. By default the text field is enabled. + * Disabling it causes only the button for date selection to be active, thus + * preventing the user from entering invalid dates. + * + * See {@link http://dev.vaadin.com/ticket/6790}. + * + * @param state + * true to enable text field, false to disable it. + */ + public void setTextFieldEnabled(boolean state) { + getState().textFieldEnabled = state; + } + + /** + * Set a description that explains the usage of the Widget for users of + * assistive devices. + * + * @param description + * String with the description + */ + public void setAssistiveText(String description) { + getState().descriptionForAssistiveDevices = description; + } + + /** + * Get the description that explains the usage of the Widget for users of + * assistive devices. + * + * @return String with the description + */ + public String getAssistiveText() { + return getState(false).descriptionForAssistiveDevices; + } +} diff --git a/server/src/main/java/com/vaadin/ui/InlineDateTimeField.java b/server/src/main/java/com/vaadin/ui/InlineDateTimeField.java new file mode 100644 index 0000000000..2c4cf290b3 --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/InlineDateTimeField.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui; + +import java.time.LocalDateTime; + +import com.vaadin.shared.ui.datefield.InlineDateTimeFieldState; + +/** + * A date time entry component, which displays the actual date selector inline. + * + * @see AbstractLocalDateTimeField + * @see DateTimeField + * @author Vaadin Ltd. + * @since 8.0 + */ +public class InlineDateTimeField extends AbstractLocalDateTimeField { + + /** + * Constructs an empty InlineDateTimeField with no caption. + */ + public InlineDateTimeField() { + super(); + } + + /** + * Constructs a new InlineDateTimeField with the given caption + * and initial text contents. + * + * @param caption + * the caption String for the editor. + * @param value + * the LocalDate value. + */ + public InlineDateTimeField(String caption, LocalDateTime value) { + super(caption, value); + } + + /** + * Constructs an empty InlineDateTimeField with caption. + * + * @param caption + * the caption of the datefield. + */ + public InlineDateTimeField(String caption) { + super(caption); + } + + @Override + protected InlineDateTimeFieldState getState() { + return (InlineDateTimeFieldState) super.getState(); + } + + @Override + protected InlineDateTimeFieldState getState(boolean markAsDirty) { + return (InlineDateTimeFieldState) super.getState(markAsDirty); + } +} diff --git a/server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java b/server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java index 6e9ff8f9bb..0f40be795b 100644 --- a/server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java +++ b/server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java @@ -21,6 +21,7 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.Collections; import java.util.Date; import java.util.Locale; @@ -42,6 +43,7 @@ import com.vaadin.server.Resource; import com.vaadin.ui.declarative.converters.DesignDateConverter; import com.vaadin.ui.declarative.converters.DesignEnumConverter; import com.vaadin.ui.declarative.converters.DesignLocalDateConverter; +import com.vaadin.ui.declarative.converters.DesignLocalDateTimeConverter; import com.vaadin.ui.declarative.converters.DesignObjectConverter; import com.vaadin.ui.declarative.converters.DesignResourceConverter; import com.vaadin.ui.declarative.converters.DesignShortcutActionConverter; @@ -178,6 +180,8 @@ public class DesignFormatter implements Serializable { converterMap.put(Date.class, new DesignDateConverter()); converterMap.put(LocalDate.class, new DesignLocalDateConverter()); + converterMap.put(LocalDateTime.class, + new DesignLocalDateTimeConverter()); converterMap.put(ShortcutAction.class, new DesignShortcutActionConverter()); converterMap.put(Resource.class, new DesignResourceConverter()); diff --git a/server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateTimeConverter.java b/server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateTimeConverter.java new file mode 100644 index 0000000000..bfc177ca0c --- /dev/null +++ b/server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateTimeConverter.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.declarative.converters; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Locale; + +import com.vaadin.data.Converter; +import com.vaadin.data.Result; +import com.vaadin.data.ValueContext; +import com.vaadin.ui.declarative.DesignAttributeHandler; + +/** + * A {@link LocalDate} converter to be used by {@link DesignAttributeHandler}. + * Provides ISO-compliant way of storing date and time. + * + * @since 8.0 + * @author Vaadin Ltd + */ +public class DesignLocalDateTimeConverter + implements Converter { + + @Override + public Result convertToModel(String value, + ValueContext context) { + for (String pattern : new String[] { "yyyy-MM-dd HH:mm:ssZ", + "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM-dd HH", + "yyyy-MM-dd", "yyyy-MM", "yyyy" }) { + try { + Locale effectiveLocale = context.getLocale() + .orElse(Locale.ENGLISH); + LocalDateTime date = DateTimeFormatter + .ofPattern(pattern, effectiveLocale) + .parse(value, LocalDateTime::from); + return Result.ok(date); + } catch (DateTimeParseException ignored) { + // not parseable, ignore and try another format + } + } + return Result.error("Could not parse date value: " + value); + } + + @Override + public String convertToPresentation(LocalDateTime value, + ValueContext context) { + return DateTimeFormatter + .ofPattern("yyyy-MM-dd HH:mm:ss", + context.getLocale().orElse(Locale.ENGLISH)) + .format(value); + } + +} diff --git a/server/src/test/java/com/vaadin/tests/data/converter/LocalDateTimeToDateConverterTest.java b/server/src/test/java/com/vaadin/tests/data/converter/LocalDateTimeToDateConverterTest.java new file mode 100644 index 0000000000..fdad1391f5 --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/data/converter/LocalDateTimeToDateConverterTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.data.converter; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.Binder; +import com.vaadin.data.ValidationException; +import com.vaadin.data.ValueContext; +import com.vaadin.data.converter.LocalDateTimeToDateConverter; +import com.vaadin.ui.DateTimeField; + +public class LocalDateTimeToDateConverterTest extends AbstractConverterTest { + + private static final LocalDateTime LOCAL_DATE = LocalDateTime.of(2017, 1, 1, + 1, 1, 1); + private static final Date DATE = createDate(); + + @Override + protected LocalDateTimeToDateConverter getConverter() { + return new LocalDateTimeToDateConverter(ZoneOffset.UTC); + } + + @Test + public void testToModel() { + assertValue(DATE, + getConverter().convertToModel(LOCAL_DATE, new ValueContext())); + } + + @Test + public void testToModelFromSqlDate() { + // Check that SQL dates also work (e.g. java.sql.Date.toInstant throws) + assertValue(new java.sql.Date(DATE.getTime()), + getConverter().convertToModel(LOCAL_DATE, new ValueContext())); + } + + @Test + public void testToPresentation() { + Assert.assertEquals(LOCAL_DATE, + getConverter().convertToPresentation(DATE, new ValueContext())); + } + + @Test + public void useWithBinder() throws ValidationException { + Binder binder = new Binder<>(); + DateTimeField dateField = new DateTimeField(); + + binder.forField(dateField).withConverter(getConverter()) + .bind(BeanWithDate::getDate, BeanWithDate::setDate); + + dateField.setValue(LOCAL_DATE); + + BeanWithDate bean = new BeanWithDate(); + binder.writeBean(bean); + + Assert.assertEquals(DATE, bean.getDate()); + } + + public static class BeanWithDate { + private Date date; + + public void setDate(Date date) { + this.date = date; + } + + public Date getDate() { + return date; + } + } + + private static Date createDate() { + Calendar calendar = Calendar + .getInstance(TimeZone.getTimeZone(ZoneOffset.UTC)); + calendar.clear(); + calendar.set(2017, Calendar.JANUARY, 1, 1, 1, 1); + return calendar.getTime(); + } + +} diff --git a/server/src/test/java/com/vaadin/tests/server/component/abstractdatefield/AbstractLocalDateTimeFieldDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/abstractdatefield/AbstractLocalDateTimeFieldDeclarativeTest.java new file mode 100644 index 0000000000..a4768c6f24 --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/server/component/abstractdatefield/AbstractLocalDateTimeFieldDeclarativeTest.java @@ -0,0 +1,110 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.server.component.abstractdatefield; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +import org.junit.Test; + +import com.vaadin.shared.ui.datefield.DateTimeResolution; +import com.vaadin.tests.server.component.abstractfield.AbstractFieldDeclarativeTest; +import com.vaadin.ui.AbstractLocalDateTimeField; + +/** + * Abstract test class which contains tests for declarative format for + * properties that are common for AbstractDateField. + *

+ * It's an abstract so it's not supposed to be run as is. Instead each + * declarative test for a real component should extend it and implement abstract + * methods to be able to test the common properties. Components specific + * properties should be tested additionally in the subclasses implementations. + * + * @author Vaadin Ltd + * + */ +public abstract class AbstractLocalDateTimeFieldDeclarativeTest + extends AbstractFieldDeclarativeTest { + + protected DateTimeFormatter DATE_FORMATTER = DateTimeFormatter + .ofPattern("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH); + + @Override + public void valueDeserialization() + throws InstantiationException, IllegalAccessException { + LocalDateTime value = LocalDateTime.of(2003, 02, 27, 10, 37, 43); + String design = String.format("<%s value='%s'/>", getComponentTag(), + DATE_FORMATTER.format(value)); + + T component = getComponentClass().newInstance(); + component.setValue(value); + + testRead(design, component); + testWrite(design, component); + } + + @Test + public void abstractDateFieldAttributesDeserialization() + throws InstantiationException, IllegalAccessException { + boolean showIsoWeeks = true; + LocalDateTime end = LocalDateTime.of(2019, 02, 27, 10, 37, 43); + LocalDateTime start = LocalDateTime.of(2001, 02, 27, 23, 12, 34); + String dateOutOfRange = "test date out of range"; + DateTimeResolution resolution = DateTimeResolution.HOUR; + String dateFormat = "test format"; + boolean lenient = true; + String parseErrorMsg = "test parse error"; + String design = String.format( + "<%s show-iso-week-numbers range-end='%s' range-start='%s' " + + "date-out-of-range-message='%s' resolution='%s' " + + "date-format='%s' lenient parse-error-message='%s'/>", + getComponentTag(), DATE_FORMATTER.format(end), + DATE_FORMATTER.format(start), dateOutOfRange, + resolution.name().toLowerCase(Locale.ENGLISH), dateFormat, + parseErrorMsg); + + T component = getComponentClass().newInstance(); + + component.setShowISOWeekNumbers(showIsoWeeks); + component.setRangeEnd(end); + component.setRangeStart(start); + component.setDateOutOfRangeMessage(dateOutOfRange); + component.setResolution(resolution); + component.setDateFormat(dateFormat); + component.setLenient(lenient); + component.setParseErrorMessage(parseErrorMsg); + + testRead(design, component); + testWrite(design, component); + } + + @Override + public void readOnlyValue() + throws InstantiationException, IllegalAccessException { + LocalDateTime value = LocalDateTime.of(2003, 02, 27, 23, 12, 34); + String design = String.format("<%s value='%s' readonly/>", + getComponentTag(), DATE_FORMATTER.format(value)); + + T component = getComponentClass().newInstance(); + component.setValue(value); + component.setReadOnly(true); + + testRead(design, component); + testWrite(design, component); + } + +} diff --git a/server/src/test/java/com/vaadin/tests/server/component/abstractfield/AbstractFieldDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/abstractfield/AbstractFieldDeclarativeTest.java index e22905c9f3..07cbcefc39 100644 --- a/server/src/test/java/com/vaadin/tests/server/component/abstractfield/AbstractFieldDeclarativeTest.java +++ b/server/src/test/java/com/vaadin/tests/server/component/abstractfield/AbstractFieldDeclarativeTest.java @@ -62,9 +62,11 @@ public abstract class AbstractFieldDeclarativeTest, V testWrite(design, component); } + @Test public abstract void valueDeserialization() throws InstantiationException, IllegalAccessException; + @Test public abstract void readOnlyValue() throws InstantiationException, IllegalAccessException; } diff --git a/server/src/test/java/com/vaadin/tests/server/component/colorpicker/AbstractColorPickerDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/AbstractColorPickerDeclarativeTest.java index 990121e704..160d8fa41a 100644 --- a/server/src/test/java/com/vaadin/tests/server/component/colorpicker/AbstractColorPickerDeclarativeTest.java +++ b/server/src/test/java/com/vaadin/tests/server/component/colorpicker/AbstractColorPickerDeclarativeTest.java @@ -75,7 +75,8 @@ public abstract class AbstractColorPickerDeclarativeTest", getComponentTag()); + String design = String.format("<%s color='#%s'/>", getComponentTag(), + rgb); T colorPicker = getComponentClass().newInstance(); int colorInt = Integer.parseInt(rgb, 16); @@ -91,7 +92,7 @@ public abstract class AbstractColorPickerDeclarativeTest", - getComponentTag()); + getComponentTag(), rgb); T colorPicker = getComponentClass().newInstance(); int colorInt = Integer.parseInt(rgb, 16); diff --git a/server/src/test/java/com/vaadin/tests/server/component/datefield/DateTimeFieldDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/datefield/DateTimeFieldDeclarativeTest.java new file mode 100644 index 0000000000..fac0152ffd --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/server/component/datefield/DateTimeFieldDeclarativeTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.server.component.datefield; + +import org.junit.Test; + +import com.vaadin.tests.server.component.abstractdatefield.AbstractLocalDateTimeFieldDeclarativeTest; +import com.vaadin.ui.DateTimeField; + +/** + * Tests the declarative support for implementations of {@link DateTimeField}. + * + * @since 8.0 + * @author Vaadin Ltd + */ +public class DateTimeFieldDeclarativeTest + extends AbstractLocalDateTimeFieldDeclarativeTest { + + @Test + public void remainingAttributes() + throws InstantiationException, IllegalAccessException { + String placeholder = "foo"; + String assistiveText = "at"; + boolean textFieldEnabled = false; + String design = String.format( + "<%s placeholder='%s' " + + "assistive-text='%s' text-field-enabled='%s'/>", + getComponentTag(), placeholder, assistiveText, + textFieldEnabled); + + DateTimeField component = getComponentClass().newInstance(); + component.setPlaceholder(placeholder); + component.setTextFieldEnabled(textFieldEnabled); + component.setAssistiveText(assistiveText); + + testRead(design, component); + testWrite(design, component); + } + + @Override + protected String getComponentTag() { + return "vaadin-date-time-field"; + } + + @Override + protected Class getComponentClass() { + return DateTimeField.class; + } + +} diff --git a/server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateTimeFieldDeclarativeTest.java b/server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateTimeFieldDeclarativeTest.java new file mode 100644 index 0000000000..c26bd41032 --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateTimeFieldDeclarativeTest.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.server.component.datefield; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.time.LocalDateTime; + +import org.junit.Test; + +import com.vaadin.shared.ui.datefield.DateTimeResolution; +import com.vaadin.tests.server.component.abstractdatefield.AbstractLocalDateTimeFieldDeclarativeTest; +import com.vaadin.ui.AbstractDateField; +import com.vaadin.ui.InlineDateTimeField; +import com.vaadin.ui.declarative.Design; + +/** + * Tests the declarative support for implementations of + * {@link AbstractDateField}. + * + * @since 7.4 + * @author Vaadin Ltd + */ +public class InlineDateTimeFieldDeclarativeTest + extends AbstractLocalDateTimeFieldDeclarativeTest { + + @Test + public void testInlineDateFieldToFromDesign() throws Exception { + InlineDateTimeField field = new InlineDateTimeField("Day is", + LocalDateTime.of(2003, 2, 27, 4, 13, 45)); + field.setShowISOWeekNumbers(true); + field.setRangeStart(LocalDateTime.of(2001, 2, 27, 6, 12, 53)); + field.setRangeEnd(LocalDateTime.of(20011, 2, 27, 3, 43, 23)); + field.setResolution(DateTimeResolution.SECOND); + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + Design.write(field, bos); + + InlineDateTimeField result = (InlineDateTimeField) Design + .read(new ByteArrayInputStream(bos.toByteArray())); + assertEquals(field.getResolution(), result.getResolution()); + assertEquals(field.getCaption(), result.getCaption()); + assertEquals(field.getValue(), result.getValue()); + assertEquals(field.getRangeStart(), result.getRangeStart()); + assertEquals(field.getRangeEnd(), result.getRangeEnd()); + } + + @Override + protected String getComponentTag() { + return "vaadin-inline-date-time-field"; + } + + @Override + protected Class getComponentClass() { + return InlineDateTimeField.class; + } + +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/datefield/DateTimeResolution.java b/shared/src/main/java/com/vaadin/shared/ui/datefield/DateTimeResolution.java index 7876fb51f4..9b7aeca48c 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/datefield/DateTimeResolution.java +++ b/shared/src/main/java/com/vaadin/shared/ui/datefield/DateTimeResolution.java @@ -16,7 +16,7 @@ package com.vaadin.shared.ui.datefield; /** - * Resolutions for DateTimeFields + * Resolutions for DateTimeFields. * * @author Vaadin Ltd. * @since 8.0 diff --git a/shared/src/main/java/com/vaadin/shared/ui/datefield/InlineDateTimeFieldState.java b/shared/src/main/java/com/vaadin/shared/ui/datefield/InlineDateTimeFieldState.java new file mode 100644 index 0000000000..ed64569c7d --- /dev/null +++ b/shared/src/main/java/com/vaadin/shared/ui/datefield/InlineDateTimeFieldState.java @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.ui.datefield; + +/** + * @author Vaadin Ltd + * + */ +public class InlineDateTimeFieldState extends AbstractTextualDateFieldState { + { + primaryStyleName = "v-inline-datefield"; + } +} diff --git a/shared/src/main/java/com/vaadin/shared/ui/datefield/LocalDateTimeFieldState.java b/shared/src/main/java/com/vaadin/shared/ui/datefield/LocalDateTimeFieldState.java index cb439dd1a8..82cfe8ee6f 100644 --- a/shared/src/main/java/com/vaadin/shared/ui/datefield/LocalDateTimeFieldState.java +++ b/shared/src/main/java/com/vaadin/shared/ui/datefield/LocalDateTimeFieldState.java @@ -21,6 +21,6 @@ package com.vaadin.shared.ui.datefield; * @author Vaadin Ltd * */ -public class LocalDateTimeFieldState extends AbstractTextualDateFieldState { +public class LocalDateTimeFieldState extends TextualDateFieldState { } diff --git a/tests/screenshots b/tests/screenshots index 203312a80a..e9f8b30d0d 160000 --- a/tests/screenshots +++ b/tests/screenshots @@ -1 +1 @@ -Subproject commit 203312a80a5f76d48fc36ef8d215f8b70b8e7545 +Subproject commit e9f8b30d0db8076f24c9253f078b2931f0ecd3fa diff --git a/uitest-common/src/main/java/com/vaadin/testbench/customelements/DateTimeFieldElement.java b/uitest-common/src/main/java/com/vaadin/testbench/customelements/DateTimeFieldElement.java new file mode 100644 index 0000000000..022acbfa8a --- /dev/null +++ b/uitest-common/src/main/java/com/vaadin/testbench/customelements/DateTimeFieldElement.java @@ -0,0 +1,27 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.testbench.customelements; + +import com.vaadin.testbench.elementsbase.ServerClass; + +/** + * @author Vaadin Ltd + * + */ +@ServerClass("com.vaadin.ui.DateTimeField") +public class DateTimeFieldElement extends DateFieldElement { + +} diff --git a/uitest-common/src/main/java/com/vaadin/testbench/customelements/InlineDateTimeFieldElement.java b/uitest-common/src/main/java/com/vaadin/testbench/customelements/InlineDateTimeFieldElement.java new file mode 100644 index 0000000000..27d6d90640 --- /dev/null +++ b/uitest-common/src/main/java/com/vaadin/testbench/customelements/InlineDateTimeFieldElement.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.testbench.customelements; + +import com.vaadin.testbench.elements.InlineDateFieldElement; +import com.vaadin.testbench.elementsbase.ServerClass; + +/** + * @author Vaadin Ltd + * + */ +@ServerClass("com.vaadin.ui.InlineDateTimeField") +public class InlineDateTimeFieldElement extends InlineDateFieldElement { + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateFieldTest.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateFieldTest.java index 369abcc80f..8ae2d48afd 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateFieldTest.java +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateFieldTest.java @@ -7,19 +7,12 @@ import java.util.LinkedHashMap; import java.util.Locale; import com.vaadin.shared.ui.datefield.DateResolution; -import com.vaadin.tests.components.TestDateField; import com.vaadin.tests.components.abstractfield.AbstractFieldTest; import com.vaadin.ui.AbstractLocalDateField; -public class AbstractDateFieldTest +public abstract class AbstractDateFieldTest extends AbstractFieldTest { - @SuppressWarnings("unchecked") - @Override - protected Class getTestClass() { - return (Class) TestDateField.class; - } - private Command setValue = new Command() { @Override diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateTimeFieldTest.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateTimeFieldTest.java new file mode 100644 index 0000000000..522fa118ed --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateTimeFieldTest.java @@ -0,0 +1,130 @@ +package com.vaadin.tests.components.datefield; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.util.LinkedHashMap; +import java.util.Locale; + +import com.vaadin.shared.ui.datefield.DateTimeResolution; +import com.vaadin.tests.components.abstractfield.AbstractFieldTest; +import com.vaadin.ui.AbstractLocalDateTimeField; + +public abstract class AbstractDateTimeFieldTest + extends AbstractFieldTest { + + private Command setValue = new Command() { + + @Override + public void execute(T c, LocalDateTime value, Object data) { + c.setValue(value); + } + }; + + @Override + protected void createActions() { + super.createActions(); + createResolutionSelectAction(CATEGORY_FEATURES); + createBooleanAction("Lenient", CATEGORY_FEATURES, false, + lenientCommand); + createBooleanAction("Show week numbers", CATEGORY_FEATURES, false, + weekNumberCommand); + createDateFormatSelectAction(CATEGORY_FEATURES); + createSetValueAction(CATEGORY_FEATURES); + + } + + private void createSetValueAction(String category) { + LinkedHashMap options = new LinkedHashMap<>(); + options.put("(null)", null); + options.put("(current time)", LocalDateTime.now()); + options.put("2010-12-12", LocalDateTime.of(2010, 12, 12, 6, 34, 23)); + createMultiClickAction("Set value", category, options, setValue, null); + } + + private void createDateFormatSelectAction(String category) { + LinkedHashMap options = new LinkedHashMap<>(); + + options.put("-", null); + options.put("d M yyyy", "d M yyyy"); + options.put("d MM yyyy", "d MM yyyy"); + options.put("d MMM yyyy", "d MMM yyyy"); + options.put("d MMMM yyyy", "d MMMM yyyy"); + options.put("dd M yyyy", "dd M yyyy"); + options.put("ddd M yyyy", "ddd M yyyy"); + options.put("d M y", "d M y"); + options.put("d M yy", "d M yy"); + options.put("d M yyy", "d M yyy"); + options.put("d M yyyy", "d M yyyy"); + options.put("d M 'custom text' yyyy", "d M 'custom text' yyyy"); + options.put("'day:'d', month:'M', year: 'yyyy", + "'day:'d', month:'M', year: 'yyyy"); + options.put(getDatePattern(new Locale("fi", "FI"), DateFormat.LONG), + getDatePattern(new Locale("fi", "FI"), DateFormat.LONG)); + options.put(getDatePattern(new Locale("fi", "FI"), DateFormat.MEDIUM), + getDatePattern(new Locale("fi", "FI"), DateFormat.MEDIUM)); + options.put(getDatePattern(new Locale("fi", "FI"), DateFormat.SHORT), + getDatePattern(new Locale("fi", "FI"), DateFormat.SHORT)); + + createSelectAction("Date format", category, options, "-", + dateFormatCommand); + + } + + private String getDatePattern(Locale locale, int dateStyle) { + DateFormat dateFormat = DateFormat.getDateInstance(dateStyle, locale); + + if (dateFormat instanceof SimpleDateFormat) { + String pattern = ((SimpleDateFormat) dateFormat).toPattern(); + return pattern; + } + return null; + + } + + private void createResolutionSelectAction(String category) { + LinkedHashMap options = new LinkedHashMap<>(); + options.put("Year", DateTimeResolution.YEAR); + options.put("Month", DateTimeResolution.MONTH); + options.put("Day", DateTimeResolution.DAY); + options.put("Hour", DateTimeResolution.HOUR); + options.put("Min", DateTimeResolution.MINUTE); + options.put("Sec", DateTimeResolution.SECOND); + + createSelectAction("Resolution", category, options, "Year", + resolutionCommand); + } + + private Command resolutionCommand = new Command() { + + @Override + public void execute(T c, DateTimeResolution value, Object data) { + c.setResolution(value); + + } + }; + private Command lenientCommand = new Command() { + + @Override + public void execute(T c, Boolean value, Object data) { + c.setLenient(false); + + } + }; + private Command weekNumberCommand = new Command() { + + @Override + public void execute(T c, Boolean value, Object data) { + c.setShowISOWeekNumbers(value); + + } + }; + private Command dateFormatCommand = new Command() { + + @Override + public void execute(T c, String value, Object data) { + c.setDateFormat(value); + } + }; + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/AriaDateTimeDisabled.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/AriaDateTimeDisabled.java new file mode 100644 index 0000000000..9c5b030d87 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/AriaDateTimeDisabled.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractReindeerTestUI; +import com.vaadin.ui.DateTimeField; +import com.vaadin.ui.VerticalLayout; + +public class AriaDateTimeDisabled extends AbstractReindeerTestUI { + + @Override + protected void setup(VaadinRequest request) { + VerticalLayout content = new VerticalLayout(); + content.setMargin(true); + content.setSpacing(true); + + final DateTimeField disabledDateField = new DateTimeField( + "Disabled DateField"); + disabledDateField.setEnabled(false); + + setContent(content); + content.addComponent(disabledDateField); + content.addComponent(new DateTimeField("Enabled DateField")); + } + + @Override + protected String getTestDescription() { + return "Test for aria-disabled attribute on DateField."; + } + + @Override + protected Integer getTicketNumber() { + return 13463; + } +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/CustomDateTimeFormat.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/CustomDateTimeFormat.java new file mode 100644 index 0000000000..90c11b6244 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/CustomDateTimeFormat.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import java.time.LocalDateTime; +import java.util.Locale; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.datefield.DateTimeResolution; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.DateTimeField; + +/** + * @author Vaadin Ltd + * + */ +public class CustomDateTimeFormat extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + setLocale(new Locale("fi", "FI")); + + DateTimeField field = new DateTimeField(); + field.setResolution(DateTimeResolution.SECOND); + field.setWidth("300px"); + + String pattern = "d. MMMM'ta 'yyyy 'klo 'H.mm.ss"; + field.setDateFormat(pattern); + + field.setValue(LocalDateTime.of(2010, 1, 1, 12, 23, 45)); + + addComponent(field); + + } + + @Override + protected String getTestDescription() { + return "Month name should be visible in text box if format pattern includes it"; + } + + @Override + protected Integer getTicketNumber() { + return 3490; + } + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffset.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffset.java index e38dd6895c..2107b9ff61 100644 --- a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffset.java +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffset.java @@ -1,18 +1,17 @@ package com.vaadin.tests.components.datefield; -import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import com.vaadin.server.VaadinRequest; -import com.vaadin.shared.ui.datefield.DateResolution; +import com.vaadin.shared.ui.datefield.DateTimeResolution; import com.vaadin.tests.components.AbstractReindeerTestUI; -import com.vaadin.tests.components.TestDateField; -import com.vaadin.ui.AbstractDateField; +import com.vaadin.ui.DateTimeField; import com.vaadin.ui.Label; public class DateFieldDayResolutionOffset extends AbstractReindeerTestUI { - private final String initialDateString = "09/01/2014"; + private final String initialDateString = "09/01/2014 00:00:00"; @Override protected void setup(VaadinRequest request) { @@ -20,8 +19,7 @@ public class DateFieldDayResolutionOffset extends AbstractReindeerTestUI { dateValue.setId("dateValue"); final DateTimeFormatter dateformat = getDateFormat(); - final AbstractDateField dateField = getDateField( - dateformat); + final DateTimeField dateField = getDateField(dateformat); addComponent(dateValue); addComponent(dateField); @@ -30,19 +28,18 @@ public class DateFieldDayResolutionOffset extends AbstractReindeerTestUI { .setValue(dateformat.format(dateField.getValue()))); } - private AbstractDateField getDateField( - DateTimeFormatter dateformat) { - final AbstractDateField dateField = new TestDateField(); - LocalDate initialDate = dateformat.parse(initialDateString, - LocalDate::from); - dateField.setResolution(DateResolution.DAY); + private DateTimeField getDateField(DateTimeFormatter dateformat) { + final DateTimeField dateField = new DateTimeField(); + LocalDateTime initialDate = dateformat.parse(initialDateString, + LocalDateTime::from); + dateField.setResolution(DateTimeResolution.DAY); dateField.setValue(initialDate); return dateField; } private DateTimeFormatter getDateFormat() { final DateTimeFormatter dateformat = DateTimeFormatter - .ofPattern("MM/dd/yyyy"); + .ofPattern("MM/dd/yyyy HH:mm:ss"); return dateformat; } diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldChangeResolution.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldChangeResolution.java new file mode 100644 index 0000000000..2bf161daf4 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldChangeResolution.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.datefield.DateTimeResolution; +import com.vaadin.tests.components.AbstractReindeerTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.DateTimeField; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; + +public class DateTimeFieldChangeResolution extends AbstractReindeerTestUI { + + public static final String DATEFIELD_ID = "datefield"; + // The ID of a button is BUTTON_BASE_ID + resolution, e.g. button-month + public static final String BUTTON_BASE_ID = "button-"; + + @Override + protected void setup(VaadinRequest request) { + final DateTimeField dateField = new DateTimeField("Enter date"); + dateField.setResolution(DateTimeResolution.YEAR); + dateField.setId(DATEFIELD_ID); + addComponent(dateField); + + Label label = new Label("Select resolution"); + addComponent(label); + HorizontalLayout hlayout = new HorizontalLayout(); + addComponent(hlayout); + for (final DateTimeResolution value : DateTimeResolution.values()) { + String resolutionString = value.toString().toLowerCase(); + Button button = new Button(resolutionString); + button.addClickListener(event -> dateField.setResolution(value)); + button.setId(BUTTON_BASE_ID + resolutionString); + hlayout.addComponent(button); + } + + } + + @Override + protected String getTestDescription() { + return "The calendar should always have the correct resolution and the text field should be empty before selecting a date."; + } + + @Override + protected Integer getTicketNumber() { + return 14174; + } + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldElementUI.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldElementUI.java new file mode 100644 index 0000000000..1481439748 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldElementUI.java @@ -0,0 +1,24 @@ +package com.vaadin.tests.components.datefield; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractReindeerTestUI; +import com.vaadin.ui.DateTimeField; +import com.vaadin.ui.InlineDateTimeField; + +public class DateTimeFieldElementUI extends AbstractReindeerTestUI { + @Override + protected void setup(VaadinRequest request) { + addComponent(new DateTimeField()); + addComponent(new InlineDateTimeField()); + } + + @Override + protected Integer getTicketNumber() { + return 17090; + } + + @Override + protected String getTestDescription() { + return "DateTimeField should be accessible using TB4 DateTimeFieldElement."; + } +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldFastForward.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldFastForward.java new file mode 100644 index 0000000000..5bb2905f14 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldFastForward.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractReindeerTestUI; +import com.vaadin.ui.InlineDateTimeField; + +public class DateTimeFieldFastForward extends AbstractReindeerTestUI { + + @Override + protected void setup(VaadinRequest request) { + addComponent(new InlineDateTimeField()); + } + + @Override + protected String getTestDescription() { + return "Tests that right-click doesn't interfere with fast-forwarding (holding down left mouse button)."; + } + + @Override + protected Integer getTicketNumber() { + return 8012; + } + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldIsValid.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldIsValid.java new file mode 100644 index 0000000000..7213cb229b --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldIsValid.java @@ -0,0 +1,53 @@ +package com.vaadin.tests.components.datefield; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.DateTimeField; + +public class DateTimeFieldIsValid extends AbstractTestUIWithLog { + + @Override + protected String getTestDescription() { + return "A dateField with invalid text should return false in isValid both when " + + "handling ValueChange event and after value is changed."; + } + + @Override + protected Integer getTicketNumber() { + return 14487; + } + + private String pattern = "dd/MM/yy H.mm"; + private DateTimeFormatter format = DateTimeFormatter.ofPattern(pattern); + + @Override + protected void setup(VaadinRequest request) { + final DateTimeField dateField = new DateTimeField("Insert Date: "); + dateField.setDateFormat(pattern); + + dateField.addValueChangeListener(event -> log("valueChange: value: " + + format(dateField.getValue()) + ", is valid: " + + (dateField.getErrorMessage() == null))); + addComponent(dateField); + addButton("check dateField", + event -> log("buttonClick: value: " + + format(dateField.getValue()) + ", is valid: " + + (dateField.getErrorMessage() == null))); + } + + /** + * @since + * @param value + * @return + */ + protected String format(LocalDateTime value) { + if (value != null) { + return format.format(value); + } else { + return null; + } + } +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldKeyboardInput.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldKeyboardInput.java new file mode 100644 index 0000000000..91de230b88 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldKeyboardInput.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import java.time.LocalDateTime; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractReindeerTestUI; +import com.vaadin.ui.DateTimeField; +import com.vaadin.ui.Label; + +public class DateTimeFieldKeyboardInput extends AbstractReindeerTestUI { + + @Override + protected void setup(VaadinRequest request) { + final DateTimeField dateField = new DateTimeField("Select date", + LocalDateTime.of(2014, 1, 15, 7, 2)); + dateField.setDateFormat("dd.MM.yyyy HH:mm"); + addComponent(dateField); + dateField.addValueChangeListener( + event -> addComponent(new Label("Date has been changed."))); + } + + @Override + public Integer getTicketNumber() { + return 16677; + } + + @Override + public String getTestDescription() { + return "When a new date is entered in the text field using the keyboard, pressing the return key after typing the date, " + + "a label with the text 'Date has been changed' should appear."; + } +} \ No newline at end of file diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldReadOnly.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldReadOnly.java new file mode 100644 index 0000000000..3b739579f1 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldReadOnly.java @@ -0,0 +1,45 @@ +package com.vaadin.tests.components.datefield; + +import java.time.LocalDateTime; +import java.util.Locale; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractReindeerTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.DateTimeField; + +public class DateTimeFieldReadOnly extends AbstractReindeerTestUI { + + @Override + protected String getTestDescription() { + return "A read-only DateField should not show the popup button and not be editable."; + } + + @Override + protected Integer getTicketNumber() { + return 3163; + } + + @Override + protected void setup(VaadinRequest request) { + final DateTimeField timeField = new DateTimeField( + "A read-only datefield"); + timeField.setCaption(null); + timeField.setIcon(null); + timeField.setWidth("15em"); + timeField.addStyleName("timeField"); + timeField.setLocale(new Locale("fi")); + + // Set date so that testing always has same time + timeField.setValue(LocalDateTime.of(2009, 6, 12, 7, 34)); + timeField.setReadOnly(true); + + addComponent(timeField); + + Button b = new Button("Switch read-only"); + b.addClickListener( + event -> timeField.setReadOnly(!timeField.isReadOnly())); + + addComponent(b); + } +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldTest.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldTest.java new file mode 100644 index 0000000000..814cf03e53 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import java.util.LinkedHashMap; + +import com.vaadin.ui.DateTimeField; + +/** + * @author Vaadin Ltd + * + */ +public class DateTimeFieldTest + extends AbstractDateTimeFieldTest { + + @Override + protected Class getTestClass() { + return DateTimeField.class; + } + + @Override + protected void createActions() { + super.createActions(); + + createInputPromptSelectAction(CATEGORY_FEATURES); + createTextEnabledAction(CATEGORY_FEATURES); + } + + private void createInputPromptSelectAction(String category) { + LinkedHashMap options = new LinkedHashMap<>(); + options.put("", null); + options.put("Please enter date", "Please enter date"); + options.put("åäöÅÄÖ", "åäöÅÄÖ"); + + createSelectAction("Input prompt", category, options, "", + new Command() { + + @Override + public void execute(DateTimeField c, String value, + Object data) { + c.setPlaceholder(value); + + } + }); + } + + private void createTextEnabledAction(String category) { + this.createBooleanAction("Text field enabled", category, true, + (field, value, data) -> field.setTextFieldEnabled(value)); + } +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/DisabledInlineDateTimeField.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/DisabledInlineDateTimeField.java new file mode 100644 index 0000000000..839da97bb4 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/DisabledInlineDateTimeField.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import java.time.LocalDateTime; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractReindeerTestUI; +import com.vaadin.ui.InlineDateTimeField; + +public class DisabledInlineDateTimeField extends AbstractReindeerTestUI { + + @Override + protected void setup(VaadinRequest request) { + InlineDateTimeField df = new InlineDateTimeField("Disabled"); + LocalDateTime date = LocalDateTime.of(2014, 6, 5, 11, 34); + df.setValue(date); + df.setEnabled(false); + addComponent(df); + + df = new InlineDateTimeField("Read-only"); + df.setValue(date); + df.setReadOnly(true); + addComponent(df); + } + + @Override + protected String getTestDescription() { + return "Testing disabled and read-only modes of InlineDateField."; + } + + @Override + protected Integer getTicketNumber() { + return 10262; + } + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/InlineDateTimeFieldTest.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/InlineDateTimeFieldTest.java new file mode 100644 index 0000000000..5beedfcff4 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/InlineDateTimeFieldTest.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import com.vaadin.ui.InlineDateTimeField; + +/** + * @author Vaadin Ltd + * + */ +public class InlineDateTimeFieldTest + extends AbstractDateTimeFieldTest { + + @Override + protected Class getTestClass() { + return InlineDateTimeField.class; + } + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/PopupDateTimeFieldStates.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/PopupDateTimeFieldStates.java new file mode 100644 index 0000000000..60c46d2f74 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/PopupDateTimeFieldStates.java @@ -0,0 +1,68 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import java.time.LocalDateTime; +import java.util.Locale; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.DateTimeField; +import com.vaadin.ui.GridLayout; + +/** + * @author Vaadin Ltd + * + */ +public class PopupDateTimeFieldStates extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + setLocale(Locale.ENGLISH); + final GridLayout gridLayout = new GridLayout(2, 2); + gridLayout.setSpacing(true); + + gridLayout.addComponent(createPopupDateTimeField(true, true)); + gridLayout.addComponent(createPopupDateTimeField(true, false)); + gridLayout.addComponent(createPopupDateTimeField(false, true)); + gridLayout.addComponent(createPopupDateTimeField(false, false)); + + getLayout().addComponent(gridLayout); + } + + @Override + protected String getTestDescription() { + return "Test that PopupDateTimeField is rendered consistently across browsers"; + } + + @Override + protected Integer getTicketNumber() { + return 14565; + } + + private static DateTimeField createPopupDateTimeField(final boolean enabled, + final boolean textFieldEnabled) { + final DateTimeField popupDatefield = new DateTimeField(); + + popupDatefield.setValue(LocalDateTime.of(2014, 9, 3, 10, 34)); + popupDatefield.setCaption("Enabled: " + enabled + + ", Text field enabled: " + textFieldEnabled); + popupDatefield.setEnabled(enabled); + popupDatefield.setTextFieldEnabled(textFieldEnabled); + return popupDatefield; + } + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/PopupTimeClosingWithEsc.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/PopupTimeClosingWithEsc.java new file mode 100644 index 0000000000..2b16397435 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/PopupTimeClosingWithEsc.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.datefield.DateTimeResolution; +import com.vaadin.tests.components.AbstractReindeerTestUI; +import com.vaadin.ui.DateTimeField; +import com.vaadin.ui.VerticalLayout; + +public class PopupTimeClosingWithEsc extends AbstractReindeerTestUI { + + @Override + protected void setup(VaadinRequest request) { + DateTimeField secondResolution = new DateTimeField("Second"); + secondResolution.setId("second"); + secondResolution.setResolution(DateTimeResolution.SECOND); + + DateTimeField minuteResolution = new DateTimeField("Minute"); + minuteResolution.setId("minute"); + minuteResolution.setResolution(DateTimeResolution.MINUTE); + + DateTimeField hourResolution = new DateTimeField("Hour"); + hourResolution.setId("hour"); + hourResolution.setResolution(DateTimeResolution.HOUR); + + DateTimeField month = new DateTimeField("Month"); + month.setId("month"); + month.setResolution(DateTimeResolution.MONTH); + + VerticalLayout layout = new VerticalLayout(); + layout.setMargin(true); + layout.setSpacing(true); + layout.addComponents(secondResolution, minuteResolution, hourResolution, + month); + setContent(layout); + } + + @Override + protected String getTestDescription() { + return "Testing that the DateField popup can be closed with ESC key."; + } + + @Override + protected Integer getTicketNumber() { + return 12317; + } + +} diff --git a/uitest/src/main/java/com/vaadin/tests/components/datefield/TimePopupSelection.java b/uitest/src/main/java/com/vaadin/tests/components/datefield/TimePopupSelection.java new file mode 100644 index 0000000000..ab63506649 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/datefield/TimePopupSelection.java @@ -0,0 +1,50 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.datefield.DateTimeResolution; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.DateTimeField; + +/** + * @author Vaadin Ltd + * + */ +public class TimePopupSelection extends AbstractTestUIWithLog { + + private static final DateTimeFormatter FORMATTER = DateTimeFormatter + .ofPattern("dd/MM/yyyy HH:mm:ss"); + + @Override + protected void setup(VaadinRequest request) { + setLocale(Locale.ENGLISH); + DateTimeField field = new DateTimeField(); + field.setResolution(DateTimeResolution.SECOND); + + field.setValue(LocalDateTime.of(2017, 1, 13, 1, 0)); + + field.addValueChangeListener( + event -> log(FORMATTER.format(event.getValue()))); + + addComponent(field); + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/AriaDateTimeDisabledTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/AriaDateTimeDisabledTest.java new file mode 100644 index 0000000000..b5e420e45b --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/AriaDateTimeDisabledTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class AriaDateTimeDisabledTest extends MultiBrowserTest { + + @Test + public void verifyAriaDisabledAttributes() { + openTestURL(); + + // Expect aria-disabled="false" on the enabled DateField. + String ariaDisabled = driver + .findElement(By + .vaadin("/VVerticalLayout[0]/VPopupTimeCalendar[1]#popupButton")) + .getAttribute("aria-disabled"); + assertEquals("false", ariaDisabled); + + // Expect aria-disabled="true" on the disabled DateField. + ariaDisabled = driver.findElement(By.cssSelector(".v-disabled button")) + .getAttribute("aria-disabled"); + assertEquals("true", ariaDisabled); + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/CustomDateTimeFormatTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/CustomDateTimeFormatTest.java new file mode 100644 index 0000000000..3016ef6cc8 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/CustomDateTimeFormatTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * @author Vaadin Ltd + * + */ +public class CustomDateTimeFormatTest extends MultiBrowserTest { + + @Test + public void checkCustomDateFormat() { + openTestURL(); + + String text = findElement(By.tagName("input")).getAttribute("value"); + assertEquals("1. tammikuuta 2010 klo 12.23.45", text); + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffsetTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffsetTest.java index 9817ec4394..26f94be717 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffsetTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffsetTest.java @@ -21,7 +21,7 @@ public class DateFieldDayResolutionOffsetTest extends MultiBrowserTest { select2ndOfSeptember(); LabelElement dateValue = $(LabelElement.class).id("dateValue"); - assertThat(dateValue.getText(), is("09/02/2014")); + assertThat(dateValue.getText(), is("09/02/2014 00:00:00")); } private void select2ndOfSeptember() { diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldChangeResolutionTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldChangeResolutionTest.java new file mode 100644 index 0000000000..cd0b6212c7 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldChangeResolutionTest.java @@ -0,0 +1,199 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import static com.vaadin.tests.components.datefield.DateFieldChangeResolution.BUTTON_BASE_ID; +import static com.vaadin.tests.components.datefield.DateFieldChangeResolution.DATEFIELD_ID; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; + +import com.vaadin.shared.ui.datefield.DateTimeResolution; +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class DateTimeFieldChangeResolutionTest extends MultiBrowserTest { + + private WebElement dateFieldButton, textField; + private WebElement resolutionSecond, resolutionMinute, resolutionHour, + resolutionDay, resolutionMonth, resolutionYear; + + @Test + public void changeResolutionBetweenYearAndMonth() throws Exception { + initialize(); + click(resolutionMonth); + checkHeaderAndBody(DateTimeResolution.MONTH, true); + click(resolutionYear); + checkHeaderAndBody(DateTimeResolution.YEAR, true); + } + + @Test + public void changeResolutionBetweenYearAndSecond() throws Exception { + initialize(); + click(resolutionSecond); + checkHeaderAndBody(DateTimeResolution.SECOND, true); + click(resolutionYear); + checkHeaderAndBody(DateTimeResolution.YEAR, true); + } + + @Test + public void changeResolutionToDayThenMonth() throws Exception { + initialize(); + checkHeaderAndBody(DateTimeResolution.YEAR, true); // check the initial + // state + click(resolutionDay); + checkHeaderAndBody(DateTimeResolution.DAY, true); + click(resolutionMonth); + checkHeaderAndBody(DateTimeResolution.MONTH, true); + } + + @Test + public void setDateAndChangeResolution() throws Exception { + initialize(); + // Set the date to previous month. + click(resolutionMonth); + openPopupDateField(); + click(driver.findElement(By.className("v-button-prevmonth"))); + closePopupDateField(); + assertFalse( + "The text field of the calendar should not be empty after selecting a date", + textField.getAttribute("value").isEmpty()); + // Change resolutions and check that the selected date is not lost and + // that the calendar has the correct resolution. + click(resolutionHour); + checkHeaderAndBody(DateTimeResolution.HOUR, false); + click(resolutionYear); + checkHeaderAndBody(DateTimeResolution.YEAR, false); + click(resolutionMinute); + checkHeaderAndBody(DateTimeResolution.MINUTE, false); + } + + private void initialize() { + openTestURL(); + WebElement dateField = driver.findElement(By.id(DATEFIELD_ID)); + dateFieldButton = dateField + .findElement(By.className("v-datefield-button")); + textField = dateField + .findElement(By.className("v-datefield-textfield")); + resolutionSecond = driver.findElement(By.id(BUTTON_BASE_ID + "second")); + resolutionMinute = driver.findElement(By.id(BUTTON_BASE_ID + "minute")); + resolutionHour = driver.findElement(By.id(BUTTON_BASE_ID + "hour")); + resolutionDay = driver.findElement(By.id(BUTTON_BASE_ID + "day")); + resolutionMonth = driver.findElement(By.id(BUTTON_BASE_ID + "month")); + resolutionYear = driver.findElement(By.id(BUTTON_BASE_ID + "year")); + } + + private void checkHeaderAndBody(DateTimeResolution resolution, + boolean textFieldIsEmpty) throws Exception { + // Popup date field has all kinds of strange timers on the + // client side + sleep(100); + // Open the popup calendar, perform checks and close the popup. + openPopupDateField(); + if (resolution.compareTo(DateTimeResolution.MONTH) <= 0) { + checkMonthHeader(); + } else { + checkYearHeader(); + } + if (resolution.compareTo(DateTimeResolution.DAY) <= 0) { + assertTrue( + "A calendar with the chosen resolution should have a body", + calendarHasBody()); + } else { + assertFalse( + "A calendar with the chosen resolution should not have a body", + calendarHasBody()); + } + if (textFieldIsEmpty) { + assertTrue("The text field of the calendar should be empty", + textField.getAttribute("value").isEmpty()); + } else { + assertFalse("The text field of the calendar should not be empty", + textField.getAttribute("value").isEmpty()); + } + closePopupDateField(); + } + + private void checkMonthHeader() { + checkHeaderForYear(); + checkHeaderForMonth(true); + } + + private void checkYearHeader() { + checkHeaderForYear(); + checkHeaderForMonth(false); + } + + private boolean calendarHasBody() { + return isElementPresent(By.className("v-datefield-calendarpanel-body")); + } + + private void checkHeaderForMonth(boolean buttonsExpected) { + // If buttonsExpected is true, check that there are buttons for changing + // the month. Otherwise check that there are no such buttons. + if (buttonsExpected) { + assertTrue( + "The calendar should have a button for switching to the previous month", + isElementPresent(By.cssSelector( + ".v-datefield-calendarpanel-header .v-datefield-calendarpanel-prevmonth .v-button-prevmonth"))); + assertTrue( + "The calendar should have a button for switching to the next month", + isElementPresent(By.cssSelector( + ".v-datefield-calendarpanel-header .v-datefield-calendarpanel-nextmonth .v-button-nextmonth"))); + } else { + assertFalse( + "The calendar should not have a button for switching to the previous month", + isElementPresent(By.cssSelector( + ".v-datefield-calendarpanel-header .v-datefield-calendarpanel-prevmonth .v-button-prevmonth"))); + assertFalse( + "The calendar should not have a button for switching to the next month", + isElementPresent(By.cssSelector( + ".v-datefield-calendarpanel-header .v-datefield-calendarpanel-nextmonth .v-button-nextmonth"))); + } + } + + private void checkHeaderForYear() { + assertTrue( + "The calendar should have a button for switching to the previous year", + isElementPresent(By.cssSelector( + ".v-datefield-calendarpanel-header .v-datefield-calendarpanel-prevyear .v-button-prevyear"))); + assertTrue("The calendar header should show the selected year", + isElementPresent(By.cssSelector( + ".v-datefield-calendarpanel-header .v-datefield-calendarpanel-month"))); + assertTrue( + "The calendar should have a button for switching to the next year", + isElementPresent(By.cssSelector( + ".v-datefield-calendarpanel-header .v-datefield-calendarpanel-nextyear .v-button-nextyear"))); + + } + + private void click(WebElement element) { + testBenchElement(element).click(5, 5); + } + + private void openPopupDateField() { + click(dateFieldButton); + } + + private void closePopupDateField() { + WebElement element = driver + .findElement(By.cssSelector(".v-datefield-calendarpanel")); + element.sendKeys(Keys.ESCAPE); + } +} \ No newline at end of file diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldElementTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldElementTest.java new file mode 100644 index 0000000000..4b2b2719c4 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldElementTest.java @@ -0,0 +1,26 @@ +package com.vaadin.tests.components.datefield; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +import org.junit.Test; + +import com.vaadin.testbench.customelements.DateTimeFieldElement; +import com.vaadin.testbench.customelements.InlineDateTimeFieldElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class DateTimeFieldElementTest extends SingleBrowserTest { + + @Test + public void dateFieldElementIsLocated() { + openTestURL(); + + assertThat($(DateTimeFieldElement.class).all().size(), is(1)); + assertThat($(InlineDateTimeFieldElement.class).all().size(), is(1)); + } + + @Override + protected Class getUIClass() { + return DateTimeFieldElementUI.class; + } +} \ No newline at end of file diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldFastForwardTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldFastForwardTest.java new file mode 100644 index 0000000000..f42afe1779 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldFastForwardTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import static org.junit.Assert.assertEquals; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.elements.VerticalLayoutElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class DateTimeFieldFastForwardTest extends MultiBrowserTest { + + @Test + public void testFastForwardOnRightMouseClick() throws Exception { + openTestURL(); + String firstMonth = getSelectedMonth(); + WebElement nextMonthButton = driver + .findElement(By.className("v-button-nextmonth")); + + // Click and hold left mouse button to start fast forwarding. + new Actions(driver).clickAndHold(nextMonthButton).perform(); + sleep(1000); + + // Right click and release the left button. + + new Actions(driver).contextClick(nextMonthButton) + .release(nextMonthButton).perform(); + + // Now the fast forwarding should be ended, get the expected month. + String expectedMonth = getSelectedMonth(); + + // Make browser context menu disappear, since it will crash IE + $(VerticalLayoutElement.class).first().click(); + + Assert.assertFalse("Month did not change during fast forward", + firstMonth.equals(expectedMonth)); + + // Wait for a while. + Thread.sleep(1000); + + // Verify that we didn't fast forward any further after the left button + // was released. + String actualMonth = getSelectedMonth(); + assertEquals(expectedMonth, actualMonth); + } + + private String getSelectedMonth() { + return driver + .findElement( + By.className("v-inline-datefield-calendarpanel-month")) + .getText(); + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldIsValidTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldIsValidTest.java new file mode 100644 index 0000000000..986db81c93 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldIsValidTest.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.customelements.AbstractDateFieldElement; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * @author Vaadin Ltd + */ +public class DateTimeFieldIsValidTest extends MultiBrowserTest { + + @Test + public void testInvalidText() throws Exception { + openTestURL(); + + waitForElementVisible(By.id("Log")); + waitForElementVisible(By.className("v-datefield")); + WebElement dateTextbox = $(AbstractDateFieldElement.class).first() + .findElement(By.className("v-textfield")); + ButtonElement button = $(ButtonElement.class).first(); + + dateTextbox.sendKeys("01/01/01 1.12", Keys.TAB); + assertLogText("1. valueChange: value: 01/01/01 1.12, is valid: true"); + button.click(); + assertLogText("2. buttonClick: value: 01/01/01 1.12, is valid: true"); + + dateTextbox.sendKeys("lala", Keys.TAB); + assertLogText("3. valueChange: value: null, is valid: false"); + button.click(); + assertLogText("4. buttonClick: value: null, is valid: false"); + + dateTextbox.clear(); + dateTextbox.sendKeys("02/02/02 2.34", Keys.TAB); + assertLogText("5. valueChange: value: 02/02/02 2.34, is valid: true"); + button.click(); + assertLogText("6. buttonClick: value: 02/02/02 2.34, is valid: true"); + } + + private void assertLogText(String expected) throws Exception { + String text = findElement(By.vaadin("PID_SLog_row_0")).getText(); + Assert.assertTrue("Expected '" + expected + "' found '" + text + "'", + text.equals(expected)); + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldKeyboardInputTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldKeyboardInputTest.java new file mode 100644 index 0000000000..715e3e67f7 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldKeyboardInputTest.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.customelements.DateTimeFieldElement; +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class DateTimeFieldKeyboardInputTest extends MultiBrowserTest { + + @Test + public void testValueChangeEvent() { + openTestURL(); + WebElement dateFieldText = $(DateTimeFieldElement.class).first() + .findElement(By.tagName("input")); + dateFieldText.clear(); + int numLabelsBeforeUpdate = $(LabelElement.class).all().size(); + dateFieldText.sendKeys("20.10.2013 7:2", Keys.RETURN); + int numLabelsAfterUpdate = $(LabelElement.class).all().size(); + assertTrue("Changing the date failed.", + numLabelsAfterUpdate == numLabelsBeforeUpdate + 1); + } +} \ No newline at end of file diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldReadOnlyTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldReadOnlyTest.java new file mode 100644 index 0000000000..a4e79ac95a --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldReadOnlyTest.java @@ -0,0 +1,46 @@ +package com.vaadin.tests.components.datefield; + +import java.io.IOException; + +import org.junit.Test; +import org.openqa.selenium.Keys; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.customelements.AbstractDateFieldElement; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class DateTimeFieldReadOnlyTest extends MultiBrowserTest { + + @Test + public void readOnlyDateFieldPopupShouldNotOpen() + throws IOException, InterruptedException { + openTestURL(); + + compareScreen("initial-date"); + toggleReadOnly(); + + openPopup(); + compareScreen("readwrite-popup-date"); + + closePopup(); + toggleReadOnly(); + compareScreen("readonly-date"); + } + + private void closePopup() { + findElement(By.className("v-datefield-calendarpanel")) + .sendKeys(Keys.RETURN); + } + + private void openPopup() { + // waiting for openPopup() in TB4 beta1: + // http://dev.vaadin.com/ticket/13766 + $(AbstractDateFieldElement.class).first() + .findElement(By.tagName("button")).click(); + } + + private void toggleReadOnly() { + $(ButtonElement.class).caption("Switch read-only").first().click(); + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/AbstractDateFieldTestTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldTestTest.java similarity index 97% rename from uitest/src/test/java/com/vaadin/tests/components/datefield/AbstractDateFieldTestTest.java rename to uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldTestTest.java index caaf84b8f7..e835ac5158 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/datefield/AbstractDateFieldTestTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldTestTest.java @@ -25,7 +25,11 @@ import org.openqa.selenium.interactions.Actions; import com.vaadin.testbench.elements.NotificationElement; import com.vaadin.tests.tb3.MultiBrowserTest; -public class AbstractDateFieldTestTest extends MultiBrowserTest { +/** + * @author Vaadin Ltd + * + */ +public class DateTimeFieldTestTest extends MultiBrowserTest { @Test public void testMakingRequired() throws InterruptedException { diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/DisabledInlineDateTimeFieldTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DisabledInlineDateTimeFieldTest.java new file mode 100644 index 0000000000..621666c924 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/DisabledInlineDateTimeFieldTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class DisabledInlineDateTimeFieldTest extends MultiBrowserTest { + + @Test + public void testDisabled() { + openTestURL(); + testNextMonthControls(".v-disabled"); + testDaySelection(".v-disabled"); + } + + @Test + public void testReadOnly() { + openTestURL(); + testNextMonthControls(".v-readonly"); + testDaySelection(".v-readonly"); + } + + private void testNextMonthControls(String cssClass) { + // Get the currently selected month. + String expectedMonth = getSelectedMonth(cssClass); + + // Attempt to click the next month button. + driver.findElement(By.cssSelector(cssClass + " .v-button-nextmonth")) + .click(); + + // Assert that we did not navigate to next month. + String actualMonth = getSelectedMonth(cssClass); + assertEquals(expectedMonth, actualMonth); + } + + private void testDaySelection(String cssClass) { + // We know that the first day element is not selected, because of the + // fixed date in the test. + WebElement nonSelectedDay = driver.findElement(By.cssSelector( + cssClass + " .v-inline-datefield-calendarpanel-day")); + + // Assert it is not selected before click. + assertFalse(nonSelectedDay.getAttribute("class").contains("selected")); + + // Click on the non-selected day. + nonSelectedDay.click(); + + // Assert that clicking did not select the day. + assertFalse(nonSelectedDay.getAttribute("class").contains("selected")); + } + + private String getSelectedMonth(String selectorPrefix) { + return driver + .findElement(By.cssSelector(selectorPrefix + + " .v-inline-datefield-calendarpanel-month")) + .getText(); + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/InlineDateTimeFieldTestTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/InlineDateTimeFieldTestTest.java new file mode 100644 index 0000000000..1ddd9ca6b0 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/InlineDateTimeFieldTestTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import org.junit.Test; + +import com.google.gwt.editor.client.Editor.Ignore; + +/** + * Reuse tests from super DateTimeFieldTestTest class. + * + * @author Vaadin Ltd + * + */ +public class InlineDateTimeFieldTestTest extends DateTimeFieldTestTest { + + @Override + @Test + @Ignore + public void testValueAfterOpeningPopupInRequiredField() + throws InterruptedException { + // no popup for inline date field + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/PopupDateFieldStatesTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/PopupDateFieldStatesTest.java index 5f82bab831..7d642faf70 100644 --- a/uitest/src/test/java/com/vaadin/tests/components/datefield/PopupDateFieldStatesTest.java +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/PopupDateFieldStatesTest.java @@ -1,8 +1,11 @@ package com.vaadin.tests.components.datefield; import java.io.IOException; +import java.util.regex.Pattern; import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; import com.vaadin.tests.tb3.MultiBrowserTest; @@ -13,6 +16,15 @@ public class PopupDateFieldStatesTest extends MultiBrowserTest { throws IOException, InterruptedException { openTestURL(); + // wait until loading indicator becomes invisible + WebElement loadingIndicator = findElement( + By.className("v-loading-indicator")); + Pattern pattern = Pattern.compile("display: *none;"); + waitUntil(driver -> { + return pattern.matcher(loadingIndicator.getAttribute("style")) + .find(); + }); + compareScreen("dateFieldStates"); } diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/PopupDateTimeFieldStatesTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/PopupDateTimeFieldStatesTest.java new file mode 100644 index 0000000000..bd97b6342b --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/PopupDateTimeFieldStatesTest.java @@ -0,0 +1,31 @@ +package com.vaadin.tests.components.datefield; + +import java.io.IOException; +import java.util.regex.Pattern; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class PopupDateTimeFieldStatesTest extends MultiBrowserTest { + + @Test + public void readOnlyDateFieldPopupShouldNotOpen() + throws IOException, InterruptedException { + openTestURL(); + + // wait until loading indicator becomes invisible + WebElement loadingIndicator = findElement( + By.className("v-loading-indicator")); + Pattern pattern = Pattern.compile("display: *none;"); + waitUntil(driver -> { + return pattern.matcher(loadingIndicator.getAttribute("style")) + .find(); + }); + + compareScreen("dateFieldStates"); + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/PopupTimeClosingWithEscTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/PopupTimeClosingWithEscTest.java new file mode 100644 index 0000000000..6bb7a67e1c --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/PopupTimeClosingWithEscTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; + +import com.vaadin.testbench.annotations.RunLocally; +import com.vaadin.testbench.parallel.Browser; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class PopupTimeClosingWithEscTest extends MultiBrowserTest { + + @Test + public void testPopupClosing() { + openTestURL(); + + testPopupClosing("second"); + testPopupClosing("minute"); + testPopupClosing("hour"); + testPopupClosing("month"); + } + + private void testPopupClosing(String dateFieldId) { + openPopup(dateFieldId); + assertTrue(isPopupVisible()); + sendEscToCalendarPanel(); + assertFalse(isPopupVisible()); + } + + private void openPopup(String dateFieldId) { + driver.findElement( + vaadinLocator("PID_S" + dateFieldId + "#popupButton")).click(); + } + + private boolean isPopupVisible() { + return !(driver.findElements(By.cssSelector(".v-datefield-popup")) + .isEmpty()); + } + + private void sendEscToCalendarPanel() { + driver.findElement(By.cssSelector(".v-datefield-calendarpanel")) + .sendKeys(Keys.ESCAPE); + } + +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/datefield/TimePopupSelectionTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/TimePopupSelectionTest.java new file mode 100644 index 0000000000..a6226de976 --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/datefield/TimePopupSelectionTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.datefield; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.Select; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.customelements.DateTimeFieldElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * @author Vaadin Ltd + * + */ +public class TimePopupSelectionTest extends MultiBrowserTest { + + @Test + public void selectDateAndTimeFromPopup() { + openTestURL(); + + DateTimeFieldElement field = $(DateTimeFieldElement.class).first(); + Assert.assertEquals("1/13/17 01:00:00 AM", field.getValue()); + + field.openPopup(); + + List timeSelects = findElement( + By.className("v-datefield-calendarpanel-time")) + .findElements(By.tagName("select")); + + new Select(timeSelects.get(0)).selectByValue("09"); + Assert.assertEquals("1/13/17 09:00:00 AM", field.getValue()); + + new Select(timeSelects.get(1)).selectByValue("35"); + Assert.assertEquals("1/13/17 09:35:00 AM", field.getValue()); + + new Select(timeSelects.get(2)).selectByValue("41"); + Assert.assertEquals("1/13/17 09:35:41 AM", field.getValue()); + + closePopup(); + + waitUntil(driver -> getLogRow(0).equals("1. 13/01/2017 09:35:41")); + } + + private void closePopup() { + findElement(By.className("v-datefield-calendarpanel")) + .sendKeys(Keys.ENTER); + } +} -- 2.39.5