Onclick button is not passing props to the functional component React - reactjs

I'm trying to pass props from an onclick event to a function when then renders a component based on those prop values. The problem is that i am not able to pass on the props to the functional component. But it returns undefined
here is the parent component that passes the props using the map function
<DropdownButton id="dropdown-basic-button" title="Dropdown Menu">
{post && (postVideoInfo = post.video_info, postVideoInfo.map((postVideoSource) => (
<Dropdown.Item key={postVideoSource.id} onClick={RenderVideoPlayer} props={postVideoSource}>{postVideoSource.audio_language}</Dropdown.Item>
)))}
</DropdownButton>
here is the functional component that renders a new component on click
const RenderVideoPlayer = (props) => {
let videoSrc = {
type: 'video',
sources: [
{
src: `${props.video_url}`,
type: 'video/mp4',
}
],
poster: '/path/to/poster.jpg',
tracks: [
{
kind: 'captions',
label: `${props.caption_language}`,
srclang: 'en',
src: `${props.video_url}`,
default: true,
}
],
}
return (
<Plyr
options={options}
source={videoSrc}
/>
)
}
That data is retrieved from an api and stored in setState

It clearly seems like RenderVideoPlayer is not being passed any info from the postVideoSource in the map.
Rewriting the onClick as this could work, as you will be passing the props (I am guessing postVideSource has the 2 pieces of info that RenderVideoPlayer is expecting):
onClick={() => {
return <RenderVideoPlayer
video_url={postVideoSource.videoUrl}
caption_language={props.captionLanguate}
/>
}
I am also assuming this RenderVideoPlayer is going to be rendered inside a modal or some overlay or some side div...

Related

Reloading a button on event change in React Typescript

I am having a simple form in React, which looks like:
const [placeOptions] = useState([
{ value: 'USA', label: 'USA' },
{ value: 'MEX', label: 'Mexico' },
]);
const [name, setName] = useState('');
const [place, setPlace] = useState('USA');
....
<input onChange={event => setName(event.target.value)} type="text"/>
<select onChange={event => setPlace(event.target.value)}>
{placeOptions.map(item => (
<option key={item.value} value={item.value}>
{item.label}
</option>
))}
</select>
<CustomButton id="custom-btn" props={[name, place]} />
The above Custom button is just rendering once and is taking the default null and 'USA' value. It should Ideally send props to every event change, possibly refreshing the component once event is triggered. I am unable to determine how do I refresh a component on event change and pass the correct state to the props.
Edit: The below is the CustomButton.tsx file:
export function CustomButton({ props, id }: { props?:any, id?:string}) {
var name = props ? props[0] : '';
var place = props ? props[1] : '';
useEffect(() => {
renderButton(id);
}
return(
<React.Fragment>
<div id={id}></div>
</React.Fragment>
);
async function renderButton(id: string) {
... // Some logic involving the props passed
}
}
Edit 2:
This the code sandbox: https://codesandbox.io/s/amazing-dust-315dk?file=/src/App.js
All I want is to change the props too and dynamically render the custom button.
The problem is how you define the name and the place variable in CustomButton Component.
Variables of javascript defined like var let, and const will not trigger re-renders in React Button. States and Props only can trigger re-renders in React Components.
So if you do something like this in the parent file:
// All same code excepet
<CutomButtom id="cutom-btn" name={name} place={place} />
You can get name and place directly from props and use them as it is like:
export function CustomButton({ name, place, id }: { name: string, place: string, id:string}){
// NO need for defining name and place now, just use them directly...
}
Another improvement you can make is to define PropsType separately:
export interface CustomButtonProps {
id: string;
name:string;
place:string;
}
export function CustomButton({name, place, id}:CustomButtonProps){
}

How to test functionality of function props in storybook?

I have a parent component, <AssetSelectorMenu>, which is composed of two child components:
export const AssetSelectorMenu = (({ assets, sortByName }) => {
return (
<>
<AssetSelectorHeader sortByName={sortByName} />
{assets && assets.map((asset) => (
<AssetSelectorRow key={asset} />
))}
</>
);
});
storybook for AssetSelectorMenu:
export const Default = () => (
<AssetSelectorMenu sortByName={action("sortByName")} assets={assets} />
);
Inside the storybook for AssetSelectorMenu, I'd like to test that the function prop sortByName actually visually sorts the assets by name. At the moment, it only makes sure it the function gets called, but visually it's not sorting the assets. How can I do that?
If you want to use state in your Storybook examples so that your components are fully working based on interaction you need to use the createElement function from React.
Here is a simple example using a Checkbox component that has it's value managed by state which simulates using a state manager like Redux or Context etc.
import { Fragment, useState, createElement } from 'react'
<Preview>
<Story name="Default">
{createElement(() => {
const [value, setValue] = useState(['Yes'])
const onChange = (event, value) => {
setValue(value)
}
return (
<Checkbox
name="checkbox"
values={[
{ label: 'Yes', value: 'Yes' },
{ label: 'No', value: 'No' }
]}
value={value}
onChange={onChange}
/>
)
})}
</Story>
</Preview>

How to Manipulate Dropdown placeholder, onFocus?

Am new to ReactJS. I need to make the "placeholder" which is set to "State" initially to Empty/Null when onClicked or onFocus and then when it's not focused on, it goes back to "State" again. Can someone help me with this, am very new to react so any help will be appreciated.
import React from "react";
import { render } from "react-dom";
import { Container, Button, Modal, Dropdown } from "semantic-ui-react";
const stateOptions = [
{ key: "AL", value: "AL", text: "Alabama" },
{ key: "NY", value: "NY", text: "New York" }
];
const App = () => (
<Dropdown
placeholder="State"
fluid
multiple
search
selection
options={stateOptions}
/>
);
render(<App />, document.getElementById("root"));
From React's perspective, placeholder is a state that needs to be changed according to user's actions (onClick, onBlur)
So create a state to hold placeholder value that need to change.
There are two ways (since v16.8.0 with the introduction of React Hooks).
Using Class Component
class DropDown extends React.Component {
defaultPlaceholderState = "State";
state = { placeholder: this.defaultPlaceholderState };
clearPlaceholder = () => this.setState({ placeholder: "" });
resetPlaceholder = () =>
this.setState({ placeholder: this.defaultPlaceholderState });
render() {
return (
<Dropdown
onClick={this.clearPlaceholder}
onFocus={this.clearPlaceholder}
onBlur={this.resetPlaceholder}
placeholder={this.state.placeholder}
fluid
multiple
search
selection
options={stateOptions}
/>
);
}
}
In the code above, placeholder declared as a state with default value set to this.defaultPlaceholderState.
When a user clicks on the dropdown, onClick clears the placeholder value by setting it to an empty string. Same for onFocus when the Dropdown is on focus.
When a user clicks outside (onBlur), resetPlaceHolder sets the placeholder value to the default this.defaultPlaceholderState.
Using Function Component with useState hook
React v16.8.0 introduces Hooks, which enables Function Components (not a Functional Component, as it refers to Functional Programming) to hold states.
You can use React.useState hook to hold placeholder value.
const DropDownUsingHook = () => {
const defaultPlaceholderState = "State";
const [placeholder, setPlaceholder] = React.useState(defaultPlaceholderState);
const clearPlaceholder = () => setPlaceholder("");
const resetPlaceholder = () => setPlaceholder(defaultPlaceholderState);
return (
<Dropdown
onClick={clearPlaceholder}
onFocus={clearPlaceholder}
onBlur={resetPlaceholder}
placeholder={placeholder}
fluid
multiple
search
selection
options={stateOptions}
/>
);
};
⚠ Note: Unlike the Class version, clearPlaceholder, resetPlaceholder methods and placeholder state don't use this. prefix.
The implementation is similar but you use useState hook to declare the state and the setter (setPlaceholder).
Refer to the Hooks documentation, Using State Hook for more info.
You can play around with the working code on CodeSandbox.

How can I display a Persona component in a CommandBar in React Fabric-UI?

I am trying to display a Persona component on the far right of my CommandBar component, which I use as a header for my application.
Here's a code snippet
const getFarItems = () => {
return [
{
key: 'profile',
text: <Persona text="Kat Larrson" />,
onClick: () => console.log('Sort')
}
]
}
const FabricHeader: React.SFC<props> = () => {
return (
<div>
<CommandBar
items={getItems()}
farItems={getFarItems()}
ariaLabel={'Use left and right arrow keys to navigate between commands'}
/>
</div>
);
}
This throws a type error because the text prop expects a string and not a component. Any help would be appreciated!
Under the ICommandBarItemProps there is a property called commandBarButtonAs that the docs state:
Method to override the render of the individual command bar button.
Note, is not used when rendered in overflow
And its default component is CommandBarButton which is basically a Button
Basically there are two ways to do this.
Keep using Button, and apply your own renderer. Basically the IButtonProps you can add onRenderChildren which would allow you to add any Component such as Persona to render. This example would show you how it is done https://codepen.io/micahgodbolt/pen/qMoYQo
const farItems = [{
// Set to null if you have submenus that want to hide the down arrow.
onRenderMenuIcon: () => null,
// Any renderer
onRenderChildren: () => ,
key: 'persona',
name: 'Persona',
iconOnly: true,
}]
Or add your own crazy component not dependent on CommandBarButton but that means you need to handle everything like focus, accessibility yourself. This example would show you how it is done https://codepen.io/mohamedhmansour/pen/GPNKwM
const farItems = [
{
key: 'persona',
name: 'Persona',
commandBarButtonAs: PersonaCommandBarComponent
}
];
function PersonaCommandBarComponent() {
return (
);
}

Apollo GraphQL React - testing Mutations with MockedProvider

I'm following the docs provided by Apollo GraphQL for testing React component mutations using MockedProvider.
In the app I'm using recompose styled-components react apollo-client react-apollo
For testing I use jest and enzyme
I have created mock queries using MockedProvider and tested my component successfully. I cannot, however, get the mutation testing to work properly. My component keeps displaying an error state for the HTML instead of displaying an OK state.
NOTE: Through compose, I compose my <TestKey/> component with handler functions and a loading and error state that will render conditionally.
Following the docs (linked above) I've created a test that looks similar to this:
COMPONENT
const Key = ({
className,
columns,
title,
data,
type,
onClick,
paginate,
error,
info,
}) => (
<div className={className}>
<div className="tableHeader">
<h4>{title}</h4>
<Wrapper>
<Tooltip
title={`What are ${title}?`}
content={[<TooltipContent key={title}>{info}</TooltipContent>]}
trigger="hover"
keyid={title}
/>
</Wrapper>
<Button type="primary" onClick={() => onClick(type)}>
<ButtonIcon type="key" />
Generate New Key
</Button>
</div>
{error && <Alert type="danger">Error: {error}</Alert>}
{data.length === 0 ? (
<NoKeys>There are no {title} generated for this account.</NoKeys>
) : (
<Table
columns={columns}
data={data}
defaultSorted={[
{
id: 'insertedAt',
asc: false,
},
]}
/>
)}
</div>
)
MUTATION
const CREATEMUTATION = gql`
mutation createKey(
$type: String!
) {
createKey(
type: $type
) {
id
token
type
insertedAt
}
}
`
EXPECTED RETURN FROM MUTATION
const createKey = {
id: '4',
token: 'ucf345',
type: 'public',
insertedAt: '2018-06-20 20:42:15.189925',
}
TEST
describe('Create Key', () => {
let tree
beforeEach(async () => {
const mockedData = [{
request: {
query: CREATEMUTATION,
variables: { type: 'public' },
},
result: {
data: {
createKey: createKey,
},
},
}]
tree = renderer.create(
<MockedProvider mocks={mockedData} removeTypename={true}>
<ThemeProvider theme={theme}>
<TestKey />
</ThemeProvider>
</MockedProvider>
)
})
****at this step in the test I should be able to call on the button in the component and trigger a click that calls the mutation****
it('check', () => {
//errors because it cannot find button
const button = tree.root.findByProps({type: 'primary'})
console.log(tree.toJSON().children);
})
})
})
CONSOLE.LOG RETURNS THE FOLLOWING IN THE TERMINAL
(it displays what's rendered when my component receives an error from GraphQL)
[ { type: 'div',
props: { className: 'sc-bwzfXH kIOmnc' },
children:
[ [Object],
'Error connecting to the Platform. Wait for a second and then click the retry button below' ]
]
I've tried this using Enzyme's mount instead of React's Test renderer and I get the same result (an error state).

Resources