You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lo...@apache.org on 2021/04/22 06:24:49 UTC
[myfaces-tobago] 01/02: feat: new datepicker
This is an automated email from the ASF dual-hosted git repository.
lofwyr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git
commit 0019dafccc1e54ef0c23e0747e885b6a04c7c883
Author: Udo Schnurpfeil <ud...@irian.eu>
AuthorDate: Wed Apr 21 18:10:24 2021 +0200
feat: new datepicker
Using HTML5 conform way
issue: TOBAGO-2071
---
.../myfaces/tobago/convert/DateTimeConverter.java | 4 +
.../tobago/facelets/ConvertDateTimeHandler.java | 4 +
.../tobago/internal/component/AbstractUIDate.java | 40 ++-
.../tobago/internal/component/AbstractUIIn.java | 1 +
.../internal/component/AbstractUITextarea.java | 4 +-
.../internal/renderkit/renderer/DateRenderer.java | 313 ++++++++++++++++++---
.../internal/renderkit/renderer/InRenderer.java | 6 +-
.../component/ConvertDateTimeTagDeclaration.java | 3 +
.../taglib/component/DateTagDeclaration.java | 1 +
.../tobago/internal/util/DateFormatUtils.java | 156 ++++++----
.../myfaces/tobago/renderkit/RendererBase.java | 6 +-
.../tobago/renderkit/html/CustomAttributes.java | 2 +
.../tobago/renderkit/html/HtmlInputTypes.java | 51 +++-
.../apache/myfaces/tobago/util/ComponentUtils.java | 3 +
.../internal/config/AbstractTobagoTestBase.java | 5 +
.../renderkit/renderer/DateRendererUnitTest.java | 178 ++++++++++--
.../internal/util/DateFormatUtilsUnitTest.java | 68 ++++-
.../renderer/date/{date.html => dateAuto.html} | 4 +-
.../date/{date-label.html => dateBoth.html} | 6 +-
.../renderer/date/{date.html => dateDate.html} | 4 +-
.../renderer/date/{date.html => dateTime.html} | 6 +-
.../date/{date.html => localDateAuto.html} | 4 +-
.../{date-label.html => localDateTimeAuto.html} | 6 +-
.../date/{date.html => localTimeAuto.html} | 6 +-
.../date/{date-label.html => testLabel.html} | 4 +-
...date-today-button.html => testTodayButton.html} | 4 +-
.../date/{date-today-button.html => text.html} | 4 +-
.../{date-label.html => zonedDateTimeAuto.html} | 6 +-
.../tobago/example/demo/DateController.java | 28 +-
.../20-component/010-input/40-date/Date.xhtml | 26 +-
.../tobago-theme-standard/src/main/js/tobago.js | 17 +-
.../src/main/ts/tobago-date.ts | 20 +-
32 files changed, 807 insertions(+), 183 deletions(-)
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/convert/DateTimeConverter.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/convert/DateTimeConverter.java
index 85f0573..d4fe8bf 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/convert/DateTimeConverter.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/convert/DateTimeConverter.java
@@ -44,6 +44,10 @@ import java.util.TimeZone;
import static org.apache.myfaces.tobago.convert.DateTimeConverter.CONVERTER_ID;
+/**
+ * @deprecated Since 5.0.0. Should work with <f:convertDateTime> since JSF 2.3.
+ */
+@Deprecated
@org.apache.myfaces.tobago.apt.annotation.Converter(id = CONVERTER_ID)
public class DateTimeConverter extends javax.faces.convert.DateTimeConverter {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/ConvertDateTimeHandler.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/ConvertDateTimeHandler.java
index 3012204..5c2dec1 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/ConvertDateTimeHandler.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/facelets/ConvertDateTimeHandler.java
@@ -39,6 +39,10 @@ import java.lang.invoke.MethodHandles;
import java.util.Locale;
import java.util.TimeZone;
+/**
+ * @deprecated Since 5.0.0. Should work with <f:convertDateTime> since JSF 2.3.
+ */
+@Deprecated
public class ConvertDateTimeHandler extends ConverterHandler {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIDate.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIDate.java
index b0141fe..ea7fb6c 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIDate.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIDate.java
@@ -19,36 +19,30 @@
package org.apache.myfaces.tobago.internal.component;
-import org.apache.myfaces.tobago.internal.util.DateFormatUtils;
-import org.apache.myfaces.tobago.util.ComponentUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.faces.context.FacesContext;
-import javax.faces.convert.Converter;
-import javax.faces.convert.DateTimeConverter;
-import java.lang.invoke.MethodHandles;
-import java.util.Date;
+import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
/**
* {@link org.apache.myfaces.tobago.internal.taglib.component.DateTagDeclaration}
*/
-public abstract class AbstractUIDate extends AbstractUIIn {
+public abstract class AbstractUIDate extends AbstractUIInput {
+
+ private transient HtmlInputTypes type;
+ private transient String pattern;
- private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ public HtmlInputTypes getType() {
+ return type;
+ }
+
+ public void setType(HtmlInputTypes type) {
+ this.type = type;
+ }
public String getPattern() {
- final FacesContext facesContext = getFacesContext();
- Converter converter = ComponentUtils.getConverter(facesContext, this, getSubmittedValue());
- if (!(converter instanceof DateTimeConverter)) {
- // hack for prototyping, if there is no value behind the component.
- converter = facesContext.getApplication().createConverter(Date.class);
- if (LOG.isWarnEnabled()) {
- LOG.warn("Can't find a converter to get a pattern in component {}! Using default.",
- getClientId(facesContext));
- }
- }
- return DateFormatUtils.findPattern((DateTimeConverter) converter);
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
}
public abstract boolean isTodayButton();
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIIn.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIIn.java
index d30e2d6..00bb977 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIIn.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUIIn.java
@@ -26,4 +26,5 @@ public abstract class AbstractUIIn extends AbstractUIInput {
public abstract String getPlaceholder();
+ public abstract boolean isPassword();
}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUITextarea.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUITextarea.java
index bcdec44..47298d3 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUITextarea.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/component/AbstractUITextarea.java
@@ -24,7 +24,9 @@ import org.apache.myfaces.tobago.sanitizer.SanitizeMode;
/**
* {@link org.apache.myfaces.tobago.internal.taglib.component.TextareaTagDeclaration}
*/
-public abstract class AbstractUITextarea extends AbstractUIIn {
+public abstract class AbstractUITextarea extends AbstractUIInput {
+
+ public abstract String getPlaceholder();
public abstract SanitizeMode getSanitize();
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRenderer.java
index 65eeeac..fed828a 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRenderer.java
@@ -19,32 +19,73 @@
package org.apache.myfaces.tobago.internal.renderkit.renderer;
+import org.apache.myfaces.tobago.component.Attributes;
import org.apache.myfaces.tobago.internal.component.AbstractUIDate;
import org.apache.myfaces.tobago.internal.context.DateTimeI18n;
+import org.apache.myfaces.tobago.internal.util.AccessKeyLogger;
import org.apache.myfaces.tobago.internal.util.DateFormatUtils;
+import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
import org.apache.myfaces.tobago.internal.util.JsonUtils;
-import org.apache.myfaces.tobago.internal.util.StringUtils;
import org.apache.myfaces.tobago.renderkit.css.BootstrapClass;
-import org.apache.myfaces.tobago.renderkit.css.CssItem;
import org.apache.myfaces.tobago.renderkit.css.FaIcons;
import org.apache.myfaces.tobago.renderkit.css.TobagoClass;
import org.apache.myfaces.tobago.renderkit.html.CustomAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlButtonTypes;
import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.renderkit.html.HtmlInputTypes;
+import org.apache.myfaces.tobago.util.ComponentUtils;
import org.apache.myfaces.tobago.util.ResourceUtils;
import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.el.ValueExpression;
import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import javax.faces.convert.DateTimeConverter;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZonedDateTime;
+import java.util.Calendar;
+import java.util.Date;
-public class DateRenderer<T extends AbstractUIDate> extends InRenderer<T> {
+public class DateRenderer<T extends AbstractUIDate> extends MessageLayoutRendererBase<T> {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ private static final String TYPE_DATE = "date";
+ private static final String TYPE_TIME = "time";
+ private static final String TYPE_BOTH = "both";
+ private static final String TYPE_LOCAL_DATE = "localDate";
+ private static final String TYPE_LOCAL_TIME = "localTime";
+ private static final String TYPE_LOCAL_DATE_TIME = "localDateTime";
+ private static final String TYPE_OFFSET_TIME = "offsetTime";
+ private static final String TYPE_OFFSET_DATE_TIME = "offsetDateTime";
+ private static final String TYPE_ZONED_DATE_TIME = "zonedDateTime";
+ private static final String TYPE_MONTH = "month";
+ private static final String TYPE_WEEK = "week";
+
+ private static final String PATTERN_DATE = "yyyy-MM-dd";
+ private static final String PATTERN_DATETIME = "yyyy-MM-dd'T'HH:mm:ss.SSS";
+ private static final String PATTERN_TIME = "HH:mm:ss.SSS";
+ private static final String PATTERN_MONTH = "yyyy-MM";
+ private static final String PATTERN_WEEK = "yyyy-'W'MM";
+
+ @Override
+ public void encodeBeginInternal(FacesContext facesContext, T component) throws IOException {
+ prepare(facesContext, component);
+ super.encodeBeginInternal(facesContext, component);
+ }
+
+ @Override
+ protected boolean isOutputOnly(T component) {
+ return component.isDisabled() || component.isReadonly();
+ }
+
@Override
public HtmlElements getComponentTag() {
return HtmlElements.TOBAGO_DATE;
@@ -52,16 +93,26 @@ public class DateRenderer<T extends AbstractUIDate> extends InRenderer<T> {
@Override
protected void writeAdditionalAttributes(
- final FacesContext facesContext, final TobagoResponseWriter writer, final T input)
+ final FacesContext facesContext, final TobagoResponseWriter writer, final T date)
throws IOException {
- super.writeAdditionalAttributes(facesContext, writer, input);
- writer.writeAttribute(HtmlAttributes.PATTERN, DateFormatUtils.toJavaScriptPattern(input.getPattern()), true);
+ super.writeAdditionalAttributes(facesContext, writer, date);
+// writer.writeAttribute(HtmlAttributes.PATTERN,
+// new DateFormatUtils.DateTimeJavaScriptPattern(input.getPattern()).getDatePattern(), true);
+ final HtmlInputTypes type = date.getType();
+ if (date.getType() == HtmlInputTypes.TEXT) {
+ // todo
+ final DateFormatUtils.DateTimeJavaScriptPattern patterns
+ = new DateFormatUtils.DateTimeJavaScriptPattern(date.getPattern());
+ writer.writeAttribute(CustomAttributes.DATE_PATTERN, patterns.getDatePattern(), true);
+ writer.writeAttribute(CustomAttributes.TIME_PATTERN, patterns.getTimePattern(), true);
+ }
+ // todo: also render pattern, either yyyy-mm-dd or in case of type=text the combination of upper
// final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// writer.writeAttribute(CustomAttributes.TODAY, sdf.format(new Date()), true); XXX seem no longer needed
final DateTimeI18n dateTimeI18n = DateTimeI18n.valueOf(facesContext.getViewRoot().getLocale());
writer.writeAttribute(CustomAttributes.I18N, JsonUtils.encode(dateTimeI18n), true);
- writer.writeAttribute(CustomAttributes.TODAY_BUTTON, input.isTodayButton());
+ writer.writeAttribute(CustomAttributes.TODAY_BUTTON, date.isTodayButton());
}
@Override
@@ -74,15 +125,59 @@ public class DateRenderer<T extends AbstractUIDate> extends InRenderer<T> {
writer.startElement(HtmlElements.DIV);
writer.writeClassAttribute(BootstrapClass.INPUT_GROUP);
- super.encodeBeginField(facesContext, component);
- }
+ final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, component);
+ final HtmlInputTypes type = component.getType();
- @Override
- public void encodeEndField(final FacesContext facesContext, final T component) throws IOException {
+ final String currentValue = getCurrentValue(facesContext, component);
+ final String clientId = component.getClientId(facesContext);
+ final String fieldId = component.getFieldId(facesContext);
+ final boolean readonly = component.isReadonly();
+ final boolean disabled = component.isDisabled();
+ final boolean required = ComponentUtils.getBooleanAttribute(component, Attributes.required);
+
+ writer.startElement(HtmlElements.INPUT);
- super.encodeEndField(facesContext, component);
+ if (component.getAccessKey() != null) {
+ writer.writeAttribute(HtmlAttributes.ACCESSKEY, Character.toString(component.getAccessKey()), false);
+ AccessKeyLogger.addAccessKey(facesContext, component.getAccessKey(), clientId);
+ }
+
+ writer.writeAttribute(HtmlAttributes.TYPE, type.getValue(), false);
+ writer.writeNameAttribute(clientId);
+ writer.writeIdAttribute(fieldId);
+ HtmlRendererUtils.writeDataAttributes(facesContext, writer, component);
+ writer.writeAttribute(HtmlAttributes.VALUE, currentValue, true);
+ writer.writeAttribute(HtmlAttributes.TITLE, title, true);
+ writer.writeAttribute(HtmlAttributes.READONLY, readonly);
+ writer.writeAttribute(HtmlAttributes.DISABLED, disabled);
+ writer.writeAttribute(HtmlAttributes.TABINDEX, component.getTabIndex());
+// if (!disabled && !readonly) {
+// writer.writeAttribute(HtmlAttributes.PLACEHOLDER, component.getPlaceholder(), true);
+// }
+
+ writer.writeClassAttribute(
+ BootstrapClass.borderColor(ComponentUtils.getMaximumSeverity(component)),
+ BootstrapClass.FORM_CONTROL,
+ component.getCustomClass());
+
+ writer.writeAttribute(HtmlAttributes.REQUIRED, required);
+ renderFocus(clientId, component.isFocus(), component.isError(), facesContext, writer);
+
+ writer.endElement(HtmlElements.INPUT);
+
+ encodeBehavior(writer, facesContext, component);
+
+ if (type.supportsDate()) {
+ encodeButton(facesContext, component, FaIcons.CALENDAR);
+ }
+ if (type.supportsTime()) {
+ encodeButton(facesContext, component, FaIcons.CLOCK_O);
+ }
+ }
+
+ private void encodeButton(final FacesContext facesContext, final T component, final FaIcons icon)
+ throws IOException {
- final String pattern = component.getPattern();
final TobagoResponseWriter writer = getResponseWriter(facesContext);
writer.startElement(HtmlElements.BUTTON);
@@ -96,33 +191,185 @@ public class DateRenderer<T extends AbstractUIDate> extends InRenderer<T> {
writer.writeAttribute(HtmlAttributes.DISABLED, component.isDisabled() || component.isReadonly());
writer.writeAttribute(HtmlAttributes.TABINDEX, component.getTabIndex());
- final boolean hasDate = StringUtils.containsAny(pattern, "yYMDdE");
- final boolean hasTime = StringUtils.containsAny(pattern, "Hhms");
-
- if (hasDate || !hasTime) { // || !hasTime is, to have at least one icon
- writer.startElement(HtmlElements.I);
- writer.writeClassAttribute(FaIcons.FA, FaIcons.CALENDAR);
- writer.endElement(HtmlElements.I);
- }
- if (hasTime) {
- writer.startElement(HtmlElements.I);
- writer.writeClassAttribute(FaIcons.FA, FaIcons.CLOCK_O);
- writer.endElement(HtmlElements.I);
- }
-
- if (StringUtils.containsAny(pattern, "GWFKzX")) {
- LOG.warn("Pattern chars 'G', 'W', 'F', 'K', 'z' and 'X' are not supported: " + pattern);
- }
+ writer.startElement(HtmlElements.I);
+ writer.writeClassAttribute(FaIcons.FA, icon);
+ writer.endElement(HtmlElements.I);
writer.endElement(HtmlElements.BUTTON);
+ }
+ @Override
+ public void encodeEndField(final FacesContext facesContext, final T component) throws IOException {
+ final TobagoResponseWriter writer = getResponseWriter(facesContext);
writer.endElement(HtmlElements.DIV);
-
writer.endElement(HtmlElements.DIV);
}
@Override
- protected CssItem getRendererCssClass() {
- return null;
+ protected String getFieldId(final FacesContext facesContext, final T component) {
+ return component.getFieldId(facesContext);
}
+
+ /**
+ * Creates a converter (if not defined any) which satifies the requirements of HTML5 like described here:
+ * <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Date_and_time_formats">MDN</a>
+ */
+ @Override
+ protected Converter getConverter(FacesContext facesContext, T component, Object value) {
+ final Converter converter = component.getConverter();
+ if (converter != null) {
+ // todo: should we warn here, if the type is not "text"?
+ return converter;
+ } else {
+ Class<?> estimatedType = estimateValueType(facesContext, component);
+ if (estimatedType == null) {
+ // todo: log debug instead of warn
+ LOG.warn("Can't estimate type (clientId='{}')!", component.getClientId(facesContext));
+ return null;
+ } else if (estimatedType.isAssignableFrom(String.class)) {
+ // todo: log debug instead of warn
+ LOG.warn("No converter for java.lang.String");
+ return null;
+ } else {
+ final DateTimeConverter dateTimeConverter
+ = (DateTimeConverter) facesContext.getApplication().createConverter("javax.faces.DateTime");
+ if (estimatedType.isAssignableFrom(LocalDateTime.class)) {
+ dateTimeConverter.setType("localDateTime");
+ dateTimeConverter.setPattern(PATTERN_DATETIME);
+ } else if (estimatedType.isAssignableFrom(LocalDate.class)) {
+ dateTimeConverter.setType("localDate");
+ dateTimeConverter.setPattern(PATTERN_DATE);
+ } else if (estimatedType.isAssignableFrom(LocalTime.class)) {
+ dateTimeConverter.setType("localTime");
+ dateTimeConverter.setPattern(PATTERN_TIME);
+ } else if (estimatedType.isAssignableFrom(ZonedDateTime.class)) {
+ dateTimeConverter.setType("zonedDateTime");
+ dateTimeConverter.setPattern(PATTERN_DATETIME);
+ } else if (estimatedType.isAssignableFrom(Date.class)) {
+ dateTimeConverter.setType("date");
+ dateTimeConverter.setPattern(PATTERN_DATE);
+ } else if (estimatedType.isAssignableFrom(Calendar.class)) {
+ dateTimeConverter.setType("date");
+ dateTimeConverter.setPattern(PATTERN_DATE);
+ } else if (estimatedType.isAssignableFrom(Number.class)) {
+ LOG.error("date");
+ dateTimeConverter.setType("date");
+ dateTimeConverter.setPattern(PATTERN_DATE);
+// } else if (estimatedType.isAssignableFrom(Month.class)) {
+// LOG.error("month");
+// dateTimeConverter.setType("month"); // XXX is there a type month?
+// } else if (estimatedType.isAssignableFrom(Week.class)) {
+// LOG.error("week");
+// dateTimeConverter.setType("week"); // XXX is there a type week?
+ // todo: ZonedDateTime
+ } else {
+ LOG.warn("Type might not be supported (type='{}' clientId='{}')!",
+ estimatedType.getName(), component.getClientId(facesContext));
+ throw new RuntimeException();
+ }
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("type='{}' pattern='{}'", dateTimeConverter.getType(), dateTimeConverter.getPattern());
+ }
+
+ return dateTimeConverter;
+ }
+ }
+ }
+
+ private Class<?> estimateValueType(final FacesContext facesContext, final T component) {
+ Class<?> estimatedType = null;
+ final ValueExpression valueExpression = component.getValueExpression("value");
+ if (valueExpression != null) {
+ try {
+ estimatedType = valueExpression.getType(facesContext.getELContext());
+ } catch (final Exception e) {
+ // ignore, seems not to be possible, when EL is a function like #{bean.getName(item.id)}
+ }
+ }
+ if (estimatedType == null) {
+ Object submittedValue = component.getSubmittedValue();
+ if (submittedValue != null) {
+ estimatedType = submittedValue.getClass();
+ }
+ }
+ if (estimatedType == null) {
+ Object value = component.getValue();
+ if (value != null) {
+ estimatedType = value.getClass();
+ }
+ }
+ return estimatedType;
+ }
+
+ private void prepare(final FacesContext facesContext, final T component) {
+ HtmlInputTypes type = component.getType();
+ String pattern = component.getPattern();
+ if (type == null || pattern == null) {
+ Converter converter = getConverter(facesContext, component, component.getSubmittedValue()); // XXX submitted?
+ if (converter instanceof DateTimeConverter) {
+ DateTimeConverter dtConverter = (DateTimeConverter) converter;
+ String t = dtConverter.getType();
+ if (TYPE_DATE.equals(t)) {
+ type = HtmlInputTypes.DATE;
+ } else if (TYPE_BOTH.equals(t)) {
+ type = HtmlInputTypes.DATETIME_LOCAL;
+ } else if (TYPE_TIME.equals(t)) {
+ type = HtmlInputTypes.TIME;
+ } else if (TYPE_LOCAL_DATE.equals(t)) {
+ type = HtmlInputTypes.DATE;
+ } else if (TYPE_LOCAL_DATE_TIME.equals(t)) {
+ type = HtmlInputTypes.DATETIME_LOCAL;
+ } else if (TYPE_LOCAL_TIME.equals(t)) {
+ type = HtmlInputTypes.TIME;
+ } else if (TYPE_ZONED_DATE_TIME.equals(t)) {
+ type = HtmlInputTypes.DATETIME_LOCAL;
+ } else if (TYPE_OFFSET_DATE_TIME.equals(t)) {
+ type = HtmlInputTypes.DATETIME_LOCAL;
+ } else if (TYPE_OFFSET_TIME.equals(t)) {
+ type = HtmlInputTypes.TIME;
+ } else if (TYPE_MONTH.equals(t)) {
+ type = HtmlInputTypes.MONTH;
+ } else if (TYPE_WEEK.equals(t)) {
+ type = HtmlInputTypes.WEEK;
+ } else {
+ type = HtmlInputTypes.TEXT;
+ }
+ pattern = dtConverter.getPattern();
+ if (pattern == null) {
+ if (TYPE_DATE.equals(t)) {
+ pattern = PATTERN_DATE;
+ } else if (TYPE_BOTH.equals(t)) {
+ pattern = PATTERN_DATETIME;
+ } else if (TYPE_TIME.equals(t)) {
+ pattern = PATTERN_TIME;
+ } else if (TYPE_LOCAL_DATE.equals(t)) {
+ pattern = PATTERN_DATE;
+ } else if (TYPE_LOCAL_DATE_TIME.equals(t)) {
+ pattern = PATTERN_DATETIME;
+ } else if (TYPE_LOCAL_TIME.equals(t)) {
+ pattern = PATTERN_TIME;
+ } else if (TYPE_ZONED_DATE_TIME.equals(t)) {
+ pattern = PATTERN_DATETIME;
+ } else if (TYPE_OFFSET_DATE_TIME.equals(t)) {
+ pattern = PATTERN_DATETIME;
+ } else if (TYPE_OFFSET_TIME.equals(t)) {
+ pattern = PATTERN_TIME;
+ } else if (TYPE_MONTH.equals(t)) {
+ pattern = PATTERN_MONTH;
+ } else if (TYPE_WEEK.equals(t)) {
+ pattern = PATTERN_WEEK;
+ } else {
+ pattern = DateFormatUtils.findPattern(dtConverter);
+ }
+ } else {
+ pattern = PATTERN_DATETIME;
+ }
+ } else {
+ type = HtmlInputTypes.TEXT;
+ }
+ }
+ component.setPattern(pattern);
+ component.setType(type);
+ }
+
}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java
index 31d083e..e2ea371 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java
@@ -79,7 +79,7 @@ public class InRenderer<T extends AbstractUIIn> extends MessageLayoutRendererBas
throws IOException {
final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, component);
final String currentValue = getCurrentValue(facesContext, component);
- final boolean password = ComponentUtils.getBooleanAttribute(component, Attributes.password);
+ final boolean password = component.isPassword();
if (LOG.isDebugEnabled()) {
LOG.debug("currentValue = '{}'", StringUtils.toConfidentialString(currentValue, password));
}
@@ -117,9 +117,7 @@ public class InRenderer<T extends AbstractUIIn> extends MessageLayoutRendererBas
if (currentValue != null && !password) {
writer.writeAttribute(HtmlAttributes.VALUE, currentValue, true);
}
- if (title != null) {
- writer.writeAttribute(HtmlAttributes.TITLE, title, true);
- }
+ writer.writeAttribute(HtmlAttributes.TITLE, title, true);
int maxLength = 0;
int minLength = 0;
String pattern = null;
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/ConvertDateTimeTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/ConvertDateTimeTagDeclaration.java
index f3b21cc..0647a11 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/ConvertDateTimeTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/ConvertDateTimeTagDeclaration.java
@@ -31,7 +31,10 @@ import javax.el.ValueExpression;
/**
* Register a DateTimeConverter instance on the UIComponent associated with the closest parent UIComponent custom
* action.
+ *
+ * @deprecated Since 5.0.0. Should work with <f:convertDateTime> since JSF 2.3.
*/
+@Deprecated
@Tag(name = "convertDateTime")
@ConverterTag(
converterId = DateTimeConverter.CONVERTER_ID,
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/DateTagDeclaration.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/DateTagDeclaration.java
index da18c25..79e9c27 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/DateTagDeclaration.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/taglib/component/DateTagDeclaration.java
@@ -86,6 +86,7 @@ public interface DateTagDeclaration
/**
* If true, a today button is displayed on the datetimepicker.
*/
+ // todo: might be deprecated
@TagAttribute
@UIComponentTagAttribute(type = "boolean", defaultValue = "false")
void setTodayButton(String required);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/DateFormatUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/DateFormatUtils.java
index 5dd7933..9f9185d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/DateFormatUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/util/DateFormatUtils.java
@@ -118,69 +118,129 @@ public final class DateFormatUtils {
* and convert it to 'vanillajs-datepicker', see https://mymth.github.io/vanillajs-datepicker/#/date-string+format
* Attention: Not every pattern char is supported.
*/
- public static String toJavaScriptPattern(final String originalPattern) {
- String pattern;
- if (originalPattern == null || originalPattern.length() > 100) {
- LOG.warn("Pattern not supported: " + originalPattern);
- pattern = "";
- } else {
- pattern = originalPattern;
- }
-
- StringBuilder analyzedPattern = new StringBuilder();
- StringBuilder nextSegment = new StringBuilder();
- boolean escMode = false;
- for (int i = 0; i < pattern.length(); i++) {
- final char currentChar = pattern.charAt(i);
- if (currentChar == '\'' && !escMode) {
- escMode = true;
- analyzedPattern.append(toJavaScriptPatternPart(nextSegment.toString()));
- nextSegment = new StringBuilder();
- } else if (currentChar == '\'' && pattern.charAt(i + 1) == '\'') {
- nextSegment.append("\\");
- nextSegment.append("'");
- i++;
- } else if (currentChar == '\'') {
- escMode = false;
- analyzedPattern.append(nextSegment);
- nextSegment = new StringBuilder();
+ public static class DateTimeJavaScriptPattern {
+
+ private StringBuilder buffer = new StringBuilder();
+ private StringBuilder datePattern;
+ private StringBuilder timePattern;
+ private String separator;
+
+ private boolean dateMode = false;
+ private boolean timeMode = false;
+
+ private int lastDateIndex = -1;
+ private int lastTimeIndex = -1;
+
+ public DateTimeJavaScriptPattern(final String javaPattern) {
+ String pattern;
+ if (javaPattern == null) {
+ LOG.warn("Empty pattern not supported!");
+ // XXX an missing pattern might be supporable without Datepicker, but with simple <tc:in>
+ pattern = "";
+ } else if (javaPattern.length() > 100) {
+ LOG.warn("Pattern too long (max = 100): '{}'!", javaPattern);
+ pattern = "";
+ } else if (javaPattern.indexOf('\'') > -1) {
+ LOG.warn("Pattern escape char ' is not supported: '{}'!", javaPattern);
+ pattern = "";
+ } else if (StringUtils.containsAny(javaPattern, "GWFKzX")) {
+ LOG.warn("Pattern chars 'G', 'W', 'F', 'K', 'z' and 'X' are not supported: '{}'!", javaPattern);
+ pattern = "";
} else {
- if (escMode) {
- nextSegment.append("\\");
- }
- nextSegment.append(currentChar);
+ pattern = javaPattern;
+ }
+
+ for (int i = 0; i < pattern.length(); i++) {
+ final char c = pattern.charAt(i);
+ checkSpit(i, c);
+ append(c);
}
}
- if (nextSegment.toString().length() > 0) {
- if (escMode) {
- analyzedPattern.append(nextSegment);
+
+ private void checkSpit(final int index, final char c) {
+
+ boolean isDate = isDate(c);
+ boolean isTime = isTime(c);
+
+ if (isDate){
+ lastDateIndex = index;
+ }
+ if (isTime){
+ lastTimeIndex = index;
+ }
+
+ if (!dateMode && !timeMode && isDate) {
+ dateMode = true;
+ datePattern = new StringBuilder(buffer);
+ buffer.setLength(0);
+ } else if (!dateMode && !timeMode && isTime) {
+ timeMode = true;
+ timePattern = new StringBuilder(buffer);
+ buffer.setLength(0);
+ } else if (!dateMode && !timeMode) {
+ LOG.warn("Prefix without date/time char currently not supported!");
+ } else if (dateMode && isTime) {
+ timeMode = true;
+ dateMode = false;
+ timePattern = new StringBuilder();
+ separator = datePattern.substring(lastDateIndex + 1);
+ datePattern.setLength(lastDateIndex + 1);
+ if (timePattern != null) {
+ LOG.warn("Pattern mixed!");
+ }
+ } else if (timeMode && isDate) {
+ timeMode = false;
+ dateMode = true;
+ datePattern = new StringBuilder();
+ separator = timePattern.substring(lastTimeIndex + 1);
+ timePattern.setLength(lastTimeIndex + 1);
+ if (datePattern != null) {
+ LOG.warn("Pattern mixed!");
+ }
} else {
- analyzedPattern.append(toJavaScriptPatternPart(nextSegment.toString()));
+ // nothing to switch
}
}
- return analyzedPattern.toString();
- }
+ private void append(final char c) {
+ final char converted = convert(c);
- public static String toJavaScriptPatternPart(String originalPattern) {
+ if (dateMode) {
+ datePattern.append(converted);
+ } else if (timeMode) {
+ timePattern.append(converted);
+ } else {
+ buffer.append(converted);
+ }
+ }
- String pattern = originalPattern;
+ public String getDatePattern() {
+ return datePattern != null ? datePattern.toString() : null;
+ }
- if (pattern.contains("G") || pattern.contains("W") || pattern.contains("F")
- || pattern.contains("K") || pattern.contains("z") || pattern.contains("X")) {
- LOG.warn("Pattern chars 'G', 'W', 'F', 'K', 'z' and 'X' are not supported: " + pattern);
- pattern = "";
+ public String getTimePattern() {
+ return timePattern != null ? timePattern.toString() : null;
}
- if (pattern.contains("M")) {
- pattern = pattern.replaceAll("M", "m");
+ public String getSeparator() {
+ return separator;
}
- if (pattern.contains("d")) {
- pattern = pattern.replaceAll("dd+", "dd");
+ public static boolean isDate(final char c) {
+ return c == 'y' || c == 'Y' || c == 'M' || c == 'L' || c == 'd';
}
- return pattern;
- }
+ private static boolean isTime(final char c) {
+ return c == 'H' || c == 'm' || c == 's';
+ }
+
+ private char convert(char c) {
+ if (c == 'M') {
+ return 'm';
+ } else {
+ return c;
+ }
+ }
+ }
}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/RendererBase.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/RendererBase.java
index 9b5cfb8..929812e 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/RendererBase.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/RendererBase.java
@@ -421,7 +421,7 @@ public abstract class RendererBase<T extends UIComponent> extends Renderer {
if (itemValue instanceof String && values != null && values.length > 0 && !(values[0] instanceof String)) {
itemValue = ComponentUtils.getConvertedValue(facesContext, component, (String) itemValue);
}
- final String formattedValue = getFormattedValue(facesContext, component, itemValue);
+ final String formattedValue = getFormattedValue(facesContext, (T) component, itemValue);
final boolean contains;
if (submittedValues == null) {
contains = ArrayUtils.contains(values, itemValue);
@@ -473,7 +473,7 @@ public abstract class RendererBase<T extends UIComponent> extends Renderer {
}
protected String getFormattedValue(
- final FacesContext facesContext, final UIComponent component, final Object currentValue)
+ final FacesContext facesContext, final T component, final Object currentValue)
throws ConverterException {
if (currentValue == null) {
@@ -492,7 +492,7 @@ public abstract class RendererBase<T extends UIComponent> extends Renderer {
* May return null, if no converter can be find.
*/
protected Converter getConverter(
- final FacesContext facesContext, final UIComponent component, final Object value) {
+ final FacesContext facesContext, final T component, final Object value) {
Converter converter = null;
if (component instanceof ValueHolder) {
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java
index fa33082..55682a6 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/CustomAttributes.java
@@ -25,6 +25,7 @@ public enum CustomAttributes implements MarkupLanguageAttributes {
COLLAPSE_TARGET("collapse-target"),
CONFIRMATION("confirmation"),
CLIENT_ID("client-id"),
+ DATE_PATTERN("date-pattern"),
DELAY("delay"),
EVENT("event"),
/**
@@ -84,6 +85,7 @@ public enum CustomAttributes implements MarkupLanguageAttributes {
* The date of today
*/
// TODAY("today"),
+ TIME_PATTERN("time-pattern"),
/**
* Show the button to set the field to today.
*/
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlInputTypes.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlInputTypes.java
index f4325e8..a86a01d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlInputTypes.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/renderkit/html/HtmlInputTypes.java
@@ -31,7 +31,47 @@ public enum HtmlInputTypes implements HtmlTypes {
FILE("file"),
HIDDEN("hidden"),
IMAGE("image"),
- BUTTON("button");
+ BUTTON("button"),
+ COLOR("color"),
+ DATE("date"),
+ /** @deprecated */
+ @Deprecated
+ DATETIME("datetime"),
+ DATETIME_LOCAL("datetime-local"),
+ EMAIL("email"),
+ MONTH("month"),
+ NUMBER("number"),
+ SEARCH("search"),
+ TEL("tel"),
+ TIME("time"),
+ URL("url"),
+ WEEK("week");
+
+ public static final String STRING_TEXT = "text";
+ public static final String STRING_PASSWORD = "password";
+ public static final String STRING_CHECKBOX = "checkbox";
+ public static final String STRING_RADIO = "radio";
+ public static final String STRING_RANGE = "range";
+ public static final String STRING_SUBMIT = "submit";
+ public static final String STRING_RESET = "reset";
+ public static final String STRING_FILE = "file";
+ public static final String STRING_HIDDEN = "hidden";
+ public static final String STRING_IMAGE = "image";
+ public static final String STRING_BUTTON = "button";
+ public static final String STRING_COLOR = "color";
+ public static final String STRING_DATE = "date";
+ /** @deprecated */
+ @Deprecated
+ public static final String STRING_DATETIME = "datetime";
+ public static final String STRING_DATETIME_LOCAL = "datetime-local";
+ public static final String STRING_EMAIL = "email";
+ public static final String STRING_MONTH = "month";
+ public static final String STRING_NUMBER = "number";
+ public static final String STRING_SEARCH = "search";
+ public static final String STRING_TEL = "tel";
+ public static final String STRING_TIME = "time";
+ public static final String STRING_URL = "url";
+ public static final String STRING_WEEK = "week";
private final String value;
@@ -43,4 +83,13 @@ public enum HtmlInputTypes implements HtmlTypes {
public String getValue() {
return value;
}
+
+ public final boolean supportsDate() {
+ return this == DATE || this == DATETIME_LOCAL || this == WEEK || this == MONTH;
+ }
+
+ public final boolean supportsTime() {
+ return this == TIME || this == DATETIME_LOCAL;
+ }
+
}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java
index 8fc4320..7bbe857 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/util/ComponentUtils.java
@@ -845,7 +845,10 @@ public final class ComponentUtils {
/**
* May return null, if no converter can be find.
+ *
+ * @deprecated since 5.0.0. Please use {@link RendererBase#getConverter}.
*/
+ @Deprecated
public static Converter getConverter(
final FacesContext facesContext, final UIComponent component, final Object value) {
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
index 15cb79f..6a27f5e 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
@@ -93,11 +93,13 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import javax.faces.component.behavior.AjaxBehavior;
+import javax.faces.convert.DateTimeConverter;
import javax.faces.render.RenderKit;
import javax.servlet.ServletContext;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
+import java.util.Date;
import java.util.Locale;
import static org.apache.myfaces.tobago.config.TobagoConfig.TOBAGO_CONFIG;
@@ -176,6 +178,9 @@ public abstract class AbstractTobagoTestBase extends AbstractJsfTestCase {
application.addBehavior(AjaxBehavior.BEHAVIOR_ID, AjaxBehavior.class.getName());
application.addBehavior(EventBehavior.BEHAVIOR_ID, EventBehavior.class.getName());
+ application.addConverter(Date.class, DateTimeConverter.class.getName());
+ application.addConverter("javax.faces.DateTime", DateTimeConverter.class.getName());
+
final RenderKit renderKit = facesContext.getRenderKit();
renderKit.addRenderer(UIBadge.COMPONENT_FAMILY, RendererTypes.BADGE, new BadgeRenderer());
renderKit.addRenderer(UIBox.COMPONENT_FAMILY, RendererTypes.BOX, new BoxRenderer());
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRendererUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRendererUnitTest.java
index 7b4d960..c72c948 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRendererUnitTest.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/renderkit/renderer/DateRendererUnitTest.java
@@ -22,64 +22,206 @@ package org.apache.myfaces.tobago.internal.renderkit.renderer;
import org.apache.myfaces.tobago.component.RendererTypes;
import org.apache.myfaces.tobago.component.Tags;
import org.apache.myfaces.tobago.component.UIDate;
-import org.apache.myfaces.tobago.convert.DateTimeConverter;
import org.apache.myfaces.tobago.util.ComponentUtils;
import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import javax.faces.convert.Converter;
+import javax.faces.convert.DateTimeConverter;
import java.io.IOException;
+import java.lang.invoke.MethodHandles;
import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.TimeZone;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.Date;
public class DateRendererUnitTest extends RendererTestBase {
+ private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private static final LocalDateTime SPUTNIK_LOCAL_DATE_TIME = LocalDateTime.of(1957, 10, 5, 0, 28, 34, 123456789);
+ private static final LocalDate SPUTNIK_LOCAL_DATE = SPUTNIK_LOCAL_DATE_TIME.toLocalDate();
+ private static final LocalTime SPUTNIK_LOCAL_TIME = SPUTNIK_LOCAL_DATE_TIME.toLocalTime();
+ private static final ZonedDateTime SPUTNIK_ZONED_DATE_TIME = SPUTNIK_LOCAL_DATE_TIME.atZone(ZoneId.of("+05:00"));
+ private static final Date SPUTNIK_DATE = Date.from(SPUTNIK_ZONED_DATE_TIME.toInstant());
+
+// Naming scheme: value-type + converter-type
+
@Test
- public void date() throws IOException {
+ public void dateBoth() throws IOException {
final UIDate d = (UIDate) ComponentUtils.createComponent(
facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+ d.setValue(SPUTNIK_DATE);
+
DateTimeConverter c = new DateTimeConverter();
- c.setPattern("dd.MM.yyyy");
+ c.setType("both");
+ c.setPattern("yyyy-MM-dd'T'HH:mm:ss");
d.setConverter(c);
+ log(d);
d.encodeAll(facesContext);
- Assertions.assertEquals(loadHtml("renderer/date/date.html"), formattedResult());
+ Assertions.assertEquals(loadHtml("renderer/date/dateBoth.html"), formattedResult());
}
@Test
- public void dateLabel() throws IOException, ParseException {
+ public void dateDate() throws IOException {
final UIDate d = (UIDate) ComponentUtils.createComponent(
facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
- d.setLabel("Label");
+ d.setValue(SPUTNIK_DATE);
+
DateTimeConverter c = new DateTimeConverter();
- c.setPattern("dd.MM.yyyy");
+ c.setType("date");
+ c.setPattern("yyyy-MM-dd");
d.setConverter(c);
- final SimpleDateFormat sdf = new SimpleDateFormat(c.getPattern());
- sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
- d.setValue(sdf.parse("10.12.2020"));
+ log(d);
d.encodeAll(facesContext);
- Assertions.assertEquals(loadHtml("renderer/date/date-label.html"), formattedResult());
+ Assertions.assertEquals(loadHtml("renderer/date/dateDate.html"), formattedResult());
}
@Test
- public void dateTodayButton() throws IOException {
+ public void dateAuto() throws IOException {
final UIDate d = (UIDate) ComponentUtils.createComponent(
facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
- d.setLabel("Label");
- d.setTodayButton(true);
+ d.setValue(SPUTNIK_DATE);
+
+ log(d);
+ d.encodeAll(facesContext);
+
+ Assertions.assertEquals(loadHtml("renderer/date/dateAuto.html"), formattedResult());
+ }
+
+ @Test
+ public void dateTime() throws IOException {
+
+ final UIDate d = (UIDate) ComponentUtils.createComponent(
+ facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+ d.setValue(SPUTNIK_DATE);
+
DateTimeConverter c = new DateTimeConverter();
- c.setPattern("dd.MM.yyyy");
+ c.setType("time");
+ c.setPattern("HH:mm:ss");
d.setConverter(c);
+ log(d);
+ d.encodeAll(facesContext);
+
+ Assertions.assertEquals(loadHtml("renderer/date/dateTime.html"), formattedResult());
+ }
+
+ @Test
+ public void testLabel() throws IOException, ParseException {
+
+ final UIDate d = (UIDate) ComponentUtils.createComponent(
+ facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+ d.setLabel("Label");
+ d.setValue(SPUTNIK_LOCAL_DATE);
+
+ log(d);
+ d.encodeAll(facesContext);
+
+ Assertions.assertEquals(loadHtml("renderer/date/testLabel.html"), formattedResult());
+ }
+
+ @Test
+ public void localDateAuto() throws IOException {
+
+ final UIDate d = (UIDate) ComponentUtils.createComponent(
+ facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+ d.setValue(SPUTNIK_LOCAL_DATE);
+
d.encodeAll(facesContext);
- Assertions.assertEquals(loadHtml("renderer/date/date-today-button.html"), formattedResult());
+ Assertions.assertEquals(loadHtml("renderer/date/localDateAuto.html"), formattedResult());
+ }
+
+ @Test
+ public void localDateTimeAuto() throws IOException {
+
+ final UIDate d = (UIDate) ComponentUtils.createComponent(
+ facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+ d.setValue(SPUTNIK_LOCAL_DATE_TIME);
+
+ d.encodeAll(facesContext);
+
+ Assertions.assertEquals(loadHtml("renderer/date/localDateTimeAuto.html"), formattedResult());
+ }
+
+ // old
+
+ @Test
+ public void zonedDateTimeAuto() throws IOException {
+
+ final UIDate d = (UIDate) ComponentUtils.createComponent(
+ facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+ d.setValue(SPUTNIK_ZONED_DATE_TIME);
+
+ d.encodeAll(facesContext);
+
+ Assertions.assertEquals(loadHtml("renderer/date/zonedDateTimeAuto.html"), formattedResult());
+ }
+
+ // todo: might be removed
+ @Test
+ public void testTodayButton() throws IOException {
+
+ final UIDate d = (UIDate) ComponentUtils.createComponent(
+ facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+ d.setLabel("Label");
+ d.setValue(SPUTNIK_LOCAL_DATE);
+ d.setTodayButton(true);
+
+ d.encodeAll(facesContext);
+
+ Assertions.assertEquals(loadHtml("renderer/date/testTodayButton.html"), formattedResult());
+ }
+
+ @Test
+ @Disabled
+ public void text() throws IOException {
+
+ final UIDate d = (UIDate) ComponentUtils.createComponent(
+ facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+ d.setValue(SPUTNIK_LOCAL_DATE_TIME);
+
+ d.encodeAll(facesContext);
+
+ Assertions.assertEquals(loadHtml("renderer/date/text.html"), formattedResult());
+ }
+
+ @Test
+ public void localTimeAuto() throws IOException {
+
+ final UIDate d = (UIDate) ComponentUtils.createComponent(
+ facesContext, Tags.date.componentType(), RendererTypes.Date, "id");
+ d.setValue(SPUTNIK_LOCAL_TIME);
+
+ d.encodeAll(facesContext);
+
+ Assertions.assertEquals(loadHtml("renderer/date/localTimeAuto.html"), formattedResult());
+ }
+
+ private void log(UIDate d) {
+ Converter<?> converter = d.getConverter();
+ String pattern = converter instanceof DateTimeConverter ? ((DateTimeConverter) converter).getPattern() : "-";
+ String type = converter instanceof DateTimeConverter ? ((DateTimeConverter) converter).getType() : "-";
+ LOG.info(
+ "type-of-value='{}' with converter='{}', pattern='{}', type='{}'",
+ d.getValue().getClass().getName(),
+ converter != null ? converter.getClass().getName() : "-",
+ pattern,
+ type);
}
}
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/DateFormatUtilsUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/DateFormatUtilsUnitTest.java
index e323434..81e6b6b 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/DateFormatUtilsUnitTest.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/util/DateFormatUtilsUnitTest.java
@@ -26,10 +26,72 @@ import org.junit.jupiter.api.Test;
public class DateFormatUtilsUnitTest extends AbstractTobagoTestBase {
@Test
- public void simple() {
+ public void date() {
final String p1 = "yyyy-MM-dd";
- final String js1 = DateFormatUtils.toJavaScriptPattern(DateFormatUtils.toJavaScriptPattern(p1));
+ final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
- Assertions.assertEquals("yyyy-mm-dd", js1);
+ Assertions.assertEquals("yyyy-mm-dd", js1.getDatePattern());
+ Assertions.assertNull(js1.getTimePattern());
+ Assertions.assertNull(js1.getSeparator());
+ }
+
+ @Test
+ public void time() {
+ final String p1 = "HH:mm"; // hour 0-23 : minute 0-59
+ final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+ Assertions.assertNull(js1.getDatePattern());
+ Assertions.assertEquals("HH:mm", js1.getTimePattern());
+ Assertions.assertNull(js1.getSeparator());
+ }
+
+ @Test
+ public void bothDateTime() {
+ final String p1 = "yyyy-MM-dd HH:mm";
+ final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+ Assertions.assertEquals("yyyy-mm-dd", js1.getDatePattern());
+ Assertions.assertEquals("HH:mm", js1.getTimePattern());
+ Assertions.assertEquals(" ", js1.getSeparator());
+ }
+
+ @Test
+ public void bothTimeDate() {
+ final String p1 = "HH:mm yyyy-MM-dd";
+ final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+ Assertions.assertEquals("yyyy-mm-dd", js1.getDatePattern());
+ Assertions.assertEquals("HH:mm", js1.getTimePattern());
+ Assertions.assertEquals(" ", js1.getSeparator());
+ }
+
+ @Test
+ public void both1() {
+ final String p1 = "HH----yyyy";
+ final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+ Assertions.assertEquals("yyyy", js1.getDatePattern());
+ Assertions.assertEquals("HH", js1.getTimePattern());
+ Assertions.assertEquals("----", js1.getSeparator());
+ }
+
+ @Test
+ public void both2() {
+ final String p1 = "::yy--ss::";
+ final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+ Assertions.assertEquals("::yy", js1.getDatePattern());
+ Assertions.assertEquals("ss::", js1.getTimePattern());
+ Assertions.assertEquals("--", js1.getSeparator());
+ }
+
+ @Test
+ public void both3() {
+ final String p1 = "::MMmm::";
+ final DateFormatUtils.DateTimeJavaScriptPattern js1 = new DateFormatUtils.DateTimeJavaScriptPattern(p1);
+
+ Assertions.assertEquals("::mm", js1.getDatePattern());
+ Assertions.assertEquals("mm::", js1.getTimePattern());
+ Assertions.assertEquals("", js1.getSeparator());
}
}
diff --git a/tobago-core/src/test/resources/renderer/date/date.html b/tobago-core/src/test/resources/renderer/date/dateAuto.html
similarity index 61%
copy from tobago-core/src/test/resources/renderer/date/date.html
copy to tobago-core/src/test/resources/renderer/date/dateAuto.html
index 1c80ca2..42e10ab 100644
--- a/tobago-core/src/test/resources/renderer/date/date.html
+++ b/tobago-core/src/test/resources/renderer/date/dateAuto.html
@@ -15,10 +15,10 @@
* limitations under the License.
-->
-<tobago-date id='id' class='tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"mi [...]
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
<div class='tobago-input-group-outer'>
<div class='input-group'>
- <input type='text' name='id' id='id::field' class='form-control'>
+ <input type='date' name='id' id='id::field' value='1957-10-04' class='form-control'>
<button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
</div>
</div>
diff --git a/tobago-core/src/test/resources/renderer/date/date-label.html b/tobago-core/src/test/resources/renderer/date/dateBoth.html
similarity index 56%
copy from tobago-core/src/test/resources/renderer/date/date-label.html
copy to tobago-core/src/test/resources/renderer/date/dateBoth.html
index 56531f5..d03ddcc 100644
--- a/tobago-core/src/test/resources/renderer/date/date-label.html
+++ b/tobago-core/src/test/resources/renderer/date/dateBoth.html
@@ -15,12 +15,12 @@
* limitations under the License.
-->
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
- <label for='id::field' class='col-form-label'>Label</label>
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
<div class='tobago-input-group-outer'>
<div class='input-group'>
- <input type='text' name='id' id='id::field' value='10.12.2020' class='form-control'>
+ <input type='datetime-local' name='id' id='id::field' value='1957-10-04T19:28:34' class='form-control'>
<button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+ <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
</div>
</div>
</tobago-date>
\ No newline at end of file
diff --git a/tobago-core/src/test/resources/renderer/date/date.html b/tobago-core/src/test/resources/renderer/date/dateDate.html
similarity index 61%
copy from tobago-core/src/test/resources/renderer/date/date.html
copy to tobago-core/src/test/resources/renderer/date/dateDate.html
index 1c80ca2..42e10ab 100644
--- a/tobago-core/src/test/resources/renderer/date/date.html
+++ b/tobago-core/src/test/resources/renderer/date/dateDate.html
@@ -15,10 +15,10 @@
* limitations under the License.
-->
-<tobago-date id='id' class='tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"mi [...]
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
<div class='tobago-input-group-outer'>
<div class='input-group'>
- <input type='text' name='id' id='id::field' class='form-control'>
+ <input type='date' name='id' id='id::field' value='1957-10-04' class='form-control'>
<button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
</div>
</div>
diff --git a/tobago-core/src/test/resources/renderer/date/date.html b/tobago-core/src/test/resources/renderer/date/dateTime.html
similarity index 57%
copy from tobago-core/src/test/resources/renderer/date/date.html
copy to tobago-core/src/test/resources/renderer/date/dateTime.html
index 1c80ca2..b78b348 100644
--- a/tobago-core/src/test/resources/renderer/date/date.html
+++ b/tobago-core/src/test/resources/renderer/date/dateTime.html
@@ -15,11 +15,11 @@
* limitations under the License.
-->
-<tobago-date id='id' class='tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"mi [...]
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
<div class='tobago-input-group-outer'>
<div class='input-group'>
- <input type='text' name='id' id='id::field' class='form-control'>
- <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+ <input type='time' name='id' id='id::field' value='19:28:34' class='form-control'>
+ <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
</div>
</div>
</tobago-date>
\ No newline at end of file
diff --git a/tobago-core/src/test/resources/renderer/date/date.html b/tobago-core/src/test/resources/renderer/date/localDateAuto.html
similarity index 61%
copy from tobago-core/src/test/resources/renderer/date/date.html
copy to tobago-core/src/test/resources/renderer/date/localDateAuto.html
index 1c80ca2..03f7296 100644
--- a/tobago-core/src/test/resources/renderer/date/date.html
+++ b/tobago-core/src/test/resources/renderer/date/localDateAuto.html
@@ -15,10 +15,10 @@
* limitations under the License.
-->
-<tobago-date id='id' class='tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"mi [...]
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
<div class='tobago-input-group-outer'>
<div class='input-group'>
- <input type='text' name='id' id='id::field' class='form-control'>
+ <input type='date' name='id' id='id::field' value='1957-10-05' class='form-control'>
<button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
</div>
</div>
diff --git a/tobago-core/src/test/resources/renderer/date/date-label.html b/tobago-core/src/test/resources/renderer/date/localDateTimeAuto.html
similarity index 56%
copy from tobago-core/src/test/resources/renderer/date/date-label.html
copy to tobago-core/src/test/resources/renderer/date/localDateTimeAuto.html
index 56531f5..1aa041b 100644
--- a/tobago-core/src/test/resources/renderer/date/date-label.html
+++ b/tobago-core/src/test/resources/renderer/date/localDateTimeAuto.html
@@ -15,12 +15,12 @@
* limitations under the License.
-->
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
- <label for='id::field' class='col-form-label'>Label</label>
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
<div class='tobago-input-group-outer'>
<div class='input-group'>
- <input type='text' name='id' id='id::field' value='10.12.2020' class='form-control'>
+ <input type='datetime-local' name='id' id='id::field' value='1957-10-05T00:28:34.123' class='form-control'>
<button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+ <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
</div>
</div>
</tobago-date>
\ No newline at end of file
diff --git a/tobago-core/src/test/resources/renderer/date/date.html b/tobago-core/src/test/resources/renderer/date/localTimeAuto.html
similarity index 57%
rename from tobago-core/src/test/resources/renderer/date/date.html
rename to tobago-core/src/test/resources/renderer/date/localTimeAuto.html
index 1c80ca2..fca219c 100644
--- a/tobago-core/src/test/resources/renderer/date/date.html
+++ b/tobago-core/src/test/resources/renderer/date/localTimeAuto.html
@@ -15,11 +15,11 @@
* limitations under the License.
-->
-<tobago-date id='id' class='tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"mi [...]
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
<div class='tobago-input-group-outer'>
<div class='input-group'>
- <input type='text' name='id' id='id::field' class='form-control'>
- <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+ <input type='time' name='id' id='id::field' value='00:28:34.123' class='form-control'>
+ <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
</div>
</div>
</tobago-date>
\ No newline at end of file
diff --git a/tobago-core/src/test/resources/renderer/date/date-label.html b/tobago-core/src/test/resources/renderer/date/testLabel.html
similarity index 64%
copy from tobago-core/src/test/resources/renderer/date/date-label.html
copy to tobago-core/src/test/resources/renderer/date/testLabel.html
index 56531f5..78d7767 100644
--- a/tobago-core/src/test/resources/renderer/date/date-label.html
+++ b/tobago-core/src/test/resources/renderer/date/testLabel.html
@@ -15,11 +15,11 @@
* limitations under the License.
-->
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
+<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0," [...]
<label for='id::field' class='col-form-label'>Label</label>
<div class='tobago-input-group-outer'>
<div class='input-group'>
- <input type='text' name='id' id='id::field' value='10.12.2020' class='form-control'>
+ <input type='date' name='id' id='id::field' value='1957-10-05' class='form-control'>
<button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
</div>
</div>
diff --git a/tobago-core/src/test/resources/renderer/date/date-today-button.html b/tobago-core/src/test/resources/renderer/date/testTodayButton.html
similarity index 64%
copy from tobago-core/src/test/resources/renderer/date/date-today-button.html
copy to tobago-core/src/test/resources/renderer/date/testTodayButton.html
index 2f3b21d..508299c 100644
--- a/tobago-core/src/test/resources/renderer/date/date-today-button.html
+++ b/tobago-core/src/test/resources/renderer/date/testTodayButton.html
@@ -15,11 +15,11 @@
* limitations under the License.
-->
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
+<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0," [...]
<label for='id::field' class='col-form-label'>Label</label>
<div class='tobago-input-group-outer'>
<div class='input-group'>
- <input type='text' name='id' id='id::field' class='form-control'>
+ <input type='date' name='id' id='id::field' value='1957-10-05' class='form-control'>
<button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
</div>
</div>
diff --git a/tobago-core/src/test/resources/renderer/date/date-today-button.html b/tobago-core/src/test/resources/renderer/date/text.html
similarity index 59%
rename from tobago-core/src/test/resources/renderer/date/date-today-button.html
rename to tobago-core/src/test/resources/renderer/date/text.html
index 2f3b21d..4a57a46 100644
--- a/tobago-core/src/test/resources/renderer/date/date-today-button.html
+++ b/tobago-core/src/test/resources/renderer/date/text.html
@@ -15,12 +15,12 @@
* limitations under the License.
-->
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
- <label for='id::field' class='col-form-label'>Label</label>
+<tobago-date id='id' class='tobago-auto-spacing' date-pattern='dd.mm.yyyy' time-pattern='HH:mm' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fr [...]
<div class='tobago-input-group-outer'>
<div class='input-group'>
<input type='text' name='id' id='id::field' class='form-control'>
<button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+ <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
</div>
</div>
</tobago-date>
\ No newline at end of file
diff --git a/tobago-core/src/test/resources/renderer/date/date-label.html b/tobago-core/src/test/resources/renderer/date/zonedDateTimeAuto.html
similarity index 56%
rename from tobago-core/src/test/resources/renderer/date/date-label.html
rename to tobago-core/src/test/resources/renderer/date/zonedDateTimeAuto.html
index 56531f5..1aa041b 100644
--- a/tobago-core/src/test/resources/renderer/date/date-label.html
+++ b/tobago-core/src/test/resources/renderer/date/zonedDateTimeAuto.html
@@ -15,12 +15,12 @@
* limitations under the License.
-->
-<tobago-date id='id' class='tobago-label-container tobago-auto-spacing' pattern='dd.mm.yyyy' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri", [...]
- <label for='id::field' class='col-form-label'>Label</label>
+<tobago-date id='id' class='tobago-auto-spacing' i18n='{"months":["January","February","March","April","May","June","July","August","September","October","November","December"],"monthsShort":["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],"days":["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],"daysShort":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"daysMin":["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],"firstDay":0,"minDays":1,"today":"Tod [...]
<div class='tobago-input-group-outer'>
<div class='input-group'>
- <input type='text' name='id' id='id::field' value='10.12.2020' class='form-control'>
+ <input type='datetime-local' name='id' id='id::field' value='1957-10-05T00:28:34.123' class='form-control'>
<button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-calendar'></i></button>
+ <button class='btn btn-secondary tobago-date-picker' type='button' title='Date Picker'><i class='fa fa-clock-o'></i></button>
</div>
</div>
</tobago-date>
\ No newline at end of file
diff --git a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/DateController.java b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/DateController.java
index f29f808..38740cf 100644
--- a/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/DateController.java
+++ b/tobago-example/tobago-example-demo/src/main/java/org/apache/myfaces/tobago/example/demo/DateController.java
@@ -28,6 +28,8 @@ import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
import java.util.Date;
@RequestScoped
@@ -36,17 +38,25 @@ public class DateController implements Serializable {
private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+ private static final LocalDateTime SPUTNIK_LOCAL_DATE_TIME
+ = LocalDateTime.of(1957, 10, 5, 0, 28, 34, 123456789);
+ private static final LocalDateTime APOLLO11_LOCAL_DATE_TIME
+ = LocalDateTime.of(1969, 7, 20, 20, 17, 40, 123456789);
+
private Date once;
private Date onchange;
private Date submitDate;
+ private LocalDateTime sputnikLdt = SPUTNIK_LOCAL_DATE_TIME;
+ private LocalDate sputnikLd = APOLLO11_LOCAL_DATE_TIME.toLocalDate();
+
public DateController() {
once = new Date();
final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
submitDate = sdf.parse("2016-05-22");
} catch (final ParseException e) {
- LOG.error("", e);
+ LOG.error("Unexpected parse exception", e);
}
}
@@ -77,4 +87,20 @@ public class DateController implements Serializable {
public void setSubmitDate(final Date submitDate) {
this.submitDate = submitDate;
}
+
+ public LocalDateTime getSputnikLdt() {
+ return sputnikLdt;
+ }
+
+ public void setSputnikLdt(LocalDateTime sputnikLdt) {
+ this.sputnikLdt = sputnikLdt;
+ }
+
+ public LocalDate getSputnikLd() {
+ return sputnikLd;
+ }
+
+ public void setSputnikLd(LocalDate sputnikLd) {
+ this.sputnikLd = sputnikLd;
+ }
}
diff --git a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/010-input/40-date/Date.xhtml b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/010-input/40-date/Date.xhtml
index 946a2f9..df69b7e 100644
--- a/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/010-input/40-date/Date.xhtml
+++ b/tobago-example/tobago-example-demo/src/main/webapp/content/20-component/010-input/40-date/Date.xhtml
@@ -42,18 +42,11 @@
<demo-highlight language="markup"><tc:date label="Date" value="\#{dateController.now}">
<f:convertDateTime pattern="dd.MM.yyyy"/>
</tc:date></demo-highlight>
- <tc:date id="dNormal" label="Date" value="#{dateController.now}">
- <f:convertDateTime pattern="dd.MM.yyyy"/>
- </tc:date>
- <tc:date id="dReadonly" label="Read Only" readonly="true" value="#{dateController.now}">
- <f:convertDateTime pattern="dd.MM.yyyy"/>
- </tc:date>
- <tc:date id="d3" label="Disabled" disabled="true" value="#{dateController.now}">
- <f:convertDateTime pattern="dd.MM.yyyy"/>
- </tc:date>
- <tc:date id="d4" value="#{dateController.now}">
- <f:convertDateTime pattern="dd.MM.yyyy"/>
- </tc:date>
+ <tc:date id="dNormal" label="Date" value="#{dateController.sputnikLd}"/>
+ <tc:date id="dReadonly" label="Read Only" readonly="true" value="#{dateController.now}"/>
+ <tc:date id="d3" label="Disabled" disabled="true" value="#{dateController.now}"/>
+ Without a label:
+ <tc:date id="d4" value="#{dateController.now}"/>
</tc:section>
<tc:section label="Focus">
<p>The following date should be selected after reloading the page. This can be done with the attribute
@@ -61,9 +54,7 @@
<demo-highlight language="markup"><tc:date label="Date (focus)" focus="true">
<f:convertDateTime pattern="dd.MM.yyyy"/>
</tc:date></demo-highlight>
- <tc:date id="d5" label="Date (focus)" focus="true">
- <f:convertDateTime pattern="dd.MM.yyyy"/>
- </tc:date>
+ <tc:date id="d5" label="Date (focus)" focus="true"/>
</tc:section>
<tc:section label="Required">
@@ -73,9 +64,7 @@
<demo-highlight language="markup"><tc:date label="Date (required)" required="true"
value="\#{dateController.once}"></demo-highlight>
<tc:form>
- <tc:date id="dreq" label="Date (required)" required="true" value="#{dateController.once}">
- <f:convertDateTime pattern="dd.MM.yyyy"/>
- </tc:date>
+ <tc:date id="dreq" label="Date (required)" required="true" value="#{dateController.once}"/>
<tc:button label="Submit"/>
</tc:form>
</tc:section>
@@ -195,7 +184,6 @@
The date can be changed by button or by entering a valid date in the textfield. If the date is not valid,
it won't be adopted.</p>
<tc:date id="ajaxinput" label="On Change" value="#{dateController.onchange}">
- <f:convertDateTime pattern="dd.MM.yyyy"/>
<f:ajax render="outputfield"/>
</tc:date>
<tc:out id="outputfield" label="On Server" value="#{dateController.onchange}">
diff --git a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
index 082edd7..4838d97 100644
--- a/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
+++ b/tobago-theme/tobago-theme-standard/src/main/js/tobago.js
@@ -10321,6 +10321,13 @@
super();
}
connectedCallback() {
+ console.debug("input type=date support", DatePicker.SUPPORTS_INPUT_TYPE_DATE);
+ if (!DatePicker.SUPPORTS_INPUT_TYPE_DATE) {
+ this.setAttribute("type", "text");
+ this.initVanillaDatePicker();
+ }
+ }
+ initVanillaDatePicker() {
var _a;
const field = this.field;
const locale = Page.page(this).locale;
@@ -10390,7 +10397,8 @@
}
}
get pattern() {
- return this.getAttribute("pattern");
+ let pattern = this.getAttribute("pattern");
+ return pattern ? pattern : "yyyy-mm-dd";
}
get i18n() {
const i18n = this.getAttribute("i18n");
@@ -10401,6 +10409,13 @@
return rootNode.getElementById(this.id + "::field");
}
}
+ DatePicker.SUPPORTS_INPUT_TYPE_DATE = (() => {
+ let input = document.createElement('input');
+ input.setAttribute('type', 'date');
+ let thisIsNoDate = 'this is not a date';
+ input.setAttribute('value', thisIsNoDate);
+ return (input.value !== thisIsNoDate);
+ })();
document.addEventListener("tobago.init", function (event) {
if (window.customElements.get("tobago-date") == null) {
window.customElements.define("tobago-date", DatePicker);
diff --git a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-date.ts b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-date.ts
index f62aea0..83edcd9 100644
--- a/tobago-theme/tobago-theme-standard/src/main/ts/tobago-date.ts
+++ b/tobago-theme/tobago-theme-standard/src/main/ts/tobago-date.ts
@@ -44,6 +44,14 @@ interface DatePickerOptions {
class DatePicker extends HTMLElement {
+ static readonly SUPPORTS_INPUT_TYPE_DATE : boolean = (() => {
+ let input = document.createElement('input');
+ input.setAttribute('type','date');
+ let thisIsNoDate = 'this is not a date';
+ input.setAttribute('value', thisIsNoDate);
+ return (input.value !== thisIsNoDate);
+ })();
+
lastValue: string;
constructor() {
@@ -51,6 +59,15 @@ class DatePicker extends HTMLElement {
}
connectedCallback(): void {
+ console.debug("input type=date support", DatePicker.SUPPORTS_INPUT_TYPE_DATE);
+
+ if (!DatePicker.SUPPORTS_INPUT_TYPE_DATE) {
+ this.setAttribute("type", "text");
+ this.initVanillaDatePicker();
+ }
+ }
+
+ initVanillaDatePicker(): void {
const field = this.field;
const locale: string = Page.page(this).locale;
@@ -132,7 +149,8 @@ class DatePicker extends HTMLElement {
}
get pattern(): string {
- return this.getAttribute("pattern");
+ let pattern = this.getAttribute("pattern");
+ return pattern ? pattern : "yyyy-mm-dd";
}
get i18n(): DatePickerI18n {