We're going to make a change, and it's easier if we've already got a
component with tests so we don't have to write system tests to check
whether the map is rendered.
This way we fix a bug we mentioned in commit 930bb753c which caused
links to documents to be broken when editing their title because the
title was used to generate the URL of the document.
Note we're still using Paperclip to render cached attachments because
this is the only case where we store files with just Paperclip and not
Active Storage.
With Active Storage, we render attachments just like any other resource,
using `polymorphic_path`. Paperclip included the `url` method in the
model; since the model doesn't have access to the request parameters
(like the host), this was inconvenient because it wasn't possible to
generate absolute URLs with Paperclip.
In order to simplify the code and make it similar to the way we used
Paperclip, we're adding a `variant` method accepting the name of a
variant and returning the variant.
Defining a behavior on hover means making it different for people using
a keyboard or a touchscreen (most of the population, nowadays).
In this case, we had an accessibility issue where the message wouldn't
disappear once it appeared. That meant that, after tabbing through all
the links and buttons in, for instance, the debates index, the page
would be filled with "participation not allowed" messages, and in order
to see the information about how many people have voted, reloading the
page was required.
For touchscreen users the behavior was similar to what we get on hover,
although we've found some inconsistencies when trying to support several
elements on the same page.
We think in proposals it makes sense to hide the "support" button when
users click on it, and the same applies to the buttonsto support and
vote investment projects. However, we aren't hiding the buttons to
agree/disagree with a debate in order to keep the information about the
current number of people agreeing and disagreeing visible.
Note we're removing some support spec methods because after these
changes the duplication isn't as obvious as it was in the past.
We were using the same logic six times regarding when we should show a
"participation not allowed" message. Since we're going to change the
current behavior, we're unifying the logic in one place so the changes
will be easier.
As mentioned in commits 5311daadf and bb958daf0, using links combined
with JavaScript to generate POST requests to the server has a few
issues.
We're also improving the keyboard access. Previously, the links were
focusable and clickable with the keyboard. Now we're disabling the
buttons when voting isn't allowed.
Since these elements can no longer be focused, we're adding an element
with `tabindex="0"` so the "participation not allowed" message is shown,
like we do in most places.
Note we're slightly changing one test because now when hovering over the
button on Chrome, the "participation not allowed" text isn't shown; it's
only shown when hovering on the parts of the `div.ballot` element
outside the button. Since we're already rewriting the behavior of the
"participation not allowed" text in a different pull request, we aren't
going to fix this behavior.
Note we're using the `budgets.investments.investment.add_label` and
`budgets.ballots.show.remove_label` internationalization keys so they're
consistent with the `budgets.investments.investment.add` and
`budgets.ballots.show.remove` keys which were already present. We aren't
unifying these keys in order to keep existing translations.
Very similar code is present in the `votes.js` file. Since the only
elements with the `js-participation-not-allowed` class also matched the
`div.supports div.participation-not-allowed` selector, for these
elements the events were executed twice.
So we can get rid of the `js-participation` class alongside all the
JavaScript code referencing it.
This way we can make the view code a bit easier to read.
We're also changing the order of the conditions a little bit so we only
check for the presence of a current user once.
To make sure we aren't breaking anything with these changes, we're
adding some tests. We're also replacing one system test checking content
with a component test, since component tests are much faster.
Just like we did in commit 0214184b2d for investments, we're removing
some possible optimizations (we don't have any benchmarks proving they
affect performance at all) in order to simplify the code.
The investement votes component `delegate` code was accidentally left
but isn't used since commit 0214184b2, so we're removing it now that
we're removing the `voted_for?` helper method.
We're starting to use buttons instead of submit inputs where possible
because buttons are easier to style; for instance, buttons allow
pseudoelements. Rails has also changed the `button_to` helper to always
generate a <button> tag in recent versions [1].
In this case, buttons get on better with flex layouts, since by default
some browsers display submit inputs with `white-space: pre`, meaning
some of the text isn't visible on small screens.
[1] See pull request 40747 in https://github.com/rails/rails
There's no real point in linking to a page offering users to choose a
heading when there's only one heading to choose.
So we're linking to the investments index instead.
As mentioned in commit bc0f04075, a <select> field which submits its
form on change causes many accessibility and usability issues. In this
case there was also an incompatibility with the advanced search filter
which caused a bug solved in commit 541a5fa89.
So the question is where to position the filters and how to display
them. One factor to take into the account is how relevant these filters
are, particularly compared to the links to select the prefered order,
since we don't usually give users the choice of both filters and orders.
Our filters don't really make sense until the valuation phase starts,
since before that phase investments aren't selected nor their
feasibility is decided.
After that phase, the only phase where citizens are really involved
is the final voting; the rest of the phases are done by valuators and
administrators. In the final voting, citizens can only vote on selected
projects, and that's the default filter during that phase.
So these filters are mainly there for information purposes, and not to
help citizens in the phases where they're actually involved (accepting
projects, selecting projects and balloting).
Orders, on the other hand, play a crucial role during the final voting
phase. Since citizens might have already voted for a few projects and
have, let's say, 100,000€ left, ordering by price allows them to find
which projects are within their remaining budget.
In conclusion, orders are more important than filters, and so they
should have a more prominent place.
For consistency with the proposals section, where we've got some links
in the sidebar (bottom part of the page on small screens) providing a
similar funcionality, like accessing selected proposals or archived or
retired proposals, we're moving the investments filters to the sidebar
(bottom part of the page on small screens) as well.
When render the investment list component with the link "see all
investments", now we redirect to groups index page when a budget has
multiple headings.
The imageable/documentable object is always the object the form builder
is based on; since we're already passing the form builder, we don't have
to pass the object as well.
The only exception are the poll answers. In this case, we're passing a
new answer as the object. That's OK; the same hack that we're using to
send the data to the answer URL without displaying existing attachments
causes the form to keep working the same way.
We don't need any row classes anymore because the <body> already has a
maximum width. As for columns, we only have one column in this form, so
we don't need them either. Besides, the form's parent element already
has a padding.
Although most CONSUL installation don't enable the translation
interface, we're adding some code to take this case into account.
As mentioned in commit 5214d89c8, there are several issues with
submitting a form when a `<select>` tag changes. In particular, keyboard
users might accidentally fire the event while browsing the options, and
screen reader users will find a form with no obvious way to submit it.
In this case, there's an extra problem: in commit be8a0dbe8 we added a
second `<select>` field to this form, which also submitted on change.
Sometimes users changed one of the values and wanted to change the other
value as well before submitting the form. However, it wasn't possible,
because we would submit it before they had a chance to change the second
value.
So now we don't submit the form on change and add a submit button. This
is similar to what we do in the "Advanced filters" we use in several
places.
Note we don't cast negative votes when users remove their support. That
way we provide compatibility for institutions who have implemented real
negative votes (in case there are / will be any), and we also keep the
database meaningful: it's not that users downvoted something; they
simply removed their upvote.
Co-Authored-By: Javi Martín <javim@elretirao.net>
Co-Authored-By: Julian Nicolas Herrero <microweb10@gmail.com>
This way it's easier to find the keys: keys related to the
`Budgets::Investments::VotesComponent` class are in the
`budgets.investments.votes` namespace.
We're making a couple of exceptions; we're not modifying the `supports`
nor the `support_title` keys because they're used in other places.
Since we're going to add an action to remove supports, having a separate
controller makes things easier.
Note there was a strange piece of code which assumed users were not
verified if they couldn't vote investments. Now the code is also
strange, since it assumes users are not verified if they can't create
votes. We might need to revisit these conditions if our logic changes in
the future.
In the previous commit I mentioned:
> If I'm right, the `investment_votes` instance variable only exists to
> avoid several database queries to get whether the current user has
> supported each of the investments.
>
> However, that doesn't make much sense when only one investment is
> shown.
Now let's discuss the case when there are several investments, like in
the investments index:
* There are 10 investments per page by default
* Each query takes less than a millisecond
* We still make a query per investment to check whether the current user
voted in a different group
* AFAIK, there have been no performance tests showing these
optimizations make the request to the investments index significantly
faster
* These optimizations make the code way more complex than it is without
them
Considering all these points, I'm removing the optimizations. I'm fine
with adding `includes` calls to preload records and avoid N+1 queries
even if there are no performance tests showing they make the application
faster because the effect on the code complexity is negligible. But
that's not the case here.
Note we're using `defined?` instead of the `||=` operator because the
`||=` operator will not serve its purpose when the result of the
operation returns `false`.
If I'm right, the `investment_votes` instance variable only exists to
avoid several database queries to get whether the current user has
supported each of the investments.
However, that doesn't make much sense when only one investment is shown.
In this case, the number of queries stays the same, and so we can
simplify the code by rendering the component with an optional parameter.
Using links combined with JavaScript to generate POST requests to the
browser has a few issues.
An obvious one is that it doesn't work for users without JavaScript
enabled (which lately I've noticed are more common than I thought, even
though I've been one of them for years). These users will reach a 404
page.
Since CONSUL isn't currently designed to work without JavaScript
enabled, let's ignore this one for now.
A not so obvious issue is screen reader users might expect the link to
take them somewhere instead of performing an action (in this case,
sending a form to the server).
There might be more issues I'm unaware of. Quoting DHH [1]:
"Turning ahrefs into POSTs is a bit of an anti-pattern, especially for
a11y reasons. Better to use button_to with a styling."
So we're using a button instead. This way we can also simplify the code
and make the button disabled for unidentified users, which automatically
makes it impossible to focus it using the keyboard.
A possible disadvantage of using `button_to` is it will create a form
tag which will be announced to screen readers as a form landmark. I've
tested it with my screen reader and everything worked fine for me, but
some screen reader users might interact with these forms in a different
way and find it confusing, particularly in the case where the button is
disabled.
With this change, we're only changing links for buttons in one place.
There are other places where we should do similar changes.
[1] See issue 33 in https://github.com/hotwired/turbo-rails/
This way blind screen reader users will know which project they're
supporting. In a list of investments, context might not be clear when a
link saying "Support" or "Support this project" is announced, but a link
saying "Support Renovate sidewalks in Main Street" is less ambiguous.
When identified users accessed the investments page, we were using the
`aria-hidden` attribute to hide this link from screen readers since
unidentified users can't support investments.
However, the link was still focusable using keyboard navigation. This
resulted in screen reader users reaching the link and being able to
click it, but getting no feedback at all about where they were or what
they were clicking on.
This is why the fourth ARIA rule says: "Do not use role="presentation" or
aria-hidden="true" on a focusable element" [1].
So we're replacing the link with a non-interactive element instead, like
we do in other places like proposals.
The accessibility of this part of the page for unidentified users still
has some issues; here we're only improving it up to a certain point.
[1] https://www.w3.org/TR/using-aria/#4thrule