Fluent UI and ReactDOM.createPortal bug - reactjs

I'm currently working with Fluent UI to create a web app, and in this app I'm attempting to create a Fluent UI Dialog with a custom component that contains a Fluent UI ComboBox inside of it. I'm calling ReactDOM.createPortal on return from the render method inside of my custom React object. Upon first use of the Dialog my ComboBox functions as expected, i.e. the dropdown shows properly and it allows me to select and interact with my page as intended. However, when closing the Dialog after having already interacted with the ComboBox, upon re-opening the Dialog the ComboBox no longer displays the dropdown (although I can see the options are still there, just somewhere in the background). I've tried adjusting the zIndex of the ComboBox, it's container, etc. with no luck. I'm starting to think that it's an issue with ReactDOM.createPortal whenever the component is re-rendered after having been interacted with in a previous render, and figured I'd see if anyone has ever tried to do something similar. Thanks so much in advance!
render() {
const {options, changeOptions} = this.store;
return (
<>
{!!this.item && buttonTexts.length && ReactDOM.createPortal(
<ComboBox
label='Title'
selectedKey={changeOptions}
onChange={this.changeOptionDisplaying}
options={options.map(text => ({ key: text, text }))}
/>,
this.item
)}
<div ref={this.setRef} dangerouslySetInnerHTML={{ __html: this.props.htmlString}} />
</>
)
}
// this.item is an HTMLDivElement

Related

Icon is not visible in PivotItem in fluent ui

I am creating a view using fluent in react. I used to pivot to display the top navigation but the icon in the tab is not visible even on using the itemIcon property in the PivotItem.
This is the code snippet I used
return <div>
<Pivot>{
headerItems.map( (item: IHeaderItem) => (
<PivotItem itemKey = {item.id} headerText = {item.name} itemIcon="Globe">
</PivotItem>
))
}
</Pivot>
</div>
By default, the font-based Fluent UI icons are not added to your bundle or loaded on the page, in order to save bytes for scenarios where you don't care about icons, or you only care about a subset.
To make the icons available, you may initialize them as follows. Note that initializeIcons() should only be called once per app and must be called before rendering any components. This is typically done in the app's top-level file just before the main ReactDOM.render() call.
import { initializeIcons } from '#fluentui/react/lib/Icons';
initializeIcons();

useState set method for modal not updating

I am trying to build a simple todo list. Basically there is a list of items. When the user clicks the 'edit' button on an item, selectedTodoItem is updated. Then a modal is shown which uses selectedTodoItem as prop.
I am also using ant.design library for the list.
Here is my code (with irrelevant bits left out):
const [selectedTodoItem, setSelectedTodoItem] = useState(selectedTodoList.items[0]);
const editItemHandler = (item) => {
setSelectedTodoItem(item);
dispatch(toggleEditTodoItemModalVisible());
}
return(
<List
className="whitebg listItems"
datasource={selectedTodoList.items}
renderItem={(item) => (
<TodoItemInList item={item} editHandler={editItemHandler} />
)}
/>
<TodoItemEditModal item={selectedTodoItem} />
);
<TodoItemInList> is basically a label and some buttons, one of which calls editHandler(item).
Here's a screenshot so you can get a better idea:
The problem is, when I load the page and click edit (blue, don't mind the delete icon) on an item, say 'Foundation', the edit modal loads fine. But when I click on another blue button, the modal still shows 'Foundation'. No matter what button I click, the modal only shows the first clicked item. It seems like selectedTodoItem is updating though, according to console.log(), but the modal's prop doesn't update with it.
Any nudge in the right direction would be much appreciated.

React widgets combobox; how to clear input or prevent selection

I'm using the combobox from React Widgets as a search UI component.
I've put in a custom render item so that when you click a search result in the dropdown, you navigate to the relevant page.
However when you select a result, the name of the selected item goes into the text input, which isn't what a user will expect when they select a search result. I think they'd expect the search term to remain, or perhaps the input to be cleared.
I like the Combobox component and haven't found another UI widget that would do what I want, so I'd like to find a solution.
Is there some way to override the selection behaviour so that clicking a list item doesn't select it? I've tried setting the 'onSelect' property but this doesn't suppress the default selection behaviour, it just adds extra functionality.
Alternatively is there a way to manually set the selection to null? The docs don't seem to show anything. I tried getting the input node's value manually to '' with reactDOM, but the value didn't change. I would guess that the component controls it.
I've wrapped the Combobox in a functional component:
function Search(props) {
...
const onSelect = (value) => {
const node = ReactDOM.findDOMNode(Search._combobox);
const input = node.getElementsByTagName('input')[0];
input.value = '';
}
return (
<Combobox
ref={(c) => Search._combobox = c}
onSelect={onSelect}
textField="name"
valueField="_id"
/>
);
}
If I set the value prop of the Combobox then it is impossible to type into it.
Any suggestions? Thank you.
The solution I found is to create my own search controls using an input and a button, and hide the native input and button with display: none. "componentDidUpdate" detects when new search results arrive and opens the dropdown to show them.
There is a manually-added 'show more...' entry at the end of search results. Clicking this increases the search limit for that group. That's the main reason I wanted to avoid showing the clicked result in the text input. The custom input is not affected by the user's selection, it always shows the search term.
My search component now looks something like this:
<div className="search">
<div className="search-controls">
<Input
onChange={this.onChangeInput}
type="text"
/>
<Button
onClick={this.toggleOpen}
title="toggle results"
>
<FontAwesomeIcon icon={['fas', 'search']} style={{ 'color': iconColors.default }} size="1x" />
</Button>
</div>
<Combobox
busy={isSearching}
data={searchResults}
onChange={() => {}}
open={open}
onSelect={this.onSelect}
textField="name"
valueField="_id"
/>
</div>

Setting state in a subcomponent of react-table closes component resetting all states with ReactJS

I've been using react-table heavily throughout my React app which has been so useful but now I've got a weird bug I believe
I've got a react table with a subcomponent structured as such
<ReactTable
data={Data}
columns={columns}
SubComponent={row => {
return (
<div>
<p>I am the subcomponent</p>
<button onClick={this.toggleHidden.bind(this)} >
Click to additional information
</button>
{!this.state.isHidden && <p>Hello world</p>}
</div>
);
}}
/>
And here's what that looks like in the browser - the blue button on the left opens and closes the subcomponent and that works just fine, I've used subcomponents like this throughout my React App with no problems
When clicking the 'Click to additional information' button I want to show additional information ('Hello world' in this example) it runs this piece of code
constructor () {
super()
this.state = {
isHidden: true
}
}
toggleHidden () {
this.setState({
isHidden: !this.state.isHidden
})
}
But what happens in the browser is that the subcomponent toggle is closed and looks like this
If I reopen the subcomponent you can see that the 'hello world' is now showing so if did work as I wanted but just automatically closing the subcomponent which I didn't want to happen
In the console I'm not getting any errors or logs from react-table - it seems that any time I try to set a state inside of the react-table subcomponent that it will automatically close like this
My guess is that setting the state has reset all the states and that is what is confusing react-table, but I don't see how it would be?
I've just updated react-table to the latest version 6.8.6 but still have the same problem
Is this a bug or is there something I've done wrong here?
i fixed this by collapseOnDataChange: false
I had a similar issue. I have a subcomponent and inside the subcomponent are buttons which trigger functions when clicked:
The delete button had the following code:
<button id="delete" onClick={this.deleteClient('id')}>Delete</button>
where deleteClient is defined as:
deleteClient = (clientRef) => {
console.log(`deleting ${clientRef}`)
}
With the above code, "deleting id" would console.log both when I opened the subcomponent and when I clicked on the button.
I resolved the problem by using a callback in onClick as such:
<button id="delete" onClick={() => this.deleteClient('id')}>Delete</button>
Now "deleting id" only console.logs once, at the appropriate time, which is when I hit "Delete".
TL;DR:
set your onClick to a callback function.
Hope that helps!

How to create popup with YouTube video that not affect main page?

I'm trying to have popup window with YouTube video that not affect the main page.
I can still interact with the main page, similar on this web site when you click track you have small popup video on right bottom corner.
I have App.js with YouTube API
{
getYouTubeApi = (searchTerms1, searchTerms2) => {
fetch(`https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=10&q=${searchTerms1} ${searchTerms2}&type=video&key=YOUR_KEY`)
.then(res=>res.json())
.then(data=>{
this.setState({videoId: data.items[0].id.videoId})
})
.catch(error=>console.log(error))
}
render(){
return (<YouTube opts={opts} videoId={this.state.videoId} />)
}
}
getYouTubeApi function I call on click on track that just bring to the top of my page where YouTube video loads.
Here is my school project.
Since this describes DOM behavior more than business logic, you could put in whatever you want in a specific component that behaves like a modal (whether by swapping out css classes or setting inline styles based on certain conditions).
To make this modal reusable, I'll suggest you make it a higher order component to wrap around <Youtube /> component or any other component you want to display with modals.
For example, you could have
const Modal = (ComponentToDisplayAsModal, props) => (
<div class="modal">
<ComponentToDisplayAsModal {...props} />
</div>
)
You could then style the modal class to behave like a modal.
On the view you wish to show your modal, you could render
Modal(Youtube, props)

Resources