]> source.dussan.org Git - vaadin-framework.git/commitdiff
Introduce DateTimeFile and InlineDateTimeField. (#8218)
authorDenis <denis@vaadin.com>
Thu, 19 Jan 2017 08:01:03 +0000 (10:01 +0200)
committerGitHub <noreply@github.com>
Thu, 19 Jan 2017 08:01:03 +0000 (10:01 +0200)
* Introduce DateTimeFile and InlineDateTimeField.

Fixes #8132

* Correct and provide declarative tests.

* Provide a date converter and UI tests.

71 files changed:
client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java
client/src/main/java/com/vaadin/client/ui/VAbstractDateFieldCalendar.java
client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java
client/src/main/java/com/vaadin/client/ui/VDateField.java
client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java
client/src/main/java/com/vaadin/client/ui/VDateTimeCalendarPanel.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/ui/VDateTimeFieldCalendar.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java
client/src/main/java/com/vaadin/client/ui/VPopupTimeCalendar.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/ui/datefield/AbstractInlineDateFieldConnector.java
client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java
client/src/main/java/com/vaadin/client/ui/datefield/DateTimeFieldConnector.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java
client/src/main/java/com/vaadin/client/ui/datefield/InlineDateTimeFieldConnector.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/ui/datefield/PopupDateFieldConnector.java
client/src/main/java/com/vaadin/client/ui/datefield/PopupDateTimeFieldConnector.java [new file with mode: 0644]
client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java
server/src/main/java/com/vaadin/data/converter/LocalDateTimeToDateConverter.java [new file with mode: 0644]
server/src/main/java/com/vaadin/data/validator/DateTimeRangeValidator.java [new file with mode: 0644]
server/src/main/java/com/vaadin/ui/AbstractLocalDateField.java
server/src/main/java/com/vaadin/ui/AbstractLocalDateTimeField.java
server/src/main/java/com/vaadin/ui/DateTimeField.java [new file with mode: 0644]
server/src/main/java/com/vaadin/ui/InlineDateTimeField.java [new file with mode: 0644]
server/src/main/java/com/vaadin/ui/declarative/DesignFormatter.java
server/src/main/java/com/vaadin/ui/declarative/converters/DesignLocalDateTimeConverter.java [new file with mode: 0644]
server/src/test/java/com/vaadin/tests/data/converter/LocalDateTimeToDateConverterTest.java [new file with mode: 0644]
server/src/test/java/com/vaadin/tests/server/component/abstractdatefield/AbstractLocalDateTimeFieldDeclarativeTest.java [new file with mode: 0644]
server/src/test/java/com/vaadin/tests/server/component/abstractfield/AbstractFieldDeclarativeTest.java
server/src/test/java/com/vaadin/tests/server/component/colorpicker/AbstractColorPickerDeclarativeTest.java
server/src/test/java/com/vaadin/tests/server/component/datefield/DateTimeFieldDeclarativeTest.java [new file with mode: 0644]
server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateTimeFieldDeclarativeTest.java [new file with mode: 0644]
shared/src/main/java/com/vaadin/shared/ui/datefield/DateTimeResolution.java
shared/src/main/java/com/vaadin/shared/ui/datefield/InlineDateTimeFieldState.java [new file with mode: 0644]
shared/src/main/java/com/vaadin/shared/ui/datefield/LocalDateTimeFieldState.java
tests/screenshots
uitest-common/src/main/java/com/vaadin/testbench/customelements/DateTimeFieldElement.java [new file with mode: 0644]
uitest-common/src/main/java/com/vaadin/testbench/customelements/InlineDateTimeFieldElement.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateFieldTest.java
uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateTimeFieldTest.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/AriaDateTimeDisabled.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/CustomDateTimeFormat.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffset.java
uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldChangeResolution.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldElementUI.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldFastForward.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldIsValid.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldKeyboardInput.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldReadOnly.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/DateTimeFieldTest.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/DisabledInlineDateTimeField.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/InlineDateTimeFieldTest.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/PopupDateTimeFieldStates.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/PopupTimeClosingWithEsc.java [new file with mode: 0644]
uitest/src/main/java/com/vaadin/tests/components/datefield/TimePopupSelection.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/AbstractDateFieldTestTest.java [deleted file]
uitest/src/test/java/com/vaadin/tests/components/datefield/AriaDateTimeDisabledTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/CustomDateTimeFormatTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffsetTest.java
uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldChangeResolutionTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldElementTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldFastForwardTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldIsValidTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldKeyboardInputTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldReadOnlyTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldTestTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/DisabledInlineDateTimeFieldTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/InlineDateTimeFieldTestTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/PopupDateFieldStatesTest.java
uitest/src/test/java/com/vaadin/tests/components/datefield/PopupDateTimeFieldStatesTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/PopupTimeClosingWithEscTest.java [new file with mode: 0644]
uitest/src/test/java/com/vaadin/tests/components/datefield/TimePopupSelectionTest.java [new file with mode: 0644]

index 601348fea41ccd73dbb4d66f00c8637a7180c22f..8a2ef4d8d536a3c9398f3b63062e8753705a64fd 100644 (file)
@@ -421,11 +421,21 @@ public abstract class VAbstractCalendarPanel<R extends Enum<R>>
         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<R extends Enum<R>>
 
     }
 
-    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<R> getDateField() {
+        return parent;
+    }
+
     public void setDateTimeService(DateTimeService dateTimeService) {
         this.dateTimeService = dateTimeService;
     }
@@ -721,7 +747,7 @@ public abstract class VAbstractCalendarPanel<R extends Enum<R>>
         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<R extends Enum<R>>
                 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<R extends Enum<R>>
             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<R extends Enum<R>>
                 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<R extends Enum<R>>
                         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<R extends Enum<R>>
      */
     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<R extends Enum<R>>
         }
 
         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<R extends Enum<R>>
         }
 
         private void setLabel() {
-            if (parent instanceof VAbstractPopupCalendar) {
-                ((VAbstractPopupCalendar) parent).setFocusedDate(this);
+            if (getDateField() instanceof VAbstractPopupCalendar) {
+                ((VAbstractPopupCalendar) getDateField()).setFocusedDate(this);
             }
         }
     }
index 4e6aa2602003679a73870b2c40baf749ebb03377..a5e47336441c858363c1ddd5cbcf208d367e7204 100644 (file)
@@ -23,14 +23,13 @@ import com.vaadin.client.ui.VAbstractCalendarPanel.SubmitListener;
 /**
  * A client side implementation for inline date field.
  */
-public abstract class VAbstractDateFieldCalendar<R extends Enum<R>>
+public abstract class VAbstractDateFieldCalendar<PANEL extends VAbstractCalendarPanel<R>, R extends Enum<R>>
         extends VDateField<R> {
 
     /** For internal use only. May be removed or replaced in the future. */
-    public final VAbstractCalendarPanel<R> calendarPanel;
+    public final PANEL calendarPanel;
 
-    public VAbstractDateFieldCalendar(VAbstractCalendarPanel<R> panel,
-            R resolution) {
+    public VAbstractDateFieldCalendar(PANEL panel, R resolution) {
         super(resolution);
         calendarPanel = panel;
         calendarPanel.setParentField(this);
index 74530835f1100b5a7f3b02aba72b7e29f0470748..3cac761272a80c42ce4d71a55afb7c6301485fdf 100644 (file)
@@ -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;
  * <code>setCalendarPanel(VAbstractCalendarPanel panel)</code> method.
  *
  */
-public abstract class VAbstractPopupCalendar<R extends Enum<R>>
+public abstract class VAbstractPopupCalendar<PANEL extends VAbstractCalendarPanel<R>, R extends Enum<R>>
         extends VAbstractTextualDate<R>
         implements Field, ClickHandler, CloseHandler<PopupPanel>, SubPartAware {
 
@@ -71,7 +70,7 @@ public abstract class VAbstractPopupCalendar<R extends Enum<R>>
     public final Button calendarToggle = new Button();
 
     /** For internal use only. May be removed or replaced in the future. */
-    public VAbstractCalendarPanel<R> 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<R extends Enum<R>>
 
     private final String CALENDAR_TOGGLE_ID = "popupButton";
 
-    public VAbstractPopupCalendar(VAbstractCalendarPanel<R> calendarPanel,
-            R resolution) {
+    public VAbstractPopupCalendar(PANEL calendarPanel, R resolution) {
         super(resolution);
 
         calendarToggle.setText("");
@@ -230,8 +228,7 @@ public abstract class VAbstractPopupCalendar<R extends Enum<R>>
             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(),
index e7a49ef5be769dec6e9b6b180668c781f036f057..25529fb5833340b8bc155b8ef0a286bfe4aefba6 100644 (file)
@@ -230,11 +230,11 @@ public abstract class VDateField<R extends Enum<R>> 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<R, Integer> dateVaules);
+    protected abstract Date getDate(Map<R, Integer> dateValues);
 
     /**
      * Returns all available resolutions as an array.
index 8c3c3a805f0895bbe321b6674671046535ef8f3f..ec0f71c72d2d70420f2b6e11a48828d804bc88d5 100644 (file)
@@ -28,7 +28,7 @@ import com.vaadin.shared.ui.datefield.DateResolution;
  *
  */
 public class VDateFieldCalendar
-        extends VAbstractDateFieldCalendar<DateResolution> {
+        extends VAbstractDateFieldCalendar<VDateCalendarPanel, DateResolution> {
 
     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 (file)
index 0000000..409afd7
--- /dev/null
@@ -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<DateTimeResolution> {
+
+    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("&nbsp;"));
+                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 (file)
index 0000000..53becf6
--- /dev/null
@@ -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<VDateTimeCalendarPanel, DateTimeResolution> {
+
+    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<DateTimeResolution, Integer> dateValues) {
+        return VPopupTimeCalendar.makeDate(dateValues);
+    }
+
+    @Override
+    protected DateTimeResolution[] doGetResolutions() {
+        return DateTimeResolution.values();
+    }
+
+}
index 20425faa8ba57de092d30abe6b994044eb76a602..b05224f07ec819a51ff431e5d6dcf02f0e9a1cf9 100644 (file)
@@ -28,7 +28,8 @@ import com.vaadin.shared.ui.datefield.DateResolution;
  * @author Vaadin Ltd
  *
  */
-public class VPopupCalendar extends VAbstractPopupCalendar<DateResolution> {
+public class VPopupCalendar
+        extends VAbstractPopupCalendar<VDateCalendarPanel, DateResolution> {
 
     public VPopupCalendar() {
         super(GWT.create(VDateCalendarPanel.class), DateResolution.YEAR);
@@ -50,20 +51,20 @@ public class VPopupCalendar extends VAbstractPopupCalendar<DateResolution> {
                 resolution == null ? DateResolution.YEAR : resolution);
     }
 
-    public static Date makeDate(Map<DateResolution, Integer> dateVaules) {
-        if (dateVaules.get(DateResolution.YEAR) == -1) {
+    public static Date makeDate(Map<DateResolution, Integer> 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<DateResolution> {
         }
     }
 
+    @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 (file)
index 0000000..af56e11
--- /dev/null
@@ -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<VDateTimeCalendarPanel, DateTimeResolution> {
+
+    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<DateTimeResolution, Integer> 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<DateTimeResolution, Integer> 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);
+    }
+
+}
index 5252c40d771089b47d2afd76b846b757fb080a6d..e5bf3e34bb75e440fff7c445d6966c6bece0f17d 100644 (file)
@@ -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 <R>
  *            the resolution type which the field is based on (day, month, ...)
+ * @param <PANEL>
+ *            Subclass of VAbstractCalendarPanel specific for the implementation
  */
-public abstract class AbstractInlineDateFieldConnector<R extends Enum<R>>
+public abstract class AbstractInlineDateFieldConnector<PANEL extends VAbstractCalendarPanel<R>, R extends Enum<R>>
         extends AbstractDateFieldConnector<R> {
 
     @Override
@@ -107,8 +110,8 @@ public abstract class AbstractInlineDateFieldConnector<R extends Enum<R>>
     }
 
     @Override
-    public VAbstractDateFieldCalendar<R> getWidget() {
-        return (VAbstractDateFieldCalendar<R>) super.getWidget();
+    public VAbstractDateFieldCalendar<PANEL, R> getWidget() {
+        return (VAbstractDateFieldCalendar<PANEL, R>) super.getWidget();
     }
 
     @Override
index 35a9420adaaac192c2f7d1636a3e09178b693bbb..d6b0f65c27a5ba1ae60f2603490e040f0160381d 100644 (file)
@@ -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<DateResolution> {
+public class DateFieldConnector
+        extends TextualDateConnector<VDateCalendarPanel, DateResolution> {
 
     @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 (file)
index 0000000..15afa1b
--- /dev/null
@@ -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<VDateTimeCalendarPanel, DateTimeResolution> {
+
+    @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();
+                        }
+                    });
+        }
+    }
+}
index 7339191ee9ddf6d5d26e47651d50afed50a5241c..4fda94b7e2deca181d54a7c800f43f7c9d31367c 100644 (file)
  */
 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<DateResolution> {
+public class InlineDateFieldConnector extends
+        AbstractInlineDateFieldConnector<VDateCalendarPanel, DateResolution> {
 
     @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 (file)
index 0000000..194de64
--- /dev/null
@@ -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<VDateTimeCalendarPanel, DateTimeResolution> {
+
+    @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();
+                        }
+                    });
+        }
+    }
+}
index bebefe96ddfc18fab8d458a24ecb5e04afe8309d..866723544a352b447e6969a7fb41f4e7a233fa3c 100644 (file)
@@ -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 (file)
index 0000000..eeda4c7
--- /dev/null
@@ -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 {
+
+}
index 2179c7a9ca7857e29c638b23eca692ebd26a60a3..0c61f3860ef6070ff7ba7e20fae5983af65ca216 100644 (file)
@@ -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<R extends Enum<R>>
+/**
+ * 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 <PANEL>
+ *            Subclass of VAbstractCalendarPanel specific for the implementation
+ * @param <R>
+ *            the resolution type which the field is based on (day, month, ...)
+ */
+public abstract class TextualDateConnector<PANEL extends VAbstractCalendarPanel<R>, R extends Enum<R>>
         extends AbstractTextualDateConnector<R> {
 
     @Override
@@ -112,17 +127,18 @@ public abstract class TextualDateConnector<R extends Enum<R>>
      * 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<R extends Enum<R>>
         }
     }
 
-    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<R> getWidget() {
-        return (VAbstractPopupCalendar<R>) super.getWidget();
+    public VAbstractPopupCalendar<PANEL, R> getWidget() {
+        return (VAbstractPopupCalendar<PANEL, R>) 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 (file)
index 0000000..e427d95
--- /dev/null
@@ -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 <code>LocalDateTime</code> and
+ * <code>Date</code>. 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<LocalDateTime, Date> {
+
+    private ZoneOffset zoneOffset;
+
+    /**
+     * Creates a new converter using the given time zone.
+     *
+     * @param zoneOffset
+     *            the time zone offset to use, not <code>null</code>
+     */
+    public LocalDateTimeToDateConverter(ZoneOffset zoneOffset) {
+        this.zoneOffset = Objects.requireNonNull(zoneOffset,
+                "Zone offset cannot be null");
+    }
+
+    @Override
+    public Result<Date> 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 (file)
index 0000000..9096a8c
--- /dev/null
@@ -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<LocalDateTime> {
+
+    /**
+     * Creates a validator for checking that a {@link LocalDateTime} is within a
+     * given range.
+     * <p>
+     * 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.
+     * </p>
+     *
+     * @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);
+    }
+
+}
index ab99fe0f28da6be0c4036bd40dda6328cf4cb52e..77547574291734aafac324f79a43140b63a67d21 100644 (file)
@@ -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
index 9be77444c67bd74514d6fb0439f8e0f788b5558c..65da2a2b8bc062da7eebe9892884ecfb7d4e8f78 100644 (file)
  */
 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<LocalDateTime, DateTimeResolution> {
@@ -45,14 +55,112 @@ public abstract class AbstractLocalDateTimeField
         super(caption, DateTimeResolution.MINUTE);
     }
 
+    /**
+     * Constructs a new <code>AbstractLocalDateTimeField</code> with the given
+     * caption and initial text contents.
+     *
+     * @param caption
+     *            the caption <code>String</code> 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<LocalDateTime> getRangeValidator() {
+        return new DateTimeRangeValidator(getDateOutOfRangeMessage(),
+                getDate(getRangeStart(), getResolution()),
+                getDate(getRangeEnd(), getResolution()));
+    }
+
+    @Override
+    protected LocalDateTime buildDate(
+            Map<DateTimeResolution, Integer> 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 (file)
index 0000000..cde5633
--- /dev/null
@@ -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 <code>DateTimeField</code> with no caption.
+     */
+    public DateTimeField() {
+        super();
+    }
+
+    /**
+     * Constructs a new <code>DateTimeField</code> with the given caption and
+     * initial text contents.
+     *
+     * @param caption
+     *            the caption <code>String</code> for the editor.
+     * @param value
+     *            the LocalDateTime value.
+     */
+    public DateTimeField(String caption, LocalDateTime value) {
+        super(caption, value);
+    }
+
+    /**
+     * Constructs an empty <code>DateTimeField</code> 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 <b>true</b> if the text field is enabled, <b>false</b> 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
+     *            <b>true</b> to enable text field, <b>false</b> 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 (file)
index 0000000..2c4cf29
--- /dev/null
@@ -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 <code>InlineDateTimeField</code> with no caption.
+     */
+    public InlineDateTimeField() {
+        super();
+    }
+
+    /**
+     * Constructs a new <code>InlineDateTimeField</code> with the given caption
+     * and initial text contents.
+     *
+     * @param caption
+     *            the caption <code>String</code> for the editor.
+     * @param value
+     *            the LocalDate value.
+     */
+    public InlineDateTimeField(String caption, LocalDateTime value) {
+        super(caption, value);
+    }
+
+    /**
+     * Constructs an empty <code>InlineDateTimeField</code> 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);
+    }
+}
index 6e9ff8f9bb98ea29053e788c2b58858f48e58c47..0f40be795ba7c507de4250f98d024cf4795a571d 100644 (file)
@@ -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 (file)
index 0000000..bfc177c
--- /dev/null
@@ -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<String, LocalDateTime> {
+
+    @Override
+    public Result<LocalDateTime> 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 (file)
index 0000000..fdad139
--- /dev/null
@@ -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<BeanWithDate> 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 (file)
index 0000000..a4768c6
--- /dev/null
@@ -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.
+ * <p>
+ * 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<T extends AbstractLocalDateTimeField>
+        extends AbstractFieldDeclarativeTest<T, LocalDateTime> {
+
+    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);
+    }
+
+}
index e22905c9f358b8ee8770876f6fdf74c460665e69..07cbcefc392fe0d8402ddbe149bab44aae62dceb 100644 (file)
@@ -62,9 +62,11 @@ public abstract class AbstractFieldDeclarativeTest<T extends AbstractField<V>, V
         testWrite(design, component);
     }
 
+    @Test
     public abstract void valueDeserialization()
             throws InstantiationException, IllegalAccessException;
 
+    @Test
     public abstract void readOnlyValue()
             throws InstantiationException, IllegalAccessException;
 }
index 990121e7045b4beb4f9266a3d8f99050572332d4..160d8fa41ad4385aa6356aae5106b2c67506f3dc 100644 (file)
@@ -75,7 +75,8 @@ public abstract class AbstractColorPickerDeclarativeTest<T extends AbstractColor
     public void valueDeserialization()
             throws InstantiationException, IllegalAccessException {
         String rgb = "fafafa";
-        String design = String.format("<%s color='#%s'/>", 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<T extends AbstractColor
             throws InstantiationException, IllegalAccessException {
         String rgb = "fafafa";
         String design = String.format("<%s color='#%s' readonly/>",
-                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 (file)
index 0000000..fac0152
--- /dev/null
@@ -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<DateTimeField> {
+
+    @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<? extends DateTimeField> 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 (file)
index 0000000..c26bd41
--- /dev/null
@@ -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<InlineDateTimeField> {
+
+    @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<? extends InlineDateTimeField> getComponentClass() {
+        return InlineDateTimeField.class;
+    }
+
+}
index 7876fb51f4704b294c465b47b2455d50b139b48e..9b7aeca48ca25c46c1e4f978a43d040f7e53e395 100644 (file)
@@ -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 (file)
index 0000000..ed64569
--- /dev/null
@@ -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";
+    }
+}
index cb439dd1a80f22bc1d91d683a3a4762f7cff2de8..82cfe8ee6fa712d7a58f9a9c3ade402a05b8bcf3 100644 (file)
@@ -21,6 +21,6 @@ package com.vaadin.shared.ui.datefield;
  * @author Vaadin Ltd
  *
  */
-public class LocalDateTimeFieldState extends AbstractTextualDateFieldState {
+public class LocalDateTimeFieldState extends TextualDateFieldState {
 
 }
index 203312a80a5f76d48fc36ef8d215f8b70b8e7545..e9f8b30d0db8076f24c9253f078b2931f0ecd3fa 160000 (submodule)
@@ -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 (file)
index 0000000..022acbf
--- /dev/null
@@ -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 (file)
index 0000000..27d6d90
--- /dev/null
@@ -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 {
+
+}
index 369abcc80fb50ddf2240f91adbe076c89db4a683..8ae2d48afd9656ca0d8381853c59975fb1ba4ecd 100644 (file)
@@ -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<T extends AbstractLocalDateField>
+public abstract class AbstractDateFieldTest<T extends AbstractLocalDateField>
         extends AbstractFieldTest<T, LocalDate> {
 
-    @SuppressWarnings("unchecked")
-    @Override
-    protected Class<T> getTestClass() {
-        return (Class<T>) TestDateField.class;
-    }
-
     private Command<T, LocalDate> setValue = new Command<T, LocalDate>() {
 
         @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 (file)
index 0000000..522fa11
--- /dev/null
@@ -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<T extends AbstractLocalDateTimeField>
+        extends AbstractFieldTest<T, LocalDateTime> {
+
+    private Command<T, LocalDateTime> setValue = new Command<T, LocalDateTime>() {
+
+        @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<String, LocalDateTime> 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<String, String> 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<String, DateTimeResolution> 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<T, DateTimeResolution> resolutionCommand = new Command<T, DateTimeResolution>() {
+
+        @Override
+        public void execute(T c, DateTimeResolution value, Object data) {
+            c.setResolution(value);
+
+        }
+    };
+    private Command<T, Boolean> lenientCommand = new Command<T, Boolean>() {
+
+        @Override
+        public void execute(T c, Boolean value, Object data) {
+            c.setLenient(false);
+
+        }
+    };
+    private Command<T, Boolean> weekNumberCommand = new Command<T, Boolean>() {
+
+        @Override
+        public void execute(T c, Boolean value, Object data) {
+            c.setShowISOWeekNumbers(value);
+
+        }
+    };
+    private Command<T, String> dateFormatCommand = new Command<T, String>() {
+
+        @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 (file)
index 0000000..9c5b030
--- /dev/null
@@ -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 (file)
index 0000000..90c11b6
--- /dev/null
@@ -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;
+    }
+
+}
index e38dd6895c668163d9d1b14f754ba6c364f9e141..2107b9ff612f96fbfc16fdc97656411ef2ebc7ca 100644 (file)
@@ -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<LocalDate, DateResolution> 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<LocalDate, DateResolution> getDateField(
-            DateTimeFormatter dateformat) {
-        final AbstractDateField<LocalDate, DateResolution> 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 (file)
index 0000000..2bf161d
--- /dev/null
@@ -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 (file)
index 0000000..1481439
--- /dev/null
@@ -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 (file)
index 0000000..5bb2905
--- /dev/null
@@ -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 (file)
index 0000000..7213cb2
--- /dev/null
@@ -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 (file)
index 0000000..91de230
--- /dev/null
@@ -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 (file)
index 0000000..3b73957
--- /dev/null
@@ -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 (file)
index 0000000..814cf03
--- /dev/null
@@ -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<DateTimeField> {
+
+    @Override
+    protected Class<DateTimeField> getTestClass() {
+        return DateTimeField.class;
+    }
+
+    @Override
+    protected void createActions() {
+        super.createActions();
+
+        createInputPromptSelectAction(CATEGORY_FEATURES);
+        createTextEnabledAction(CATEGORY_FEATURES);
+    }
+
+    private void createInputPromptSelectAction(String category) {
+        LinkedHashMap<String, String> options = new LinkedHashMap<>();
+        options.put("<none>", null);
+        options.put("Please enter date", "Please enter date");
+        options.put("åäöÅÄÖ", "åäöÅÄÖ");
+
+        createSelectAction("Input prompt", category, options, "<none>",
+                new Command<DateTimeField, String>() {
+
+                    @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 (file)
index 0000000..839da97
--- /dev/null
@@ -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 (file)
index 0000000..5beedfc
--- /dev/null
@@ -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<InlineDateTimeField> {
+
+    @Override
+    protected Class<InlineDateTimeField> 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 (file)
index 0000000..60c46d2
--- /dev/null
@@ -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 (file)
index 0000000..2b16397
--- /dev/null
@@ -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 (file)
index 0000000..ab63506
--- /dev/null
@@ -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/AbstractDateFieldTestTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/AbstractDateFieldTestTest.java
deleted file mode 100644 (file)
index caaf84b..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.By;
-import org.openqa.selenium.Dimension;
-import org.openqa.selenium.WebElement;
-import org.openqa.selenium.interactions.Actions;
-
-import com.vaadin.testbench.elements.NotificationElement;
-import com.vaadin.tests.tb3.MultiBrowserTest;
-
-public class AbstractDateFieldTestTest extends MultiBrowserTest {
-
-    @Test
-    public void testMakingRequired() throws InterruptedException {
-        setDebug(true);
-        openTestURL();
-        Thread.sleep(1000);
-        menu("Component");
-        menuSub("State");
-        menu("Required");
-        assertRequiredIndicatorVisible();
-        assertNoErrorNotification();
-    }
-
-    private void assertRequiredIndicatorVisible() {
-        getDriver().findElement(By.className("v-required-field-indicator"));
-    }
-
-    private void assertNoErrorNotification() {
-        if (isElementPresent(NotificationElement.class)) {
-            Assert.fail("Notification was present");
-        }
-    }
-
-    @Test
-    public void testValueAfterOpeningPopupInRequiredField()
-            throws InterruptedException {
-        setDebug(true);
-        openTestURL();
-        Thread.sleep(1000);
-        menu("Component");
-        menuSub("State");
-        menu("Required");
-
-        assertRequiredIndicatorVisible();
-
-        menu("Component");
-        menuSub("Features");
-        menuSub("Resolution");
-        menu("Month");
-
-        menu("Component");
-        menuSub("Listeners");
-        menu("Value change listener");
-
-        String inputtedValue = "2/12";
-        getInput().sendKeys(inputtedValue);
-
-        openPopup();
-        closePopup();
-        String actual = getInput().getAttribute("value");
-        Assert.assertEquals(inputtedValue, actual);
-        assertNoErrorNotification();
-
-    }
-
-    private void openPopup() throws InterruptedException {
-        Dimension size = getInput().getSize();
-        new Actions(getDriver()).moveToElement(getInput(), 0, 0)
-                .moveByOffset(size.getWidth() + 5, size.getHeight() / 2)
-                .click();
-        // This fails in Opera for some weird reason
-        // getDriver().findElement(By.className("v-datefield-button")).click();
-    }
-
-    private WebElement getInput() {
-        return getDriver().findElement(By.xpath("//input"));
-    }
-
-    private void closePopup() {
-        getDriver().findElement(By.tagName("body")).click();
-    }
-
-    /**
-     * @since
-     * @param string
-     */
-    private void menuSub(String string) {
-        getDriver().findElement(By.xpath("//span[text() = '" + string + "']"))
-                .click();
-        new Actions(getDriver()).moveByOffset(100, 0).build().perform();
-    }
-
-    /**
-     * @since
-     * @param string
-     */
-    private void menu(String string) {
-        getDriver().findElement(By.xpath("//span[text() = '" + string + "']"))
-                .click();
-
-    }
-
-}
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 (file)
index 0000000..b5e420e
--- /dev/null
@@ -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 (file)
index 0000000..3016ef6
--- /dev/null
@@ -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);
+    }
+
+}
index 9817ec439455944737de195353d7ca5134f48f07..26f94be7173e16b7ceb2a9a27a73920c08d23f35 100644 (file)
@@ -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 (file)
index 0000000..cd0b621
--- /dev/null
@@ -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 (file)
index 0000000..4b2b271
--- /dev/null
@@ -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 (file)
index 0000000..f42afe1
--- /dev/null
@@ -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 (file)
index 0000000..986db81
--- /dev/null
@@ -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 (file)
index 0000000..715e3e6
--- /dev/null
@@ -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 (file)
index 0000000..a4e79ac
--- /dev/null
@@ -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/DateTimeFieldTestTest.java b/uitest/src/test/java/com/vaadin/tests/components/datefield/DateTimeFieldTestTest.java
new file mode 100644 (file)
index 0000000..e835ac5
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.By;
+import org.openqa.selenium.Dimension;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.interactions.Actions;
+
+import com.vaadin.testbench.elements.NotificationElement;
+import com.vaadin.tests.tb3.MultiBrowserTest;
+
+/**
+ * @author Vaadin Ltd
+ *
+ */
+public class DateTimeFieldTestTest extends MultiBrowserTest {
+
+    @Test
+    public void testMakingRequired() throws InterruptedException {
+        setDebug(true);
+        openTestURL();
+        Thread.sleep(1000);
+        menu("Component");
+        menuSub("State");
+        menu("Required");
+        assertRequiredIndicatorVisible();
+        assertNoErrorNotification();
+    }
+
+    private void assertRequiredIndicatorVisible() {
+        getDriver().findElement(By.className("v-required-field-indicator"));
+    }
+
+    private void assertNoErrorNotification() {
+        if (isElementPresent(NotificationElement.class)) {
+            Assert.fail("Notification was present");
+        }
+    }
+
+    @Test
+    public void testValueAfterOpeningPopupInRequiredField()
+            throws InterruptedException {
+        setDebug(true);
+        openTestURL();
+        Thread.sleep(1000);
+        menu("Component");
+        menuSub("State");
+        menu("Required");
+
+        assertRequiredIndicatorVisible();
+
+        menu("Component");
+        menuSub("Features");
+        menuSub("Resolution");
+        menu("Month");
+
+        menu("Component");
+        menuSub("Listeners");
+        menu("Value change listener");
+
+        String inputtedValue = "2/12";
+        getInput().sendKeys(inputtedValue);
+
+        openPopup();
+        closePopup();
+        String actual = getInput().getAttribute("value");
+        Assert.assertEquals(inputtedValue, actual);
+        assertNoErrorNotification();
+
+    }
+
+    private void openPopup() throws InterruptedException {
+        Dimension size = getInput().getSize();
+        new Actions(getDriver()).moveToElement(getInput(), 0, 0)
+                .moveByOffset(size.getWidth() + 5, size.getHeight() / 2)
+                .click();
+        // This fails in Opera for some weird reason
+        // getDriver().findElement(By.className("v-datefield-button")).click();
+    }
+
+    private WebElement getInput() {
+        return getDriver().findElement(By.xpath("//input"));
+    }
+
+    private void closePopup() {
+        getDriver().findElement(By.tagName("body")).click();
+    }
+
+    /**
+     * @since
+     * @param string
+     */
+    private void menuSub(String string) {
+        getDriver().findElement(By.xpath("//span[text() = '" + string + "']"))
+                .click();
+        new Actions(getDriver()).moveByOffset(100, 0).build().perform();
+    }
+
+    /**
+     * @since
+     * @param string
+     */
+    private void menu(String string) {
+        getDriver().findElement(By.xpath("//span[text() = '" + string + "']"))
+                .click();
+
+    }
+
+}
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 (file)
index 0000000..621666c
--- /dev/null
@@ -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 (file)
index 0000000..1ddd9ca
--- /dev/null
@@ -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
+    }
+}
index 5f82bab83112a3990cd16e8b6e0595b099880d23..7d642faf707310c36e092c53a9a2c841cecc9785 100644 (file)
@@ -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 (file)
index 0000000..bd97b63
--- /dev/null
@@ -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 (file)
index 0000000..6bb7a67
--- /dev/null
@@ -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 (file)
index 0000000..a6226de
--- /dev/null
@@ -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<WebElement> 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);
+    }
+}