Bootstrap dropdown height not updating with change in data - Angular - html

I'm sorry I'm reposting this but my previous question was marked as closed due to absence of minimal reproducible code. I have attached a stackblitz link with this one.
Original question - Bootstrap search-select dropdown but now when I try to search through the list and the dropdown opens on top, the list gets reduced and floats mid-air
Please don't close this one too. This is very important for my project and I have been stuck on this one for 16 hrs.
I have created a custom search select dropdown using the bootstrap dropdown in my Angular 8 project. The functionality works perfectly but the issue lies when the dropdown opens up on top of the element. Now when I search using the search box, the list gets updated accordingly and gets shortened but it keeps floating mid-air. You can see the issue in the GIF below.
Issue GIF - https://imgur.com/a/SeMVjns
As you can see in the first case when I search, the list gets shortened and it floats mid-air. What I want is when the list gets shortened, it still appear directly above the Expert dropdown button without any space in between.
Below is my HTML code:
<div class="dropdown h-100" [ngClass]="{'statusDropdownContainer': config.src != 'unavailability'}">
<a class="btn btn-white dropdown-toggle mb-2 statusFilterDropdown h-100 w-100 flex-middle" [ngClass]="{'btnDisable': disable, 'srcUnavailability': config.src == 'unavailability' }" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="text-truncate">{{config.src != 'unavailability' ? config.dropdownTitle + ':' : ''}} {{selectedValue ? selectedValue : 'All'}}</span>
</a>
<div class="dropdown-menu w-100 pt-0" aria-labelledby="dropdownMenuButton">
<input type="text" class="w-100 p-2 searchInput" [ngModel]="searchValue" (ngModelChange)="filterDropdown($event)" placeholder="{{config.placeholder}}">
<a *ngFor="let option of filteredList; let i=index" class="dropdown-item pointer text-contain" [ngClass]="{'alternateBackground': i%2 == 0 }" (click)="selectValue(option.name, option.unique_code)">
{{option.name}}
</a>
<div *ngIf="filteredList.length <=0" class="text-center text-muted mt-1">No {{config.user}} found</div>
</div>
</div>
What CSS changes do I need to make?
Stackblitz - https://stackblitz.com/edit/angular-ivy-nghkhp?file=src/app/app.component.html
Edit: I found the exact issue that's causing the problem. Bootstrap's dropdown uses popperJS to position the dropdown. PopperJS calculates the dropdown's position on the scroll event, so whenever the window scrolls, the dropdown's dimensions are recalculated (uses transform CSS). But in my case, since there is no scroll, the dimensions are not calculated and the dropdown has the same height as before the user searches something in the search box. But what I want is for the dimensions to be recalculated every time there is a change in the list's data. Any suggestions would be helpful. Thanks.

Bootstrap's dropdown uses popperJS to position the dropdown. PopperJS calculates the dropdown's position on the scroll event, so whenever the window scrolls, the dropdown's dimensions are recalculated (uses transform CSS).
One approach could be to give the height of the body slightly more than the viewport height and then programmatically trigger the scroll on ngModelChange
STACKBLITZ SOLUTION
You may add to CSS (You may also hide the vertical scroll-bar if not required with overflow-y: hidden;)
body {
min-height: 101vh;
}
And you might add scroll down 1px and scroll up 1px in your filterDropdown(e) method on ngModelChange as below to achieve the desired effect.
filterDropdown(e) {
console.log("e in filterDropdown -------> ", e);
window.scrollTo(window.scrollX, window.scrollY + 1);
let searchString = e.toLowerCase();
if (!searchString) {
this.filteredList = this.data.slice();
return;
} else {
this.filteredList = this.data.filter(
user => user.name.toLowerCase().indexOf(searchString) > -1
);
}
window.scrollTo(window.scrollX, window.scrollY - 1);
console.log("this.filteredList indropdown -------> ", this.filteredList);
}

Related

Unable to use arrow keys when focused on accordion (Wordpress)

I'm still learning about how to code for accessibility, and I'm hitting a wall on this wordpress website I'm working on that was developed by a previous developer. There's a page on our site that has accordion items that when focused on by tabbing end up disabling any movement with arrows keys (specifically, using the up and down arrow keys to scroll the page). When I tab past the accordion items, I am able to scroll with the arrow keys again.
I do notice that the html element has attributes that shift based on what's focused:
data-whatelement="button" when the arrow keys are still functioning, and data-whatelement="a" when they're not. I don't know if that's relevant here though.
Here's a snippit of one of the accordion items (the expand button is contained within)
<div id="General" class="sidebar-anchor" style="display: block;">
<div class="accordion-option">
<h2 class="font-s-1-5"><span style="width:90%;word-wrap: break-word;">General</span></h2><button
class="toggle-accordion" accordion-id="General" data-accordion="ssm-General"
aria-label="expand/collapse General questions and answers" type="button" role="tablist"
data-t="v84lyv-t"></button>
<style>
.accordion-option .toggle-accordion::before {
content: "Expand";
}
.accordion-option .toggle-accordion.General::before {
content: "Collapse";
}
</style>
<ul>
<li class="ssm-General" data-accordion="ssm-General" data-allow-all-closed="true" role="tablist"
data-t="ard470-t">
<div class="accordion-item" data-accordion-item="" data-parent="">
<a href="#" class="accordion-title" aria-controls="b4i8sa-accordion" role="tab"
id="b4i8sa-accordion-label" aria-expanded="false" aria-selected="false">Random Text</a>
<div class="accordion-content" data-tab-content="" role="tabpanel"
aria-labelledby="b4i8sa-accordion-label" aria-hidden="true" id="b4i8sa-accordion">
<div class="panel-body">
<p>Random Text</p>
</div>
</div>
</div>
</li>
<li class="ssm-General" data-accordion="ssm-General" data-allow-all-closed="true" role="tablist"
data-t="je376n-t">
<div class="accordion-item" data-accordion-item="" data-parent="">
<a href="#" class="accordion-title" aria-controls="2lmfgr-accordion" role="tab"
id="2lmfgr-accordion-label" aria-expanded="false" aria-selected="false">Random text</a>
<div class="accordion-content" data-tab-content="" role="tabpanel"
aria-labelledby="2lmfgr-accordion-label" aria-hidden="true" id="2lmfgr-accordion">
<div class="panel-body"></div>
</div>
</div>
</li>
<li class="ssm-General" data-accordion="ssm-General" data-allow-all-closed="true" role="tablist"
data-t="c1olgt-t">
</li>
</ul>
</div>
</div>
I figured this might have to do with how the JS is coded, but the JS to me seems to be relatively straight forward and wouldn't have any sort of affect on blocking arrow functions. Is there an attribute or class that's being added that somehow interferes in a way that I'm not aware of?
Here's the relevant JS for reference:
//Expand All and Collapse All functions for Qs and As
jQuery(function(){
jQuery(document).on('click', '.toggle-accordion', function(){
var accordionId = jQuery(this).attr("accordion-id");
var accordionData = jQuery(this).attr("data-accordion");
var classID = "."+ accordionData;
jQuery(this).toggleClass(accordionId);
triggerAccordions = document.querySelectorAll("."+accordionId);
console.log(triggerAccordions.length);
if (triggerAccordions.length == 0) {
collapseAll(classID);
} else {
expandAll(classID);
}
});
function collapseAll($class) {
jQuery($class).each(function () {
var $acc = jQuery(this);
var $openSections = $acc.find(".accordion-item.is-active .accordion-content");
$openSections.each(function (i, section) {
$acc.foundation("up", jQuery(section));
});
});
};
function expandAll($class) {
jQuery($class).each(function () {
var $acc = jQuery(this);
var $openSections = $acc.find(".accordion-item .accordion-content");
$openSections.each(function (i, section) {
$acc.foundation("down", jQuery(section));
});
});
};
});
It's not quite clear what you're asking so let me provide some accordion information to make sure we're all on the same page.
An accordion can have multiple sections that expand/collapse. Whether an expanded section collapses a previous expanded section is up to the developer. Some accordions allow multiple sections to be open and others only allow one section to be open.
When you have multiple sections (in theory, you could have an accordion with just one expandable section but that's generally called a disclosure widget, so in essence, an accordion is multiple disclosure widgets), you can treat the entire accordion as one tab stop OR you can treat each section in the accordion as a tab stop.
From a UX perspective, users are more aware of using TAB to navigate between elements rather than using an arrow key, but you'd have to do user testing to determine which is best for you.
In any event, if you treat the entire accordion as one tab stop, then the up/down arrow keys are generally used to move the focus between the accordion sections, similar to how a radio group works. In the aforementioned design spec, you'll see that arrow key implementation is optional.
The example in the spec uses tab to move between the sections.
Ignoring accordions for the moment, the arrow keys in a browser usually just scroll the page. Up/down for vertical scrolling and left/right for horizontal scrolling.
Now, with all that being said, one of your statements didn't make sense:
When I tab past the accordion items, I am able to move with the arrow keys again.
When you tab past the accordion so that your focus is now on an interactive element (link, button, etc) that is not associated with the accordion, what do you mean that you can move with the arrow keys? Are you saying you can navigate to different interactive elements using the arrow keys?

When bootstrap dropdown item is selected, button text disappears

I have a bootstrap dropdown menu. When I choose an item from the dropdown menu, the text of the button is changed to the text value of the choosen item (implemented by the help of this answer: https://stackoverflow.com/a/60029507/7061548).
I have to reload the window whenever an item from the dropdown menu has been chosen because I update the sessionstorage. This works fine after page reload. But I have a problem before reload. As soon as I click on an item from the dropdown menu, the menu button becomes fully green and the button text disappears just before the page is reloaded.
I found out that if I remove the class "btn-outline-success" from the button the problem seems to be gone. I have tried to apply css styles on various elements but didn't succeed in solving the problem.
Here is my code:
HTML:
<div class="m-dropdown">
<button type="button" class="btn btn-outline-succes m-btn dropdown-toggle" data-toggle="dropdown" aria-haspopup="true">
{{selectedColor}}
</button>
<div class="dropdown-menu">
<a class="dropdown-item" href="#" (click)="setColorGreen('Green')">Green</a>
<a class="dropdown-item" href="#" (click)="setColorBlue('Blue')">Blue</a>
</div>
</div>
TS:
...
selectedColor: string;
ngOnInit() {
this.selectedColor = "Green";
}
setColorGreen(color: string) {
this.selectedColor = "Green";
...
window.location.reload();
}
setColorBlue(color: string) {
this.selectedColor = "Blue";
...
window.location.reload();
}
...
As soon as I click on an item from the dropdown menu, the menu button becomes fully green and the button text disappears just before the page is reloaded.
When you click the button Bootstrap fires some JS to trigger the dropdown.
It's using the .dropdown-toggle class and the data-toggle="dropdown":
<button type="button" class="btn btn-outline-succes m-btn dropdown-toggle" data-toggle="dropdown" aria-haspopup="true">
I'm guessing that Bootstrap is marking the button as disabled so it can prevent double-clicks. And I'm guessing that the style change you're seeing is a result of some class changes that Bootstrap is making in JS as part of the button's click function.
If you have any defined CSS on the button, it could be conflicting with what Bootstrap is trying to do.
See the effect of clicking on a button on Bootstrap's docs page. Does your change look at all familiar?
You can further test this in Chrome DevTools by looking at what fires when you click the button. Then put a breakpoint there and click the button.
Or you can use DevTools to force element state on the button to see what happens in different states (:active, :hover, etc).

React Bootstrap dropup menu partially visible in the datagrid

The picture is worth a thousand words, basically what bothers me is the following issue:
I'm sorry for the blurred text but the company policy doesn't allow me to reveal much... As you can see from the screenshot, I have a dropdown menu (set to dropup) which isn't completely visible. This isn't the issue for most of the rows when the data grid is huge with lots of data, but it's always the issue for the first few rows or when there is just a few data in the data grid.
I have added some code used to the JSBin (not a working example) but only the parts of it because, well, company policy. I hope the code provided will be at least a little bit useful. Please note that this is built with React Bootstrap. I have included some of the JSX used as well as the CSS classes relevant to the data grid and HTML visible when the project is compiled.
https://jsbin.com/wuvuhemewo/edit?html,css,js,output
I cannot even remember everything I've tried, starting from adjusting z-index almost everywhere (desperate times require desperate measures) to trying every possible solution I could find on google and here on Stack Overflow.
Here is what I get once I expand the shy Options "dropup" menu:
<div class="datagrid__options-btn show dropup">
<button aria-haspopup="true" aria-expanded="true" id="dropdown-basic-button" type="button" class="dropdown-toggle btn btn-link">Options</button>
<div x-placement="top-start" aria-labelledby="dropdown-basic-button" class="dropdown-menu show" style="position: absolute; top: auto; left: 0px; margin: 0px; right: auto; bottom: 0px; transform: translate(2.4px, -16px);" data-popper-reference-hidden="false" data-popper-escaped="false" data-popper-placement="top-start">
Option1
Option2
Option3
Option4
Option5
Option6
Option7
</div>
UPDATE:
what I figured out in the meantime is that overflow-x is what is "eating" away the dropdown menu. When I remove it from these two:
<div class="dg-table__wrapper">
<div class="table-responsive">
the dropdown becomes visible, but now the datagrid is too wide and any try in adding the overflow just eats out the dropdown. I have found this on Stack Overflow: https://stackoverflow.com/a/6433475
Could it be that that is somehow the issue here?
You are correct, this issue is because of the overflow CSS property. It's not really elaborated in the docs, but you can utilize the prop popperConfig of Dropdown.Menu and make the menu have the CSS property position: fixed and have dynamic positioning thanks to Popper.js which is utilized by React Bootstrap.
const popperConfig = {
strategy: "fixed"
};
return (
<div style={containerStyle}>
<Row>
<Dropdown>
<Dropdown.Toggle variant="success" id="dropdown-basic">
Fixed Popper
</Dropdown.Toggle>
<Dropdown.Menu popperConfig={popperConfig}>
<Dropdown.Item href="#/action-1">Action</Dropdown.Item>
<Dropdown.Item href="#/action-2">Another action</Dropdown.Item>
<Dropdown.Item href="#/action-3">Something else</Dropdown.Item>
<Dropdown.Item href="#/action-4">Something else</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
...
CodeSandBox: https://codesandbox.io/s/react-bootstrap-fixed-dropdown-popper-fqdnu?file=/src/App.js
Drop Directions: https://react-bootstrap.netlify.app/components/dropdowns/#drop-directions
On the same Popper.js documentation I linked, there are also other useful techniques such as Using Modifiers like Prevent Overflow - which can be an alternative solution to the issue. It boils down to the developer's UX perspective.
In addition, here is alternate solution I've written about recently using reactstrap instead of react-bootstrap: Reactstrap DropdownMenu bottom overflow issue
995faf8e76605e973 answer https://stackoverflow.com/a/63633030/14181063 helped! This is how it looks after modifying my code:
const popperConfig = {
strategy: "fixed"
};
<Dropdown id="dropdown-basic-button" drop="up" className="datagrid__options-btn">
<Dropdown.Toggle variant="link">Options</Dropdown.Toggle>
<Dropdown.Menu popperConfig={popperConfig}>
{row.options.map((action, i) => {
return (
<Dropdown.Item
key={i}
onClick={(e) => {
e.preventDefault();
functions.doAction(action, row.originalIndex);
}}
>
{action}
</Dropdown.Item>
);
})}
</Dropdown.Menu>
</Dropdown>
Also, in the meantime, I have figured out that adding style={{position: "static"}} to the DropdownButton element works too! However, I will stick to the solution posted by 95faf8e76605e973 as it seems more correct :)
<DropdownButton
id="dropdown-basic-button"
title="Options"
variant="link"
drop="up"
style={{ position: "static" }}
className="datagrid__options-btn"
>

fxHide.gt-sm Not working

I have the following div and it shows a login button on a nav bar, but if the screen is small I want to hide this button. For some reason when I add the fxHide.gt-sm=true it doesn't hide when I make the screen smaller. How can I fix this?
<div fxHide.gt-sm="true">
<ng-template #login>
<button
mat-icon-button
[routerLink]="['/auth']"
[style.width]="'auto'"
[style.overflow]="'visible'"
matTooltip="Login or Register"
class="topbar-button-right">
<span>Login</span>
<mat-icon>exit_to_app</mat-icon>
</button>
</ng-template>
</div>
What you want:
Default behavior: shown
If lt-md (less than medium screen) => hide
With your current implementation you have:
Default behavior: shown
If gt-sm (greater than small screen) => hide
Now, what you want translates into:
<div fxShow fxHide.lt-md>
You could invert the logic into
<div fxHide fxShow.gt-sm>
That should do it.
fxHide.gt-sm means hide it when it's greater than small. Change it to fxShow.gt-sm if you only want it visible on larger screen.
Faced the same thing but after importing FlexLayoutModule it worked.
If the OP is having issues making the fxHide command work at all, this question may be a duplicate of Angular Material FlexLayout fxHide does not hide
You must ensure that FlexLayout is imported in all modules that wish to use it:
https://stackoverflow.com/a/62672981/4440629

Targeting a DIV in HTML

How can I target a div without missing the title part? Let's say I have this code:
<div class="row featurette" id="Feeding">
<div class="col-md-7">
<h2 class="featurette-heading">1. Transformational Feeding </h2>
<p class="lead text-justify">As TransDev provides supplemental feeding (lunch three times per week) to undernourished school children, the mothers of the participating children are encouraged to plant vegetables to ensure availability of food on the table. With the increase in price of commodities, food production, will provide an additional income for the family.</p>
</div>
<div class="col-md-5">
<img class="featurette-image img-responsive" src="contents/gallery/7.jpg" alt="Generic placeholder image">
</div>
</div>
and for the button:
<a class="btn btn-warning" href="index.php?pl1=about#Feeding" role="button">Learn more</a>
But whenever I click the button, it looks like this:
Output
When it should look like this:
How it should be
So maybe it's my navbar that messes the content? Is it something that I can fix with css? I tried putting the ID on the <h2>, but the result is the same.
Your code is working correctly, clicking you anchor scrolls the page so that the h2 is in the top. Problem is with your menu that has fixed positioning and is overlapping it.
This should be fixable with JS by overriding default a behaviour and writing your custom logic that adds the height of your navbars to the target scroll position, like so:
function scrollToAnchor(id){
var navbarHeight = $(".menu").height(); // or whatever seletor you use for navbar
var aTarget = $(id);
$('html,body').animate({scrollTop: aTarget.offset().top-navbarHeight},'slow');
}
You need to trigger this function when clicking on an item that is supposed to do the scroll. Assuming your a looks like this:
<a class="menu-item btn btn-warning" href="Feeding" role="button">Learn more</a>
you can use do:
$('a.menu-item').click(function(){
scrollToAnchor($(this).attr('href'));
});
But if you want to use a full path in your a that does the page reload and than scrolls to the id than your're gonna need to play with reading an url, extracting value after hash and than triggering scrollToAnchor, like t his:
$(document).ready(function(){
if(window.location.hash) {
var hash = window.location.hash;
scrollToAnchor(hash);
}
});
Here's the working demo on jsFiddle: http://jsfiddle.net/ony0590k/