The language attribute is present in all layouts since commit 025923ac4,
so there's no need to use the language selector locale. Besides, it
wouldn't work if there's only one locale and the language selector isn't
shown.
Since forms are landmarks, screen reader users might navigate to the
form. But then they were going to find an empty form with no way to
toggle it.
Moving the button inside the form means screen reader users navigating
to the form will find the button to toggle it.
It also helps us simplifying the code; there's no need to use
data-attributes to communicate whether the form should be visible since
now we can easily use the button `aria-expanded` attribute.
We could further simplify the JavaScript if we used a CSS rule to
show/hide the form fields based on the toggle button `aria-expanded`
attribute. However, implementing the "slide" animation we use when
toggling the form with CSS is difficult and unreliable.
We were using the form and then showing it with JavaScript when advanced
search terms were present. Now we hide it with JavaScript when no
advanced search are present. This means users without JavaScript
(including users with JavaScript enabled but bad internet connections
preventing the JavaScript to load) can now access the form.
The other main difference between the two versions is the way the form
flashes while JavaScript is loading.
Previously, the form would always be hidden when no terms had been
introduced. However, when these terms were present, after submitting the
form it would briefly be hidden and then shown again.
Now the opposite happens. When advanced search terms are present, the
form is shown at all times. However, when they aren't, the form is
briefly shown before it disappears.
Here the previous behavior is arguably better because most of the time
these terms will not be present.
So basically we're significantly improving the experience of some users
at the cost of slightly worsen the experience of other users.
We're also hiding the button to show the form when JavaScript is
disabled, since in this scenario it's useless. We're using the `hidden`
attribute so hidden buttons can be detected in CSS.
Users (particularly, screen reader users) usually identify links with
things that take you somewhere, and buttons with things that either send
forms or change things on the page.
Using a button we can also use the `aria-expanded` attribute, meaning
screen reader users will know that the button has two states ("expanded"
and "collapsed"), the current state of the button, and will get
immediate feedback when clicking the button because the new state of the
button will be announced.
Thanks to this change, we can also slightly simplify the code; we
obviously have to remove the (useless) `href` attribute, and we don't
have to prevent the default event in JavaScript since there's no default
event for buttons with `type="button"`.
Note we're keeping this section's original design (which had one button
to add a new group which after being pressed was replaced by a button to
cancel) but we aren't using Foundation's `data-toggle` because there
were a couple of usability and accessibility issues.
First, using `data-toggle` multiple times and applying it to multiple
elements led to the "cancel" button not being available after submitting
a form with errors. Fixing it made the code more complicated.
Second, the "Add new group" button always had the `aria-expanded`
attribute set to "true", so my screen reader was announcing the button
as expanded even when it wasn't. I didn't manage to fix it using
`data-toggle`.
Finally, after pressing either the "Add new group" and "Cancel" buttons,
the keyboard focus was lost since the elements disappeared.
So we're simplifying the HTML and adding some custom JavaScript to be
able to handle the focus and manually setting the `aria-expanded`
attribute.
Co-Authored-By: Javi Martín <javim@elretirao.net>
Co-Authored-By: Julian Herrero <microweb10@gmail.com>
Now it's easier to change the investments filter. Previously we had to
go back to the budget index page, change the filter there, and then
select one heading.
Now the links to change the current filter in the budget index page
aren't needed anymore.
We were using list items with the checkbox role. This is probably
confusing since list items have a listitem role, and so we were
basically using a list with no listitem.
We could add a `<span role="checkbox">` tag inside the list item, but
using real checkboxes is probably easier. We're also adding a test to
verify the checkboxes native behavior is compatible with our code.
Note we're using the "enter" key to toggle the "checked" status of the
SDG. This is probably not intuitive for screen reader users who might
try to submit the form using the "enter" key after selecting a goal.
However, the alternative would be even less intuitive for sighted
keyboard users, since for them these icons look like links or buttons
and they would accidentally submit the form when trying to select a
goal.
Since we haven't come up with a better interface yet, for now we're
following the principle of least damage; we consider submitting the form
against a user's will is less annoying than forcing users to move to a
different field before being able to submit the form.
Also note we can't write `check` in the tests because Capybara will try
to click the checkbox, which is hidden by the image in the label.
Using a button tag, it's possible for every user to "click" the element.
Besides, we don't need to call the `preventDefault` function, because
buttons with type "button" don't do anything by default.
We were using a custom icon because in the past social-share-button
didn't have support for whatsapp. But now that it does, we can remove
our custom icon.
Note we're using the `_app` suffix because that's the name of the icon
meant for mobile devices.
Note we're using both the `hidden` and `disabled` properties to
guarantee compatibility with user agents which might still display the
option even when using the `hidden` attribute or hiding it with
`display: none`. We could also use `hide()` and `show()` instead of the
`hidden` property, but since we're using the `disabled` property, I
thought the code would be easier to read if we used properties in both
cases.
Also note users will no longer be able to get, let's say, debates which
are related to goal 1 and target 2.1. We think this use case is highly
unlikely and there's no need to take it into account.
Add "display_text" to allow customize the text that we want render on tag.
For Goals we render "SDG1", "SDG2"..
For Targets we render the code "1.1", "1.A"...
- When we click on an icon we add a new tag with the Goal related to the input or
we remove the tag when that label already exists.
- Manage goals icons status when add or remove related targets:
Whenever there is a tag related to Goal, either the Goal itself or a Target, the icon
will be "checked".
When there is no related Goal or Target it will no longer be marked as checked.
This will allow autocomplete for the loaded values in suggestions settings.
We remove commas on tag to allow to jquery.amsify.suggestag.js use comma
as delimiter.
This component allows you to add Goals and Targets in a single
input to relate it to any resource.
We use the new added library to render them as tags.
Now that comments and TOC can be closed at the same time, we use a flex
layout so the main content uses the available width.
We're also making the comments work better on medium-sized screens,
since previously they had a fixed width and now the width is adapted to
the size of the screen.
Since now the comment box element has a relative position instead of an
absolute one, we need to consider the draft panel height when
calculating the comment box position.
We were using JavaScript to show/hide the Table of Contents.
In my humble opinion, the <details> tag has a few shortcomings [1][2],
which means we should be careful about when to use it.
IMHO a Table of Contents is a good candidate for this tag because it's a
very common pattern to add a show/hide behavior for it, even if using it
means the "navigation" role (which we are *not* using anyway) wouldn't
be identified correctly.
I'm adding a <details> tag to the comments section as well for
consistency and in order to simplify the code. I'm not sure this is as
good an application of the <details> tag, though, but then again I'm not
sure about the interface we use to show/hide the comments (and this
feeling is increased by the fact that we use a different interface on
small screens). If we decide to change the interface in the future, we
might consider using the <details> tag for the Table of Contents but not
for the comments.
Since the <details> tag is not supported on Internet Explorer, I'm
only adding styles to this tag using the `:not([open])` option. On
Internet Explorer <details> will always be opened and so these styles
will be ignored.
[1] https://adrianroselli.com/2019/04/details-summary-are-not-insert-control-here.html
[2] https://daverupert.com/2019/12/why-details-is-not-an-accordion/
According to the jQuery upgrade guide:
> It is almost always a mistake to use .removeAttr("checked") on a DOM
> element. The only time it might be useful is if the DOM is later going
> to be serialized back to an HTML string. In all other cases,
> .prop("checked", false) should be used instead.
It worked differently after upgrading to jQuery 3. According to the
jQuery upgrade guide:
> It is almost always a mistake to use .removeAttr("checked") on a DOM
> element. The only time it might be useful is if the DOM is later going
> to be serialized back to an HTML string. In all other cases,
> .prop("checked", false) should be used instead.
We've had to add a couple of hacks in order to make jQuery UI datepicker
work with Turbolinks, and one of our tests is failing because the
datepicker changes its height when changing from a month with 5 weeks to
a month with 6 weeks.
We could add a workaround so the test still passes (jQuery UI doesn't
provide a configuration option to always displays 6 weeks in the
datepicker), but I think it's easier to just use the HTML5 native date
input field, which also allows us to simplify the code a bit and IMHO it
improves the user experience, particularly when using mobile phones.
Since date fields are not supported in Safari and Internet Explorer,
we're still using the jQuery UI datepicker on those browsers (and on any
other browser not supporting date fields).
Due to these changes, we're moving the tests checking datepicker's
behaviour to the dashboard. I've choosing not to change the public pages
because I'm not 100% sure everybody would like this change (some people
prefer the datepicker because we can configure the way it looks).
When Foundation initializes a page that has any sticky element, it
loads a window scroll listener into window object to handle the sticky
element positioning when scrolling. This works great until user moves
to a page without sticky elements and the window listeners remain
attached to the window object on this new page too when there is no
need to run the scrollListener and there is no sticky elements so the
window scroll listener cannot find the sticky object on that page a lot
of errors are thrown when user scrolls.
With this approach we are destroying sticky elements before leaving
the page which also remove the Sticky scrollListener from the window
object.
I do not know how to write a test to demonstrate this fix, but at least
there is some steps to reproduce this behavior:
0. Undo this commit
1. Go to any proposal page
2. Click on any link (with Turbolinks enabled)
3. Scroll the page
4. Check the console log. Yo will find as error occurrences as pixels you scrolled.
The JavaScript involved wasn't working since we removed the disable-date
attribute in commit 73ff6881.
We're also improving the JavaScript in two ways:
First, we trigger the `change` event immediately, so when the page loads
date fields are disabled when phases are disabled.
And second, we don't remove the selected dates when disabling a phase,
so disabling it and enabling it again will keep the selected values.
When a user recovers a page from browser history where placed a
marker in different map pane (visible map layer) marker was
successfully added to the map but the map center is the one
defined at Settings map properties so the marker was not visible
to the user.
Now when map_location form has valid coordinates we use them
instead of default map center settings. This will avoid the user to
have to rellocate the marker (or find the correct pane where the
marker was added) if already placed.
When using an editable map is better to load marker latitude, longitude and
map zoom from form fields so we can show the marker at latest position defined
by user when the page was restored from browser history.
To reproduce this behavior:
0. Undo this commit
1. Go to new proposal page
2. Place the proposal map marker
3. Go away to any other page
4. Restore new proposal page from browser history.
At this point you should not see the recently placed marker.
The same thing happens when editing a proposal.
If we do not do this a map could be initialized twice times or more
when restoring a page with a map causing weird UI effects and
loading some map layers also twice times or more.
Need to add a maps array to be able to store all initialized
(visible) maps so we can destroy them when needed. Notice that
we are destroying maps also when admin settings tabs changes
(only visible ones), this is again to avoid to re-initialize map more
than once when users navigate through settings tabs, another
option to the settings issue could be to detect if the map was
already initialized to skip uneeded initialization.
It was being duplicated when restoring a page by using browser
history. With this solution we will avoid to have screen readers
descriptions more than once inside any sociual share button.
We need to use page body event delegation so it will work with any
element even with the ones added through ajax, in this case the
annotation comments box form. By doing this way we do not need
this code on the server response anymore.
Furthermore JS events defined at ajax responses are not part of
application javascript and are lost when restoring a page from
browser cache, you can try to apply the same event delegation
technique to the `erb` file and it wont work just because events
added dinamically are not treated the same than `application.js`
code.
To reproduce the error:
1. Load an annotatable draft version
2. Move to any other page
3. Go back
Now "Publish comment" button wont work.
If we do not destroy annotator app before storing the page at
browser cache we will unnecesarily initialize annotations twice (or
more) duplicating Annotator HTML markup and causing
unexpected errors.
Without this commit you will find an error when restoring a page with
annotator, you can click on any annotation and you will see the annotation
comments are being loaded twice.
IMO this is an idempotency issue within Annotator JS library.
Patch extracted from here the comments on turbolinks issue 253 and
converted to vanilla javascript.
The hide action over datepickers ensures us that opened datepickers
will be closed before leving the page. Previously if you open any
datepicker and then move to previous page you will keep seeing the
datepicker in the restored page.
Turbolinks 5 handles page caching differently; it saves the current HTML
and, when the page is restored using the browser's back button, it
copies it and triggers the `turbolinks:load` event, which, in our case,
triggers our `initialize_modules` function.
This isn't a problem regarding event handlers, since they're removed
when caching the page (except fot the ones attached to the `document`).
However, it is a problem for functions that, once the document is ready,
scan the DOM and add certain elements. In this case, an element might be
added a second time when the page is restored.
So we need to either check an item has already been added before adding
it or remove it before caching the page. Since this is going to be a
common pattern, we're adding a function to handle these non-idempotent
parts of the application, so it mirrors our `initialize_modules`
function.
We're also moving the `destroy` function definition after the
`initialize` function definition, which makes more sense since we
initialize things before destroying them.
This is the reason why this feature was implemented in the first
place: it's easy to open the editor, make some changes, close it, and
continue without realizing the changes have not been saved.
In the rest of the forms, this functionality is quite lacking. For
starters, some forms warn if there are unsaved changes, while some forms
don't, which is highly inconsistent and disorients users.
Furthermore, we were having problems with this feature after upgrading
Turbolinks, particularly in forms using CKEditor. In these cases, a lot
of hacking needs to be done in order to make this feature work properly,
since CKEditor adds some formatting automatically, and if this is done
after the form is serialized, we'll get some unexpected behavior. On the
other hand, comparing the value of a textarea against its `defaultValue`
property will work on every edge case, including using the browser's
back button or reloading the page.
Finally, users are used to the way web forms work, and aren't used to be
asked for confirmation when they change their mind and decide to leave
the page without saving the changes. Asking them for confirmation will
be annoying in most cases. Besides that, if they accidentally leave the
page, they can use the browser's back button and they'll recover the
unsaved changes.
It's true this won't happen it they accidentally close the browser's
window, but our WatchFormChanges functionality didn't work in this case
either. Using the "beforeunload" event adds more problems than it
solves, since it doesn't support custom messages (or, to be more
precise, modern browsers ignore custom messages), and it doesn't get
along with turbolinks.
Co-Authored-By: Senén Rodero Rodríguez <senenrodero@gmail.com>
Turbolinks 5 doesn't follow the browser's standard behaviour of ignoring
links pointing to "#", so we're preventing the turbolinks events in this
situation:
We didn't upgrade Turbolinks when we upgraded to Rails 5 so we didn't
upgrade too many things at the same time, and postponed it... until now
:).
Note upgrading Turbolinks fixes an issue with foundation's sticky when
using the browser's back and forward buttons. We're adding tests for
these scenarios.
Co-authored-by: Senén Rodero Rodríguez <senenrodero@gmail.com>