Skip to content

Commit

Permalink
Implement sticky buttons for main CTAs in Mobile UI (decidim#13002)
Browse files Browse the repository at this point in the history
* remove stiky menu mobile

* remove sticky menu mobile view

* remove main-bar__links-mobile from spec

* fix spec lint error

* add login next to mobile menu

* apply new design for header_defautl and header_login_ photo

* install stylelint

* create mobile_header_spec file

* add mobile search block

* add trailing newline

* remove dupliacate id

* add missing tag

* add search to the menu

* hide last svg when dropdown is close

* add language-chooser to mobile-menu

* hide login when dropdown open

* add spec to test header features

* add trailing newlines

* configure sticky header

* change spelling

* change spelling

* remove duplicate id

* resolve review comments

* display flash messages

* remove unused code

* fix duplicated ids

* change ids name

* check spelling

* check spelling

* add dinamics ids

* separate desktop and mobile dropdowns

* fix specs

* add guard clause for sticky header

* adjust header width

* switch of logo to favicon on mobile

* display favicon when mobile

* add user_initials helper spec check

* include application helper in devise_controller

* refactor text_initials method

* custom cta-button for meeting

* change smile-line for initials when no image

* custom meeting register button for mobile

* run linter

* add sticky cta proposal button

* add proposals with threshold sticky-cta

* add survey-cta sticky button

* align survey sections buttons

* add more-info dropdown for mobile

* custom budget fixed-cta-button

* custom participatory budget view for mobile

* fix orders_spec

* add dinamic footer margin

* remove footer margin from desktop

* refactor sticky_header script

* add sticky button in progressbar for desktop

* removed rubocop changed files

* removed rubocop changed files

* position cta-button for desktop

* checkout tailwind.config.js.erb file to develop version

* checkout tailwind.config.js.erb file to develop version

* check for multiple ctas buttons

* refactor adjustCtasButtons function

* display progressbox-buttons

* add media queries with tailwind classes

* remove single quotes

* remove extra-margin in budget view

* add budget-summary_content_header.js file

* fix broke design on desktop

* remove trailling whitespaces

* resolve requested changes for sticky_header.js

* fix regressions in desktop meetings

* resolve requested changes

* apply review

---------

Co-authored-by: Ivan Vergés <[email protected]>
  • Loading branch information
ElviaBth and microstudi authored Jun 25, 2024
1 parent 838a313 commit 41607b5
Show file tree
Hide file tree
Showing 20 changed files with 154 additions and 60 deletions.
48 changes: 30 additions & 18 deletions decidim-budgets/app/packs/stylesheets/budgets.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,8 @@
}

&-summary {
@apply bg-tertiary;

box-shadow: 0 4px 6px 0 rgba(211, 211, 211, 0.25);

&__content {
@apply p-4 md:p-0;
@apply bg-tertiary p-4 md:h-full md:p-0;
}

&__container {
Expand All @@ -21,7 +17,7 @@
}

&__content__header {
@apply col-span-2 text-2xl mb-2;
@apply col-span-2 text-2xl pb-2 mb-2;

.ql-editor {
@apply inline-block text-2xl;
Expand All @@ -37,24 +33,20 @@
}

&__progressbar {
@apply my-4 relative;
@apply my-0 lg:my-4 relative;

.budget-progress {
@apply bg-white rounded;
@apply bg-white rounded h-2;
}

&--minimum {
@apply absolute;

.progress-meter--minimum-mark {
@apply h-[20px] ml-auto w-[2px] bg-black block;
}
@apply absolute h-2 bg-gray-3 rounded-s;
}

&--meter {
background-color: var(--success);

@apply h-[20px] rounded;
@apply h-2 rounded-s absolute;
}
}

Expand All @@ -71,11 +63,11 @@
}

&__progressbar-legend {
@apply text-sm block;
@apply text-xs md:text-sm block;
}

&__progressbar-legend-strong {
@apply font-bold block;
@apply font-semibold block text-[10px] md:text-sm text-nowrap;
}
}

Expand Down Expand Up @@ -231,7 +223,23 @@
}

.budget-summary__button-modal {
@apply col-span-12 mb-4 pb-4;
@apply hidden lg:text-secondary lg:block lg:col-span-12 lg:mb-4 lg:pb-4;
}

.budget-summary__button-dropdown {
@apply col-span-12 text-secondary lg:hidden;

&_text {
@apply flex gap-4 justify-start;

svg {
@apply w-5 h-5;
}
}

p {
@apply text-gray-4 font-normal text-[13px];
}
}

.progressbox-fixed-wrapper {
Expand Down Expand Up @@ -262,7 +270,11 @@
}

.budget-summary__button-modal {
@apply col-span-10 col-start-2 justify-start;
@apply hidden lg:text-secondary lg:block lg:col-span-10 lg:col-start-2 lg:justify-start;
}

.budget-summary__button-dropdown {
@apply block text-secondary col-span-10 col-start-2 justify-start lg:hidden;
}

.progressbox-fixed-wrapper {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<span class="budget-summary__progressbar-legend budget-summary__progressbar-marks_right" data-total-allocation="<%= current_order.available_allocation %>">
<%= t("budget", scope: "decidim.budgets.projects.order_progress") %>
<strong class="budget-summary__progressbar-legend-strong">
<% if current_order.projects_rule? %>
<%= current_order.maximum_projects %>
<% else %>
<%= budget_to_currency(budget.total_budget) %>
<% end %>
</strong>
<%= t("budget", scope: "decidim.budgets.projects.order_progress") %>
</span>
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,29 @@
<div class="budget-summary__progressbar">
<div class="budget-progress" role="progressbar" aria-label="<%= t("budget", scope: "decidim.budgets.projects.order_progress") %>" aria-valuenow="<%= current_order_budget_percent %>" aria-valuetext="<%= current_order_budget_percent %> %" aria-valuemin="0" aria-valuemax="100">
<div class="budget-summary__progressbar--minimum" style="width: <%= current_order_budget_percent_minimum %>%">
<span class="progress-meter--minimum-mark"></span>
</div>
<div class="budget-summary__progressbar--meter" style="width: <%= current_order_budget_percent %>%"> </div>
</div>
<div class="budget-summary__progressbar-marks">
<span class="budget-summary__progressbar-legend" style="width: <%= current_order_budget_percent_minimum %>%;">
<%= t("assigned", scope: "decidim.budgets.projects.order_progress") %>
<strong id="order-total-budget<%= "-responsive" if responsive %>" class="budget-summary__progressbar-legend-strong">
<%= render partial: "decidim/budgets/projects/order_total_budget" %>
</strong>
<%= t("assigned", scope: "decidim.budgets.projects.order_progress") %>
</span>
<% if current_order.minimum_budget > 0 %>
<span class="budget-summary__progressbar-legend budget-summary__progressbar-marks_center">
<%= t("minimum", scope: "decidim.budgets.projects.order_progress") %>
<strong class="budget-summary__progressbar-legend-strong">
<%= budget_to_currency(current_order.minimum_budget) %>
</strong>
<%= t("minimum", scope: "decidim.budgets.projects.order_progress") %>
</span>
<% end %>
<%= render partial: "decidim/budgets/projects/order_progress_progressbar_marks_right" %>
</div>
</div>
</div>
<div class="budget-summary__progressbox-buttons">
<div class="budget-summary__progressbox-buttons layout-aside__ctas-buttons" data-sticky-buttons>
<% if !current_order_checked_out? && voting_open? %>
<button class="button button__secondary button__lg w-full" data-dialog-open="budget-confirm" <%= budget_confirm_disabled_attr %>>
<%= t("vote", scope: "decidim.budgets.projects.budget_summary") %>
Expand All @@ -69,7 +68,6 @@
<div class="budget-summary__progressbox layout-item__main">
<div class="budget-progress relative" role="progressbar" aria-valuenow="<%= current_order_budget_percent %>" aria-valuetext="<%= current_order_budget_percent %>%" aria-valuemin="0" aria-valuemax="100">
<div class="budget-summary__progressbar--minimum" style="width: <%= current_order_budget_percent_minimum %>%">
<span class="progress-meter--minimum-mark"></span>
</div>
<div class="budget-summary__progressbar--meter" style="width: <%= current_order_budget_percent %>%">
</div>
Expand All @@ -92,7 +90,7 @@
<%= render partial: "decidim/budgets/projects/order_progress_progressbar_marks_right" %>
</div>
</div>
<div class="layout-item__aside budget-summary__progressbox-buttons">
<div class="layout-item__aside budget-summary__progressbox-buttons hidden lg:block">
<% if !current_order_checked_out? && voting_open? %>
<button class="button button__secondary button__lg mt-2 md:mt-0 w-full" data-dialog-open="budget-confirm" <%= budget_confirm_disabled_attr %>>
<span>
Expand All @@ -111,3 +109,14 @@
</div>
</div>
</div>

<div class="budget-summary__button-dropdown">
<button id="dropdown-trigger-button-dropdown" data-component="dropdown" data-target="dropdown-menu-button-dropdown" class="budget-summary__button-dropdown_text">
<%= t("more_information", scope: "decidim.budgets.budget_information_modal") %>
<%= icon "arrow-down-s-line" %>
<%= icon "arrow-up-s-line" %>
</button>
<p id="dropdown-menu-button-dropdown">
<%= cell("decidim/budgets/budget_information_modal", @budget).call(:more_information) %>
</p>
</div>
2 changes: 1 addition & 1 deletion decidim-budgets/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ en:
back_to: Back to %{component_name}
close_modal: Close modal
continue: Continue
more_information: More information
more_information: More information about budget
budgets_list:
budgets: Budgets
cancel_order:
Expand Down
10 changes: 5 additions & 5 deletions decidim-budgets/spec/system/orders_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@

it "displays total budget" do
within ".budget-summary", match: :first do
expect(page).to have_content("Budget\n€0")
expect(page).to have_content("€0\nBudget")
end
end
end
Expand All @@ -129,7 +129,7 @@
expect(page).to have_css ".budget-list__data--added", count: 1

within ".budget-summary__progressbar-marks", match: :first do
expect(page).to have_content(/Assigned\s€25,000,000/)
expect(page).to have_content(/€25,000,000\sAssigned/)
end
within ".budget__list--header" do
expect(page).to have_content(/Added\s1/)
Expand Down Expand Up @@ -162,7 +162,7 @@
expect(page).to have_css ".budget-list__data--added", count: 1

within ".budget-summary__progressbar-marks", match: :first do
expect(page).to have_content(/Assigned\s€25,000,000/)
expect(page).to have_content(/€25,000,000\sAssigned/)
end
within ".budget__list--header" do
expect(page).to have_content(/Added\s1/)
Expand Down Expand Up @@ -278,7 +278,7 @@
visit_budget

within ".budget-summary__progressbar-marks", match: :first do
expect(page).to have_content(/Assigned\s€25,000,000/)
expect(page).to have_content(/€25,000,000\sAssigned/)
end
within ".budget__list--header" do
expect(page).to have_content(/Added\s1/)
Expand All @@ -289,7 +289,7 @@
end

within ".budget-summary__progressbar-marks", match: :first do
expect(page).to have_content(/Assigned\s€0/)
expect(page).to have_content(/€0\sAssigned/)
end
within ".budget__list--header" do
expect(page).to have_content(/Added\s0/)
Expand Down
14 changes: 8 additions & 6 deletions decidim-core/app/cells/decidim/progress_bar/show.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
<%= content_tag :span, total if total != 0 %>
</div>

<%= content_tag :div, units_name_text, class: "progress-bar__units" if units_name %>
<div class="proposals__aside-progress">
<%= content_tag :div, units_name_text, class: "progress-bar__units" if units_name %>

<% if total != 0 %>
<div class="progress-bar" role="progressbar" aria-label="<%= units_name.present? ? units_name_text : t("decidim.shared.progress") %>" aria-valuenow="<%= number_with_precision(percentage, separator: ".", precision: 2) %>" aria-valuemin="0" aria-valuemax="100" aria-valuetext="<%= number_to_percentage(percentage, precision: 2) %>">
<div style="width: <%= percentage %>%"></div>
</div>
<% end %>
<% if total != 0 %>
<div class="progress-bar" role="progressbar" aria-label="<%= units_name.present? ? units_name_text : t("decidim.shared.progress") %>" aria-valuenow="<%= number_with_precision(percentage, separator: ".", precision: 2) %>" aria-valuemin="0" aria-valuemax="100" aria-valuetext="<%= number_to_percentage(percentage, precision: 2) %>">
<div style="width: <%= percentage %>%"></div>
</div>
<% end %>
</div>
</div>
37 changes: 33 additions & 4 deletions decidim-core/app/packs/src/decidim/sticky_header.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,37 @@
// This script implements a sticky header that hides when participants scroll down and shows when they scroll up.
// Sticky headers allow users to quickly access the navigation, search, and utility-navigation elements without scrolling up to the top of the page.
// They increase the discoverability of the elements in the header.
// This script implements the sticky header and the sticky buttons. The sticky header hides when participants scroll down and shows when they scroll up.
// Sticky headers allow users to quickly access the navigation, search, and utility-navigation elements without scrolling up to the top of the page. They increase the discoverability of the elements in the header.
// The sticky buttons in the other hand, those are some of the main Call to Actions (CTAs) that remain accessible on the screen as the user scrolls through the detailed view of the Meetings, Proposals, Surveys, and Budgets components.

let prevScroll = window.scrollY;
const stickyHeader = document.querySelector("[data-sticky-header]");
const footer = document.querySelector("footer");
const stickyButtons = document.querySelectorAll("[data-sticky-buttons]");

const isElementInViewport = (element) => {
const rect = element.getBoundingClientRect();
return rect.top >= 0 && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight);
};

const adjustCtasButtons = () => {
if (!stickyButtons || !stickyButtons.length) {
return;
}

let visibleButtons = Array.from(stickyButtons).filter(isElementInViewport);

if (visibleButtons.length > 0) {
const marginBottom = Math.max(...visibleButtons.map((ctasButton) => ctasButton.offsetHeight));
footer.style.marginBottom = `${marginBottom}px`;
} else {
footer.style.marginBottom = 0;
}
};

if (stickyHeader) {
document.addEventListener("scroll", () => {
// if a subelement is not visible it has no offsetParent
const header = document.getElementById("main-bar").offsetParent;
if (header) {
if (header && window.getComputedStyle(stickyHeader).position === "fixed") {
let currentScroll = window.scrollY;
let goingDown = prevScroll > currentScroll;
let change = Math.abs(prevScroll - currentScroll);
Expand All @@ -20,6 +43,12 @@ if (stickyHeader) {
}
prevScroll = currentScroll;
}

adjustCtasButtons();
}
});

document.addEventListener("on:toggle", () => {
adjustCtasButtons();
});
};
2 changes: 1 addition & 1 deletion decidim-core/app/packs/stylesheets/decidim/_flash.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
}

&__message {
@apply text-black font-medium text-md;
@apply text-black font-medium text-md flex-col;

a {
@apply underline text-secondary;
Expand Down
13 changes: 13 additions & 0 deletions decidim-core/app/packs/stylesheets/decidim/_layout.scss
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,16 @@
.layout-main__buttons {
@apply flex gap-x-1 items-center;
}

.layout-aside__ctas-buttons {
@apply fixed md:relative inset-x-0 bottom-0 z-30 md:z-0 flex flex-row-reverse md:flex-col items-center md:items-stretch last:[&>button]:font-semibold first:[&>button]:text-lg justify-around bg-white md:bg-transparent gap-4 md:gap-0 p-4 md:p-0 shadow-inner md:shadow-none first:[&>*]:grow md:first:[&>*]:grow-0 last:[&>*]:w-1/2 md:last:[&>*]:w-auto h-20 md:h-fit first:[&>button]:py-3;

.meeting__aside-progress,
.proposals__aside-progress {
@apply flex flex-col-reverse md:flex-col md:w-auto;
}
}

.survey-section-buttons {
@apply flex-row last:[&>button]:font-semibold md:[&>button]:font-normal last:[&>button]:text-lg md:last:[&>button]:text-sm md:first:[&>button]:text-sm last:[&>button]:py-3 md:last:[&>button]:py-1.5 md:first:[&>button]:py-1.5;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div id="dropdown-menu-search-mobile" class="main-bar__links-mobile__search" aria-hidden="true">
<div class="main-bar">
<div class="main-bar__logo">
<%= render partial: "layouts/decidim/logo", locals: { organization: current_organization } %>
</div>

<div>
<div class="main-bar__links-mobile__trigger" onclick="document.querySelector('#dropdown-trigger-links-mobile-search').click();">
<%= icon "close-line" %>
</div>
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="answer-questionnaire__submit">
<div class="answer-questionnaire__submit layout-aside__ctas-buttons survey-section-buttons" data-sticky-buttons>
<% if !first_step? %>
<button type="button" class="button button__sm button__text-secondary" data-toggle="<%= [previous_step_dom_id, current_step_dom_id].join(" ") %>">
<button type="button" class="button button__sm button__transparent-secondary" data-toggle="<%= [previous_step_dom_id, current_step_dom_id].join(" ") %>" data-survey-buttons>
<%= icon "arrow-left-line", class: "fill-current" %>
<%= t("decidim.forms.step_navigation.show.back") %>
</button>
Expand All @@ -14,7 +14,7 @@
**confirm_data
) %>
<% else %>
<button type="button" class="button button__sm button__secondary" data-toggle="<%= [next_step_dom_id, current_step_dom_id].join(" ") %>">
<button type="button" class="button button__sm button__secondary" data-toggle="<%= [next_step_dom_id, current_step_dom_id].join(" ") %>" data-survey-buttons>
<%= t("decidim.forms.step_navigation.show.continue") %>
<%= icon "arrow-right-line", class: "fill-current" %>
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ def current_step_dom_id
def confirm_data
{ data: {
confirm: t("decidim.forms.step_navigation.show.are_you_sure"),
disable: true
disable: true,
data: "survey-buttons"
} }
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<div class="meeting__aside-progress">
<span><%= t("remaining_slots", scope: "decidim.meetings.meetings.show", count: model.remaining_slots) %></span>
<progress value="<%= model.remaining_slots %>" max="<%= model.available_slots %>"></progress>
<span class="meeting__aside-progress-label"><%= model.remaining_slots %> / <%= model.available_slots %></span>
</div>
Loading

0 comments on commit 41607b5

Please sign in to comment.