Skip to content

Commit

Permalink
fix: issue2551390 - Replace text input/calendar popup with native dat…
Browse files Browse the repository at this point in the history
…e input

Docs, code and test changes for the changeover to a native date
element.

See issue for details.
  • Loading branch information
rouilj committed Jan 18, 2025
1 parent e5e527c commit caf5612
Show file tree
Hide file tree
Showing 24 changed files with 1,022 additions and 14 deletions.
4 changes: 4 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ Fixed:
- issue2551391, partial fix for issue1513369. input fields were
not getting id's assigned. Fixed automatic id assignment to
input fields. Thinko in the code. (John Rouillard)
- issue2551390 - Replace text input/calendar popup with native
date input. Also add double-click and exit keyboard handlers to
allow copy/paste/editing the text version of the date. (John
Rouillard)

Features:

Expand Down
4 changes: 2 additions & 2 deletions doc/customizing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ caches the schema).
<td tal:content="structure context/due_date/field" />
</tr>

If you want to show only the date part of due_date then do this instead::
If you want to show the date and time for due_date then do this instead::

<tr>
<th>Due Date</th>
<td tal:content="structure python:context.due_date.field(format='%Y-%m-%d')" />
<td tal:content="structure context/due_date/field_time" />
</tr>

3. Add the property to the ``issue.index.html`` page::
Expand Down
44 changes: 37 additions & 7 deletions doc/reference.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3070,17 +3070,28 @@ There are several methods available on these wrapper objects:
tri-state yes/no/neither selection. This method may take some
arguments:

size
size (default 30)
Sets the width in characters of the edit field

format (Date properties only)
Sets the format of the date in the field - uses the same
format string argument as supplied to the ``pretty`` method
below.
Sets the format of the date in the field - uses the
same format string argument as supplied to the
``pretty`` method below. If you use this, it will
prevent the use of browser native date inputs. It is
useful if you want partial dates. For example using
``format="%Y-%m"`` with ``type="text"`` will display a
text edit box with the year and month part of your
date.

type (depends on property type)
Sets the type property of the input. To change a date
property field from a native date input to a text
input you would use ``type="text"``.

popcal (Date properties only)
Include the JavaScript-based popup calendar for date
selection. Defaults to on.
Include a link to the JavaScript-based popup calendar
for date selection. Defaults to off/False since native
date inputs supply popup calendars.

y_label, n_label, u_label (Boolean properties only)
Set the labels for the true/false/undefined
Expand All @@ -3104,6 +3115,10 @@ There are several methods available on these wrapper objects:
attribute without a value. This is useful for boolean
properties like ``required``.

field_time (Date properties only)
Create a browser native input for editing date and time.
The field method creates an input for editing
month/day/year (without time).

rst only on String properties - render the value of the property
as ReStructuredText (requires the :ref:`Docutils
Expand Down Expand Up @@ -3164,7 +3179,11 @@ There are several methods available on these wrapper objects:
is returned if the value is ``None`` otherwise it
is converted to a string.

popcal Generate a link to a popup calendar which may be used to
popcal This is deprecated with Roundup 2.5 which uses the
native HTML5 date input. The native date input
includes a calendar popup on modern broswers.

Generate a link to a popup calendar which may be used to
edit the date field, for example::

<span tal:replace="structure context/due/popcal" />
Expand Down Expand Up @@ -4027,6 +4046,17 @@ This example indicates that the value sent back to the user is actually
comma-separated value content (i.e. something to load into a
spreadsheet or database).

CSS for the web interface
-------------------------

The web interface can be completely redesigned by the admin, However
some parts of Roundup use classes or set attributes that can be
selected by css to change the look of the element.

The ``datecopy.js`` module used to allow editing a date value with a
text input assigns the ``mode_textdate`` class to the input when it is
in text mode. The class is removed when it is not in text mode.


8-bit character set support in Web interface
--------------------------------------------
Expand Down
142 changes: 142 additions & 0 deletions doc/upgrading.txt
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,148 @@ defusedxml.

.. _defusedxml: https://pypi.org/project/defusedxml/

Use native date inputs (optional)
---------------------------------

Roundup now uses native date or datetime-local inputs for Date()
properties. These inputs take the place of the text input and
calendar popup from earlier Roundup versions. Modern browsers
come with a built-in calendar for date selection, so the
``(cal)`` calendar link is no longer needed. These native inputs
show the date based on the browser's locale and translate terms
into the local language.

If you do nothing, simple uses of the field() method will
generate date inputs to allow selection of a date. Input fields
for Date() properties will not have the ``(cal)`` link
anymore. Complex uses will not be upgraded and will operate like
earlier Roundup versions.

To upgrade all date properties, there are four changes to make:

1. Replace ``field`` calls with ``field_time`` where needed.

2. Remove the format argument from field() calls on Date()
properties.

3. Remove popcal() calls.

4. Include datecopy.js in page.html.

Use field_time() where needed
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The format used by ``field`` does not include hours, minutes or
seconds. If your users need to enter times, you should change
these calls to use ``field_time``. The arguments are the same as
for field.

Remove format argument
~~~~~~~~~~~~~~~~~~~~~~

Speaking of arguments, avoid setting the date format if you want
to use native date inputs. The date value needs a specific format
for date or datetime-local inputs. If you include the `format`
argument in the `field` method, it should be removed.

The `field` method uses the format ``%Y-%m-%d``. The
``field_time`` method uses the format ``%Y-%m-%dT%H:%M:%S``. If
you use these exact formats, Roundup will accept them and use a
native date input.

.. highlight:: text

If you use an format that doesn't match, you will see a text
input and a logged warning message like::

Format '%Y-%m' prevents use of modern date input.
Remove format from field() call in template test.item.
Using text input.

.. highlight:: default

The log message will appear if your logging level is set to
WARNING or lower. (Refer to your tracker's :ref:`config.ini
logging section <config-ini-section-logging>` for details on
logging levels.)

If you want to use a text input for a specific date format, you
can add ``type="text"`` to the field() argument list to suppress
the warning. By default using a format argument will show the
popup calendar link. You can disable the link by setting
``popcal=False`` in the field() call. If you have::

tal:content="structure python:context.duedate.field(
placeholder='YYYY-MM, format='%Y-%m')"

changing it to::

tal:content="structure python:context.duedate.field(
type='text',
placeholder='YYYY-MM, format='%Y-%m',
popcal=False)"

will generate the input as in Roundup 2.4 or earlier without a
popcal link.

If you are using a path expression like::

tal:content="context/duedate/field"

change it to::

tal:content="structure python:context.duedate.field(
type='text')"

to get the input from before Roundup 2.5 with a popcal link.

Remove popcal
~~~~~~~~~~~~~

If you use the ``popcal()`` method directly in your templates, you
can remove them. The browser's native date selection calendar can
be used instead.

Add copy/paste/edit on double-click using datecopy.js
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

There is no way to copy/paste using a native datetime-local or
date input. With the datecopy.js file installed, double-clicking
on the input turns it into a normal text input with the ability
to copy, paste, or manually edit the date.

To set this up, take either ``datecopy.js`` or the smaller
version, ``datecopy.min.js``, from the ``html`` folder of the
classic tracker template. Put the file in the ``html`` folder of
your tracker home.

After you install the datecopy file, you can add the script
directly to a page using::

<script tal:attributes="nonce request/client/client_nonce"
tal:content="structure python:utils.readfile('datecopy.min.js')">
</script>

or get the file in a separate download using a regular script
tag::

<script type="text/javascript" src="@@file/datecopy.js">
</script>

You can place these at the end of ``page.html`` just before the
close body ``</body>`` tag. This is the method used in the
classic template. This forces the file to be run for every page
even those that don't have any date inputs. However, it is cached
after the first download.

Alternatively you can inline or link to it using a script tag
only on pages that will have a date input. For example
``issue.item.html``.

There is no support for activating text mode using the
keyboard. Tablet/touch support is mixed. Chrome supports
double-tap to activate text mode input. Firefox does not.

Change in REST response for invalid CORS requests (info)
--------------------------------------------------------

Expand Down
34 changes: 33 additions & 1 deletion doc/user_guide.txt
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,14 @@ Classhelper`_ for details.
Date properties
~~~~~~~~~~~~~~~

Date properties are usually shown using a native HTML date
element. This provides a calendar button for choosing the
date. The date is shown in the normal format for your location.

Native date inputs do not allow the use of partial forms as
defined below. For this reason, you may edit a date/time
stamp directly.

Date-and-time stamps are specified with the date in
international standard format (``yyyy-mm-dd``) joined to the time
(``hh:mm:ss``) by a period ``.``. Dates in this form can be easily
Expand All @@ -191,7 +199,7 @@ just the year may be omitted. If the time is given, the time is
interpreted in the user's local time zone. The Date constructor takes
care of these conversions. In the following examples, suppose that
``yyyy`` is the current year, ``mm`` is the current month, and ``dd`` is
the current day of the month.
the current day of the month and the local timezone is GMT-5.

- "2000-04-17" means <Date 2000-04-17.00:00:00>
- "01-25" means <Date yyyy-01-25.00:00:00>
Expand All @@ -202,6 +210,30 @@ the current day of the month.
- "8:47:11" means <Date yyyy-mm-dd.13:47:11>
- the special date "." means "right now"

The native date input doesn't allow copy or paste. Roundup
enhances the native date field. If you double-click on a native
date field, it changes to a text input mode with the date already
selected. You can use control-C to copy the date or control-V to
paste into the field. Double-clicking also lets you add seconds
in a date-time value if you need to.

It will switch back to a date input and save the value when:

- you move to another field using the mouse or the Tab key.
- you press enter/return (press return again if you want to
submit the form).

If you press Escape, it will restore the original value and
change back to a date input.

When using native date elements in text input mode, the date
looks like a full data with ``T`` replacing ``.``. If the ``T``
is missing, the native date elements will not recognize the value
as a date.

There is no support for activating text mode using the
keyboard. Tablet/touch support is mixed. Chrome supports
double-tap to activate text mode input. Firefox does not.

When searching, a plain date entered as a search field will match that date
exactly in the database. We may also accept ranges of dates. You can
Expand Down
52 changes: 51 additions & 1 deletion roundup/cgi/templating.py
Original file line number Diff line number Diff line change
Expand Up @@ -2252,7 +2252,16 @@ def now(self, str_interval=None):
return DateHTMLProperty(self._client, self._classname, self._nodeid,
self._prop, self._formname, ret)

def field(self, size=30, default=None, format=_marker, popcal=True,

def field_time(self, size=30, default=None, format=_marker, popcal=None,
**kwargs):

kwargs.setdefault("type", "datetime-local")
field = self.field(size=size, default=default, format=format,
popcal=popcal, **kwargs)
return field

def field(self, size=30, default=None, format=_marker, popcal=None,
**kwargs):
"""Render a form edit field for the property
Expand All @@ -2269,6 +2278,47 @@ def field(self, size=30, default=None, format=_marker, popcal=True,
else:
return self.pretty(format)

kwargs.setdefault("type", "date")

if kwargs["type"] in ["date", "datetime-local"]:
acceptable_formats = {
"date": "%Y-%m-%d",
"datetime-local": "%Y-%m-%dT%H:%M:%S"
}

if format is not self._marker: # user set format
if format != acceptable_formats[kwargs["type"]]:
# format is incompatible with date type
kwargs['type'] = "text"
if popcal is not False:
popcal = True
logger.warning(self._(
"Format '%(format)s' prevents use of modern "
"date input. Remove format from field() call in "
"template %(class)s.%(template)s. "
"Using text input.") % {
"format": format,
"class": self._client.classname,
"template": self._client.template
})

"""
raise ValueError(self._(
"When using input type of '%(field_type)s', the "
"format must not be set, or must be "
"'%(format_string)s' to match RFC3339 date "
"or date-time. Current format is '%(format)s'.") % {
"field_type": kwargs["type"],
"format_string":
acceptable_formats[kwargs["type"]],
"format": format,
})"""
else:
# https://developer.mozilla.org/en-US/docs/Web/HTML/Date_and_time_formats#local_date_and_time_strings
# match date-time format in
# https://www.rfc-editor.org/rfc/rfc3339
format = acceptable_formats[kwargs['type']]

value = self._value

if value is None:
Expand Down
2 changes: 1 addition & 1 deletion roundup/date.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def cmp(a, b):
date_re = re.compile(r'''^
((?P<y>\d\d\d\d)([/-](?P<m>\d\d?)([/-](?P<d>\d\d?))?)? # yyyy[-mm[-dd]]
|(?P<a>\d\d?)[/-](?P<b>\d\d?))? # or mm-dd
(?P<n>\.)? # .
(?P<n>[.T])? # . or T
(((?P<H>\d?\d):(?P<M>\d\d))?(:(?P<S>\d\d?(\.\d+)?))?)? # hh:mm:ss
(?:(?P<tz>\s?[+-]\d{4})|(?P<o>[\d\smywd\-+]+))? # time-zone offset, offset
$''', re.VERBOSE)
Expand Down
Loading

0 comments on commit caf5612

Please sign in to comment.