How to control focussed option while options change order with react-select - react-select

I'm using a search feature which reorders the options as you type. React-select maintains the focus of what was originally the first option, even if that option then moves to a different place in the list. So when you type the focus jumps up and down the screen. I'd like the focussed option to stay as the first option while typing.
Is there any way to control which option is focussed?

Adding a randomly generated "value" property to the options passed into react-select seems to create the functionality I was looking for.
// code for loadOptions prop in react-select's AsyncCreatable
import uniq from "uniqid";
// code to get search results
const resultsWithRandomValue = results.map(r => ({ ...r.item, value: uniq() }));
It's not ideal, but anything else would need changes to react-select, and I'm not sure how much need there is for this behaviour in general.

Related

Load options on the first open of the Async drop down menu

When I provide loadOptions to an Async control it loads options on mount.
If I pass autoload={false} then it doesn't load options neither on mount nor on open. But it loads options on the first close (or type, or blur).
If I pass onCloseResetsInput={false} then it doesn't load options until I type something. (showing "Type to search" in the menu)
Async provides onOpen handler, but I didn't find the way to use it in this situation. (and react-select#2.0.0-alpha.2 doesn't have it)
So the user needs to type a character, then delete it, to see the full list of options.
How can this be avoided?
Example sandbox: https://codesandbox.io/s/mjkmowr91j
Solution demo: https://codesandbox.io/s/o51yw14l59
I used the Async options loaded externally section from the react-select repo.
We start by loading the options on the Select's onFocus and also set the state to isLoading: true. When we receive the options we save them in the state and render them in the options.
I also keep track of optionsLoaded so that only on the first focus do we trigger the call to get options.
In our use case, we have several of these select inputs on a single page, all async, so the requests to the server will pile up, and are completely unnecessary in a lot of cases (users won't even bother clicking).
I found a workaround for this issue that'll work for my use case on 2.0.0-beta.6:
Include defaultOptions
Add 2 members to your class that will store the resolve/reject methods for the promise.
In your loadOptions function, check if the input is '', if so, create a new promise, and store the values of resolve/reject within your class members, and return that promise. Otherwise, just return the promise normally to get your results.
Add an onFocus handler, and within it call the function to get your results, but also add .then and .catch callbacks passing the resolve and reject functions you stored previously.
Essentially, this makes react-select think you're working on getting the results with a long-running promise, but you don't actually even try to load the values until the field is selected.
I'm not 100% positive there aren't any negative side effects as I just wrote this, but it seems like a good place to start.
Hope this helps someone. I may submit a feature request for this.
In order to load options when user focus first time, set defaultOptions={true}
Thanks, Alexei Darmin for the solution, I was struggling with this... while testing it I converted the solution to a react functional component and added real API fetching.
Here is a working demo, I hope it helps someone

how to display multi level menu using ListViews

I have a set of multilevel data to be display using abovementioned component. Usually in PHP I simply iterate the data to display it as li but coming from web background, I just can't put it all together when using react-native. What is the right way to display a set of menu
FYI, I'm also using react-native-router-flux to manage the router.
If you want to build you own menu, you'll have to customize the renderRow of your ListView. If you take a look at RN's official doc on ListView here, it shows that you can specify a sectionID, for example.
You can then specify how each row renders.
renderRow: function (rowData, sectionID, rowID, highlightRow) => renderable
Takes a data entry from the data source and its ids and should return
a renderable component to be rendered as the row. By default the data
is exactly what was put into the data source, but it's also possible
to provide custom extractors. ListView can be notified when a row is
being highlighted by calling highlightRow(sectionID, rowID). This sets
a boolean value of adjacentRowHighlighted in renderSeparator, allowing
you to control the separators above and below the highlighted row. The
highlighted state of a row can be reset by calling highlightRow(null).
Another option is using open source modules built by the community.
The one closest to your needs I could find is : https://github.com/jaysoo/react-native-menu

safari OSX voiceover not reading aria label for input

I'm trying to get voiceover to work on safari however, it seems when I tab through elements it doesnt read out the aria-label of the new input box in a certain scenario.
Scenario:
When tabbing onto the next element and the on blur of the current element does something to the dom then it will not read out the aria-label of the next element.
Here is an example
http://plnkr.co/edit/x0c67oIl0wlQEguBIQVZ?p=preview
Notice if you take out the onblur function below then it works fine.
<input id="test" onblur="blurer()" onfocus="focuser()"/>
In this case, the issue isn't the presence of a blurer, but rather the contents of your blurer and corresponding focuser functions. Together these two functions are toggling the hidden state of a nearbye element. This is interupting the announcement. There's a role announcement that also occurs. The full annoucement (when text is populated in the edit text control) should be:
"The edited text" contents selected/unselected, "your aria label", edit text.
The quoted portions are parts you control, the other portions are parts controlled by the OS/VoiceOver's interaction with it, calculated automatically by the state of the control and other aria values.
The announcement we're getting is simply
"The edited text"
So, it's not an issue with the aria-label specifically. But rather, you are causing the entire announcement of the element to be interrupted.
When your blur and focus functions trigger you muck with the VoiceOver's response (or the OS's communication of) these events. Not sure what about your functions is causing this. Regardless, a trick that helps in these circumstances is to add a setTimeout to your code. By separating your function and the actual focus/blur event, you can allow the accessibility APIs to do their thing, before mucking with styles and such on the page. Here is an example that makes your little code snippet work. Just replace the contents of your javascript file with this:
function blurer(){
window.setTimeout(function() {
document.getElementById('myDiv').style.display = 'none';//
}, 0);
}
function focuser(){
window.setTimeout(function() {
document.getElementById('myDiv').style.display = 'block';//
}, 0);
}
In general I like to avoid setTimeouts because they create race conditions. However, setTimeouts of 0 are acceptable, because there is no race condition. You're just decoupling the firing event and the execution of your code by pushing your code to the end of the queue. When hacking around VoiceOver, setTimeout(someFunction, 0) works quite well for a lot of cases.

Win8 JS App: How can one prevent backward navigation? Can't set WinJS.Navigation.canGoBack

Fairly new to developing for Windows 8, I'm working on an app that has a rather flat model. I have looked and looked, but can't seem to find a clear answer on how to set a WinJS page to prevent backward navigation. I have tried digging into the API, but it doesn't say anything on the matter.
The code I'm attempting to use is
WinJS.Navigation.canGoBack = false;
No luck, it keeps complaining about the property being read only, however, there are no setter methods to change it.
Thanks ahead of time,
~Sean
canGoBack does only have a getter (defined in base.js), and it reflects the absence or presence of the backstack; namely nav.history.backstack.
The appearance of the button itself is controlled by the disabled attribute on the associated button DOM object, which in turn is part of a CSS selector controlling visibility. So if you do tinker with the display of the Back button yourself be aware that the navigation plumbing is doing the same.
Setting the backstack explicitly is possible; there's a sample the Navigation and Navigation History Sample that includes restoring a history as well as preventing navigation using beforenavigate, with the following code:
// in ready
WinJS.Navigation.addEventListener("beforenavigate", this.beforenavigate);
//
beforenavigate: function (eventObject) {
// This function gives you a chance to veto navigation. This demonstrates that capability
if (this.shouldPreventNavigation) {
WinJS.log && WinJS.log("Navigation to " + eventObject.detail.location + " was prevented", "sample", "status");
eventObject.preventDefault();
}
},
You can't change canGoBack, but you can disable the button to hide it and free the history stack.
// disabling and hiding backbutton
document.querySelector(".win-backbutton").disabled = true;
// freeing navigation stack
WinJS.Navigation.history.backStack = [];
This will prevent going backward and still allow going forward.
So lots of searching and attempting different methods of disabling the Back Button, finally found a decent solution. It has been adapted from another stackoverflow question.
Original algorithm: How to Get Element By Class in JavaScript?
MY SOLUTION
At the beginning of a fragment page, right as the page definition starts declaring the ready: function, I used an adapted version of the above algorithm and used the resulting element selection to set the disabled attribute.
// Retrieve Generated Back Button
var elems = document.getElementsByTagName('*'), i;
for (i in elems)
{
if((" "+elems[i].className+" ").indexOf("win-backbutton") > -1)
{
var d = elems[i];
}
}
// Disable the back button
d.setAttribute("disabled", "disabled");
The code gets all elements from the page's DOM and filters it for the generated back button. When the proper element is found, it is assigned to a variable and from there we can set the disabled property.
I couldn't find a lot of documentation on working around the default navigation in a WinJS Navigation app, so here are some methods that failed (for reference purposes):
Getting the element by class and setting | May have failed from doing it wrong, as I have little experience with HTML and javascript.
Using the above method, but setting the attribute within the for loop breaks the app and causes it to freeze for unknown reasons.
Setting the attribute in the default.js before the navigation is finished. | The javascript calls would fail to recognize either methods called or DOM elements, presumably due to initialization state of the page.
There were a few others, but I think there must be a better way to go about retrieving the element after a page loads. If anyone can enlighten me, I would be most grateful.
~Sean R.

Checking if an element is really visible to the user

I'd like to check whether the user can see an element in the current web browser view without scrolling.
What I've found can check whether the element is somewhere on the page.
Another hint suggested to check the elements position but then I would need to get the dimensions of the visible window of the browser plus its x/y offset to 0/0.
I would be grateful if someone could point me to a solution that does not need JavaScript code.
How do you define 'visible to the user'? How do you propose to check it?
If it has a height? If it isn't hidden by CSS? What if it's parent element is hidden by CSS?
The most reliable way will be to use Selenium's built in .Displayed property (if you are using C#, Java has something similiar), and combine it with jQuery's 'visible' selector: http://api.jquery.com/visible-selector/. Example below, in C#.
var element = Driver.FindElement(By.Id("test"));
bool isVisible = element.Displayed;
var javascriptCapableDriver = (IJavascriptExecutor)Driver;
bool jQueryBelivesElementIsVisible = javascriptCapableDriver.ExecuteScript("return $('#myElement').is(:visible);");
bool elementIsVisible = isVisible && jQueryBelievesElementIsVisible;
This will get the majority of cases.
It isn't possible without client side code, or in the preparation that someone else finds a way that it can be done in a server side language, I highly doubt it will be pretty, readable or reliable.
jQuery has the offset() method:
http://api.jquery.com/offset/
This, however, won't work when taking into account borders, margins, that kind of stuff.