Skip to content

Commit

Permalink
Issue #3144 - Bootstrap 5 accordion replaces old accordion for:
Browse files Browse the repository at this point in the history
-  Usage Statistics page.
- Write Plan on Plan's page.
- Template Phase Details accordion sections.
  • Loading branch information
John Pinto committed Aug 3, 2023
1 parent 236affb commit dfa50e7
Show file tree
Hide file tree
Showing 22 changed files with 367 additions and 265 deletions.
57 changes: 57 additions & 0 deletions app/assets/stylesheets/blocks/_accordion.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@use '../variables/colours' as *;
@use "../../../../node_modules/bootstrap/scss/bootstrap" as *;

// Remove box-shadow and highlight from Accordion header
.accordion-header a {
text-decoration: none !important;
box-shadow: none !important;
}

.accordion-header a:hover,
.accordion-header a:active,
.accordion-header a:focus,
.accordion-header a:focus-visible {
text-decoration: none !important;
box-shadow: none !important;
outline: none !important;
}


// move fa-arrows-alt to right
h2.accordion-header {
display: flex !important;
align-items: center !important;
color: $color-accordion-button !important;
background-color: $color-accordion-button-bg !important;
}

h2.accordion-header > a.i {
margin-left: auto !important;
}
// The Accordion color variables used are set in _colors.scss
.accordion-button {
font-size: inherit;
color: $color-accordion-button !important;
background-color: $color-accordion-button-bg !important;
}

// An explanation of how we change the Bootstrap arrow icon for the accordion to use the footawesome icons.
// First we get the fontawesome plus.svg and minus.svg for use in background image from:
// node_modules/@fortawesome/fontawesome-free/svgs/solid/plus.svg (in .accordion-button.collapsed::after)
// node_modules/@fortawesome/fontawesome-free/svgs/solid/minus.svg
// We then encode the svg.
// Next the svg fill attribute is added with color set by a variable $color-accordion-button-icon in the _colours.scss.
// This allows us to change color of symbols.
// "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512' fill='#{$color-accordion-button-icon}'
// SVG we are using are encoded with fill added as fill='#{$color-accordion-button-icon}':
// After encoding # symbols in svg with the unicode %23. Hence the use of the replace function.
// We could have done this without str-replace by adding encoded fill='%23{$color-accordion-button-icon}'.
// We now use svg as the background-image for the .accordion-button class as follows.
.accordion-button::after, .accordion-button.collapsed::after {
background-image: str-replace(url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512' fill='#{$color-accordion-button-icon}' %3E%3C!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --%3E%3Cpath d='M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z'/%3E%3C/svg%3E"), '#', '%23') !important;
}

.accordion-button:not(.collapsed)::after {
background-image: str-replace(url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 448 512' fill='#{$color-accordion-button-icon}' %3E%3C!-- Font Awesome Free 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --%3E%3Cpath d='M416 208H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h384c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z'/%3E%3C/svg%3E"), '#', '%23') !important;
transition: all 0.5s;
}
1 change: 1 addition & 0 deletions app/assets/stylesheets/blocks/_index.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@use 'accessibility';
@use 'accordion';
@use 'alerts';
@use 'autocomplete';
@use 'buttons';
Expand Down
15 changes: 7 additions & 8 deletions app/assets/stylesheets/blocks/_spinner.scss
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@

.spinner-border {
position: fixed;
top: 48%;
left: 43%;
animation: spinner-border 1.5s linear infinite !important;
position: fixed !important;
top: 48% !important;
left: 43% !important;
width: 2rem !important;
height: 2rem !important;
}

.spinner-border img {
height: 80px;
width: 80px;
}
9 changes: 9 additions & 0 deletions app/assets/stylesheets/utils/_functions.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@function str-replace($string, $search, $replace: '') {
$index: str-index($string, $search);

@if $index {
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}

@return $string;
}
1 change: 1 addition & 0 deletions app/assets/stylesheets/utils/_index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
@use 'font_size';
@use 'icons';
@use 'margins';
@use 'functions';
9 changes: 9 additions & 0 deletions app/assets/stylesheets/variables/_colours.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ $color-footer-background: $color-navbar-background;
$color-tooltip-background: $color-primary-background;
$color-icon-bar-background: $color-white;


// Text
$color-primary-text: $color-white;
$color-secondary-text: $color-grey;
Expand Down Expand Up @@ -64,3 +65,11 @@ $color-fa: $color-grey;

// Focus colors
$color-focus-outline: $color-blue-alice-darkest;

// Accordion colors
$color-accordion-button: $color-white;
$color-accordion-button-icon: $color-white;
$color-accordion-button-bg: $color-grey-darkest;
$color-accordion-button-active-bg: darken($color-accordion-button-bg, 10%) ;


20 changes: 10 additions & 10 deletions app/javascript/src/orgAdmin/phases/newEdit.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// import 'bootstrap-sass/assets/javascripts/bootstrap/collapse';
import { Tinymce } from '../../utils/tinymce.js';
import { Tinymce } from '../../utils/tinymce';
import { isObject, isString } from '../../utils/isType';
import getConstant from '../../utils/constants';
import { addAsterisks } from '../../utils/requiredField';
Expand Down Expand Up @@ -61,7 +61,7 @@ $(() => {
// For some reason the toolbar options are retained after the call to Tinymce.init() on
// the views/notifications/edit.js file. Tried 'Object.assign' instead of '$.extend' but it
// made no difference
const prefix = 'collapseSection'
const prefix = 'collapseSection';
let sectionId = selector;
if (sectionId.startsWith(prefix)) {
sectionId = `sc_${sectionId.replace(prefix, '')}_section_description`
Expand Down Expand Up @@ -90,20 +90,20 @@ $(() => {

// Attach handlers for the Section expansion
$(parentSelector).on('ajax:before', 'a.ajaxified-section[data-remote="true"]', (e) => {
const panelBody = $(e.target).parent().find('.panel-body');
return panelBody.attr('data-loaded') === 'false';
const accordionBody = $(e.target).parents('.accordion-item').find('.accordion-collapse').find('.accordion-body');
return accordionBody.attr('data-loaded') === 'false';
});

$(parentSelector).on('ajax:success', 'a.ajaxified-section[data-remote="true"]', (e) => {
const panelBody = $(e.target).parent().find('.panel-body');
const panel = panelBody.parent();
if (isObject(panelBody)) {
const accordionBody = $(e.target).parents('.accordion-item').find('.accordion-collapse').find('.accordion-body');
const accordionCollapse = accordionBody.parents('.accordion-collapse');
if (isObject(accordionBody)) {
// Display the section's html
panelBody.attr('data-loaded', 'true');
panelBody.html(e.detail[0].html);
accordionBody.attr('data-loaded', 'true');
accordionBody.html(e.detail[0].html);

// Wire up the section
initSection(`${panel.attr('id')}`);
initSection(`${accordionCollapse.attr('id')}`);
}
});

Expand Down
121 changes: 94 additions & 27 deletions app/javascript/src/utils/accordion.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,96 @@
* <a href="#" data-toggle-direction="hide"><%= _('collapse all') %></a>
* </div>
*
* Your accordion should follow the Boostrap 3.x layout:
* Your accordion should follow the Boostrap 5.x layout:
* ------------------------------------------------------------
* <div id="accordion" class="panel-group" role="tablist" aria-multiselectable="true">
* <div class="panel panel-default">
* <div class="panel-heading" role="tab" id="headingA">
* <h2 class="panel-title">
* <a role="button" data-toggle="collapse" data-parent="accordion"
* href="#collapseA" aria-controls="collapseA" aria-expanded="false">
* Section A
* </a>
* </h2>
n*
* <div class="accordion" id="accordionDefault">
*
* <div class="accordion-item">
* <h2 class="accordion-header" id="headingDefaultOne">
* <button
* class="accordion-button"
* type="button"
* data-bs-toggle="collapse"
* data-bs-target="#collapseDefaultOne"
* aria-expanded="false"
* aria-controls="collapseDefaultOne"
* >
* Accordion Item #1
* </button>
* </h2>
*
* <div
* id="collapseDefaultOne"
* class="accordion-collapse collapse show"
* aria-labelledby="headingDefaultOne"
* data-bs-parent="#accordionDefault"
* >
* <div class="accordion-body">
* Accordion Body #1
* </div>
* </div>
*
* </div>
*
* <div class="accordion-item">
* <h2 class="accordion-header" id="headingDefaultTwo">
* <button
* class="accordion-button collapsed"
* type="button"
* data-bs-toggle="collapse"
* data-bs-target="#collapseDefaultTwo"
* aria-expanded="false"
* aria-controls="collapseDefaultTwo"
* >
* Accordion Item #2
* </button>
* </h2>
*
* <div
* id="collapseDefaultTwo"
* class="accordion-collapse collapse"
* aria-labelledby="headingDefaultTwo"
* data-bs-parent="#accordionDefault"
* >
* <div class="accordion-body">
* Accordion Body #2
* </div>
* <div id="collapseA" class="panel-collapse collapse" role="tabpanel"
* aria-labelledby="headingA">
* <div class="panel-body">
* This is test section A.
* </div>
* </div>
*
* </div>
*
* <div class="accordion-item">
* <h2 class="accordion-header" id="headingDefaultThree">
* <button
* class="accordion-button collapsed"
* type="button"
* data-bs-toggle="collapse"
* data-bs-target="#collapseDefaultThree"
* aria-expanded="false"
* aria-controls="collapseDefaultThree"
* >
* Accordion Item #3
* </button>
* </h2>
*
* <div
* id="collapseDefaultThree"
* class="accordion-collapse collapse"
* aria-labelledby="headingDefaultThree"
* data-bs-parent="#accordionDefault"
* >
* <div class="accordion-body">
* Accordion Body #3
* </div>
* </div>
*
* </div>
*
* </div>
*
*/

$(() => {
$('body').on('click', '.accordion-controls a', (e) => {
e.preventDefault();
Expand All @@ -38,23 +107,21 @@ $(() => {
const direction = target.attr('data-toggle-direction');
const parentTargetName = currentTarget.parent().attr('data-parent');
if (direction) {
// Selects all .panel elements where the parent is currentTarget.attr('data-parent') and
// after gets the immediately children whose class selector is panel-collapse
// Selects all .accordion-item elements where the parent is
// currentTarget.attr('data-parent') and
// after gets the immediately children whose class selector is accordion-item
const parentTarget = $(`#${parentTargetName}`).length ? $(`#${parentTargetName}`) : $(`.${parentTargetName}`);
$(parentTarget).find('.panel').find('.panel-collapse').each((i, el) => {
const panelCollapse = $(el);
// Expands or collapses the panel according to the
parentTarget.find('.accordion-item').each((i, el) => {
const accordionItem = $(el);
// Expands or collapses according to the
// direction passed (e.g. show --> expands, hide --> collapses)
if (direction === 'show') {
if (!panelCollapse.find('.panel-body').attr('data-loaded') || !panelCollapse.hasClass('in')) {
panelCollapse.prev()[0].click();
}
accordionItem.find('.accordion-button').addClass('collapsed');
accordionItem.find('.accordion-collapse').addClass('show');
} else {
panelCollapse.collapse(direction);
accordionItem.find('.accordion-button').removeClass('collapsed');
accordionItem.find('.accordion-collapse').removeClass('show');
}
// Sets icon at panel-title accordingly
panelCollapse.prev().find('i.fa')
.removeClass('fa-plus fa-minus').addClass(direction === 'show' ? 'fa-minus' : 'fa-plus');
});
}
});
Expand Down
11 changes: 5 additions & 6 deletions app/views/guidance_groups/_index_by_theme.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,26 @@
<% question_guidance_id = "#{question.object_id}-#{guidances.object_id}" %>
<%# if guidances with this theme have not been output %>
<% if (guidances.map(&:id) - guidances_output).any? %>
<div class="card card-default">
<div class="accordion-item card card-default">
<div
class="heading-button"
class="accordion-button collapsed"
role="button"
data-toggle="collapse"
href="<%= "##{question_guidance_id}" %>"
aria-expanded="false"
aria-controls="<%= "##{question_guidance_id}" %>">
<div class="card-heading" role="tab" id="<%= "card-heading-#{question_guidance_id}" %>">
<h2 class="card-title">
<h2 class="accordion-header">
<%= theme.title %>
<i class="fas fa-plus pull-right" aria-hidden="true"></i>
</h2>
</div>
</div>
<div
id="<%= "#{question_guidance_id}" %>"
class="card-collapse collapse"
class="accordion-collapse collapse"
role="tabpanel"
aria-labelledby="<%= "card-heading-#{question_guidance_id}" %>">
<div class="card-body">
<div class="accordion-body">
<% multiple = false %>
<% guidances.each do |guidance| %>
<% if multiple %>
Expand Down
13 changes: 6 additions & 7 deletions app/views/guidance_groups/_show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,22 @@
</div>
<% end %>
<% group.keys.each_with_index do |theme, i| %>
<div class="card card-default">
<div class="heading-button" role="button" data-toggle="collapse"
data-parent="<%= question.id %>-guidance"
<div class="accordion-item card card-default">
<div class="accordion-button collapsed" role="button" data-bs-toggle="collapse"
data-bs-parent="#<%= question.id %>-guidance"
href="#collapse-guidance-<%= question.id %>-<%= guidance_accordion_id %>-<%= i %>"
aria-expanded="false"
aria-controls="#collapse-guidance-<%= question.id %>-<%= guidance_accordion_id %>-<%= i %>">

<div class="card-heading" role="tab" id="heading-<%= i %>">
<h2 class="card-title">
<h2 class="accordion-header">
<%= theme %>
<i class="fas fa-plus pull-right" aria-hidden="true"></i>
</h2>
</div>
</div>
<div id="collapse-guidance-<%= question.id %>-<%= guidance_accordion_id %>-<%= i %>" class="card-collapse collapse" role="tabpanel"
<div id="collapse-guidance-<%= question.id %>-<%= guidance_accordion_id %>-<%= i %>" class="accordion-collapse collapse" role="tabpanel"
aria-labelledby="heading-guidance-<%= question.id %>-<%= guidance_accordion_id %>-<%= i %>">
<div class="card-body">
<div class="accordion-body">
<% group[theme].each do |guidance| %>
<%= sanitize guidance %>
<% end %>
Expand Down
Loading

0 comments on commit dfa50e7

Please sign in to comment.