Browse Source

Refactor AbstractDateField. (#8146)

First round for #8132.
tags/8.0.0.beta2
Denis 7 years ago
parent
commit
3ef30789d6
65 changed files with 2413 additions and 1463 deletions
  1. 3
    0
      all/src/main/templates/release-notes.html
  2. 5
    5
      client/src/main/java/com/vaadin/client/DateTimeService.java
  3. 1
    1
      client/src/main/java/com/vaadin/client/ui/AbstractComponentConnector.java
  4. 189
    59
      client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java
  5. 69
    0
      client/src/main/java/com/vaadin/client/ui/VAbstractDateFieldCalendar.java
  6. 730
    0
      client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java
  7. 77
    65
      client/src/main/java/com/vaadin/client/ui/VAbstractTextualDate.java
  8. 46
    0
      client/src/main/java/com/vaadin/client/ui/VDateCalendarPanel.java
  9. 99
    34
      client/src/main/java/com/vaadin/client/ui/VDateField.java
  10. 47
    46
      client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java
  11. 48
    666
      client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java
  12. 38
    38
      client/src/main/java/com/vaadin/client/ui/datefield/AbstractDateFieldConnector.java
  13. 127
    0
      client/src/main/java/com/vaadin/client/ui/datefield/AbstractInlineDateFieldConnector.java
  14. 65
    0
      client/src/main/java/com/vaadin/client/ui/datefield/AbstractTextualDateConnector.java
  15. 14
    156
      client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java
  16. 10
    71
      client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java
  17. 142
    19
      client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java
  18. 4
    4
      compatibility-client/src/main/java/com/vaadin/v7/client/ui/VPopupCalendar.java
  19. 1
    1
      compatibility-server/src/main/java/com/vaadin/v7/data/validator/DateRangeValidator.java
  20. 1
    1
      compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/DateRangeValidatorTest.java
  21. 4
    3
      server/src/main/java/com/vaadin/ui/AbstractComponent.java
  22. 166
    117
      server/src/main/java/com/vaadin/ui/AbstractDateField.java
  23. 139
    0
      server/src/main/java/com/vaadin/ui/AbstractLocalDateField.java
  24. 58
    0
      server/src/main/java/com/vaadin/ui/AbstractLocalDateTimeField.java
  25. 7
    7
      server/src/main/java/com/vaadin/ui/DateField.java
  26. 2
    2
      server/src/main/java/com/vaadin/ui/InlineDateField.java
  27. 8
    3
      server/src/main/java/com/vaadin/ui/declarative/DesignAttributeHandler.java
  28. 4
    4
      server/src/test/java/com/vaadin/tests/server/component/abstractdatefield/AbstractLocalDateFieldDeclarativeTest.java
  29. 2
    2
      server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldDeclarativeTest.java
  30. 38
    1
      server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldListenersTest.java
  31. 2
    2
      server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateFieldDeclarativeTest.java
  32. 16
    16
      server/src/test/java/com/vaadin/tests/server/component/datefield/ResolutionTest.java
  33. 2
    2
      server/src/test/java/com/vaadin/ui/DateFieldTestCase.java
  34. 21
    7
      shared/src/main/java/com/vaadin/shared/ui/datefield/AbstractDateFieldState.java
  35. 26
    0
      shared/src/main/java/com/vaadin/shared/ui/datefield/AbstractTextualDateFieldState.java
  36. 9
    8
      shared/src/main/java/com/vaadin/shared/ui/datefield/DateResolution.java
  37. 27
    0
      shared/src/main/java/com/vaadin/shared/ui/datefield/DateTimeResolution.java
  38. 1
    1
      shared/src/main/java/com/vaadin/shared/ui/datefield/InlineDateFieldState.java
  39. 26
    0
      shared/src/main/java/com/vaadin/shared/ui/datefield/LocalDateFieldState.java
  40. 26
    0
      shared/src/main/java/com/vaadin/shared/ui/datefield/LocalDateTimeFieldState.java
  41. 8
    15
      shared/src/main/java/com/vaadin/shared/ui/datefield/TextualDateFieldState.java
  42. 2
    2
      uitest/src/main/java/com/vaadin/tests/TestForBasicApplicationLayout.java
  43. 2
    2
      uitest/src/main/java/com/vaadin/tests/components/TestDateField.java
  44. 9
    9
      uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateFieldTest.java
  45. 2
    2
      uitest/src/main/java/com/vaadin/tests/components/datefield/AriaDisabled.java
  46. 4
    4
      uitest/src/main/java/com/vaadin/tests/components/datefield/CustomDateFormat.java
  47. 4
    4
      uitest/src/main/java/com/vaadin/tests/components/datefield/CustomDateFormatEEE.java
  48. 5
    5
      uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldChangeResolution.java
  49. 2
    2
      uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldClose.java
  50. 7
    5
      uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffset.java
  51. 3
    2
      uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldIsValid.java
  52. 2
    2
      uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldPopupClosing.java
  53. 2
    2
      uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldPopupClosingOnDetach.java
  54. 2
    2
      uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldReadOnly.java
  55. 5
    5
      uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldWhenChangingValueAndEnablingParent.java
  56. 6
    6
      uitest/src/main/java/com/vaadin/tests/components/datefield/DateFields.java
  57. 2
    2
      uitest/src/main/java/com/vaadin/tests/components/datefield/DisabledDateFieldPopup.java
  58. 2
    2
      uitest/src/main/java/com/vaadin/tests/components/datefield/DisabledInlineDateField.java
  59. 3
    8
      uitest/src/main/java/com/vaadin/tests/components/datefield/DisabledParentLayout.java
  60. 6
    6
      uitest/src/main/java/com/vaadin/tests/components/datefield/InlineDateFields.java
  61. 8
    8
      uitest/src/main/java/com/vaadin/tests/components/datefield/PopupClosingWithEsc.java
  62. 2
    2
      uitest/src/main/java/com/vaadin/tests/components/datefield/PopupDateFieldExtendedRange.java
  63. 2
    2
      uitest/src/main/java/com/vaadin/tests/components/gridlayout/GridLayoutCellSizesUI.java
  64. 13
    13
      uitest/src/main/java/com/vaadin/tests/themes/valo/DateFields.java
  65. 10
    10
      uitest/src/test/java/com/vaadin/tests/components/datefield/DateFieldChangeResolutionTest.java

+ 3
- 0
all/src/main/templates/release-notes.html View File

@@ -174,6 +174,9 @@
<li><tt>Grid</tt> selection API has been removed from component level to <tt>GridSelectionModel</tt> which is available via <tt>Grid::getSelectionModel()</tt></li>
<li><tt>Grid::setSelectionModel(GridSelectionModel)</tt> visibility has been changed from <tt>public</tt> to <tt>protected</tt> to reduce confusion with <tt>Grid::setSelectionMode</tt></li>
</ul>
<ul><h4>Client side widget specific API changes</h4>
<li><tt>VTextualDate</tt></li> widget class is removed and replaced by abstract <tt>VAbstractTextualDate</tt> class which is supposed to be inherited by concrete date field implementation widgets
</ul>
<ul><h4>Component specific visual changes</h4>
<li>The default width of <tt>Label</tt> is now undefined, matching other components</li>
<li>The default width for <tt>ComboBox</tt> pop-up is now 100 % (previously undefined)</li>

+ 5
- 5
client/src/main/java/com/vaadin/client/DateTimeService.java View File

@@ -22,7 +22,7 @@ import java.util.logging.Logger;

import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.i18n.shared.DateTimeFormat;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;

/**
* This class provides date/time parsing services to all components on the
@@ -203,7 +203,7 @@ public class DateTimeService {
}

public static boolean isInRange(Date date, Date rangeStart, Date rangeEnd,
Resolution resolution) {
DateResolution resolution) {
Date s;
Date e;
if (rangeStart.after(rangeEnd)) {
@@ -217,19 +217,19 @@ public class DateTimeService {
long end = e.getYear() * 10000000000l;
long target = date.getYear() * 10000000000l;

if (resolution == Resolution.YEAR) {
if (resolution == DateResolution.YEAR) {
return (start <= target && end >= target);
}
start += s.getMonth() * 100000000l;
end += e.getMonth() * 100000000l;
target += date.getMonth() * 100000000l;
if (resolution == Resolution.MONTH) {
if (resolution == DateResolution.MONTH) {
return (start <= target && end >= target);
}
start += s.getDate() * 1000000l;
end += e.getDate() * 1000000l;
target += date.getDate() * 1000000l;
if (resolution == Resolution.DAY) {
if (resolution == DateResolution.DAY) {
return (start <= target && end >= target);
}
start += s.getHours() * 10000l;

+ 1
- 1
client/src/main/java/com/vaadin/client/ui/AbstractComponentConnector.java View File

@@ -706,7 +706,7 @@ public abstract class AbstractComponentConnector extends AbstractConnector
* updated in another widget in addition to the one returned by the
* <code>Connector</code>'s {@link #getWidget()}, or if the prefix should be
* different. For example see
* {@link com.vaadin.client.ui.datefield.DateFieldConnector#setWidgetStyleNameWithPrefix(String, String, boolean)}
* {@link com.vaadin.client.ui.datefield.TextualDateConnector#setWidgetStyleNameWithPrefix(String, String, boolean)}
* </p>
*
* @param styleName

client/src/main/java/com/vaadin/client/ui/VCalendarPanel.java → client/src/main/java/com/vaadin/client/ui/VAbstractCalendarPanel.java View File

@@ -18,6 +18,10 @@ package com.vaadin.client.ui;

import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.google.gwt.aria.client.Roles;
import com.google.gwt.aria.client.SelectedValue;
@@ -51,13 +55,22 @@ import com.vaadin.client.BrowserInfo;
import com.vaadin.client.DateTimeService;
import com.vaadin.client.VConsole;
import com.vaadin.client.WidgetUtil;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.util.SharedUtil;

/**
* Abstract calendar panel to show and select a date using a resolution. The
* class is parameterized by the date resolution enumeration type.
*
* @author Vaadin Ltd
*
* @param <R>
* the resolution type which this field is based on (day, month, ...)
*/
@SuppressWarnings("deprecation")
public class VCalendarPanel extends FocusableFlexTable implements
KeyDownHandler, KeyPressHandler, MouseOutHandler, MouseDownHandler,
MouseUpHandler, BlurHandler, FocusHandler, SubPartAware {
public abstract class VAbstractCalendarPanel<R extends Enum<R>>
extends FocusableFlexTable implements KeyDownHandler, KeyPressHandler,
MouseOutHandler, MouseDownHandler, MouseUpHandler, BlurHandler,
FocusHandler, SubPartAware {

public interface SubmitListener {

@@ -96,9 +109,9 @@ public class VCalendarPanel extends FocusableFlexTable implements
*/
private class VEventButton extends Button {
public VEventButton() {
addMouseDownHandler(VCalendarPanel.this);
addMouseOutHandler(VCalendarPanel.this);
addMouseUpHandler(VCalendarPanel.this);
addMouseDownHandler(VAbstractCalendarPanel.this);
addMouseOutHandler(VAbstractCalendarPanel.this);
addMouseUpHandler(VAbstractCalendarPanel.this);
}
}

@@ -131,7 +144,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
}

Date newDate = ((Day) event.getSource()).getDate();
if (!isDateInsideRange(newDate, Resolution.DAY)) {
if (!isDateInsideRange(newDate,
getResolution(VAbstractCalendarPanel.this::isDay))) {
return;
}
if (newDate.getMonth() != displayedMonth.getMonth()
@@ -158,7 +172,7 @@ public class VCalendarPanel extends FocusableFlexTable implements

private FlexTable days = new FlexTable();

private Resolution resolution = Resolution.YEAR;
private R resolution;

private Timer mouseTimer;

@@ -184,11 +198,11 @@ public class VCalendarPanel extends FocusableFlexTable implements

private boolean hasFocus = false;

private VDateField parent;
private VDateField<R> parent;

private boolean initialRenderDone = false;

public VCalendarPanel() {
public VAbstractCalendarPanel() {
getElement().setId(DOM.createUniqueId());
setStyleName(VDateField.CLASSNAME + "-calendarpanel");
Roles.getGridRole().set(getElement());
@@ -207,7 +221,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
addBlurHandler(this);
}

public void setParentField(VDateField parent) {
public void setParentField(VDateField<R> parent) {
this.parent = parent;
}

@@ -221,7 +235,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
*/
private void focusDay(Date date) {
// Only used when calender body is present
if (isDay(getResolution())) {
if (acceptDayFocus()) {
if (focusedDay != null) {
focusedDay.removeStyleDependentName(CN_FOCUSED);
}
@@ -233,7 +247,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
int cellCount = days.getCellCount(i);
for (int j = 0; j < cellCount; j++) {
Widget widget = days.getWidget(i, j);
if (widget != null && widget instanceof Day) {
if (widget != null
&& widget instanceof VAbstractCalendarPanel.Day) {
Day curday = (Day) widget;
if (curday.getDate().equals(date)) {
curday.addStyleDependentName(CN_FOCUSED);
@@ -247,8 +262,80 @@ public class VCalendarPanel extends FocusableFlexTable implements
}
}

private boolean isDay(Resolution resolution) {
return Resolution.DAY.equals(resolution);
/**
* Returns {@code true} if current resolution assumes handling focus event
* for day UI component.
*
* @return {@code true} if day focus events should be handled, {@code false}
* otherwise
*/
protected abstract boolean acceptDayFocus();

/**
* Returns {@code true} if the provided {@code resolution} represents a day.
*
* @param resolution
* the given resolution
* @return {@code true} if the {@code resolution} represents a day
*/
protected abstract boolean isDay(R resolution);

/**
* Returns {@code true} if the provided {@code resolution} represents a
* month.
*
* @param resolution
* the given resolution
* @return {@code true} if the {@code resolution} represents a month
*/
protected abstract boolean isMonth(R resolution);

/**
* Returns {@code true} if the provided {@code resolution} represents an
* year.
*
* @param resolution
* the given resolution
* @return {@code true} if the {@code resolution} represents a year
*/
protected boolean isYear(R resolution) {
return parent.isYear(resolution);
}

/**
* Returns {@code true} if the {@code resolution} representation is strictly
* below month (day, hour, etc..).
*
* @param resolution
* the given resolution
* @return whether the {@code resolution} is below the month resolution
*/
protected abstract boolean isBelowMonth(R resolution);

/**
* Returns all available resolutions for the widget.
*
* @return all available resolutions
*/
protected Stream<R> getResolutions() {
return parent.getResolutions();
}

/**
* Finds the resolution by the {@code filter}.
*
* @param filter
* predicate to filter resolutions
* @return the resolution accepted by the {@code filter}
*/
protected R getResolution(Predicate<R> filter) {
List<R> resolutions = getResolutions().filter(filter)
.collect(Collectors.toList());
assert resolutions
.size() == 1 : "The result of filtering by the predicate "
+ "contains unexpected number of resolution items :"
+ resolutions.size();
return resolutions.get(0);
}

/**
@@ -271,7 +358,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
int cellCount = days.getCellCount(i);
for (int j = 0; j < cellCount; j++) {
Widget widget = days.getWidget(i, j);
if (widget != null && widget instanceof Day) {
if (widget != null
&& widget instanceof VAbstractCalendarPanel.Day) {
Day curday = (Day) widget;
if (curday.getDate().equals(date)) {
curday.addStyleDependentName(CN_SELECTED);
@@ -289,7 +377,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
* Updates year, month, day from focusedDate to value
*/
private void selectFocused() {
if (focusedDate != null && isDateInsideRange(focusedDate, resolution)) {
if (focusedDate != null
&& isDateInsideRange(focusedDate, getResolution())) {
if (value == null) {
// No previously selected value (set to null on server side).
// Create a new date using current date and time
@@ -324,11 +413,11 @@ public class VCalendarPanel extends FocusableFlexTable implements
return false;
}

public Resolution getResolution() {
public R getResolution() {
return resolution;
}

public void setResolution(Resolution resolution) {
public void setResolution(R resolution) {
this.resolution = resolution;
}

@@ -460,14 +549,15 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date prevMonthDate = (Date) focusedDate.clone();
removeOneMonth(prevMonthDate);

if (!isDateInsideRange(prevMonthDate, Resolution.MONTH)) {
R month = getResolution(VAbstractCalendarPanel.this::isMonth);
if (!isDateInsideRange(prevMonthDate, month)) {
prevMonth.addStyleName(CN_OUTSIDE_RANGE);
} else {
prevMonth.removeStyleName(CN_OUTSIDE_RANGE);
}
Date nextMonthDate = (Date) focusedDate.clone();
addOneMonth(nextMonthDate);
if (!isDateInsideRange(nextMonthDate, Resolution.MONTH)) {
if (!isDateInsideRange(nextMonthDate, month)) {
nextMonth.addStyleName(CN_OUTSIDE_RANGE);
} else {
nextMonth.removeStyleName(CN_OUTSIDE_RANGE);
@@ -476,7 +566,8 @@ public class VCalendarPanel extends FocusableFlexTable implements

Date prevYearDate = (Date) focusedDate.clone();
prevYearDate.setYear(prevYearDate.getYear() - 1);
if (!isDateInsideRange(prevYearDate, Resolution.YEAR)) {
R year = getResolution(VAbstractCalendarPanel.this::isYear);
if (!isDateInsideRange(prevYearDate, year)) {
prevYear.addStyleName(CN_OUTSIDE_RANGE);
} else {
prevYear.removeStyleName(CN_OUTSIDE_RANGE);
@@ -484,7 +575,7 @@ public class VCalendarPanel extends FocusableFlexTable implements

Date nextYearDate = (Date) focusedDate.clone();
nextYearDate.setYear(nextYearDate.getYear() + 1);
if (!isDateInsideRange(nextYearDate, Resolution.YEAR)) {
if (!isDateInsideRange(nextYearDate, year)) {
nextYear.addStyleName(CN_OUTSIDE_RANGE);
} else {
nextYear.removeStyleName(CN_OUTSIDE_RANGE);
@@ -521,7 +612,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
* @param date
* @return
*/
private boolean isDateInsideRange(Date date, Resolution minResolution) {
private boolean isDateInsideRange(Date date, R minResolution) {
assert (date != null);

return isAcceptedByRangeEnd(date, minResolution)
@@ -539,8 +630,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
* @param minResolution
* @return
*/
private boolean isAcceptedByRangeStart(Date date,
Resolution minResolution) {
private boolean isAcceptedByRangeStart(Date date, R minResolution) {
assert (date != null);

// rangeStart == null means that we accept all values below rangeEnd
@@ -551,10 +641,10 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date valueDuplicate = (Date) date.clone();
Date rangeStartDuplicate = (Date) rangeStart.clone();

if (minResolution == Resolution.YEAR) {
if (isYear(minResolution)) {
return valueDuplicate.getYear() >= rangeStartDuplicate.getYear();
}
if (minResolution == Resolution.MONTH) {
if (isMonth(minResolution)) {
valueDuplicate = clearDateBelowMonth(valueDuplicate);
rangeStartDuplicate = clearDateBelowMonth(rangeStartDuplicate);
} else {
@@ -576,7 +666,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
* @param minResolution
* @return
*/
private boolean isAcceptedByRangeEnd(Date date, Resolution minResolution) {
private boolean isAcceptedByRangeEnd(Date date, R minResolution) {
assert (date != null);

// rangeEnd == null means that we accept all values above rangeStart
@@ -587,10 +677,10 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date valueDuplicate = (Date) date.clone();
Date rangeEndDuplicate = (Date) rangeEnd.clone();

if (minResolution == Resolution.YEAR) {
if (isYear(minResolution)) {
return valueDuplicate.getYear() <= rangeEndDuplicate.getYear();
}
if (minResolution == Resolution.MONTH) {
if (isMonth(minResolution)) {
valueDuplicate = clearDateBelowMonth(valueDuplicate);
rangeEndDuplicate = clearDateBelowMonth(rangeEndDuplicate);
} else {
@@ -667,7 +757,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
if (day > 6) {
day = 0;
}
if (isDay(getResolution())) {
if (isBelowMonth(getResolution())) {
days.setHTML(headerRow, firstWeekdayColumn + i, "<strong>"
+ getDateTimeService().getShortDay(day) + "</strong>");
} else {
@@ -705,7 +795,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
day.setStyleName(
parent.getStylePrimaryName() + "-calendarpanel-day");

if (!isDateInsideRange(dayDate, Resolution.DAY)) {
if (!isDateInsideRange(dayDate, getResolution(this::isDay))) {
day.addStyleDependentName(CN_OUTSIDE_RANGE);
}

@@ -771,7 +861,24 @@ public class VCalendarPanel extends FocusableFlexTable implements
* selected.
*/
public void renderCalendar(boolean updateDate) {
doRenderCalendar(updateDate);

initialRenderDone = true;
}

/**
* Performs the rendering required by the {@link #renderCalendar(boolean)}.
* Subclasses may override this method to provide a custom implementation
* avoiding {@link #renderCalendar(boolean)} override. The latter method
* contains a common logic which should not be overriden.
*
* @param updateDate
* The value false prevents setting the selected date of the
* calendar based on focusedDate. That can be used when only the
* resolution of the calendar is changed and no date has been
* selected.
*/
protected void doRenderCalendar(boolean updateDate) {
super.setStylePrimaryName(
parent.getStylePrimaryName() + "-calendarpanel");

@@ -788,15 +895,13 @@ public class VCalendarPanel extends FocusableFlexTable implements
focusChangeListener.focusChanged(new Date(focusedDate.getTime()));
}

final boolean needsMonth = !getResolution().equals(Resolution.YEAR);
final boolean needsMonth = !isYear(getResolution());
boolean needsBody = isDay(getResolution());
buildCalendarHeader(needsMonth);
clearCalendarBody(!needsBody);
if (needsBody) {
buildCalendarBody();
}

initialRenderDone = true;
}

/**
@@ -809,7 +914,7 @@ public class VCalendarPanel extends FocusableFlexTable implements

Date focusCopy = ((Date) focusedDate.clone());
focusCopy.setDate(focusedDate.getDate() + days);
if (!isDateInsideRange(focusCopy, resolution)) {
if (!isDateInsideRange(focusCopy, getResolution())) {
// If not inside allowed range, then do not move anything
return;
}
@@ -850,14 +955,16 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date requestedNextMonthDate = (Date) focusedDate.clone();
addOneMonth(requestedNextMonthDate);

if (!isDateInsideRange(requestedNextMonthDate, Resolution.MONTH)) {
if (!isDateInsideRange(requestedNextMonthDate,
getResolution(this::isMonth))) {
return;
}

// Now also checking whether the day is inside the range or not. If not
// inside,
// correct it
if (!isDateInsideRange(requestedNextMonthDate, Resolution.DAY)) {
if (!isDateInsideRange(requestedNextMonthDate,
getResolution(this::isDay))) {
requestedNextMonthDate = adjustDateToFitInsideRange(
requestedNextMonthDate);
}
@@ -910,11 +1017,13 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date requestedPreviousMonthDate = (Date) focusedDate.clone();
removeOneMonth(requestedPreviousMonthDate);

if (!isDateInsideRange(requestedPreviousMonthDate, Resolution.MONTH)) {
if (!isDateInsideRange(requestedPreviousMonthDate,
getResolution(this::isMonth))) {
return;
}

if (!isDateInsideRange(requestedPreviousMonthDate, Resolution.DAY)) {
if (!isDateInsideRange(requestedPreviousMonthDate,
getResolution(this::isDay))) {
requestedPreviousMonthDate = adjustDateToFitInsideRange(
requestedPreviousMonthDate);
}
@@ -935,12 +1044,12 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date previousYearDate = (Date) focusedDate.clone();
previousYearDate.setYear(previousYearDate.getYear() - years);
// Do not focus if not inside range
if (!isDateInsideRange(previousYearDate, Resolution.YEAR)) {
if (!isDateInsideRange(previousYearDate, getResolution(this::isYear))) {
return;
}
// If we remove one year, but have to roll back a bit, fit it
// into the calendar. Also the months have to be changed
if (!isDateInsideRange(previousYearDate, Resolution.DAY)) {
if (!isDateInsideRange(previousYearDate, getResolution(this::isDay))) {
previousYearDate = adjustDateToFitInsideRange(previousYearDate);

focusedDate.setYear(previousYearDate.getYear());
@@ -977,12 +1086,12 @@ public class VCalendarPanel extends FocusableFlexTable implements
Date nextYearDate = (Date) focusedDate.clone();
nextYearDate.setYear(nextYearDate.getYear() + years);
// Do not focus if not inside range
if (!isDateInsideRange(nextYearDate, Resolution.YEAR)) {
if (!isDateInsideRange(nextYearDate, getResolution(this::isYear))) {
return;
}
// If we add one year, but have to roll back a bit, fit it
// into the calendar. Also the months have to be changed
if (!isDateInsideRange(nextYearDate, Resolution.DAY)) {
if (!isDateInsideRange(nextYearDate, getResolution(this::isDay))) {
nextYearDate = adjustDateToFitInsideRange(nextYearDate);

focusedDate.setYear(nextYearDate.getYear());
@@ -1339,15 +1448,15 @@ public class VCalendarPanel extends FocusableFlexTable implements
return false;
}

else if (resolution == Resolution.YEAR) {
else if (isYear(getResolution())) {
return handleNavigationYearMode(keycode, ctrl, shift);
}

else if (resolution == Resolution.MONTH) {
else if (isMonth(getResolution())) {
return handleNavigationMonthMode(keycode, ctrl, shift);
}

else if (resolution == Resolution.DAY) {
else if (isDay(getResolution())) {
return handleNavigationDayMode(keycode, ctrl, shift);
}

@@ -1461,8 +1570,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
// Timer is first used for a 500ms delay after mousedown. After that has
// elapsed, another timer is triggered to go off every 150ms. Both
// timers are cancelled on mouseup or mouseout.
if (event.getNativeButton() == NativeEvent.BUTTON_LEFT
&& event.getSource() instanceof VEventButton) {
if (event.getNativeButton() == NativeEvent.BUTTON_LEFT && event
.getSource() instanceof VAbstractCalendarPanel.VEventButton) {
final VEventButton sender = (VEventButton) event.getSource();
processClickEvent(sender);
mouseTimer = new Timer() {
@@ -1517,7 +1626,27 @@ public class VCalendarPanel extends FocusableFlexTable implements
* The date to set
*/
public void setDate(Date currentDate) {
doSetDate(currentDate, false, () -> {
});
}

/**
* The actual implementation of the logic which sets the data of the Panel.
* The method {@link #setDate(Date)} just delegate a call to this method
* providing additional config parameters.
*
* @param currentDate
* currentDate The date to set
* @param needRerender
* if {@code true} then calendar will be rerendered regardless of
* internal logic, otherwise the decision will be made on the
* internal state inside the method
* @param focusAction
* an additional action which will be executed in case
* rerendering is not required
*/
protected void doSetDate(Date currentDate, boolean needRerender,
Runnable focusAction) {
// Check that we are not re-rendering an already active date
if (currentDate == value && currentDate != null) {
return;
@@ -1525,7 +1654,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
boolean currentDateWasAdjusted = false;
// Check that selected date is inside the allowed range
if (currentDate != null
&& !isDateInsideRange(currentDate, resolution)) {
&& !isDateInsideRange(currentDate, getResolution())) {
currentDate = adjustDateToFitInsideRange(currentDate);
currentDateWasAdjusted = true;
}
@@ -1566,13 +1695,14 @@ public class VCalendarPanel extends FocusableFlexTable implements
}

// Re-render calendar if the displayed month is changed.
if (oldDisplayedMonth == null || value == null
if (needRerender || oldDisplayedMonth == null || value == null
|| oldDisplayedMonth.getYear() != value.getYear()
|| oldDisplayedMonth.getMonth() != value.getMonth()) {
renderCalendar();
} else {
focusDay(focusedDate);
selectFocused();
focusAction.run();
}

if (!hasFocus) {
@@ -1666,7 +1796,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
*/
@Override
public void onBlur(final BlurEvent event) {
if (event.getSource() instanceof VCalendarPanel) {
if (event.getSource() instanceof VAbstractCalendarPanel) {
hasFocus = false;
focusDay(null);
}
@@ -1681,7 +1811,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
*/
@Override
public void onFocus(FocusEvent event) {
if (event.getSource() instanceof VCalendarPanel) {
if (event.getSource() instanceof VAbstractCalendarPanel) {
hasFocus = true;

// Focuses the current day if the calendar shows the days
@@ -1749,7 +1879,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
* @param subElement
* @return true if {@code w} is a parent of subElement, false otherwise.
*/
private boolean contains(Widget w, Element subElement) {
protected boolean contains(Widget w, Element subElement) {
if (w == null || w.getElement() == null) {
return false;
}
@@ -1782,7 +1912,7 @@ public class VCalendarPanel extends FocusableFlexTable implements
Iterator<Widget> iter = days.iterator();
while (iter.hasNext()) {
Widget w = iter.next();
if (w instanceof Day) {
if (w instanceof VAbstractCalendarPanel.Day) {
Day day = (Day) w;
if (day.getDate().equals(date)) {
return day.getElement();
@@ -1846,8 +1976,8 @@ public class VCalendarPanel extends FocusableFlexTable implements
}

private void setLabel() {
if (parent instanceof VPopupCalendar) {
((VPopupCalendar) parent).setFocusedDate(this);
if (parent instanceof VAbstractPopupCalendar) {
((VAbstractPopupCalendar) parent).setFocusedDate(this);
}
}
}

+ 69
- 0
client/src/main/java/com/vaadin/client/ui/VAbstractDateFieldCalendar.java View File

@@ -0,0 +1,69 @@
/*
* 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 com.google.gwt.event.dom.client.DomEvent;
import com.vaadin.client.ui.VAbstractCalendarPanel.FocusOutListener;
import com.vaadin.client.ui.VAbstractCalendarPanel.SubmitListener;

/**
* A client side implementation for inline date field.
*/
public abstract class VAbstractDateFieldCalendar<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 VAbstractDateFieldCalendar(VAbstractCalendarPanel<R> panel,
R resolution) {
super(resolution);
calendarPanel = panel;
calendarPanel.setParentField(this);
add(calendarPanel);
calendarPanel.setSubmitListener(new SubmitListener() {
@Override
public void onSubmit() {
updateValueFromPanel();
}

@Override
public void onCancel() {
// TODO Auto-generated method stub

}
});
calendarPanel.setFocusOutListener(new FocusOutListener() {
@Override
public boolean onFocusOut(DomEvent<?> event) {
updateValueFromPanel();
return false;
}
});
}

@SuppressWarnings("deprecation")
public abstract void updateValueFromPanel();

public void setTabIndex(int tabIndex) {
calendarPanel.getElement().setTabIndex(tabIndex);
}

public int getTabIndex() {
return calendarPanel.getElement().getTabIndex();
}
}

+ 730
- 0
client/src/main/java/com/vaadin/client/ui/VAbstractPopupCalendar.java View File

@@ -0,0 +1,730 @@
/*
* 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.Locale;

import com.google.gwt.aria.client.Id;
import com.google.gwt.aria.client.LiveValue;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ComputedStyle;
import com.vaadin.client.VConsole;
import com.vaadin.client.ui.VAbstractCalendarPanel.FocusOutListener;
import com.vaadin.client.ui.VAbstractCalendarPanel.SubmitListener;
import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.shared.ui.datefield.TextualDateFieldState;

/**
* Represents a date selection component with a text field and a popup date/time
* selector.
*
* <b>Note:</b> To change the keyboard assignments used in the popup dialog you
* should extend <code>com.vaadin.client.ui.VAbstractCalendarPanel</code> and
* then pass set it by calling the
* <code>setCalendarPanel(VAbstractCalendarPanel panel)</code> method.
*
*/
public abstract class VAbstractPopupCalendar<R extends Enum<R>>
extends VAbstractTextualDate<R>
implements Field, ClickHandler, CloseHandler<PopupPanel>, SubPartAware {

/** For internal use only. May be removed or replaced in the future. */
public final Button calendarToggle = new Button();

/** For internal use only. May be removed or replaced in the future. */
public VAbstractCalendarPanel<R> calendar;

/** For internal use only. May be removed or replaced in the future. */
public final VOverlay popup;

/** For internal use only. May be removed or replaced in the future. */
public boolean parsable = true;

private boolean open = false;

/*
* #14857: If calendarToggle button is clicked when calendar popup is
* already open we should prevent calling openCalendarPanel() in onClick,
* since we don't want to reopen it again right after it closes.
*/
private boolean preventOpenPopupCalendar = false;
private boolean cursorOverCalendarToggleButton = false;
private boolean toggleButtonClosesWithGuarantee = false;

private boolean textFieldEnabled = true;

private String captionId;

private Label selectedDate;

private Element descriptionForAssisitveDevicesElement;

private final String CALENDAR_TOGGLE_ID = "popupButton";

public VAbstractPopupCalendar(VAbstractCalendarPanel<R> calendarPanel,
R resolution) {
super(resolution);

calendarToggle.setText("");
calendarToggle.addClickHandler(this);

calendarToggle.addDomHandler(new MouseOverHandler() {
@Override
public void onMouseOver(MouseOverEvent event) {
cursorOverCalendarToggleButton = true;
}
}, MouseOverEvent.getType());

calendarToggle.addDomHandler(new MouseOutHandler() {
@Override
public void onMouseOut(MouseOutEvent event) {
cursorOverCalendarToggleButton = false;
}
}, MouseOutEvent.getType());

// -2 instead of -1 to avoid FocusWidget.onAttach to reset it
calendarToggle.getElement().setTabIndex(-2);

Roles.getButtonRole().set(calendarToggle.getElement());
Roles.getButtonRole().setAriaHiddenState(calendarToggle.getElement(),
true);

add(calendarToggle);

// Description of the usage of the widget for assisitve device users
descriptionForAssisitveDevicesElement = DOM.createDiv();
descriptionForAssisitveDevicesElement.setInnerText(
TextualDateFieldState.DESCRIPTION_FOR_ASSISTIVE_DEVICES);
AriaHelper.ensureHasId(descriptionForAssisitveDevicesElement);
Roles.getTextboxRole().setAriaDescribedbyProperty(text.getElement(),
Id.of(descriptionForAssisitveDevicesElement));
AriaHelper.setVisibleForAssistiveDevicesOnly(
descriptionForAssisitveDevicesElement, true);

calendar = calendarPanel;
calendar.setParentField(this);
calendar.setFocusOutListener(new FocusOutListener() {
@Override
public boolean onFocusOut(DomEvent<?> event) {
event.preventDefault();
closeCalendarPanel();
return true;
}
});

// FIXME: Problem is, that the element with the provided id does not
// exist yet in html. This is the same problem as with the context menu.
// Apply here the same fix (#11795)
Roles.getTextboxRole().setAriaControlsProperty(text.getElement(),
Id.of(calendar.getElement()));
Roles.getButtonRole().setAriaControlsProperty(
calendarToggle.getElement(), Id.of(calendar.getElement()));

calendar.setSubmitListener(new SubmitListener() {
@Override
public void onSubmit() {
// Update internal value and send valuechange event if immediate
updateValue(calendar.getDate());

// Update text field (a must when not immediate).
buildDate(true);

closeCalendarPanel();
}

@Override
public void onCancel() {
closeCalendarPanel();
}
});

popup = new VOverlay(true, false);
popup.setOwner(this);

FlowPanel wrapper = new FlowPanel();
selectedDate = new Label();
selectedDate.setStyleName(getStylePrimaryName() + "-selecteddate");
AriaHelper.setVisibleForAssistiveDevicesOnly(selectedDate.getElement(),
true);

Roles.getTextboxRole().setAriaLiveProperty(selectedDate.getElement(),
LiveValue.ASSERTIVE);
Roles.getTextboxRole().setAriaAtomicProperty(selectedDate.getElement(),
true);
wrapper.add(selectedDate);
wrapper.add(calendar);

popup.setWidget(wrapper);
popup.addCloseHandler(this);

DOM.setElementProperty(calendar.getElement(), "id",
"PID_VAADIN_POPUPCAL");

sinkEvents(Event.ONKEYDOWN);

updateStyleNames();
}

@Override
protected void onAttach() {
super.onAttach();
DOM.appendChild(RootPanel.get().getElement(),
descriptionForAssisitveDevicesElement);
}

@Override
protected void onDetach() {
super.onDetach();
descriptionForAssisitveDevicesElement.removeFromParent();
closeCalendarPanel();
}

@SuppressWarnings("deprecation")
public void updateValue(Date newDate) {
Date currentDate = getCurrentDate();
if (currentDate == null || newDate.getTime() != currentDate.getTime()) {
setCurrentDate((Date) newDate.clone());
getClient().updateVariable(getId(),
getResolutionVariable(
calendar.getResolution(calendar::isYear)),
newDate.getYear() + 1900, false);
if (!calendar.isYear(getCurrentResolution())) {
getClient().updateVariable(getId(),
getResolutionVariable(
calendar.getResolution(calendar::isMonth))
.toLowerCase(Locale.ENGLISH),
newDate.getMonth() + 1, false);
if (!calendar.isMonth(getCurrentResolution())) {
getClient().updateVariable(getId(),
getResolutionVariable(
calendar.getResolution(calendar::isDay)),
newDate.getDate(), false);
}
}
}
}

/**
* Checks whether the text field is enabled.
*
* @see VAbstractPopupCalendar#setTextFieldEnabled(boolean)
* @return The current state of the text field.
*/
public boolean isTextFieldEnabled() {
return textFieldEnabled;
}

/**
* Sets the state of the text field of this component. 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
*/
public void setTextFieldEnabled(boolean textFieldEnabled) {
this.textFieldEnabled = textFieldEnabled;
updateTextFieldEnabled();
}

protected void updateTextFieldEnabled() {
boolean reallyEnabled = isEnabled() && isTextFieldEnabled();
// IE has a non input disabled themeing that can not be overridden so we
// must fake the functionality using readonly and unselectable
if (BrowserInfo.get().isIE()) {
if (!reallyEnabled) {
text.getElement().setAttribute("unselectable", "on");
text.getElement().setAttribute("readonly", "");
text.setTabIndex(-2);
} else if (reallyEnabled
&& text.getElement().hasAttribute("unselectable")) {
text.getElement().removeAttribute("unselectable");
text.getElement().removeAttribute("readonly");
text.setTabIndex(0);
}
} else {
text.setEnabled(reallyEnabled);
}

if (reallyEnabled) {
calendarToggle.setTabIndex(-1);
Roles.getButtonRole()
.setAriaHiddenState(calendarToggle.getElement(), true);
} else {
calendarToggle.setTabIndex(0);
Roles.getButtonRole()
.setAriaHiddenState(calendarToggle.getElement(), false);
}

handleAriaAttributes();
}

/**
* Set correct tab index for disabled text field in IE as the value set in
* setTextFieldEnabled(...) gets overridden in
* TextualDateConnection.updateFromUIDL(...)
*
* @since 7.3.1
*/
public void setTextFieldTabIndex() {
if (BrowserInfo.get().isIE() && !textFieldEnabled) {
// index needs to be -2 because FocusWidget updates -1 to 0 onAttach
text.setTabIndex(-2);
}
}

@Override
public void bindAriaCaption(
com.google.gwt.user.client.Element captionElement) {
if (captionElement == null) {
captionId = null;
} else {
captionId = captionElement.getId();
}

if (isTextFieldEnabled()) {
super.bindAriaCaption(captionElement);
} else {
AriaHelper.bindCaption(calendarToggle, captionElement);
}

handleAriaAttributes();
}

private void handleAriaAttributes() {
Widget removeFromWidget;
Widget setForWidget;

if (isTextFieldEnabled()) {
setForWidget = text;
removeFromWidget = calendarToggle;
} else {
setForWidget = calendarToggle;
removeFromWidget = text;
}

Roles.getFormRole()
.removeAriaLabelledbyProperty(removeFromWidget.getElement());
if (captionId == null) {
Roles.getFormRole()
.removeAriaLabelledbyProperty(setForWidget.getElement());
} else {
Roles.getFormRole().setAriaLabelledbyProperty(
setForWidget.getElement(), Id.of(captionId));
}
}

/*
* (non-Javadoc)
*
* @see
* com.google.gwt.user.client.ui.UIObject#setStyleName(java.lang.String)
*/
@Override
public void setStyleName(String style) {
super.setStyleName(style);
updateStyleNames();
}

@Override
public void setStylePrimaryName(String style) {
removeStyleName(getStylePrimaryName() + "-popupcalendar");
super.setStylePrimaryName(style);
updateStyleNames();
}

@Override
protected void updateStyleNames() {
super.updateStyleNames();
if (getStylePrimaryName() != null && calendarToggle != null) {
addStyleName(getStylePrimaryName() + "-popupcalendar");
calendarToggle.setStyleName(getStylePrimaryName() + "-button");
popup.setStyleName(getStylePrimaryName() + "-popup");
calendar.setStyleName(getStylePrimaryName() + "-calendarpanel");
}
}

/**
* Opens the calendar panel popup
*/
public void openCalendarPanel() {

if (!open && !readonly && isEnabled()) {
open = true;

if (getCurrentDate() != null) {
calendar.setDate((Date) getCurrentDate().clone());
} else {
calendar.setDate(new Date());
}

// clear previous values
popup.setWidth("");
popup.setHeight("");
popup.setPopupPositionAndShow(new PopupPositionCallback());
} else {
VConsole.error("Cannot reopen popup, it is already open!");
}
}

/*
* (non-Javadoc)
*
* @see
* com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event
* .dom.client.ClickEvent)
*/
@Override
public void onClick(ClickEvent event) {
if (event.getSource() == calendarToggle && isEnabled()) {
if (!preventOpenPopupCalendar) {
openCalendarPanel();
}
preventOpenPopupCalendar = false;
}
}

/*
* (non-Javadoc)
*
* @see
* com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google.gwt
* .event.logical.shared.CloseEvent)
*/
@Override
public void onClose(CloseEvent<PopupPanel> event) {
if (event.getSource() == popup) {
buildDate();
if (!BrowserInfo.get().isTouchDevice() && textFieldEnabled) {
/*
* Move focus to textbox, unless on touch device (avoids opening
* virtual keyboard) or if textField is disabled.
*/
focus();
}

open = false;

if (cursorOverCalendarToggleButton
&& !toggleButtonClosesWithGuarantee) {
preventOpenPopupCalendar = true;
}

toggleButtonClosesWithGuarantee = false;
}
}

/**
* Sets focus to Calendar panel.
*
* @param focus
*/
public void setFocus(boolean focus) {
calendar.setFocus(focus);
}

@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
updateTextFieldEnabled();
calendarToggle.setEnabled(enabled);
Roles.getButtonRole().setAriaDisabledState(calendarToggle.getElement(),
!enabled);
}

/**
* Sets the content of a special field for assistive devices, so that they
* can recognize the change and inform the user (reading out in case of
* screen reader)
*
* @param selectedDate
* Date that is currently selected
*/
public void setFocusedDate(Date selectedDate) {
this.selectedDate.setText(DateTimeFormat.getFormat("dd, MMMM, yyyy")
.format(selectedDate));
}

/**
* For internal use only. May be removed or replaced in the future.
*
* @see com.vaadin.client.ui.VAbstractTextualDate#buildDate()
*/
@Override
public void buildDate() {
// Save previous value
String previousValue = getText();
super.buildDate();

// Restore previous value if the input could not be parsed
if (!parsable) {
setText(previousValue);
}
updateTextFieldEnabled();
}

/**
* Update the text field contents from the date. See {@link #buildDate()}.
*
* @param forceValid
* true to force the text field to be updated, false to only
* update if the parsable flag is true.
*/
protected void buildDate(boolean forceValid) {
if (forceValid) {
parsable = true;
}
buildDate();
}

/*
* (non-Javadoc)
*
* @see com.vaadin.client.ui.VDateField#onBrowserEvent(com.google
* .gwt.user.client.Event)
*/
@Override
public void onBrowserEvent(com.google.gwt.user.client.Event event) {
super.onBrowserEvent(event);
if (DOM.eventGetType(event) == Event.ONKEYDOWN
&& event.getKeyCode() == getOpenCalenderPanelKey()) {
openCalendarPanel();
event.preventDefault();
}
}

/**
* Get the key code that opens the calendar panel. By default it is the down
* key but you can override this to be whatever you like
*
* @return
*/
protected int getOpenCalenderPanelKey() {
return KeyCodes.KEY_DOWN;
}

/**
* Closes the open popup panel
*/
public void closeCalendarPanel() {
if (open) {
toggleButtonClosesWithGuarantee = true;
popup.hide(true);
}
}

@Override
public com.google.gwt.user.client.Element getSubPartElement(
String subPart) {
if (subPart.equals(CALENDAR_TOGGLE_ID)) {
return calendarToggle.getElement();
}

return super.getSubPartElement(subPart);
}

@Override
public String getSubPartName(
com.google.gwt.user.client.Element subElement) {
if (calendarToggle.getElement().isOrHasChild(subElement)) {
return CALENDAR_TOGGLE_ID;
}

return super.getSubPartName(subElement);
}

/**
* Set a description that explains the usage of the Widget for users of
* assistive devices.
*
* @param descriptionForAssistiveDevices
* String with the description
*/
public void setDescriptionForAssistiveDevices(
String descriptionForAssistiveDevices) {
descriptionForAssisitveDevicesElement
.setInnerText(descriptionForAssistiveDevices);
}

/**
* Get the description that explains the usage of the Widget for users of
* assistive devices.
*
* @return String with the description
*/
public String getDescriptionForAssistiveDevices() {
return descriptionForAssisitveDevicesElement.getInnerText();
}

/**
* Sets the start range for this component. The start range is inclusive,
* and it depends on the current resolution, what is considered inside the
* range.
*
* @param startDate
* - the allowed range's start date
*/
public void setRangeStart(Date rangeStart) {
calendar.setRangeStart(rangeStart);
}

/**
* Sets the end range for this component. The end range is inclusive, and it
* depends on the current resolution, what is considered inside the range.
*
* @param endDate
* - the allowed range's end date
*/
public void setRangeEnd(Date rangeEnd) {
calendar.setRangeEnd(rangeEnd);
}

private class PopupPositionCallback implements PositionCallback {

@Override
public void setPosition(int offsetWidth, int offsetHeight) {
final int width = offsetWidth;
final int height = offsetHeight;
final int browserWindowWidth = Window.getClientWidth()
+ Window.getScrollLeft();
final int windowHeight = Window.getClientHeight()
+ Window.getScrollTop();
int left = calendarToggle.getAbsoluteLeft();

// Add a little extra space to the right to avoid
// problems with IE7 scrollbars and to make it look
// nicer.
int extraSpace = 30;

boolean overflow = left + width + extraSpace > browserWindowWidth;
if (overflow) {
// Part of the popup is outside the browser window
// (to the right)
left = browserWindowWidth - width - extraSpace;
}

int top = calendarToggle.getAbsoluteTop();
int extraHeight = 2;
boolean verticallyRepositioned = false;
ComputedStyle style = new ComputedStyle(popup.getElement());
int[] margins = style.getMargin();
int desiredPopupBottom = top + height
+ calendarToggle.getOffsetHeight() + margins[0]
+ margins[2];

if (desiredPopupBottom > windowHeight) {
int updatedLeft = left;
left = getLeftPosition(left, width, style, overflow);

// if position has not been changed then it means there is no
// space to make popup fully visible
if (updatedLeft == left) {
// let's try to show popup on the top of the field
int updatedTop = top - extraHeight - height - margins[0]
- margins[2];
verticallyRepositioned = updatedTop >= 0;
if (verticallyRepositioned) {
top = updatedTop;
}
}
// Part of the popup is outside the browser window
// (below)
if (!verticallyRepositioned) {
verticallyRepositioned = true;
top = windowHeight - height - extraSpace + extraHeight;
}
}
if (verticallyRepositioned) {
popup.setPopupPosition(left, top);
} else {
popup.setPopupPosition(left,
top + calendarToggle.getOffsetHeight() + extraHeight);
}
doSetFocus();
}

private int getLeftPosition(int left, int width, ComputedStyle style,
boolean overflow) {
if (positionRightSide()) {
// Show to the right of the popup button unless we
// are in the lower right corner of the screen
if (overflow) {
return left;
} else {
return left + calendarToggle.getOffsetWidth();
}
} else {
int[] margins = style.getMargin();
int desiredLeftPosition = calendarToggle.getAbsoluteLeft()
- width - margins[1] - margins[3];
if (desiredLeftPosition >= 0) {
return desiredLeftPosition;
} else {
return left;
}
}
}

private boolean positionRightSide() {
int buttonRightSide = calendarToggle.getAbsoluteLeft()
+ calendarToggle.getOffsetWidth();
int textRightSide = text.getAbsoluteLeft() + text.getOffsetWidth();
return buttonRightSide >= textRightSide;
}

private void doSetFocus() {
/*
* We have to wait a while before focusing since the popup needs to
* be opened before we can focus
*/
Timer focusTimer = new Timer() {
@Override
public void run() {
setFocus(true);
}
};

focusTimer.schedule(100);
}
}

}

client/src/main/java/com/vaadin/client/ui/VTextualDate.java → client/src/main/java/com/vaadin/client/ui/VAbstractTextualDate.java View File

@@ -19,12 +19,9 @@ package com.vaadin.client.ui;
import java.util.Date;

import com.google.gwt.aria.client.Roles;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
@@ -39,10 +36,20 @@ import com.vaadin.client.ui.aria.HandlesAriaCaption;
import com.vaadin.client.ui.aria.HandlesAriaInvalid;
import com.vaadin.client.ui.aria.HandlesAriaRequired;
import com.vaadin.shared.EventId;
import com.vaadin.shared.ui.datefield.Resolution;

public class VTextualDate extends VDateField implements Field, ChangeHandler,
Focusable, SubPartAware, HandlesAriaCaption, HandlesAriaInvalid,
/**
* Abstract textual date field base implementation. Provides a text box as an
* editor for a date. The class is parameterized by the date resolution
* enumeration type.
*
* @author Vaadin Ltd
*
* @param <R>
* the resolution type which this field is based on (day, month, ...)
*/
public abstract class VAbstractTextualDate<R extends Enum<R>>
extends VDateField<R> implements Field, ChangeHandler, Focusable,
SubPartAware, HandlesAriaCaption, HandlesAriaInvalid,
HandlesAriaRequired, KeyDownHandler {

private static final String PARSE_ERROR_CLASSNAME = "-parseerror";
@@ -51,51 +58,30 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
public final TextBox text;

/** For internal use only. May be removed or replaced in the future. */
public String formatStr;
public boolean lenient;

private final String TEXTFIELD_ID = "field";

/** For internal use only. May be removed or replaced in the future. */
public boolean lenient;
public String formatStr;

public VTextualDate() {
super();
public VAbstractTextualDate(R resoluton) {
super(resoluton);
text = new TextBox();
text.addChangeHandler(this);
text.addFocusHandler(new FocusHandler() {
@Override
public void onFocus(FocusEvent event) {
text.addStyleName(VTextField.CLASSNAME + "-"
+ VTextField.CLASSNAME_FOCUS);
if (getClient() != null && getClient()
.hasEventListeners(VTextualDate.this, EventId.FOCUS)) {
getClient().updateVariable(getId(), EventId.FOCUS, "",
true);
}

// Needed for tooltip event handling
VTextualDate.this.fireEvent(event);
}
});
text.addBlurHandler(new BlurHandler() {
@Override
public void onBlur(BlurEvent event) {
text.removeStyleName(VTextField.CLASSNAME + "-"
+ VTextField.CLASSNAME_FOCUS);
String value = getText();
if (getClient() != null && getClient()
.hasEventListeners(VTextualDate.this, EventId.BLUR)) {
getClient().updateVariable(getId(), EventId.BLUR, "", true);
}

// Needed for tooltip event handling
VTextualDate.this.fireEvent(event);
}
});
text.addFocusHandler(
event -> fireBlurFocusEvent(event, true, EventId.FOCUS));
text.addBlurHandler(
event -> fireBlurFocusEvent(event, false, EventId.BLUR));
if (BrowserInfo.get().isIE()) {
addDomHandler(this, KeyDownEvent.getType());
}
add(text);
}

/**
* Updates style names for the widget (and its children).
*/
protected void updateStyleNames() {
if (text != null) {
text.setStyleName(VTextField.CLASSNAME);
@@ -103,9 +89,14 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
}
}

/**
* Gets the date format string for the current locale.
*
* @return the format string
*/
protected String getFormatString() {
if (formatStr == null) {
if (currentResolution == Resolution.YEAR) {
if (isYear(getCurrentResolution())) {
formatStr = "yyyy"; // force full year
} else {

@@ -224,34 +215,39 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
getClient().updateVariable(getId(), "dateString", text.getText(),
false);

updateDateVariables();
}

/**
* Updates variables to send a response to the server.
* <p>
* The method can be overridden by subclasses to provide a custom logic for
* date variables to avoid overriding the {@link #onChange(ChangeEvent)}
* method.
*/
protected void updateDateVariables() {
// Update variables
// (only the smallest defining resolution needs to be
// immediate)
Date currentDate = getDate();
getClient().updateVariable(getId(), "year",
getClient().updateVariable(getId(),
getResolutionVariable(getResolutions().filter(this::isYear)
.findFirst().get()),
currentDate != null ? currentDate.getYear() + 1900 : -1,
currentResolution == Resolution.YEAR);
if (currentResolution.compareTo(Resolution.MONTH) <= 0) {
getClient().updateVariable(getId(), "month",
currentDate != null ? currentDate.getMonth() + 1 : -1,
currentResolution == Resolution.MONTH);
}
if (currentResolution.compareTo(Resolution.DAY) <= 0) {
getClient().updateVariable(getId(), "day",
currentDate != null ? currentDate.getDate() : -1,
currentResolution == Resolution.DAY);
}
isYear(getCurrentResolution()));
}

private String cleanFormat(String format) {
// Remove unnecessary d & M if resolution is too low
if (currentResolution.compareTo(Resolution.DAY) > 0) {
format = format.replaceAll("d", "");
}
if (currentResolution.compareTo(Resolution.MONTH) > 0) {
format = format.replaceAll("M", "");
}

/**
* Clean date format string to make it suitable for
* {@link #getFormatString()}.
*
* @see #getFormatString()
*
* @param format
* date format string
* @return cleaned up string
*/
protected String cleanFormat(String format) {
// Remove unsupported patterns
// TODO support for 'G', era designator (used at least in Japan)
format = format.replaceAll("[GzZwWkK]", "");
@@ -311,8 +307,6 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
this.text.setText(text);
}

private final String TEXTFIELD_ID = "field";

@Override
public com.google.gwt.user.client.Element getSubPartElement(
String subPart) {
@@ -342,4 +336,22 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler,
onChange(null);
}
}

private void fireBlurFocusEvent(DomEvent<?> event,
boolean addFocusStyleName, String eventId) {
String styleName = VTextField.CLASSNAME + "-"
+ VTextField.CLASSNAME_FOCUS;
if (addFocusStyleName) {
text.addStyleName(styleName);
} else {
text.removeStyleName(styleName);
}
if (getClient() != null && getClient()
.hasEventListeners(VAbstractTextualDate.this, eventId)) {
getClient().updateVariable(getId(), eventId, "", true);
}

// Needed for tooltip event handling
fireEvent(event);
}
}

+ 46
- 0
client/src/main/java/com/vaadin/client/ui/VDateCalendarPanel.java View File

@@ -0,0 +1,46 @@
/*
* 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 com.vaadin.shared.ui.datefield.DateResolution;

/**
* @author Vaadin Ltd
*
*/
public class VDateCalendarPanel extends VAbstractCalendarPanel<DateResolution> {

@Override
protected boolean acceptDayFocus() {
return isDay(getResolution());
}

@Override
protected boolean isDay(DateResolution resolution) {
return DateResolution.DAY.equals(resolution);
}

@Override
protected boolean isMonth(DateResolution resolution) {
return DateResolution.MONTH.equals(resolution);
}

@Override
protected boolean isBelowMonth(DateResolution resolution) {
return isDay(resolution);
}

}

+ 99
- 34
client/src/main/java/com/vaadin/client/ui/VDateField.java View File

@@ -17,14 +17,25 @@
package com.vaadin.client.ui;

import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Stream;

import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasEnabled;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.DateTimeService;
import com.vaadin.shared.ui.datefield.Resolution;

public class VDateField extends FlowPanel implements Field, HasEnabled {
/**
* A very base widget class for a date field.
*
* @author Vaadin Ltd
*
* @param <R>
* the resolution type which this field is based on (day, month, ...)
*/
public abstract class VDateField<R extends Enum<R>> extends FlowPanel
implements Field, HasEnabled {

public static final String CLASSNAME = "v-datefield";

@@ -34,18 +45,7 @@ public class VDateField extends FlowPanel implements Field, HasEnabled {
/** For internal use only. May be removed or replaced in the future. */
public ApplicationConnection client;

/** For internal use only. May be removed or replaced in the future. */
public static String resolutionToString(Resolution res) {
if (res == Resolution.DAY) {
return "day";
}
if (res == Resolution.MONTH) {
return "month";
}
return "year";
}

protected Resolution currentResolution = Resolution.YEAR;
private R currentResolution;

protected String currentLocale;

@@ -64,33 +64,17 @@ public class VDateField extends FlowPanel implements Field, HasEnabled {

protected boolean showISOWeekNumbers = false;

public VDateField() {
public VDateField(R resolution) {
setStyleName(CLASSNAME);
dts = new DateTimeService();
currentResolution = resolution;
}

/**
* For internal use only. May be removed or replaced in the future.
*/
public static Date getTime(int year, int month, int day) {
Date date = new Date(2000 - 1900, 0, 1);
if (year >= 0) {
date.setYear(year - 1900);
}
if (month >= 0) {
date.setMonth(month - 1);
}
if (day >= 0) {
date.setDate(day);
}
return date;
}

public Resolution getCurrentResolution() {
public R getCurrentResolution() {
return currentResolution;
}

public void setCurrentResolution(Resolution currentResolution) {
public void setCurrentResolution(R currentResolution) {
this.currentResolution = currentResolution;
}

@@ -110,6 +94,20 @@ public class VDateField extends FlowPanel implements Field, HasEnabled {
this.date = date;
}

/**
* Set the current date using a map with date values.
* <p>
* The map contains integer representation of values per resolution. The
* method should construct a date based on the map and set it via
* {@link #setCurrentDate(Date)}
*
* @param dateValues
* a map with date values to convert into a date
*/
public void setCurrentDate(Map<R, Integer> dateValues) {
setCurrentDate(getDate(dateValues));
}

public boolean isReadonly() {
return readonly;
}
@@ -182,4 +180,71 @@ public class VDateField extends FlowPanel implements Field, HasEnabled {
protected void setDate(Date date) {
this.date = date;
}

/**
* Returns a resolution variable name for the given {@code resolution}.
*
* @param resolution
* the given resolution
* @return the resolution variable name
*/
public String getResolutionVariable(R resolution) {
return resolution.name().toLowerCase(Locale.ENGLISH);
}

/**
* Returns all available resolutions for the field in the ascending order
* (which is the same as order of enumeration ordinals).
* <p>
* The method uses {@link #doGetResolutions()} to make sure that the order
* is the correct one.
*
* @see #doGetResolutions()
*
* @return stream of all available resolutions in the ascending order.
*/
public Stream<R> getResolutions() {
return Stream.of(doGetResolutions()).sorted();
}

/**
* Returns a current resolution as a string.
* <p>
* The method is used to generate a style name for the current resolution.
*
* @return the current resolution as a string
*/
public abstract String resolutionAsString();

/**
* Checks whether the given {@code resolution} represents an year.
*
* @param resolution
* the given resolution
* @return {@code true} if the {@code resolution} represents an year
*/
public abstract boolean isYear(R resolution);

/**
* Returns a date based on the provided date values map.
*
* @see #setCurrentDate(Map)
*
* @param dateVaules
* 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);

/**
* Returns all available resolutions as an array.
* <p>
* No any order is required (in contrary to {@link #getResolutions()}.
*
* @see #getResolutions()
*
* @return all available resolutions
*/
protected abstract R[] doGetResolutions();

}

+ 47
- 46
client/src/main/java/com/vaadin/client/ui/VDateFieldCalendar.java View File

@@ -13,48 +13,25 @@
* 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.event.dom.client.DomEvent;
import com.vaadin.client.ui.VCalendarPanel.FocusOutListener;
import com.vaadin.client.ui.VCalendarPanel.SubmitListener;
import com.vaadin.shared.ui.datefield.Resolution;
import com.google.gwt.core.shared.GWT;
import com.vaadin.shared.ui.datefield.DateResolution;

/**
* A client side implementation for InlineDateField
* A client side implementation for InlineDateField.
*
* @author Vaadin Ltd
*
*/
public class VDateFieldCalendar extends VDateField {

/** For internal use only. May be removed or replaced in the future. */
public final VCalendarPanel calendarPanel;
public class VDateFieldCalendar
extends VAbstractDateFieldCalendar<DateResolution> {

public VDateFieldCalendar() {
super();
calendarPanel = new VCalendarPanel();
calendarPanel.setParentField(this);
add(calendarPanel);
calendarPanel.setSubmitListener(new SubmitListener() {
@Override
public void onSubmit() {
updateValueFromPanel();
}

@Override
public void onCancel() {
// TODO Auto-generated method stub

}
});
calendarPanel.setFocusOutListener(new FocusOutListener() {
@Override
public boolean onFocusOut(DomEvent<?> event) {
updateValueFromPanel();
return false;
}
});
super(GWT.create(VDateCalendarPanel.class), DateResolution.YEAR);
}

/**
@@ -62,10 +39,9 @@ public class VDateFieldCalendar extends VDateField {
* <p>
* For internal use only. May be removed or replaced in the future.
*/
@Override
@SuppressWarnings("deprecation")
public void updateValueFromPanel() {

// If field is invisible at the beginning, client can still be null when
// this function is called.
if (getClient() == null) {
@@ -76,25 +52,50 @@ public class VDateFieldCalendar extends VDateField {
Date currentDate = getCurrentDate();
if (currentDate == null || date2.getTime() != currentDate.getTime()) {
setCurrentDate((Date) date2.clone());
getClient().updateVariable(getId(), "year", date2.getYear() + 1900,
false);
if (getCurrentResolution().compareTo(Resolution.YEAR) < 0) {
getClient().updateVariable(getId(), "month",
getClient().updateVariable(getId(),
getResolutionVariable(DateResolution.YEAR),
// Java Date uses the year aligned to 1900 (no to zero).
// So we should add 1900 to get a correct year aligned to 0.
date2.getYear() + 1900, false);
if (getCurrentResolution().compareTo(DateResolution.YEAR) < 0) {
getClient().updateVariable(getId(),
getResolutionVariable(DateResolution.MONTH),
date2.getMonth() + 1, false);
if (getCurrentResolution().compareTo(Resolution.MONTH) < 0) {
getClient().updateVariable(getId(), "day", date2.getDate(),
false);
if (getCurrentResolution()
.compareTo(DateResolution.MONTH) < 0) {
getClient().updateVariable(getId(),
getResolutionVariable(DateResolution.DAY),
date2.getDate(), false);
}
}
getClient().sendPendingVariableChanges();
}
}

public void setTabIndex(int tabIndex) {
calendarPanel.getElement().setTabIndex(tabIndex);
@Override
public void setCurrentResolution(DateResolution resolution) {
super.setCurrentResolution(
resolution == null ? DateResolution.YEAR : resolution);
}

@Override
public String resolutionAsString() {
return getResolutionVariable(getCurrentResolution());
}

@Override
public boolean isYear(DateResolution resolution) {
return DateResolution.YEAR.equals(resolution);
}

@Override
protected DateResolution[] doGetResolutions() {
return DateResolution.values();
}

public int getTabIndex() {
return calendarPanel.getElement().getTabIndex();
@Override
protected Date getDate(Map<DateResolution, Integer> dateVaules) {
return VPopupCalendar.makeDate(dateVaules);
}

}

+ 48
- 666
client/src/main/java/com/vaadin/client/ui/VPopupCalendar.java View File

@@ -13,709 +13,91 @@
* 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.aria.client.Id;
import com.google.gwt.aria.client.LiveValue;
import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.BrowserInfo;
import com.vaadin.client.ComputedStyle;
import com.vaadin.client.VConsole;
import com.vaadin.client.ui.VCalendarPanel.FocusOutListener;
import com.vaadin.client.ui.VCalendarPanel.SubmitListener;
import com.vaadin.client.ui.aria.AriaHelper;
import com.vaadin.shared.ui.datefield.DateFieldState;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;

/**
* Represents a date selection component with a text field and a popup date
* selector.
*
* <b>Note:</b> To change the keyboard assignments used in the popup dialog you
* should extend <code>com.vaadin.client.ui.VCalendarPanel</code> and then pass
* set it by calling the <code>setCalendarPanel(VCalendarPanel panel)</code>
* method.
*
* @author Vaadin Ltd
*
*/
public class VPopupCalendar extends VTextualDate
implements Field, ClickHandler, CloseHandler<PopupPanel>, SubPartAware {

/** For internal use only. May be removed or replaced in the future. */
public final Button calendarToggle = new Button();

/** For internal use only. May be removed or replaced in the future. */
public VCalendarPanel calendar;

/** For internal use only. May be removed or replaced in the future. */
public final VOverlay popup;

/** For internal use only. May be removed or replaced in the future. */
public boolean parsable = true;

private boolean open = false;

/*
* #14857: If calendarToggle button is clicked when calendar popup is
* already open we should prevent calling openCalendarPanel() in onClick,
* since we don't want to reopen it again right after it closes.
*/
private boolean preventOpenPopupCalendar = false;
private boolean cursorOverCalendarToggleButton = false;
private boolean toggleButtonClosesWithGuarantee = false;

private boolean textFieldEnabled = true;

private String captionId;

private Label selectedDate;

private Element descriptionForAssisitveDevicesElement;
public class VPopupCalendar extends VAbstractPopupCalendar<DateResolution> {

public VPopupCalendar() {
super();

calendarToggle.setText("");
calendarToggle.addClickHandler(this);

calendarToggle.addDomHandler(new MouseOverHandler() {
@Override
public void onMouseOver(MouseOverEvent event) {
cursorOverCalendarToggleButton = true;
}
}, MouseOverEvent.getType());

calendarToggle.addDomHandler(new MouseOutHandler() {
@Override
public void onMouseOut(MouseOutEvent event) {
cursorOverCalendarToggleButton = false;
}
}, MouseOutEvent.getType());

// -2 instead of -1 to avoid FocusWidget.onAttach to reset it
calendarToggle.getElement().setTabIndex(-2);

Roles.getButtonRole().set(calendarToggle.getElement());
Roles.getButtonRole().setAriaHiddenState(calendarToggle.getElement(),
true);

add(calendarToggle);

// Description of the usage of the widget for assisitve device users
descriptionForAssisitveDevicesElement = DOM.createDiv();
descriptionForAssisitveDevicesElement
.setInnerText(DateFieldState.DESCRIPTION_FOR_ASSISTIVE_DEVICES);
AriaHelper.ensureHasId(descriptionForAssisitveDevicesElement);
Roles.getTextboxRole().setAriaDescribedbyProperty(text.getElement(),
Id.of(descriptionForAssisitveDevicesElement));
AriaHelper.setVisibleForAssistiveDevicesOnly(
descriptionForAssisitveDevicesElement, true);

calendar = GWT.create(VCalendarPanel.class);
calendar.setParentField(this);
calendar.setFocusOutListener(new FocusOutListener() {
@Override
public boolean onFocusOut(DomEvent<?> event) {
event.preventDefault();
closeCalendarPanel();
return true;
}
});

// FIXME: Problem is, that the element with the provided id does not
// exist yet in html. This is the same problem as with the context menu.
// Apply here the same fix (#11795)
Roles.getTextboxRole().setAriaControlsProperty(text.getElement(),
Id.of(calendar.getElement()));
Roles.getButtonRole().setAriaControlsProperty(
calendarToggle.getElement(), Id.of(calendar.getElement()));

calendar.setSubmitListener(new SubmitListener() {
@Override
public void onSubmit() {
// Update internal value and send valuechange event if immediate
updateValue(calendar.getDate());

// Update text field (a must when not immediate).
buildDate(true);

closeCalendarPanel();
}

@Override
public void onCancel() {
closeCalendarPanel();
}
});

popup = new VOverlay(true, false);
popup.setOwner(this);

FlowPanel wrapper = new FlowPanel();
selectedDate = new Label();
selectedDate.setStyleName(getStylePrimaryName() + "-selecteddate");
AriaHelper.setVisibleForAssistiveDevicesOnly(selectedDate.getElement(),
true);

Roles.getTextboxRole().setAriaLiveProperty(selectedDate.getElement(),
LiveValue.ASSERTIVE);
Roles.getTextboxRole().setAriaAtomicProperty(selectedDate.getElement(),
true);
wrapper.add(selectedDate);
wrapper.add(calendar);

popup.setWidget(wrapper);
popup.addCloseHandler(this);

DOM.setElementProperty(calendar.getElement(), "id",
"PID_VAADIN_POPUPCAL");

sinkEvents(Event.ONKEYDOWN);

updateStyleNames();
}

@Override
protected void onAttach() {
super.onAttach();
DOM.appendChild(RootPanel.get().getElement(),
descriptionForAssisitveDevicesElement);
}

@Override
protected void onDetach() {
super.onDetach();
descriptionForAssisitveDevicesElement.removeFromParent();
closeCalendarPanel();
}

@SuppressWarnings("deprecation")
public void updateValue(Date newDate) {
Date currentDate = getCurrentDate();
if (currentDate == null || newDate.getTime() != currentDate.getTime()) {
setCurrentDate((Date) newDate.clone());
getClient().updateVariable(getId(), "year",
newDate.getYear() + 1900, false);
if (getCurrentResolution().compareTo(Resolution.YEAR) < 0) {
getClient().updateVariable(getId(), "month",
newDate.getMonth() + 1, false);
if (getCurrentResolution().compareTo(Resolution.MONTH) < 0) {
getClient().updateVariable(getId(), "day",
newDate.getDate(), false);
}
}
}
}

/**
* Checks whether the text field is enabled.
*
* @see VPopupCalendar#setTextFieldEnabled(boolean)
* @return The current state of the text field.
*/
public boolean isTextFieldEnabled() {
return textFieldEnabled;
}

/**
* Sets the state of the text field of this component. 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
*/
public void setTextFieldEnabled(boolean textFieldEnabled) {
this.textFieldEnabled = textFieldEnabled;
updateTextFieldEnabled();
}

protected void updateTextFieldEnabled() {
boolean reallyEnabled = isEnabled() && isTextFieldEnabled();
// IE has a non input disabled themeing that can not be overridden so we
// must fake the functionality using readonly and unselectable
if (BrowserInfo.get().isIE()) {
if (!reallyEnabled) {
text.getElement().setAttribute("unselectable", "on");
text.getElement().setAttribute("readonly", "");
text.setTabIndex(-2);
} else if (reallyEnabled
&& text.getElement().hasAttribute("unselectable")) {
text.getElement().removeAttribute("unselectable");
text.getElement().removeAttribute("readonly");
text.setTabIndex(0);
}
} else {
text.setEnabled(reallyEnabled);
}

if (reallyEnabled) {
calendarToggle.setTabIndex(-1);
Roles.getButtonRole()
.setAriaHiddenState(calendarToggle.getElement(), true);
} else {
calendarToggle.setTabIndex(0);
Roles.getButtonRole()
.setAriaHiddenState(calendarToggle.getElement(), false);
}

handleAriaAttributes();
}

/**
* Set correct tab index for disabled text field in IE as the value set in
* setTextFieldEnabled(...) gets overridden in
* TextualDateConnection.updateFromUIDL(...)
*
* @since 7.3.1
*/
public void setTextFieldTabIndex() {
if (BrowserInfo.get().isIE() && !textFieldEnabled) {
// index needs to be -2 because FocusWidget updates -1 to 0 onAttach
text.setTabIndex(-2);
}
}

@Override
public void bindAriaCaption(
com.google.gwt.user.client.Element captionElement) {
if (captionElement == null) {
captionId = null;
} else {
captionId = captionElement.getId();
}

if (isTextFieldEnabled()) {
super.bindAriaCaption(captionElement);
} else {
AriaHelper.bindCaption(calendarToggle, captionElement);
}

handleAriaAttributes();
}

private void handleAriaAttributes() {
Widget removeFromWidget;
Widget setForWidget;

if (isTextFieldEnabled()) {
setForWidget = text;
removeFromWidget = calendarToggle;
} else {
setForWidget = calendarToggle;
removeFromWidget = text;
}

Roles.getFormRole()
.removeAriaLabelledbyProperty(removeFromWidget.getElement());
if (captionId == null) {
Roles.getFormRole()
.removeAriaLabelledbyProperty(setForWidget.getElement());
} else {
Roles.getFormRole().setAriaLabelledbyProperty(
setForWidget.getElement(), Id.of(captionId));
}
super(GWT.create(VDateCalendarPanel.class), DateResolution.YEAR);
}

/*
* (non-Javadoc)
*
* @see
* com.google.gwt.user.client.ui.UIObject#setStyleName(java.lang.String)
*/
@Override
public void setStyleName(String style) {
super.setStyleName(style);
updateStyleNames();
protected DateResolution[] doGetResolutions() {
return DateResolution.values();
}

@Override
public void setStylePrimaryName(String style) {
removeStyleName(getStylePrimaryName() + "-popupcalendar");
super.setStylePrimaryName(style);
updateStyleNames();
public String resolutionAsString() {
return getResolutionVariable(getCurrentResolution());
}

@Override
protected void updateStyleNames() {
super.updateStyleNames();
if (getStylePrimaryName() != null && calendarToggle != null) {
addStyleName(getStylePrimaryName() + "-popupcalendar");
calendarToggle.setStyleName(getStylePrimaryName() + "-button");
popup.setStyleName(getStylePrimaryName() + "-popup");
calendar.setStyleName(getStylePrimaryName() + "-calendarpanel");
}
}

/**
* Opens the calendar panel popup
*/
public void openCalendarPanel() {

if (!open && !readonly && isEnabled()) {
open = true;

if (getCurrentDate() != null) {
calendar.setDate((Date) getCurrentDate().clone());
} else {
calendar.setDate(new Date());
}

// clear previous values
popup.setWidth("");
popup.setHeight("");
popup.setPopupPositionAndShow(new PopupPositionCallback());
} else {
VConsole.error("Cannot reopen popup, it is already open!");
}
public void setCurrentResolution(DateResolution resolution) {
super.setCurrentResolution(
resolution == null ? DateResolution.YEAR : resolution);
}

/*
* (non-Javadoc)
*
* @see
* com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event
* .dom.client.ClickEvent)
*/
@Override
public void onClick(ClickEvent event) {
if (event.getSource() == calendarToggle && isEnabled()) {
if (!preventOpenPopupCalendar) {
openCalendarPanel();
}
preventOpenPopupCalendar = false;
public static Date makeDate(Map<DateResolution, Integer> dateVaules) {
if (dateVaules.get(DateResolution.YEAR) == -1) {
return null;
}
}

/*
* (non-Javadoc)
*
* @see
* com.google.gwt.event.logical.shared.CloseHandler#onClose(com.google.gwt
* .event.logical.shared.CloseEvent)
*/
@Override
public void onClose(CloseEvent<PopupPanel> event) {
if (event.getSource() == popup) {
buildDate();
if (!BrowserInfo.get().isTouchDevice() && textFieldEnabled) {
/*
* Move focus to textbox, unless on touch device (avoids opening
* virtual keyboard) or if textField is disabled.
*/
focus();
}

open = false;

if (cursorOverCalendarToggleButton
&& !toggleButtonClosesWithGuarantee) {
preventOpenPopupCalendar = true;
}

toggleButtonClosesWithGuarantee = false;
Date date = new Date(2000 - 1900, 0, 1);
int year = dateVaules.get(DateResolution.YEAR);
if (year >= 0) {
date.setYear(year - 1900);
}
}

/**
* Sets focus to Calendar panel.
*
* @param focus
*/
public void setFocus(boolean focus) {
calendar.setFocus(focus);
}

@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
updateTextFieldEnabled();
calendarToggle.setEnabled(enabled);
Roles.getButtonRole().setAriaDisabledState(calendarToggle.getElement(),
!enabled);
}

/**
* Sets the content of a special field for assistive devices, so that they
* can recognize the change and inform the user (reading out in case of
* screen reader)
*
* @param selectedDate
* Date that is currently selected
*/
public void setFocusedDate(Date selectedDate) {
this.selectedDate.setText(DateTimeFormat.getFormat("dd, MMMM, yyyy")
.format(selectedDate));
}

/**
* For internal use only. May be removed or replaced in the future.
*
* @see com.vaadin.client.ui.VTextualDate#buildDate()
*/
@Override
public void buildDate() {
// Save previous value
String previousValue = getText();
super.buildDate();

// Restore previous value if the input could not be parsed
if (!parsable) {
setText(previousValue);
int month = dateVaules.get(DateResolution.MONTH);
if (month >= 0) {
date.setMonth(month - 1);
}
updateTextFieldEnabled();
}

/**
* Update the text field contents from the date. See {@link #buildDate()}.
*
* @param forceValid
* true to force the text field to be updated, false to only
* update if the parsable flag is true.
*/
protected void buildDate(boolean forceValid) {
if (forceValid) {
parsable = true;
int day = dateVaules.get(DateResolution.DAY);
if (day >= 0) {
date.setDate(day);
}
buildDate();
return date;
}

/*
* (non-Javadoc)
*
* @see com.vaadin.client.ui.VDateField#onBrowserEvent(com.google
* .gwt.user.client.Event)
*/
@Override
public void onBrowserEvent(com.google.gwt.user.client.Event event) {
super.onBrowserEvent(event);
if (DOM.eventGetType(event) == Event.ONKEYDOWN
&& event.getKeyCode() == getOpenCalenderPanelKey()) {
openCalendarPanel();
event.preventDefault();
}
}

/**
* Get the key code that opens the calendar panel. By default it is the down
* key but you can override this to be whatever you like
*
* @return
*/
protected int getOpenCalenderPanelKey() {
return KeyCodes.KEY_DOWN;
}

/**
* Closes the open popup panel
*/
public void closeCalendarPanel() {
if (open) {
toggleButtonClosesWithGuarantee = true;
popup.hide(true);
}
public boolean isYear(DateResolution resolution) {
return DateResolution.YEAR.equals(resolution);
}

private final String CALENDAR_TOGGLE_ID = "popupButton";

@Override
public com.google.gwt.user.client.Element getSubPartElement(
String subPart) {
if (subPart.equals(CALENDAR_TOGGLE_ID)) {
return calendarToggle.getElement();
}

return super.getSubPartElement(subPart);
protected Date getDate(Map<DateResolution, Integer> dateValues) {
return makeDate(dateValues);
}

@Override
public String getSubPartName(
com.google.gwt.user.client.Element subElement) {
if (calendarToggle.getElement().isOrHasChild(subElement)) {
return CALENDAR_TOGGLE_ID;
}

return super.getSubPartName(subElement);
}

/**
* Set a description that explains the usage of the Widget for users of
* assistive devices.
*
* @param descriptionForAssistiveDevices
* String with the description
*/
public void setDescriptionForAssistiveDevices(
String descriptionForAssistiveDevices) {
descriptionForAssisitveDevicesElement
.setInnerText(descriptionForAssistiveDevices);
}

/**
* Get the description that explains the usage of the Widget for users of
* assistive devices.
*
* @return String with the description
*/
public String getDescriptionForAssistiveDevices() {
return descriptionForAssisitveDevicesElement.getInnerText();
}

/**
* Sets the start range for this component. The start range is inclusive,
* and it depends on the current resolution, what is considered inside the
* range.
*
* @param startDate
* - the allowed range's start date
*/
public void setRangeStart(Date rangeStart) {
calendar.setRangeStart(rangeStart);
}

/**
* Sets the end range for this component. The end range is inclusive, and it
* depends on the current resolution, what is considered inside the range.
*
* @param endDate
* - the allowed range's end date
*/
public void setRangeEnd(Date rangeEnd) {
calendar.setRangeEnd(rangeEnd);
}

private class PopupPositionCallback implements PositionCallback {

@Override
public void setPosition(int offsetWidth, int offsetHeight) {
final int width = offsetWidth;
final int height = offsetHeight;
final int browserWindowWidth = Window.getClientWidth()
+ Window.getScrollLeft();
final int windowHeight = Window.getClientHeight()
+ Window.getScrollTop();
int left = calendarToggle.getAbsoluteLeft();

// Add a little extra space to the right to avoid
// problems with IE7 scrollbars and to make it look
// nicer.
int extraSpace = 30;

boolean overflow = left + width + extraSpace > browserWindowWidth;
if (overflow) {
// Part of the popup is outside the browser window
// (to the right)
left = browserWindowWidth - width - extraSpace;
}

int top = calendarToggle.getAbsoluteTop();
int extraHeight = 2;
boolean verticallyRepositioned = false;
ComputedStyle style = new ComputedStyle(popup.getElement());
int[] margins = style.getMargin();
int desiredPopupBottom = top + height
+ calendarToggle.getOffsetHeight() + margins[0]
+ margins[2];

if (desiredPopupBottom > windowHeight) {
int updatedLeft = left;
left = getLeftPosition(left, width, style, overflow);

// if position has not been changed then it means there is no
// space to make popup fully visible
if (updatedLeft == left) {
// let's try to show popup on the top of the field
int updatedTop = top - extraHeight - height - margins[0]
- margins[2];
verticallyRepositioned = updatedTop >= 0;
if (verticallyRepositioned) {
top = updatedTop;
}
}
// Part of the popup is outside the browser window
// (below)
if (!verticallyRepositioned) {
verticallyRepositioned = true;
top = windowHeight - height - extraSpace + extraHeight;
}
}
if (verticallyRepositioned) {
popup.setPopupPosition(left, top);
} else {
popup.setPopupPosition(left,
top + calendarToggle.getOffsetHeight() + extraHeight);
}
doSetFocus();
}

private int getLeftPosition(int left, int width, ComputedStyle style,
boolean overflow) {
if (positionRightSide()) {
// Show to the right of the popup button unless we
// are in the lower right corner of the screen
if (overflow) {
return left;
} else {
return left + calendarToggle.getOffsetWidth();
}
} else {
int[] margins = style.getMargin();
int desiredLeftPosition = calendarToggle.getAbsoluteLeft()
- width - margins[1] - margins[3];
if (desiredLeftPosition >= 0) {
return desiredLeftPosition;
} else {
return left;
}
}
}

private boolean positionRightSide() {
int buttonRightSide = calendarToggle.getAbsoluteLeft()
+ calendarToggle.getOffsetWidth();
int textRightSide = text.getAbsoluteLeft() + text.getOffsetWidth();
return buttonRightSide >= textRightSide;
}

private void doSetFocus() {
/*
* We have to wait a while before focusing since the popup needs to
* be opened before we can focus
*/
Timer focusTimer = new Timer() {
@Override
public void run() {
setFocus(true);
}
};

focusTimer.schedule(100);
protected void updateDateVariables() {
super.updateDateVariables();
// Update variables
// (only the smallest defining resolution needs to be
// immediate)
Date currentDate = getDate();
if (getCurrentResolution().compareTo(DateResolution.MONTH) <= 0) {
getClient().updateVariable(getId(),
getResolutionVariable(DateResolution.MONTH),
currentDate != null ? currentDate.getMonth() + 1 : -1,
getCurrentResolution() == DateResolution.MONTH);
}
if (getCurrentResolution().compareTo(DateResolution.DAY) <= 0) {
getClient().updateVariable(getId(),
getResolutionVariable(DateResolution.DAY),
currentDate != null ? currentDate.getDate() : -1,
getCurrentResolution() == DateResolution.DAY);
}
}


+ 38
- 38
client/src/main/java/com/vaadin/client/ui/datefield/AbstractDateFieldConnector.java View File

@@ -15,6 +15,12 @@
*/
package com.vaadin.client.ui.datefield;

import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.LocaleNotLoadedException;
import com.vaadin.client.Paintable;
@@ -22,12 +28,10 @@ import com.vaadin.client.UIDL;
import com.vaadin.client.VConsole;
import com.vaadin.client.ui.AbstractFieldConnector;
import com.vaadin.client.ui.VDateField;
import com.vaadin.client.ui.VTextualDate;
import com.vaadin.shared.ui.datefield.DateFieldConstants;
import com.vaadin.shared.ui.datefield.Resolution;

public class AbstractDateFieldConnector extends AbstractFieldConnector
implements Paintable {
public abstract class AbstractDateFieldConnector<R extends Enum<R>>
extends AbstractFieldConnector implements Paintable {

@Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
@@ -62,46 +66,42 @@ public class AbstractDateFieldConnector extends AbstractFieldConnector
uidl.getBooleanAttribute(DateFieldConstants.ATTR_WEEK_NUMBERS)
&& getWidget().dts.getFirstDayOfWeek() == 1);

Resolution newResolution;
if (uidl.hasVariable("day")) {
newResolution = Resolution.DAY;
} else if (uidl.hasVariable("month")) {
newResolution = Resolution.MONTH;
} else {
newResolution = Resolution.YEAR;
}

// Remove old stylename that indicates current resolution
setWidgetStyleName(
getWidget().getStylePrimaryName() + "-" + VDateField
.resolutionToString(getWidget().getCurrentResolution()),
false);
setWidgetStyleName(getWidget().getStylePrimaryName() + "-"
+ getWidget().resolutionAsString(), false);

getWidget().setCurrentResolution(newResolution);
updateResolution(uidl);

// Add stylename that indicates current resolution
setWidgetStyleName(
getWidget().getStylePrimaryName() + "-" + VDateField
.resolutionToString(getWidget().getCurrentResolution()),
true);

final Resolution resolution = getWidget().getCurrentResolution();
final int year = uidl.getIntVariable("year");
final int month = resolution.compareTo(Resolution.MONTH) <= 0
? uidl.getIntVariable("month") : -1;
final int day = resolution.compareTo(Resolution.DAY) <= 0
? uidl.getIntVariable("day") : -1;

// Construct new date for this datefield (only if not null)
if (year > -1) {
getWidget().setCurrentDate(VTextualDate.getTime(year, month, day));
} else {
getWidget().setCurrentDate(null);
}
setWidgetStyleName(getWidget().getStylePrimaryName() + "-"
+ getWidget().resolutionAsString(), true);

getWidget().setCurrentDate(getTimeValues(uidl));
}

private void updateResolution(UIDL uidl) {
Optional<R> newResolution = getWidget().getResolutions().filter(
res -> uidl.hasVariable(getWidget().getResolutionVariable(res)))
.findFirst();

getWidget().setCurrentResolution(newResolution.orElse(null));
}

protected Map<R, Integer> getTimeValues(UIDL uidl) {
Stream<R> resolutions = getWidget().getResolutions();
R resolution = getWidget().getCurrentResolution();
return resolutions
.collect(Collectors.toMap(Function.identity(),
res -> (resolution.compareTo(res) <= 0)
? uidl.getIntVariable(
getWidget().getResolutionVariable(res))
: -1));
}

@SuppressWarnings("unchecked")
@Override
public VDateField getWidget() {
return (VDateField) super.getWidget();
public VDateField<R> getWidget() {
return (VDateField<R>) super.getWidget();
}

}

+ 127
- 0
client/src/main/java/com/vaadin/client/ui/datefield/AbstractInlineDateFieldConnector.java View File

@@ -0,0 +1,127 @@
/*
* 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.ApplicationConnection;
import com.vaadin.client.UIDL;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.VAbstractCalendarPanel.FocusChangeListener;
import com.vaadin.client.ui.VAbstractDateFieldCalendar;
import com.vaadin.shared.ui.datefield.InlineDateFieldState;

/**
* Base class for inline data field connector.
*
* @author Vaadin Ltd
*
* @param <R>
* the resolution type which the field is based on (day, month, ...)
*/
public abstract class AbstractInlineDateFieldConnector<R extends Enum<R>>
extends AbstractDateFieldConnector<R> {

@Override
@SuppressWarnings("deprecation")
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
super.updateFromUIDL(uidl, client);
if (!isRealUpdate(uidl)) {
return;
}

getWidget().calendarPanel
.setShowISOWeekNumbers(getWidget().isShowISOWeekNumbers());
getWidget().calendarPanel
.setDateTimeService(getWidget().getDateTimeService());
getWidget().calendarPanel
.setResolution(getWidget().getCurrentResolution());
Date currentDate = getWidget().getCurrentDate();
if (currentDate != null) {
getWidget().calendarPanel.setDate(new Date(currentDate.getTime()));
} else {
getWidget().calendarPanel.setDate(null);
}

updateListeners();

// Update possible changes
getWidget().calendarPanel.renderCalendar();
}

/**
* Updates listeners registered (or register them) for the widget based on
* the current resolution.
* <p>
* Subclasses may override this method to keep the common logic inside the
* {@link #updateFromUIDL(UIDL, ApplicationConnection)} method as is and
* customizing only listeners logic.
*/
protected void updateListeners() {
if (isResolutionMonthOrHigher()) {
getWidget().calendarPanel
.setFocusChangeListener(new FocusChangeListener() {
@Override
public void focusChanged(Date date) {
Date date2 = new Date();
if (getWidget().calendarPanel.getDate() != null) {
date2.setTime(getWidget().calendarPanel
.getDate().getTime());
}
/*
* Update the value of calendarPanel
*/
date2.setYear(date.getYear());
date2.setMonth(date.getMonth());
getWidget().calendarPanel.setDate(date2);
/*
* Then update the value from panel to server
*/
getWidget().updateValueFromPanel();
}
});
} else {
getWidget().calendarPanel.setFocusChangeListener(null);
}
}

@Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
super.onStateChanged(stateChangeEvent);
getWidget().setTabIndex(getState().tabIndex);
getWidget().calendarPanel.setRangeStart(getState().rangeStart);
getWidget().calendarPanel.setRangeEnd(getState().rangeEnd);
}

@Override
public VAbstractDateFieldCalendar<R> getWidget() {
return (VAbstractDateFieldCalendar<R>) super.getWidget();
}

@Override
public InlineDateFieldState getState() {
return (InlineDateFieldState) super.getState();
}

/**
* 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();

}

+ 65
- 0
client/src/main/java/com/vaadin/client/ui/datefield/AbstractTextualDateConnector.java View File

@@ -0,0 +1,65 @@
/*
* 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.client.ApplicationConnection;
import com.vaadin.client.UIDL;
import com.vaadin.client.ui.VAbstractTextualDate;
import com.vaadin.shared.ui.datefield.AbstractTextualDateFieldState;

public abstract class AbstractTextualDateConnector<R extends Enum<R>>
extends AbstractDateFieldConnector<R> {

@Override
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
R origRes = getWidget().getCurrentResolution();
String oldLocale = getWidget().getCurrentLocale();
super.updateFromUIDL(uidl, client);
if (origRes != getWidget().getCurrentResolution()
|| oldLocale != getWidget().getCurrentLocale()) {
// force recreating format string
getWidget().formatStr = null;
}
if (uidl.hasAttribute("format")) {
getWidget().formatStr = uidl.getStringAttribute("format");
}

getWidget().lenient = !uidl.getBooleanAttribute("strict");

getWidget().buildDate();
// not a FocusWidget -> needs own tabindex handling
getWidget().text.setTabIndex(getState().tabIndex);

if (getWidget().isReadonly()) {
getWidget().text.addStyleDependentName("readonly");
} else {
getWidget().text.removeStyleDependentName("readonly");
}

}

@Override
public VAbstractTextualDate<R> getWidget() {
return (VAbstractTextualDate<R>) super.getWidget();
}

@Override
public AbstractTextualDateFieldState getState() {
return (AbstractTextualDateFieldState) super.getState();
}

}

+ 14
- 156
client/src/main/java/com/vaadin/client/ui/datefield/DateFieldConnector.java View File

@@ -13,124 +13,25 @@
* License for the specific language governing permissions and limitations under
* the License.
*/

package com.vaadin.client.ui.datefield;

import java.util.Date;

import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
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.VCalendarPanel.FocusChangeListener;
import com.vaadin.client.ui.VPopupCalendar;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.datefield.DateFieldState;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.ui.AbstractDateField;

@Connect(AbstractDateField.class)
public class DateFieldConnector extends TextualDateConnector {

/*
* (non-Javadoc)
*
* @see com.vaadin.client.ui.AbstractConnector#init()
*/
@Override
protected void init() {
getWidget().popup.addCloseHandler(new CloseHandler<PopupPanel>() {
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.shared.ui.datefield.LocalDateFieldState;
import com.vaadin.ui.AbstractLocalDateField;

@Override
public void onClose(CloseEvent<PopupPanel> event) {
/*
* FIXME This is a hack so we do not have to rewrite half of the
* datefield so values are not sent while selecting a date
* (#6252).
*
* The datefield will now only set the date UIDL variables while
* the user is selecting year/month/date/time and not send them
* directly. Only when the user closes the popup (by clicking on
* a day/enter/clicking outside of popup) then the new value is
* communicated to the server.
*/
getConnection().getServerRpcQueue().flush();
}
});
}
/**
* @author Vaadin Ltd
*
*/
@Connect(AbstractLocalDateField.class)
public class DateFieldConnector extends TextualDateConnector<DateResolution> {

/*
* (non-Javadoc)
*
* @see com.vaadin.client.ui.VTextualDate#updateFromUIDL(com.vaadin
* .client.UIDL, com.vaadin.client.ApplicationConnection)
*/
@Override
@SuppressWarnings("deprecation")
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {

String oldLocale = getWidget().getCurrentLocale();

getWidget().parsable = uidl.getBooleanAttribute("parsable");

super.updateFromUIDL(uidl, client);

getWidget().calendar
.setDateTimeService(getWidget().getDateTimeService());
getWidget().calendar
.setShowISOWeekNumbers(getWidget().isShowISOWeekNumbers());
if (getWidget().calendar.getResolution() != getWidget()
.getCurrentResolution()) {
boolean hasSelectedDate = false;
getWidget().calendar
.setResolution(getWidget().getCurrentResolution());
if (getWidget().calendar.getDate() != null
&& getWidget().getCurrentDate() != null) {
hasSelectedDate = true;
getWidget().calendar
.setDate((Date) getWidget().getCurrentDate().clone());
}
// force re-render when changing resolution only
getWidget().calendar.renderCalendar(hasSelectedDate);
}

// Force re-render of calendar if locale has changed (#12153)
if (!getWidget().getCurrentLocale().equals(oldLocale)) {
getWidget().calendar.renderCalendar();
}

if (getWidget().getCurrentResolution()
.compareTo(Resolution.MONTH) >= 0) {
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());
}
});
} else {
getWidget().calendar.setFocusChangeListener(null);
}

if (getWidget().isReadonly()) {
getWidget().calendarToggle.addStyleName(
VPopupCalendar.CLASSNAME + "-button-readonly");
} else {
getWidget().calendarToggle.removeStyleName(
VPopupCalendar.CLASSNAME + "-button-readonly");
}

getWidget().setDescriptionForAssistiveDevices(
getState().descriptionForAssistiveDevices);

getWidget().setTextFieldTabIndex();
protected boolean isResolutionAboveMonth() {
return getWidget().getCurrentResolution()
.compareTo(DateResolution.MONTH) >= 0;
}

@Override
@@ -139,50 +40,7 @@ public class DateFieldConnector extends TextualDateConnector {
}

@Override
public DateFieldState getState() {
return (DateFieldState) super.getState();
}

@Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
super.onStateChanged(stateChangeEvent);
getWidget().setTextFieldEnabled(getState().textFieldEnabled);
getWidget().setRangeStart(nullSafeDateClone(getState().rangeStart));
getWidget().setRangeEnd(nullSafeDateClone(getState().rangeEnd));
}

private Date nullSafeDateClone(Date date) {
if (date == null) {
return null;
} else {
return (Date) date.clone();
}
}

@Override
protected void setWidgetStyleName(String styleName, boolean add) {
super.setWidgetStyleName(styleName, add);

// update the style change to popup calendar widget
getWidget().popup.setStyleName(styleName, add);
}

@Override
protected void setWidgetStyleNameWithPrefix(String prefix, String styleName,
boolean add) {
super.setWidgetStyleNameWithPrefix(prefix, styleName, add);

// update the style change to popup calendar widget with the correct
// prefix
if (!styleName.startsWith("-")) {
getWidget().popup.setStyleName(
getWidget().getStylePrimaryName() + "-popup-" + styleName,
add);
} else {
getWidget().popup.setStyleName(
getWidget().getStylePrimaryName() + "-popup" + styleName,
add);
}
public LocalDateFieldState getState() {
return (LocalDateFieldState) super.getState();
}

}

+ 10
- 71
client/src/main/java/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java View File

@@ -15,88 +15,27 @@
*/
package com.vaadin.client.ui.datefield;

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.VCalendarPanel.FocusChangeListener;
import com.vaadin.client.ui.VDateFieldCalendar;
import com.vaadin.shared.ui.Connect;
import com.vaadin.shared.ui.datefield.InlineDateFieldState;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.ui.InlineDateField;

/**
* @author Vaadin Ltd
*
*/
@Connect(InlineDateField.class)
public class InlineDateFieldConnector extends AbstractDateFieldConnector {

@Override
@SuppressWarnings("deprecation")
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
super.updateFromUIDL(uidl, client);
if (!isRealUpdate(uidl)) {
return;
}

getWidget().calendarPanel
.setShowISOWeekNumbers(getWidget().isShowISOWeekNumbers());
getWidget().calendarPanel
.setDateTimeService(getWidget().getDateTimeService());
getWidget().calendarPanel
.setResolution(getWidget().getCurrentResolution());
Date currentDate = getWidget().getCurrentDate();
if (currentDate != null) {
getWidget().calendarPanel.setDate(new Date(currentDate.getTime()));
} else {
getWidget().calendarPanel.setDate(null);
}

if (getWidget().getCurrentResolution()
.compareTo(Resolution.MONTH) >= 0) {
getWidget().calendarPanel
.setFocusChangeListener(new FocusChangeListener() {
@Override
public void focusChanged(Date date) {
Date date2 = new Date();
if (getWidget().calendarPanel.getDate() != null) {
date2.setTime(getWidget().calendarPanel
.getDate().getTime());
}
/*
* Update the value of calendarPanel
*/
date2.setYear(date.getYear());
date2.setMonth(date.getMonth());
getWidget().calendarPanel.setDate(date2);
/*
* Then update the value from panel to server
*/
getWidget().updateValueFromPanel();
}
});
} else {
getWidget().calendarPanel.setFocusChangeListener(null);
}

// Update possible changes
getWidget().calendarPanel.renderCalendar();
}
public class InlineDateFieldConnector
extends AbstractInlineDateFieldConnector<DateResolution> {

@Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
super.onStateChanged(stateChangeEvent);
getWidget().setTabIndex(getState().tabIndex);
getWidget().calendarPanel.setRangeStart(getState().rangeStart);
getWidget().calendarPanel.setRangeEnd(getState().rangeEnd);
protected boolean isResolutionMonthOrHigher() {
return getWidget().getCurrentResolution()
.compareTo(DateResolution.MONTH) >= 0;
}

@Override
public VDateFieldCalendar getWidget() {
return (VDateFieldCalendar) super.getWidget();
}

@Override
public InlineDateFieldState getState() {
return (InlineDateFieldState) super.getState();
}
}

+ 142
- 19
client/src/main/java/com/vaadin/client/ui/datefield/TextualDateConnector.java View File

@@ -16,49 +16,172 @@

package com.vaadin.client.ui.datefield;

import java.util.Date;

import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.user.client.ui.PopupPanel;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.UIDL;
import com.vaadin.client.ui.VTextualDate;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.client.communication.StateChangeEvent;
import com.vaadin.client.ui.VAbstractCalendarPanel.FocusChangeListener;
import com.vaadin.client.ui.VAbstractPopupCalendar;
import com.vaadin.shared.ui.datefield.TextualDateFieldState;

public class TextualDateConnector extends AbstractDateFieldConnector {
public abstract class TextualDateConnector<R extends Enum<R>>
extends AbstractTextualDateConnector<R> {

@Override
protected void init() {
getWidget().popup.addCloseHandler(new CloseHandler<PopupPanel>() {

@Override
public void onClose(CloseEvent<PopupPanel> event) {
/*
* FIXME This is a hack so we do not have to rewrite half of the
* datefield so values are not sent while selecting a date
* (#6252).
*
* The datefield will now only set the date UIDL variables while
* the user is selecting year/month/date/time and not send them
* directly. Only when the user closes the popup (by clicking on
* a day/enter/clicking outside of popup) then the new value is
* communicated to the server.
*/
getConnection().getServerRpcQueue().flush();
}
});
}

@Override
@SuppressWarnings("deprecation")
public void updateFromUIDL(UIDL uidl, ApplicationConnection client) {
Resolution origRes = getWidget().getCurrentResolution();
String oldLocale = getWidget().getCurrentLocale();

getWidget().parsable = uidl.getBooleanAttribute("parsable");

super.updateFromUIDL(uidl, client);
if (origRes != getWidget().getCurrentResolution()
|| oldLocale != getWidget().getCurrentLocale()) {
// force recreating format string
getWidget().formatStr = null;
}
if (uidl.hasAttribute("format")) {
getWidget().formatStr = uidl.getStringAttribute("format");

getWidget().calendar
.setDateTimeService(getWidget().getDateTimeService());
getWidget().calendar
.setShowISOWeekNumbers(getWidget().isShowISOWeekNumbers());
if (getWidget().calendar.getResolution() != getWidget()
.getCurrentResolution()) {
boolean hasSelectedDate = false;
getWidget().calendar
.setResolution(getWidget().getCurrentResolution());
if (getWidget().calendar.getDate() != null
&& getWidget().getCurrentDate() != null) {
hasSelectedDate = true;
getWidget().calendar
.setDate((Date) getWidget().getCurrentDate().clone());
}
// force re-render when changing resolution only
getWidget().calendar.renderCalendar(hasSelectedDate);
}

getWidget().lenient = !uidl.getBooleanAttribute("strict");
// Force re-render of calendar if locale has changed (#12153)
if (!getWidget().getCurrentLocale().equals(oldLocale)) {
getWidget().calendar.renderCalendar();
}

getWidget().buildDate();
// not a FocusWidget -> needs own tabindex handling
getWidget().text.setTabIndex(getState().tabIndex);
updateListeners();

if (getWidget().isReadonly()) {
getWidget().text.addStyleDependentName("readonly");
getWidget().calendarToggle.addStyleName(
VAbstractPopupCalendar.CLASSNAME + "-button-readonly");
} else {
getWidget().text.removeStyleDependentName("readonly");
getWidget().calendarToggle.removeStyleName(
VAbstractPopupCalendar.CLASSNAME + "-button-readonly");
}

getWidget().setDescriptionForAssistiveDevices(
getState().descriptionForAssistiveDevices);

getWidget().setTextFieldTabIndex();
}

/**
* Updates listeners registered (or register them) for the widget based on
* the current resolution.
* <p>
* Subclasses may override this method to keep the common logic inside the
* {@link #updateFromUIDL(UIDL, ApplicationConnection)} method as is and
* customizing only listeners logic.
*/
protected void updateListeners() {
if (isResolutionAboveMonth()) {
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());
}
});
} else {
getWidget().calendar.setFocusChangeListener(null);
}
}

protected abstract boolean isResolutionAboveMonth();

@Override
public VTextualDate getWidget() {
return (VTextualDate) super.getWidget();
public VAbstractPopupCalendar<R> getWidget() {
return (VAbstractPopupCalendar<R>) super.getWidget();
}

@Override
public TextualDateFieldState getState() {
return (TextualDateFieldState) super.getState();
}

@Override
public void onStateChanged(StateChangeEvent stateChangeEvent) {
super.onStateChanged(stateChangeEvent);
getWidget().setTextFieldEnabled(getState().textFieldEnabled);
getWidget().setRangeStart(nullSafeDateClone(getState().rangeStart));
getWidget().setRangeEnd(nullSafeDateClone(getState().rangeEnd));
}

private Date nullSafeDateClone(Date date) {
if (date == null) {
return null;
} else {
return (Date) date.clone();
}
}

@Override
protected void setWidgetStyleName(String styleName, boolean add) {
super.setWidgetStyleName(styleName, add);

// update the style change to popup calendar widget
getWidget().popup.setStyleName(styleName, add);
}

@Override
protected void setWidgetStyleNameWithPrefix(String prefix, String styleName,
boolean add) {
super.setWidgetStyleNameWithPrefix(prefix, styleName, add);

// update the style change to popup calendar widget with the correct
// prefix
if (!styleName.startsWith("-")) {
getWidget().popup.setStyleName(
getWidget().getStylePrimaryName() + "-popup-" + styleName,
add);
} else {
getWidget().popup.setStyleName(
getWidget().getStylePrimaryName() + "-popup" + styleName,
add);
}
}

}

+ 4
- 4
compatibility-client/src/main/java/com/vaadin/v7/client/ui/VPopupCalendar.java View File

@@ -62,9 +62,9 @@ import com.vaadin.v7.shared.ui.datefield.Resolution;
* selector.
*
* <b>Note:</b> To change the keyboard assignments used in the popup dialog you
* should extend <code>com.vaadin.client.ui.VCalendarPanel</code> and then pass
* set it by calling the <code>setCalendarPanel(VCalendarPanel panel)</code>
* method.
* should extend <code>com.vaadin.v7.client.ui.VCalendarPanel</code> and then
* pass set it by calling the
* <code>setCalendarPanel(VCalendarPanel panel)</code> method.
*
*/
public class VPopupCalendar extends VTextualDate
@@ -500,7 +500,7 @@ public class VPopupCalendar extends VTextualDate
/**
* For internal use only. May be removed or replaced in the future.
*
* @see com.vaadin.client.ui.VTextualDate#buildDate()
* @see com.vaadin.v7.client.ui.VTextualDate#buildDate()
*/
@Override
public void buildDate() {

+ 1
- 1
compatibility-server/src/main/java/com/vaadin/v7/data/validator/DateRangeValidator.java View File

@@ -17,7 +17,7 @@ package com.vaadin.v7.data.validator;

import java.util.Date;

import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.v7.shared.ui.datefield.Resolution;

/**
* Validator for validating that a Date is inside a given range.

+ 1
- 1
compatibility-server/src/test/java/com/vaadin/v7/tests/data/validator/DateRangeValidatorTest.java View File

@@ -11,8 +11,8 @@ import java.util.TimeZone;
import org.junit.Before;
import org.junit.Test;

import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.v7.data.validator.DateRangeValidator;
import com.vaadin.v7.shared.ui.datefield.Resolution;

public class DateRangeValidatorTest {
Calendar startDate = new GregorianCalendar(TimeZone.getTimeZone("GMT"),

+ 4
- 3
server/src/main/java/com/vaadin/ui/AbstractComponent.java View File

@@ -1223,9 +1223,10 @@ public abstract class AbstractComponent extends AbstractClientConnector

/**
* Returns a collection of attributes that should not be handled by the
* basic implementation of the {@link #readDesign(Element, DesignContext)} and {@link #writeDesign(Element, DesignContext)}
* methods. Typically these are handled in a custom way in the overridden
* versions of the above methods
* basic implementation of the {@link #readDesign(Element, DesignContext)}
* and {@link #writeDesign(Element, DesignContext)} methods. Typically these
* are handled in a custom way in the overridden versions of the above
* methods
*
* @since 7.4
*

+ 166
- 117
server/src/main/java/com/vaadin/ui/AbstractDateField.java View File

@@ -15,24 +15,31 @@
*/
package com.vaadin.ui;

import java.io.Serializable;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.util.Calendar;
import java.util.Date;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jsoup.nodes.Element;

import com.googlecode.gentyref.GenericTypeReflector;
import com.vaadin.data.Result;
import com.vaadin.data.ValidationResult;
import com.vaadin.data.ValueContext;
import com.vaadin.data.validator.DateRangeValidator;
import com.vaadin.data.validator.RangeValidator;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.BlurNotifier;
@@ -43,9 +50,9 @@ import com.vaadin.server.PaintException;
import com.vaadin.server.PaintTarget;
import com.vaadin.server.UserError;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.datefield.AbstractDateFieldState;
import com.vaadin.shared.ui.datefield.DateFieldConstants;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.TextualDateFieldState;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.ui.declarative.DesignAttributeHandler;
import com.vaadin.ui.declarative.DesignContext;

@@ -55,20 +62,26 @@ import com.vaadin.ui.declarative.DesignContext;
* @author Vaadin Ltd
*
* @since 8.0
*
*
* @param <T>
* type of date ({@code LocalDate} or {@code LocalDateTime}).
* @param <R>
* resolution enumeration type
*
*/
public abstract class AbstractDateField extends AbstractField<LocalDate>
public abstract class AbstractDateField<T extends Temporal & TemporalAdjuster & Serializable & Comparable<? super T>, R extends Enum<R>>
extends AbstractField<T>
implements LegacyComponent, FocusNotifier, BlurNotifier {

/**
* Value of the field.
*/
private LocalDate value;
private T value;

/**
* Specified smallest modifiable unit for the date field.
*/
private Resolution resolution = Resolution.DAY;
private R resolution;

/**
* Overridden format string
@@ -94,8 +107,6 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>

private String defaultParseErrorMessage = "Date format not recognized";

private static Map<Resolution, String> variableNameForResolution = new HashMap<>();

private String dateOutOfRangeMessage = "Date is out of allowed range";

/**
@@ -105,42 +116,46 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
*/
private boolean preventValueChangeEvent;

static {
variableNameForResolution.put(Resolution.DAY, "day");
variableNameForResolution.put(Resolution.MONTH, "month");
variableNameForResolution.put(Resolution.YEAR, "year");
}

/* Constructors */

/**
* Constructs an empty <code>DateField</code> with no caption.
* Constructs an empty <code>AbstractDateField</code> with no caption and
* specified {@code resolution}.
*
* @param resolution
* initial resolution for the field
*/
public AbstractDateField() {
public AbstractDateField(R resolution) {
this.resolution = resolution;
}

/**
* Constructs an empty <code>DateField</code> with caption.
* Constructs an empty <code>AbstractDateField</code> with caption.
*
* @param caption
* the caption of the datefield.
* @param resolution
* initial resolution for the field
*/
public AbstractDateField(String caption) {
public AbstractDateField(String caption, R resolution) {
this(resolution);
setCaption(caption);
}

/**
* Constructs a new <code>DateField</code> with the given caption and
* initial text contents.
* Constructs a new <code>AbstractDateField</code> with the given caption
* and initial text contents.
*
* @param caption
* the caption <code>String</code> for the editor.
* @param value
* the LocalDate value.
* the date/time value.
* @param resolution
* initial resolution for the field
*/
public AbstractDateField(String caption, LocalDate value) {
public AbstractDateField(String caption, T value, R resolution) {
this(caption, resolution);
setValue(value);
setCaption(caption);
}

/* Component basic features */
@@ -174,17 +189,16 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
* app or refresh.
*/

final LocalDate currentDate = getValue();
final T currentDate = getValue();

// Only paint variables for the resolution and up, e.g. Resolution DAY
// paints DAY,MONTH,YEAR
for (Resolution res : Resolution
.getResolutionsHigherOrEqualTo(resolution)) {
for (R res : getResolutionsHigherOrEqualTo(getResolution())) {
int value = -1;
if (currentDate != null) {
value = getDateValue(currentDate, res);
value = getDatePart(currentDate, res);
}
target.addVariable(this, variableNameForResolution.get(res), value);
target.addVariable(this, getResolutionVariable(res), value);
}
}

@@ -195,15 +209,15 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
*/
@Override
public void changeVariables(Object source, Map<String, Object> variables) {
if (!isReadOnly() && (variables.containsKey("year")
|| variables.containsKey("month")
|| variables.containsKey("day")
Set<String> resolutionNames = getResolutions()
.map(this::getResolutionVariable).collect(Collectors.toSet());
resolutionNames.retainAll(variables.keySet());
if (!isReadOnly() && (!resolutionNames.isEmpty()
|| variables.containsKey("dateString"))) {

// Old and new dates
final LocalDate oldDate = getValue();
LocalDate newDate = null;
final T oldDate = getValue();
T newDate = null;

// this enables analyzing invalid input on the server
final String newDateString = (String) variables.get("dateString");
@@ -211,15 +225,15 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>

// Gets the new date in parts
boolean hasChanges = false;
Map<Resolution, Integer> calendarFields = new HashMap<>();
Map<R, Integer> calendarFields = new HashMap<>();

for (Resolution resolution : Resolution
.getResolutionsHigherOrEqualTo(getResolution())) {
for (R resolution : getResolutionsHigherOrEqualTo(
getResolution())) {
// Only handle what the client is allowed to send. The same
// resolutions that are painted
String variableName = variableNameForResolution.get(resolution);
String variableName = getResolutionVariable(resolution);

Integer value = getDateValue(oldDate, resolution);
int value = getDatePart(oldDate, resolution);
if (variables.containsKey(variableName)) {
Integer newValue = (Integer) variables.get(variableName);
if (newValue >= 0) {
@@ -234,15 +248,12 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
if (!hasChanges) {
newDate = null;
} else {
newDate = LocalDate.of(calendarFields.get(Resolution.YEAR),
calendarFields.getOrDefault(Resolution.MONTH, 1),
calendarFields.getOrDefault(Resolution.DAY, 1));
newDate = buildDate(calendarFields);
}

if (newDate == null && dateString != null
&& !dateString.isEmpty()) {
Result<LocalDate> parsedDate = handleUnparsableDateString(
dateString);
Result<T> parsedDate = handleUnparsableDateString(dateString);
if (parsedDate.isError()) {

/*
@@ -332,8 +343,8 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
* @param startDate
* - the allowed range's start date
*/
public void setRangeStart(LocalDate startDate) {
Date date = convertLocalDate(startDate);
public void setRangeStart(T startDate) {
Date date = convertToDate(startDate);
if (date != null && getState().rangeEnd != null
&& date.after(getState().rangeEnd)) {
throw new IllegalStateException(
@@ -367,21 +378,21 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
/**
* Gets the resolution.
*
* @return int
* @return the date/time field resolution
*/
public Resolution getResolution() {
public R getResolution() {
return resolution;
}

/**
* Sets the resolution of the DateField.
*
* The default resolution is {@link Resolution#DAY} since Vaadin 7.0.
* The default resolution is {@link DateResolution#DAY} since Vaadin 7.0.
*
* @param resolution
* the resolution to set.
* the resolution to set, not {@code null}
*/
public void setResolution(Resolution resolution) {
public void setResolution(R resolution) {
this.resolution = resolution;
markAsDirty();
}
@@ -396,8 +407,8 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
* - the allowed range's end date (inclusive, based on the
* current resolution)
*/
public void setRangeEnd(LocalDate endDate) {
Date date = convertLocalDate(endDate);
public void setRangeEnd(T endDate) {
Date date = convertToDate(endDate);
if (date != null && getState().rangeStart != null
&& getState().rangeStart.after(date)) {
throw new IllegalStateException(
@@ -412,8 +423,8 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
*
* @return the precise rangeStart used, may be null.
*/
public LocalDate getRangeStart() {
return convertDate(getState(false).rangeStart);
public T getRangeStart() {
return convertFromDate(getState(false).rangeStart);
}

/**
@@ -421,8 +432,8 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
*
* @return the precise rangeEnd used, may be null.
*/
public LocalDate getRangeEnd() {
return convertDate(getState(false).rangeEnd);
public T getRangeEnd() {
return convertFromDate(getState(false).rangeEnd);
}

/**
@@ -482,7 +493,7 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
}

@Override
public LocalDate getValue() {
public T getValue() {
return value;
}

@@ -494,7 +505,7 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
* the new value, may be {@code null}
*/
@Override
public void setValue(LocalDate value) {
public void setValue(T value) {
/*
* First handle special case when the client side component have a date
* string but value is null (e.g. unparsable date string typed in by the
@@ -580,17 +591,28 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
}

@Override
@SuppressWarnings("unchecked")
public void readDesign(Element design, DesignContext designContext) {
super.readDesign(design, designContext);
if (design.hasAttr("value") && !design.attr("value").isEmpty()) {
LocalDate date = DesignAttributeHandler.getFormatter()
.parse(design.attr("value"), LocalDate.class);
// formatting will return null if it cannot parse the string
if (date == null) {
Logger.getLogger(AbstractDateField.class.getName()).info(
"cannot parse " + design.attr("value") + " as date");
Type dateType = GenericTypeReflector.getTypeParameter(getClass(),
AbstractDateField.class.getTypeParameters()[0]);
if (dateType instanceof Class<?>) {
Class<?> clazz = (Class<?>) dateType;
T date = (T) DesignAttributeHandler.getFormatter()
.parse(design.attr("value"), clazz);
// formatting will return null if it cannot parse the string
if (date == null) {
Logger.getLogger(AbstractDateField.class.getName())
.info("cannot parse " + design.attr("value")
+ " as date");
}
doSetValue(date);
} else {
throw new RuntimeException("Cannot detect resoluton type "
+ Optional.ofNullable(dateType).map(Type::getTypeName)
.orElse(null));
}
doSetValue(date);
}
}

@@ -629,22 +651,22 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
* date string to handle
* @return result that contains parsed Date as a value or an error
*/
protected Result<LocalDate> handleUnparsableDateString(String dateString) {
protected Result<T> handleUnparsableDateString(String dateString) {
return Result.error(getParseErrorMessage());
}

@Override
protected TextualDateFieldState getState() {
return (TextualDateFieldState) super.getState();
protected AbstractDateFieldState getState() {
return (AbstractDateFieldState) super.getState();
}

@Override
protected TextualDateFieldState getState(boolean markAsDirty) {
return (TextualDateFieldState) super.getState(markAsDirty);
protected AbstractDateFieldState getState(boolean markAsDirty) {
return (AbstractDateFieldState) super.getState(markAsDirty);
}

@Override
protected void doSetValue(LocalDate value) {
protected void doSetValue(T value) {
// Also set the internal dateString
if (value != null) {
dateString = value.toString();
@@ -659,10 +681,7 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
uiHasValidDateString = true;
setComponentError(new UserError(currentParseErrorMessage));
} else {
DateRangeValidator validator = new DateRangeValidator(
getDateOutOfRangeMessage(),
getDate(getRangeStart(), getResolution()),
getDate(getRangeEnd(), getResolution()));
RangeValidator<T> validator = getRangeValidator();
ValidationResult result = validator.apply(value,
new ValueContext(this));
if (result.isError()) {
@@ -671,50 +690,80 @@ public abstract class AbstractDateField extends AbstractField<LocalDate>
}
}

private LocalDate getDate(LocalDate date, Resolution forResolution) {
if (date == null) {
return null;
}
if (forResolution == Resolution.YEAR) {
return date.withDayOfYear(1);
} else if (forResolution == Resolution.MONTH) {
return date.withDayOfMonth(1);
} else {
return date;
}
}
/**
* Returns a date integer value part for the given {@code date} for the
* given {@code resolution}.
*
* @param date
* the given date
* @param resolution
* the resolution to extract a value from the date by
* @return the integer value part of the date by the given resolution
*/
protected abstract int getDatePart(T date, R resolution);

private int getDateValue(LocalDate date, Resolution resolution) {
LocalDate value = date;
if (value == null) {
value = LocalDate.of(1, 1, 1);
}
switch (resolution) {
case DAY:
return value.getDayOfMonth();
case MONTH:
return value.getMonthValue();
case YEAR:
return value.getYear();
default:
assert false : "Unexpected resolution argument " + resolution;
return -1;
}
/**
* Builds date by the given {@code resolutionValues} which is a map whose
* keys are resolution and integer values.
* <p>
* This is the opposite to {@link #getDatePart(Temporal, Enum)}.
*
* @param resolutionValues
* date values to construct a date
* @return date built from the given map of date values
*/
protected abstract T buildDate(Map<R, Integer> resolutionValues);

/**
* Returns a custom date range validator which is applicable for the type
* {@code T}.
*
* @return the date range validator
*/
protected abstract RangeValidator<T> getRangeValidator();

/**
* Converts {@link Date} to date type {@code T}.
*
* @param date
* a date to convert
* @return object of type {@code T} representing the {@code date}
*/
protected abstract T convertFromDate(Date date);

/**
* Converts the object of type {@code T} to {@link Date}.
* <p>
* This is the opposite to {@link #convertFromDate(Date)}.
*
* @param date
* the date of type {@code T}
* @return converted date of type {@code Date}
*/
protected abstract Date convertToDate(T date);

private String getResolutionVariable(R resolution) {
return resolution.name().toLowerCase(Locale.ENGLISH);
}

private Date convertLocalDate(LocalDate date) {
if (date == null) {
return null;
@SuppressWarnings("unchecked")
private Stream<R> getResolutions() {
Type resolutionType = GenericTypeReflector.getTypeParameter(getClass(),
AbstractDateField.class.getTypeParameters()[1]);
if (resolutionType instanceof Class<?>) {
Class<?> clazz = (Class<?>) resolutionType;
return Stream.of(clazz.getEnumConstants())
.map(object -> (R) object);
} else {
throw new RuntimeException("Cannot detect resoluton type "
+ Optional.ofNullable(resolutionType).map(Type::getTypeName)
.orElse(null));
}
return Date.from(date.atStartOfDay(ZoneOffset.UTC).toInstant());
}

private LocalDate convertDate(Date date) {
if (date == null) {
return null;
}
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneOffset.UTC)
.toLocalDate();
private Iterable<R> getResolutionsHigherOrEqualTo(R resoution) {
return getResolutions().skip(resolution.ordinal())
.collect(Collectors.toList());
}

}

+ 139
- 0
server/src/main/java/com/vaadin/ui/AbstractLocalDateField.java View File

@@ -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.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.Map;

import com.vaadin.data.validator.DateRangeValidator;
import com.vaadin.data.validator.RangeValidator;
import com.vaadin.shared.ui.datefield.AbstractTextualDateFieldState;
import com.vaadin.shared.ui.datefield.DateResolution;

/**
* @author Vaadin Ltd
*
*/
public abstract class AbstractLocalDateField
extends AbstractDateField<LocalDate, DateResolution> {

/**
* Constructs an empty <code>AbstractLocalDateField</code> with no caption.
*/
public AbstractLocalDateField() {
super(DateResolution.DAY);
}

/**
* Constructs an empty <code>AbstractLocalDateField</code> with caption.
*
* @param caption
* the caption of the datefield.
*/
public AbstractLocalDateField(String caption) {
super(caption, DateResolution.DAY);
}

/**
* Constructs a new <code>AbstractLocalDateField</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 AbstractLocalDateField(String caption, LocalDate value) {
super(caption, value, DateResolution.DAY);
}

@Override
protected int getDatePart(LocalDate date, DateResolution resolution) {
LocalDate value = date;
if (value == null) {
value = LocalDate.of(1, 1, 1);
}
switch (resolution) {
case DAY:
return value.getDayOfMonth();
case MONTH:
return value.getMonthValue();
case YEAR:
return value.getYear();
default:
assert false : "Unexpected resolution argument " + resolution;
return -1;
}
}

@Override
protected LocalDate buildDate(
Map<DateResolution, Integer> resolutionValues) {
return LocalDate.of(resolutionValues.get(DateResolution.YEAR),
resolutionValues.getOrDefault(DateResolution.MONTH, 1),
resolutionValues.getOrDefault(DateResolution.DAY, 1));
}

@Override
protected RangeValidator<LocalDate> getRangeValidator() {
return new DateRangeValidator(getDateOutOfRangeMessage(),
getDate(getRangeStart(), getResolution()),
getDate(getRangeEnd(), getResolution()));
}

@Override
protected AbstractTextualDateFieldState getState() {
return (AbstractTextualDateFieldState) super.getState();
}

@Override
protected AbstractTextualDateFieldState getState(boolean markAsDirty) {
return (AbstractTextualDateFieldState) super.getState(markAsDirty);
}

@Override
protected LocalDate convertFromDate(Date date) {
if (date == null) {
return null;
}
return Instant.ofEpochMilli(date.getTime()).atZone(ZoneOffset.UTC)
.toLocalDate();
}

@Override
protected Date convertToDate(LocalDate date) {
if (date == null) {
return null;
}
return Date.from(date.atStartOfDay(ZoneOffset.UTC).toInstant());
}

private LocalDate getDate(LocalDate date, DateResolution forResolution) {
if (date == null) {
return null;
}
if (forResolution == DateResolution.YEAR) {
return date.withDayOfYear(1);
} else if (forResolution == DateResolution.MONTH) {
return date.withDayOfMonth(1);
} else {
return date;
}
}
}

+ 58
- 0
server/src/main/java/com/vaadin/ui/AbstractLocalDateTimeField.java View File

@@ -0,0 +1,58 @@
/*
* 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.DateTimeResolution;
import com.vaadin.shared.ui.datefield.LocalDateTimeFieldState;

/**
* @author Vaadin Ltd
*
*/
public abstract class AbstractLocalDateTimeField
extends AbstractDateField<LocalDateTime, DateTimeResolution> {

/**
* Constructs an empty <code>AbstractLocalDateTimeField</code> with no
* caption.
*/
public AbstractLocalDateTimeField() {
super(DateTimeResolution.MINUTE);
}

/**
* Constructs an empty <code>AbstractLocalDateTimeField</code> with caption.
*
* @param caption
* the caption of the datefield.
*/
public AbstractLocalDateTimeField(String caption) {
super(caption, DateTimeResolution.MINUTE);
}

@Override
protected LocalDateTimeFieldState getState() {
return (LocalDateTimeFieldState) super.getState();
}

@Override
protected LocalDateTimeFieldState getState(boolean markAsDirty) {
return (LocalDateTimeFieldState) super.getState(markAsDirty);
}

}

+ 7
- 7
server/src/main/java/com/vaadin/ui/DateField.java View File

@@ -17,17 +17,17 @@ package com.vaadin.ui;

import java.time.LocalDate;

import com.vaadin.shared.ui.datefield.DateFieldState;
import com.vaadin.shared.ui.datefield.LocalDateFieldState;

/**
* A date entry component, which displays the actual date selector as a popup.
*
* @see AbstractDateField
* @see AbstractLocalDateField
* @see InlineDateField
* @author Vaadin Ltd.
* @since 8.0
*/
public class DateField extends AbstractDateField {
public class DateField extends AbstractLocalDateField {

/**
* Constructs an empty <code>DateField</code> with no caption.
@@ -81,13 +81,13 @@ public class DateField extends AbstractDateField {
}

@Override
protected DateFieldState getState() {
return (DateFieldState) super.getState();
protected LocalDateFieldState getState() {
return (LocalDateFieldState) super.getState();
}

@Override
protected DateFieldState getState(boolean markAsDirty) {
return (DateFieldState) super.getState(markAsDirty);
protected LocalDateFieldState getState(boolean markAsDirty) {
return (LocalDateFieldState) super.getState(markAsDirty);
}

/**

+ 2
- 2
server/src/main/java/com/vaadin/ui/InlineDateField.java View File

@@ -22,12 +22,12 @@ import com.vaadin.shared.ui.datefield.InlineDateFieldState;
/**
* A date entry component, which displays the actual date selector inline.
*
* @see AbstractDateField
* @see AbstractLocalDateField
* @see DateField
* @author Vaadin Ltd.
* @since 8.0
*/
public class InlineDateField extends AbstractDateField {
public class InlineDateField extends AbstractLocalDateField {

/**
* Constructs an empty <code>InlineDateField</code> with no caption.

+ 8
- 3
server/src/main/java/com/vaadin/ui/declarative/DesignAttributeHandler.java View File

@@ -21,6 +21,7 @@ import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -36,6 +37,7 @@ import org.jsoup.nodes.Attributes;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;

import com.googlecode.gentyref.GenericTypeReflector;
import com.vaadin.data.Converter;
import com.vaadin.data.ValueContext;
import com.vaadin.shared.ui.AlignmentInfo;
@@ -115,8 +117,9 @@ public class DesignAttributeHandler implements Serializable {
success = false;
} else {
// we have a value from design attributes, let's use that
Object param = getFormatter().parse(value,
setter.getParameterTypes()[0]);
Type[] types = GenericTypeReflector
.getExactParameterTypes(setter, target.getClass());
Object param = getFormatter().parse(value, (Class<?>) types[0]);
setter.invoke(target, param);
success = true;
}
@@ -208,7 +211,9 @@ public class DesignAttributeHandler implements Serializable {
Object value = getter.invoke(component);
Object defaultValue = getter.invoke(defaultInstance);
writeAttribute(attribute, attr, value, defaultValue,
(Class) getter.getReturnType(), context);
(Class) GenericTypeReflector.getExactReturnType(getter,
component.getClass()),
context);
} catch (Exception e) {
getLogger().log(Level.SEVERE,
"Failed to invoke getter for attribute " + attribute,

server/src/test/java/com/vaadin/tests/server/component/abstractdatefield/AbstractDateFieldDeclarativeTest.java → server/src/test/java/com/vaadin/tests/server/component/abstractdatefield/AbstractLocalDateFieldDeclarativeTest.java View File

@@ -20,9 +20,9 @@ import java.util.Locale;

import org.junit.Test;

import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.server.component.abstractfield.AbstractFieldDeclarativeTest;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;

/**
* Abstract test class which contains tests for declarative format for
@@ -36,7 +36,7 @@ import com.vaadin.ui.AbstractDateField;
* @author Vaadin Ltd
*
*/
public abstract class AbstractDateFieldDeclarativeTest<T extends AbstractDateField>
public abstract class AbstractLocalDateFieldDeclarativeTest<T extends AbstractLocalDateField>
extends AbstractFieldDeclarativeTest<T, LocalDate> {

@Override
@@ -60,7 +60,7 @@ public abstract class AbstractDateFieldDeclarativeTest<T extends AbstractDateFie
LocalDate end = LocalDate.of(2019, 01, 15);
LocalDate start = LocalDate.of(2001, 02, 11);
String dateOutOfRange = "test date out of range";
Resolution resolution = Resolution.MONTH;
DateResolution resolution = DateResolution.MONTH;
String dateFormat = "test format";
boolean lenient = true;
String parseErrorMsg = "test parse error";

+ 2
- 2
server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldDeclarativeTest.java View File

@@ -17,7 +17,7 @@ package com.vaadin.tests.server.component.datefield;

import org.junit.Test;

import com.vaadin.tests.server.component.abstractdatefield.AbstractDateFieldDeclarativeTest;
import com.vaadin.tests.server.component.abstractdatefield.AbstractLocalDateFieldDeclarativeTest;
import com.vaadin.ui.DateField;

/**
@@ -27,7 +27,7 @@ import com.vaadin.ui.DateField;
* @author Vaadin Ltd
*/
public class DateFieldDeclarativeTest
extends AbstractDateFieldDeclarativeTest<DateField> {
extends AbstractLocalDateFieldDeclarativeTest<DateField> {

@Test
public void remainingAttributes()

+ 38
- 1
server/src/test/java/com/vaadin/tests/server/component/datefield/DateFieldListenersTest.java View File

@@ -1,7 +1,14 @@
package com.vaadin.tests.server.component.datefield;

import java.io.Serializable;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalAdjuster;
import java.util.Date;
import java.util.Map;

import org.junit.Test;

import com.vaadin.data.validator.RangeValidator;
import com.vaadin.event.FieldEvents.BlurEvent;
import com.vaadin.event.FieldEvents.BlurListener;
import com.vaadin.event.FieldEvents.FocusEvent;
@@ -11,7 +18,37 @@ import com.vaadin.ui.AbstractDateField;

public class DateFieldListenersTest extends AbstractListenerMethodsTestBase {

public static class TestDateField extends AbstractDateField {
public static class TestDateField<T extends Temporal & TemporalAdjuster & Serializable & Comparable<? super T>, R extends Enum<R>>
extends AbstractDateField<T, R> {

public TestDateField() {
super(null);
}

@Override
protected int getDatePart(T date, R resolution) {
return 0;
}

@Override
protected T buildDate(Map<R, Integer> resolutionValues) {
return null;
}

@Override
protected RangeValidator<T> getRangeValidator() {
return null;
}

@Override
protected T convertFromDate(Date date) {
return null;
}

@Override
protected Date convertToDate(T date) {
return null;
}

}


+ 2
- 2
server/src/test/java/com/vaadin/tests/server/component/datefield/InlineDateFieldDeclarativeTest.java View File

@@ -21,7 +21,7 @@ import java.time.LocalDate;

import org.junit.Test;

import com.vaadin.tests.server.component.abstractdatefield.AbstractDateFieldDeclarativeTest;
import com.vaadin.tests.server.component.abstractdatefield.AbstractLocalDateFieldDeclarativeTest;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.InlineDateField;
import com.vaadin.ui.declarative.Design;
@@ -34,7 +34,7 @@ import com.vaadin.ui.declarative.Design;
* @author Vaadin Ltd
*/
public class InlineDateFieldDeclarativeTest
extends AbstractDateFieldDeclarativeTest<InlineDateField> {
extends AbstractLocalDateFieldDeclarativeTest<InlineDateField> {

@Test
public void testInlineDateFieldToFromDesign() throws Exception {

+ 16
- 16
server/src/test/java/com/vaadin/tests/server/component/datefield/ResolutionTest.java View File

@@ -4,39 +4,39 @@ import java.util.ArrayList;

import org.junit.Test;

import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.util.TestUtil;

public class ResolutionTest {

@Test
public void testResolutionHigherOrEqualToYear() {
Iterable<Resolution> higherOrEqual = Resolution
.getResolutionsHigherOrEqualTo(Resolution.YEAR);
ArrayList<Resolution> expected = new ArrayList<>();
expected.add(Resolution.YEAR);
Iterable<DateResolution> higherOrEqual = DateResolution
.getResolutionsHigherOrEqualTo(DateResolution.YEAR);
ArrayList<DateResolution> expected = new ArrayList<>();
expected.add(DateResolution.YEAR);
TestUtil.assertIterableEquals(expected, higherOrEqual);
}

@Test
public void testResolutionHigherOrEqualToDay() {
Iterable<Resolution> higherOrEqual = Resolution
.getResolutionsHigherOrEqualTo(Resolution.DAY);
ArrayList<Resolution> expected = new ArrayList<>();
expected.add(Resolution.DAY);
expected.add(Resolution.MONTH);
expected.add(Resolution.YEAR);
Iterable<DateResolution> higherOrEqual = DateResolution
.getResolutionsHigherOrEqualTo(DateResolution.DAY);
ArrayList<DateResolution> expected = new ArrayList<>();
expected.add(DateResolution.DAY);
expected.add(DateResolution.MONTH);
expected.add(DateResolution.YEAR);
TestUtil.assertIterableEquals(expected, higherOrEqual);

}

@Test
public void testResolutionLowerThanYear() {
Iterable<Resolution> higherOrEqual = Resolution
.getResolutionsLowerThan(Resolution.YEAR);
ArrayList<Resolution> expected = new ArrayList<>();
expected.add(Resolution.MONTH);
expected.add(Resolution.DAY);
Iterable<DateResolution> higherOrEqual = DateResolution
.getResolutionsLowerThan(DateResolution.YEAR);
ArrayList<DateResolution> expected = new ArrayList<>();
expected.add(DateResolution.MONTH);
expected.add(DateResolution.DAY);
TestUtil.assertIterableEquals(expected, higherOrEqual);

}

+ 2
- 2
server/src/test/java/com/vaadin/ui/DateFieldTestCase.java View File

@@ -12,12 +12,12 @@ import org.junit.Test;

public class DateFieldTestCase {

private AbstractDateField dateField;
private AbstractLocalDateField dateField;
private LocalDate date;

@Before
public void setup() {
dateField = new AbstractDateField() {
dateField = new AbstractLocalDateField() {
};
date = LocalDate.now();
}

shared/src/main/java/com/vaadin/shared/ui/datefield/DateFieldState.java → shared/src/main/java/com/vaadin/shared/ui/datefield/AbstractDateFieldState.java View File

@@ -15,20 +15,34 @@
*/
package com.vaadin.shared.ui.datefield;

import com.vaadin.shared.annotations.DelegateToWidget;
import java.util.Date;

import com.vaadin.shared.AbstractFieldState;
import com.vaadin.shared.annotations.NoLayout;

public class DateFieldState extends TextualDateFieldState {
public static final String DESCRIPTION_FOR_ASSISTIVE_DEVICES = "Arrow down key opens calendar element for choosing the date";
/**
* Shared state for the AbstractDateField component.
*
* @author Vaadin Ltd
*
*/
public class AbstractDateFieldState extends AbstractFieldState {

{
primaryStyleName = "v-datefield";
}

public boolean textFieldEnabled = true;
/*
* Start range that has been cleared, depending on the resolution of the
* date field
*/
@NoLayout
public String descriptionForAssistiveDevices = DESCRIPTION_FOR_ASSISTIVE_DEVICES;
public Date rangeStart = null;

/*
* End range that has been cleared, depending on the resolution of the date
* field
*/
@NoLayout
@DelegateToWidget
public String placeholder = null;
public Date rangeEnd = null;
}

+ 26
- 0
shared/src/main/java/com/vaadin/shared/ui/datefield/AbstractTextualDateFieldState.java View File

@@ -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;

/**
* Shared state for the AbstractLocalDateField component.
*
* @author Vaadin Ltd
*
*/
public class AbstractTextualDateFieldState extends AbstractDateFieldState {

}

shared/src/main/java/com/vaadin/shared/ui/datefield/Resolution.java → shared/src/main/java/com/vaadin/shared/ui/datefield/DateResolution.java View File

@@ -24,7 +24,7 @@ import java.util.List;
* @author Vaadin Ltd.
* @since 7.0
*/
public enum Resolution {
public enum DateResolution {
DAY, MONTH, YEAR;

/**
@@ -36,10 +36,10 @@ public enum Resolution {
* The resolution to start from
* @return An iterable for the resolutions higher or equal to r
*/
public static Iterable<Resolution> getResolutionsHigherOrEqualTo(
Resolution r) {
List<Resolution> resolutions = new ArrayList<>();
Resolution[] values = Resolution.values();
public static Iterable<DateResolution> getResolutionsHigherOrEqualTo(
DateResolution r) {
List<DateResolution> resolutions = new ArrayList<>();
DateResolution[] values = DateResolution.values();
for (int i = r.ordinal(); i < values.length; i++) {
resolutions.add(values[i]);
}
@@ -55,9 +55,10 @@ public enum Resolution {
* The resolution to start from
* @return An iterable for the resolutions lower than r
*/
public static List<Resolution> getResolutionsLowerThan(Resolution r) {
List<Resolution> resolutions = new ArrayList<>();
Resolution[] values = Resolution.values();
public static List<DateResolution> getResolutionsLowerThan(
DateResolution r) {
List<DateResolution> resolutions = new ArrayList<>();
DateResolution[] values = DateResolution.values();
for (int i = r.ordinal() - 1; i >= 0; i--) {
resolutions.add(values[i]);
}

+ 27
- 0
shared/src/main/java/com/vaadin/shared/ui/datefield/DateTimeResolution.java View File

@@ -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.shared.ui.datefield;

/**
* Resolutions for DateTimeFields
*
* @author Vaadin Ltd.
* @since 8.0
*/
public enum DateTimeResolution {
SECOND, MINUTE, HOUR, DAY, MONTH, YEAR;

}

+ 1
- 1
shared/src/main/java/com/vaadin/shared/ui/datefield/InlineDateFieldState.java View File

@@ -15,7 +15,7 @@
*/
package com.vaadin.shared.ui.datefield;

public class InlineDateFieldState extends TextualDateFieldState {
public class InlineDateFieldState extends AbstractTextualDateFieldState {
{
primaryStyleName = "v-inline-datefield";
}

+ 26
- 0
shared/src/main/java/com/vaadin/shared/ui/datefield/LocalDateFieldState.java View File

@@ -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;

/**
* Shared state for the DateField component.
*
* @author Vaadin Ltd
*
*/
public class LocalDateFieldState extends TextualDateFieldState {

}

+ 26
- 0
shared/src/main/java/com/vaadin/shared/ui/datefield/LocalDateTimeFieldState.java View File

@@ -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;

/**
* Shared state for the DateTimeField component.
*
* @author Vaadin Ltd
*
*/
public class LocalDateTimeFieldState extends AbstractTextualDateFieldState {

}

+ 8
- 15
shared/src/main/java/com/vaadin/shared/ui/datefield/TextualDateFieldState.java View File

@@ -15,27 +15,20 @@
*/
package com.vaadin.shared.ui.datefield;

import java.util.Date;

import com.vaadin.shared.AbstractFieldState;
import com.vaadin.shared.annotations.DelegateToWidget;
import com.vaadin.shared.annotations.NoLayout;

public class TextualDateFieldState extends AbstractFieldState {
public class TextualDateFieldState extends AbstractTextualDateFieldState {
public static final String DESCRIPTION_FOR_ASSISTIVE_DEVICES = "Arrow down key opens calendar element for choosing the date";

{
primaryStyleName = "v-datefield";
}

/*
* Start range that has been cleared, depending on the resolution of the
* date field
*/
public boolean textFieldEnabled = true;
@NoLayout
public Date rangeStart = null;

/*
* End range that has been cleared, depending on the resolution of the date
* field
*/
public String descriptionForAssistiveDevices = DESCRIPTION_FOR_ASSISTIVE_DEVICES;
@NoLayout
public Date rangeEnd = null;
@DelegateToWidget
public String placeholder = null;
}

+ 2
- 2
uitest/src/main/java/com/vaadin/tests/TestForBasicApplicationLayout.java View File

@@ -19,7 +19,7 @@ package com.vaadin.tests;
import java.util.Locale;

import com.vaadin.server.Sizeable;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.Button;
@@ -83,7 +83,7 @@ public class TestForBasicApplicationLayout extends CustomComponent {
controls.addComponent(click2);
reportLayout.addComponent(controls);
final AbstractDateField cal = new TestDateField();
cal.setResolution(Resolution.DAY);
cal.setResolution(DateResolution.DAY);
cal.setLocale(new Locale("en", "US"));
reportLayout.addComponent(cal);
reportLayout.setExpandRatio(controls, 1);

+ 2
- 2
uitest/src/main/java/com/vaadin/tests/components/TestDateField.java View File

@@ -17,13 +17,13 @@ package com.vaadin.tests.components;

import java.time.LocalDate;

import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;

/**
* @author Vaadin Ltd
*
*/
public class TestDateField extends AbstractDateField {
public class TestDateField extends AbstractLocalDateField {

/**
* Constructs an empty <code>DateField</code> with no caption.

+ 9
- 9
uitest/src/main/java/com/vaadin/tests/components/datefield/AbstractDateFieldTest.java View File

@@ -6,12 +6,12 @@ import java.time.LocalDate;
import java.util.LinkedHashMap;
import java.util.Locale;

import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.tests.components.abstractfield.AbstractFieldTest;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;

public class AbstractDateFieldTest<T extends AbstractDateField>
public class AbstractDateFieldTest<T extends AbstractLocalDateField>
extends AbstractFieldTest<T, LocalDate> {

@SuppressWarnings("unchecked")
@@ -90,19 +90,19 @@ public class AbstractDateFieldTest<T extends AbstractDateField>
}

private void createResolutionSelectAction(String category) {
LinkedHashMap<String, Resolution> options = new LinkedHashMap<>();
options.put("Year", Resolution.YEAR);
options.put("Month", Resolution.MONTH);
options.put("Day", Resolution.DAY);
LinkedHashMap<String, DateResolution> options = new LinkedHashMap<>();
options.put("Year", DateResolution.YEAR);
options.put("Month", DateResolution.MONTH);
options.put("Day", DateResolution.DAY);

createSelectAction("Resolution", category, options, "Year",
resolutionCommand);
}

private Command<T, Resolution> resolutionCommand = new Command<T, Resolution>() {
private Command<T, DateResolution> resolutionCommand = new Command<T, DateResolution>() {

@Override
public void execute(T c, Resolution value, Object data) {
public void execute(T c, DateResolution value, Object data) {
c.setResolution(value);

}

+ 2
- 2
uitest/src/main/java/com/vaadin/tests/components/datefield/AriaDisabled.java View File

@@ -18,7 +18,7 @@ package com.vaadin.tests.components.datefield;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;
import com.vaadin.ui.VerticalLayout;

public class AriaDisabled extends AbstractReindeerTestUI {
@@ -29,7 +29,7 @@ public class AriaDisabled extends AbstractReindeerTestUI {
content.setMargin(true);
content.setSpacing(true);

final AbstractDateField disabledDateField = new TestDateField(
final AbstractLocalDateField disabledDateField = new TestDateField(
"Disabled DateField");
disabledDateField.setEnabled(false);


+ 4
- 4
uitest/src/main/java/com/vaadin/tests/components/datefield/CustomDateFormat.java View File

@@ -4,18 +4,18 @@ import java.time.LocalDate;
import java.util.Locale;

import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;

public class CustomDateFormat extends AbstractTestUI {

@Override
protected void setup(VaadinRequest request) {
Locale locale = new Locale("fi", "FI");
AbstractDateField df = new TestDateField();
df.setResolution(Resolution.DAY);
AbstractLocalDateField df = new TestDateField();
df.setResolution(DateResolution.DAY);
df.setLocale(locale);
df.setWidth("300px");


+ 4
- 4
uitest/src/main/java/com/vaadin/tests/components/datefield/CustomDateFormatEEE.java View File

@@ -19,19 +19,19 @@ import java.time.LocalDate;
import java.util.Locale;

import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;
import com.vaadin.ui.VerticalLayout;

public class CustomDateFormatEEE extends AbstractReindeerTestUI {

@Override
protected void setup(VaadinRequest request) {
AbstractDateField df = new TestDateField(
AbstractLocalDateField df = new TestDateField(
"Should display 14/03/2014 Fri");
df.setResolution(Resolution.DAY);
df.setResolution(DateResolution.DAY);
df.setLocale(new Locale("en", "US"));

String pattern = "dd/MM/yyyy EEE";

+ 5
- 5
uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldChangeResolution.java View File

@@ -16,9 +16,9 @@
package com.vaadin.tests.components.datefield;

import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;
import com.vaadin.ui.Button;
import com.vaadin.ui.DateField;
import com.vaadin.ui.HorizontalLayout;
@@ -32,8 +32,8 @@ public class DateFieldChangeResolution extends AbstractReindeerTestUI {

@Override
protected void setup(VaadinRequest request) {
final AbstractDateField dateField = new DateField("Enter date");
dateField.setResolution(Resolution.YEAR);
final AbstractLocalDateField dateField = new DateField("Enter date");
dateField.setResolution(DateResolution.YEAR);
dateField.setId(DATEFIELD_ID);
addComponent(dateField);

@@ -41,7 +41,7 @@ public class DateFieldChangeResolution extends AbstractReindeerTestUI {
addComponent(l);
HorizontalLayout hlayout = new HorizontalLayout();
addComponent(hlayout);
for (final Resolution value : Resolution.values()) {
for (final DateResolution value : DateResolution.values()) {
String resolutionString = value.toString().toLowerCase();
Button button = new Button(resolutionString);
button.addClickListener(event -> dateField.setResolution(value));

+ 2
- 2
uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldClose.java View File

@@ -18,7 +18,7 @@ package com.vaadin.tests.components.datefield;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;

public class DateFieldClose extends AbstractReindeerTestUI {

@@ -26,7 +26,7 @@ public class DateFieldClose extends AbstractReindeerTestUI {

@Override
protected void setup(VaadinRequest request) {
final AbstractDateField df = new TestDateField();
final AbstractLocalDateField df = new TestDateField();
df.setId(DATEFIELD_ID);
addComponent(df);
}

+ 7
- 5
uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffset.java View File

@@ -4,7 +4,7 @@ import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
@@ -20,7 +20,8 @@ public class DateFieldDayResolutionOffset extends AbstractReindeerTestUI {
dateValue.setId("dateValue");

final DateTimeFormatter dateformat = getDateFormat();
final AbstractDateField dateField = getDateField(dateformat);
final AbstractDateField<LocalDate, DateResolution> dateField = getDateField(
dateformat);

addComponent(dateValue);
addComponent(dateField);
@@ -29,11 +30,12 @@ public class DateFieldDayResolutionOffset extends AbstractReindeerTestUI {
.setValue(dateformat.format(dateField.getValue())));
}

private AbstractDateField getDateField(DateTimeFormatter dateformat) {
final AbstractDateField dateField = new TestDateField();
private AbstractDateField<LocalDate, DateResolution> getDateField(
DateTimeFormatter dateformat) {
final AbstractDateField<LocalDate, DateResolution> dateField = new TestDateField();
LocalDate initialDate = dateformat.parse(initialDateString,
LocalDate::from);
dateField.setResolution(Resolution.DAY);
dateField.setResolution(DateResolution.DAY);
dateField.setValue(initialDate);
return dateField;
}

+ 3
- 2
uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldIsValid.java View File

@@ -6,7 +6,7 @@ import java.time.format.DateTimeFormatter;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractTestUIWithLog;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;

public class DateFieldIsValid extends AbstractTestUIWithLog {

@@ -26,7 +26,8 @@ public class DateFieldIsValid extends AbstractTestUIWithLog {

@Override
protected void setup(VaadinRequest request) {
final AbstractDateField dateField = new TestDateField("Insert Date: ");
final AbstractLocalDateField dateField = new TestDateField(
"Insert Date: ");
dateField.setDateFormat(pattern);

dateField.addValueChangeListener(event -> log("valueChange: value: "

+ 2
- 2
uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldPopupClosing.java View File

@@ -18,7 +18,7 @@ package com.vaadin.tests.components.datefield;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;

public class DateFieldPopupClosing extends AbstractReindeerTestUI {

@@ -26,7 +26,7 @@ public class DateFieldPopupClosing extends AbstractReindeerTestUI {

@Override
protected void setup(VaadinRequest request) {
final AbstractDateField df = new TestDateField();
final AbstractLocalDateField df = new TestDateField();
df.setId(DATEFIELD_ID);
addComponent(df);
}

+ 2
- 2
uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldPopupClosingOnDetach.java View File

@@ -21,7 +21,7 @@ import java.util.TimerTask;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;

public class DateFieldPopupClosingOnDetach extends AbstractReindeerTestUI {

@@ -30,7 +30,7 @@ public class DateFieldPopupClosingOnDetach extends AbstractReindeerTestUI {
// Use polling to notice the removal of DateField.
getUI().setPollInterval(500);

final AbstractDateField df = new TestDateField();
final AbstractLocalDateField df = new TestDateField();
getLayout().addLayoutClickListener(event -> {
// Use a background Thread to remove the DateField 1 second
// after being clicked.

+ 2
- 2
uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldReadOnly.java View File

@@ -6,7 +6,7 @@ import java.util.Locale;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;
import com.vaadin.ui.Button;

public class DateFieldReadOnly extends AbstractReindeerTestUI {
@@ -23,7 +23,7 @@ public class DateFieldReadOnly extends AbstractReindeerTestUI {

@Override
protected void setup(VaadinRequest request) {
final AbstractDateField timeField = new TestDateField(
final AbstractLocalDateField timeField = new TestDateField(
"A read-only datefield");
timeField.setCaption(null);
timeField.setIcon(null);

+ 5
- 5
uitest/src/main/java/com/vaadin/tests/components/datefield/DateFieldWhenChangingValueAndEnablingParent.java View File

@@ -5,7 +5,7 @@ import java.time.LocalDate;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.DateField;
import com.vaadin.ui.VerticalLayout;
@@ -22,8 +22,8 @@ public class DateFieldWhenChangingValueAndEnablingParent
main.setMargin(true);
setContent(main);

final AbstractDateField df1 = createDateField(true);
final AbstractDateField df2 = createDateField(false);
final AbstractLocalDateField df1 = createDateField(true);
final AbstractLocalDateField df2 = createDateField(false);
final DateField pdf1 = createPopupDateField(true, true);
final DateField pdf2 = createPopupDateField(true, false);
final DateField pdf3 = createPopupDateField(false, true);
@@ -50,8 +50,8 @@ public class DateFieldWhenChangingValueAndEnablingParent
});
}

private AbstractDateField createDateField(boolean enabled) {
AbstractDateField df = new TestDateField(
private AbstractLocalDateField createDateField(boolean enabled) {
AbstractLocalDateField df = new TestDateField(
"DateField, " + (enabled ? "enabled" : "disabled"));
df.setEnabled(enabled);
df.setId("DATEFIELD_" + (enabled ? "ENABLED" : "DISABLED"));

+ 6
- 6
uitest/src/main/java/com/vaadin/tests/components/datefield/DateFields.java View File

@@ -5,7 +5,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;

import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.ComponentTestCase;
import com.vaadin.ui.Component;
import com.vaadin.ui.DateField;
@@ -46,7 +46,7 @@ public class DateFields extends ComponentTestCase<DateField> {
pd.setWidth(width);
pd.setValue(LocalDate.of(1970, 05, 23));
pd.setLocale(locale);
pd.setResolution(Resolution.YEAR);
pd.setResolution(DateResolution.YEAR);

return pd;
}
@@ -65,10 +65,10 @@ public class DateFields extends ComponentTestCase<DateField> {
}

private Component createResolutionSelectAction() {
LinkedHashMap<String, Resolution> options = new LinkedHashMap<>();
options.put("Year", Resolution.YEAR);
options.put("Month", Resolution.MONTH);
options.put("Day", Resolution.DAY);
LinkedHashMap<String, DateResolution> options = new LinkedHashMap<>();
options.put("Year", DateResolution.YEAR);
options.put("Month", DateResolution.MONTH);
options.put("Day", DateResolution.DAY);
return createSelectAction("Resolution", options, "Year",
(field, value, data) -> field.setResolution(value));
}

+ 2
- 2
uitest/src/main/java/com/vaadin/tests/components/datefield/DisabledDateFieldPopup.java View File

@@ -18,13 +18,13 @@ package com.vaadin.tests.components.datefield;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;

public class DisabledDateFieldPopup extends AbstractReindeerTestUI {

@Override
protected void setup(VaadinRequest request) {
AbstractDateField field = new TestDateField();
AbstractLocalDateField field = new TestDateField();
field.setEnabled(false);
addComponent(field);
}

+ 2
- 2
uitest/src/main/java/com/vaadin/tests/components/datefield/DisabledInlineDateField.java View File

@@ -19,14 +19,14 @@ import java.time.LocalDate;

import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;
import com.vaadin.ui.InlineDateField;

public class DisabledInlineDateField extends AbstractReindeerTestUI {

@Override
protected void setup(VaadinRequest request) {
AbstractDateField df = new InlineDateField("Disabled");
AbstractLocalDateField df = new InlineDateField("Disabled");
LocalDate date = LocalDate.of(2014, 6, 5);
df.setValue(date);
df.setEnabled(false);

+ 3
- 8
uitest/src/main/java/com/vaadin/tests/components/datefield/DisabledParentLayout.java View File

@@ -18,7 +18,7 @@ package com.vaadin.tests.components.datefield;
import com.vaadin.server.VaadinRequest;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;
import com.vaadin.ui.Button;
import com.vaadin.ui.VerticalLayout;

@@ -36,18 +36,13 @@ public class DisabledParentLayout extends AbstractReindeerTestUI {
content.setMargin(true);

final VerticalLayout pane = new VerticalLayout();
AbstractDateField dateField = new TestDateField();
AbstractLocalDateField dateField = new TestDateField();
pane.addComponent(dateField);

content.addComponent(pane);

Button button = new Button("Test");
button.addClickListener(new Button.ClickListener() {
@Override
public void buttonClick(Button.ClickEvent event) {
pane.setEnabled(!pane.isEnabled());
}
});
button.addClickListener(event -> pane.setEnabled(!pane.isEnabled()));
content.addComponent(button);

addComponent(content);

+ 6
- 6
uitest/src/main/java/com/vaadin/tests/components/datefield/InlineDateFields.java View File

@@ -5,7 +5,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;

import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.ComponentTestCase;
import com.vaadin.ui.Component;
import com.vaadin.ui.InlineDateField;
@@ -51,7 +51,7 @@ public class InlineDateFields extends ComponentTestCase<InlineDateField> {
pd.setWidth(width);
pd.setValue(LocalDate.of(1970, 05, 23));
pd.setLocale(locale);
pd.setResolution(Resolution.YEAR);
pd.setResolution(DateResolution.YEAR);

return pd;
}
@@ -70,10 +70,10 @@ public class InlineDateFields extends ComponentTestCase<InlineDateField> {
}

private Component createResolutionSelectAction() {
LinkedHashMap<String, Resolution> options = new LinkedHashMap<>();
options.put("Year", Resolution.YEAR);
options.put("Month", Resolution.MONTH);
options.put("Day", Resolution.DAY);
LinkedHashMap<String, DateResolution> options = new LinkedHashMap<>();
options.put("Year", DateResolution.YEAR);
options.put("Month", DateResolution.MONTH);
options.put("Day", DateResolution.DAY);
return createSelectAction("Resolution", options, "Year",
(field, value, data) -> field.setResolution(value));
}

+ 8
- 8
uitest/src/main/java/com/vaadin/tests/components/datefield/PopupClosingWithEsc.java View File

@@ -16,27 +16,27 @@
package com.vaadin.tests.components.datefield;

import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.AbstractLocalDateField;
import com.vaadin.ui.VerticalLayout;

public class PopupClosingWithEsc extends AbstractReindeerTestUI {

@Override
protected void setup(VaadinRequest request) {
AbstractDateField df1 = new TestDateField("Day");
AbstractLocalDateField df1 = new TestDateField("Day");
df1.setId("day");
df1.setResolution(Resolution.DAY);
df1.setResolution(DateResolution.DAY);

AbstractDateField df2 = new TestDateField("Month");
AbstractLocalDateField df2 = new TestDateField("Month");
df2.setId("month");
df2.setResolution(Resolution.MONTH);
df2.setResolution(DateResolution.MONTH);

AbstractDateField df3 = new TestDateField("Year");
AbstractLocalDateField df3 = new TestDateField("Year");
df3.setId("year");
df3.setResolution(Resolution.YEAR);
df3.setResolution(DateResolution.YEAR);

VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);

+ 2
- 2
uitest/src/main/java/com/vaadin/tests/components/datefield/PopupDateFieldExtendedRange.java View File

@@ -5,7 +5,7 @@ import java.util.Locale;
import java.util.stream.Stream;

import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.DateField;
@@ -52,7 +52,7 @@ public class PopupDateFieldExtendedRange extends AbstractReindeerTestUI {

private DateField makeDateField() {
DateField pdf = new DateField();
pdf.setResolution(Resolution.DAY);
pdf.setResolution(DateResolution.DAY);
pdf.setValue(LocalDate.of(2011, 1, 1));
return pdf;
}

+ 2
- 2
uitest/src/main/java/com/vaadin/tests/components/gridlayout/GridLayoutCellSizesUI.java View File

@@ -1,7 +1,7 @@
package com.vaadin.tests.components.gridlayout;

import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.AbstractReindeerTestUI;
import com.vaadin.ui.Button;
import com.vaadin.ui.GridLayout;
@@ -32,7 +32,7 @@ public class GridLayoutCellSizesUI extends AbstractReindeerTestUI {
grid.addComponent(new Button("3x1 button"), 1, 1, 3, 1);
grid.addComponent(new Label("1x2 cell"), 1, 2, 1, 3);
final InlineDateField date = new InlineDateField("A 2x2 date field");
date.setResolution(Resolution.DAY);
date.setResolution(DateResolution.DAY);
grid.addComponent(date, 2, 2, 3, 3);

grid.setMargin(true);

+ 13
- 13
uitest/src/main/java/com/vaadin/tests/themes/valo/DateFields.java View File

@@ -22,7 +22,7 @@ import java.util.Locale;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.server.UserError;
import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.tests.components.TestDateField;
import com.vaadin.ui.AbstractDateField;
import com.vaadin.ui.Button;
@@ -90,58 +90,58 @@ public class DateFields extends VerticalLayout implements View {

date = new TestDateField("Day resolution");
setDate(date);
date.setResolution(Resolution.DAY);
date.setResolution(DateResolution.DAY);
row.addComponent(date);

date = new TestDateField("Month resolution");
setDate(date);
date.setResolution(Resolution.MONTH);
date.setResolution(DateResolution.MONTH);
row.addComponent(date);

date = new TestDateField("Year resolution");
setDate(date);
date.setResolution(Resolution.YEAR);
date.setResolution(DateResolution.YEAR);
row.addComponent(date);

date = new TestDateField("Custom color");
setDate(date);
date.setResolution(Resolution.DAY);
date.setResolution(DateResolution.DAY);
date.addStyleName("color1");
row.addComponent(date);

date = new TestDateField("Custom color");
setDate(date);
date.setResolution(Resolution.DAY);
date.setResolution(DateResolution.DAY);
date.addStyleName("color2");
row.addComponent(date);

date = new TestDateField("Custom color");
setDate(date);
date.setResolution(Resolution.DAY);
date.setResolution(DateResolution.DAY);
date.addStyleName("color3");
row.addComponent(date);

date = new TestDateField("Small");
setDate(date);
date.setResolution(Resolution.DAY);
date.setResolution(DateResolution.DAY);
date.addStyleName(ValoTheme.DATEFIELD_SMALL);
row.addComponent(date);

date = new TestDateField("Large");
setDate(date);
date.setResolution(Resolution.DAY);
date.setResolution(DateResolution.DAY);
date.addStyleName(ValoTheme.DATEFIELD_LARGE);
row.addComponent(date);

date = new TestDateField("Borderless");
setDate(date);
date.setResolution(Resolution.DAY);
date.setResolution(DateResolution.DAY);
date.addStyleName(ValoTheme.DATEFIELD_BORDERLESS);
row.addComponent(date);

date = new TestDateField("Week numbers");
setDate(date);
date.setResolution(Resolution.DAY);
date.setResolution(DateResolution.DAY);
date.setLocale(new Locale("fi", "fi"));
date.setShowISOWeekNumbers(true);
row.addComponent(date);
@@ -153,13 +153,13 @@ public class DateFields extends VerticalLayout implements View {

date = new TestDateField("Tiny");
setDate(date);
date.setResolution(Resolution.DAY);
date.setResolution(DateResolution.DAY);
date.addStyleName(ValoTheme.DATEFIELD_TINY);
row.addComponent(date);

date = new TestDateField("Huge");
setDate(date);
date.setResolution(Resolution.DAY);
date.setResolution(DateResolution.DAY);
date.addStyleName(ValoTheme.DATEFIELD_HUGE);
row.addComponent(date);


+ 10
- 10
uitest/src/test/java/com/vaadin/tests/components/datefield/DateFieldChangeResolutionTest.java View File

@@ -24,7 +24,7 @@ import org.junit.Test;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;

import com.vaadin.shared.ui.datefield.Resolution;
import com.vaadin.shared.ui.datefield.DateResolution;
import com.vaadin.testbench.By;
import com.vaadin.tests.tb3.MultiBrowserTest;

@@ -37,19 +37,19 @@ public class DateFieldChangeResolutionTest extends MultiBrowserTest {
public void changeResolutionBetweenYearAndMonth() throws Exception {
initialize();
click(resolutionMonth);
checkHeaderAndBody(Resolution.MONTH, true);
checkHeaderAndBody(DateResolution.MONTH, true);
click(resolutionYear);
checkHeaderAndBody(Resolution.YEAR, true);
checkHeaderAndBody(DateResolution.YEAR, true);
}

@Test
public void changeResolutionToDayThenMonth() throws Exception {
initialize();
checkHeaderAndBody(Resolution.YEAR, true); // check the initial state
checkHeaderAndBody(DateResolution.YEAR, true); // check the initial state
click(resolutionDay);
checkHeaderAndBody(Resolution.DAY, true);
checkHeaderAndBody(DateResolution.DAY, true);
click(resolutionMonth);
checkHeaderAndBody(Resolution.MONTH, true);
checkHeaderAndBody(DateResolution.MONTH, true);
}

@Test
@@ -66,7 +66,7 @@ public class DateFieldChangeResolutionTest extends MultiBrowserTest {
// Change resolutions and check that the selected date is not lost and
// that the calendar has the correct resolution.
click(resolutionYear);
checkHeaderAndBody(Resolution.YEAR, false);
checkHeaderAndBody(DateResolution.YEAR, false);
}

private void initialize() {
@@ -81,19 +81,19 @@ public class DateFieldChangeResolutionTest extends MultiBrowserTest {
resolutionYear = driver.findElement(By.id(BUTTON_BASE_ID + "year"));
}

private void checkHeaderAndBody(Resolution resolution,
private void checkHeaderAndBody(DateResolution 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(Resolution.MONTH) <= 0) {
if (resolution.compareTo(DateResolution.MONTH) <= 0) {
checkMonthHeader();
} else {
checkYearHeader();
}
if (resolution.compareTo(Resolution.DAY) <= 0) {
if (resolution.compareTo(DateResolution.DAY) <= 0) {
assertTrue(
"A calendar with the chosen resolution should have a body",
calendarHasBody());

Loading…
Cancel
Save