'Unknown props' error when using react-tab-panel - reactjs

I'm receiving an error when trying to implement the react-tab-panel in my application. I've followed the installation instructions to no avail.
This is the code which is producing the error:
import React from 'react';
import TabPanel from 'react-tab-panel'
const tabStyle = (props) => {
const baseStyle = {
padding: 10
}
return Object.assign(
baseStyle,
props.active?
{ color: 'red' }:
{ background: 'gray' }
)
}
const ExperimentDetail = ({ tabStyle }) => (
<div className="experiment-detail">
<TabPanel
tabAlign="center"
tabStyle={tabStyle}
>
<div tabTitle="first tab"> first </div>
<div tabTitle="second tab"> second </div>
<div tabTitle="third tab"> third </div>
</TabPanel>
</div>
);
export default ExperimentDetail;
The output is showing up as so, which is close to correct but with slight errors.
Thanks in advance for the help!

Reason is, you are using active props in TabStyle, but you are not passing in from ExperimentDetail component. One more thing you need to change, you need to use TabStyle name instead of tabStyle, because each react component must start with a uppercase.
And the main point is TabStyle is a react component, it will not return an object, it will return a html part so you can't use it with styling.
Use it in this way:
If you want to make a generic function that will return the style object, then write it in this way, create a separate file abc.js, put that function in that file, like this:
export function tabStyle(active){
const baseStyle = {
padding: 10
}
return Object.assign(
baseStyle,
props.active?
{ color: 'red' }:
{ background: 'gray' }
)
}
Import this function in your component:
import {tabStyle} from './abc /*path to this file*/';
Then use it in this way:
const ExperimentDetail = () => (
<div className="experiment-detail">
<TabPanel
tabAlign="center"
tabStyle={tabStyle(true)}
>
<div tabTitle="first tab"> first </div>
<div tabTitle="second tab"> second </div>
<div tabTitle="third tab"> third </div>
</TabPanel>
</div>
);
export default ExperimentDetail;
Check the working jsfiddle example: https://jsfiddle.net/ghoLrkjj/

Related

Testing-librairy : How to check a text when it is returned by a function

I have a react component which displays a text of which some words may change depending on the value of the object
import React, { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Card, Col, Row } from 'react-bootstrap';
function RenderPressMedia(props: any) {
const { t } = useTranslation();
const [pressPlanned, setPressPlanned] = useState<any[]>([]);
useEffect(() => {
if (!props.pressPlannedData) return;
setPressPlanned(props.pressPlannedData);
}, [props.pressPlannedData]);
const renderMedia = (media: string) => {
switch (media) {
case 'PHONE':
return t('press.media.phone');
case 'LETTER':
return t('press.media.letter');
case 'EMAIL':
return t('press.media.email');
case 'SMS':
return t('press.media.sms');
}
};
const renderPress = (media: string) => {
return (
<>
{t(`press.text`, {
media: renderMedia(media),
})}
</>
);
};
return (
<Row>
{pressPlanned.length > 0 &&
pressPlanned.map((press, index) => (
<Col lg={12} className="col-main" key={index}>
<Card>
<Card.Body>
<ul className="d-flex flex-row m-0 list-unstyled align-items-center">
<li aria-label="Reminder to do or reminder for today">
{renderPress(press.media)}
</li>
</ul>
</Card.Body>
</Card>
</Col>
))}
</Row>
);
}
export default RenderPressMedia;
renderPressMedia function returns a translation which will change depending on the variable sent.
Component that works very well.
But when I want to perform a test on this component with testing-library, I cannot verify the content returned by the pressMedia function.
Here is the test carried out.
import React from 'react';
import { render, screen } from '#testing-library/react';
import RenderPressMedia from '../render-press-media';
const data: any[] = [
{
id: 65,
media: 'LETTER',
firstPlannedDate: '2021-09-03',
plannedDate: '2021-09-03',
comment: 'autogenerated',
createdDate: '2021-08-27T09:43:52',
lastModifiedDate: '2021-08-27T09:43:52',
},
];
describe('<RenderPressMedia/>', () => {
it('should display an render press media with data', () => {
//given
render(<RenderPressMedia pressPlannedData={data} />);
//then
expect(screen.getByText(/letter/i)).toBeInTheDocument();
});
});
The test consists in verifying the presence of the word 'letter' returned by the pressMedia function
expect(screen.getByText(/letter/i)).toBeInTheDocument();
But unfortunately I got an error message
Unable to find an element with the text: /courrier/i. This could be because the
text is broken up by multiple elements. In this case, you can provide a function
for your text matcher to make your matcher more flexible.
Ignored nodes: comments, <script />, <style />
<body>
<div>
<div
class="row"
>
<div
class="col-main col-lg-12"
>
<div
class="card"
>
<div
class="card-body"
>
<ul
class="d-flex flex-row m-0 list-unstyled align-items-center"
>
<li
aria-label="Reminder to do or reminder for today"
>
press.text
</li>
</ul>
</div>
</div>
</div>
</div>
TestingLibraryElementError: Unable to find an element with the text: /courrier/i.
This could be because the text is broken up by multiple elements. In this case, you
can provide a function for your text matcher to make your matcher more flexible.
the pressMedia function in testingLibrairy does not return its contents so impossible to verify the presence of the word letter, return value is press.text
If you have a solution to correct this problem.
It looks like, it's happening because you're using the i18n functions, but also it's mocked and just gives back to you the text which received as a parameter.
Any case, in this case, what you're doing is pretty well. But also you're testing the translation. So, it can be more complicated and hard to maintain.
So, I'd recommend to test if it's using the correct media would be something like:
<li aria-label="Reminder to do or reminder for today" data-testid="component" data-media={press.media}>
{renderPress(press.media)}
</li>
And in the testing part:
expect(screen.getByTestId('component')).toHaveAttribute('data-media', data.media);
In this case, ignoring the language, You know which kind of media has your component.
I created this github repo as example:
Repo: https://github.com/joseglego/test-testing-library/
Component: https://github.com/joseglego/test-testing-library/blob/main/src/RenderPressMedia.js
Test: https://github.com/joseglego/test-testing-library/blob/main/src/RenderPressMedia.test.js
Specific commit: https://github.com/joseglego/test-testing-library/commit/f3c222f92313c909c8d4a1f359daf01bdd0f880d
Basically, that test is running on my local.

React accordion with correlating image outside accordion section

I can't find any examples of accordions where the active class is related to an element outside of the accordion. I'm trying to get an image to change on the side of the accordion, where each image is related to a specific object. I managed to get something working using absolute positioning, but I'm looking for a more elegant solution so I can manipulate styling better.
I can get it to work while the image is inside the accordion under the info text, but can't figure out the styling issue. I think I need to do some refactoring or do away with the array mapping to get it to work but I'm not sure.
Here is a codesandbox of more or less what I want to achieve but without the restriction of absolute positioning - https://codesandbox.io/s/ecstatic-taussig-f084t?file=/src/App.js
You can remove your img tag from your renderedItems and do something like this:
import React, { useState } from "react";
const Accordion = ({ items }) => {
const [activeIndex, setActiveIndex] = useState(0);
const onTitleClick = (index) => {
setActiveIndex(index);
};
const renderedItems = items.map((item, index) => {
const active = index === activeIndex ? "active" : "";
return (
<div key={item.title}>
<div className={`title ${active}`} onClick={() => onTitleClick(index)}>
<i className="dropdown icon"></i>
{item.title}
</div>
<div className={`content ${active}`}>
<p>{item.content}</p>
</div>
</div>
);
});
return (
<div className="container-gallery">
<div className="ui styled accordion">{renderedItems}</div>
<img
className={`content `}
src={`${items[activeIndex].image}`}
style={{
height: "200px",
width: "200px"
}}
alt="img"
/>
</div>
);
};
export default Accordion;
And for the style I don't know what you are using so I made css for the example:
.container-gallery{
display:flex;
flex-wrap:no-wrap;
justify-content: space-between;
}
here a sandBox link

Unable to load images dynamically in react app

I have this component :-
const CollectionItem = ({ item }) => (
<div className='collection-item'>
<div
className='image'
style={{
backgroundImage: `url(${item.imageUrl})`
}} />
</div>
</div>
)
my images urls are like ../../assets/images/imageName.jpg
i have tried loading a an image in the css file associated with this component with this exact url. It is working with css, but not in jsx. Please help.
Generally, how you load images in react is to import the image from the specified folder (webpack converts it into the correct source behind the scenes), and then to use that imported variable as the src of the image.
import imgSource from "../../assets/images/imageName.jpg";
const CollectionItem = ({ item }) => (
<div className='collection-item'>
<div
className='image'
style={{
backgroundImage: `url(${imgSource})`
}} />
</div>
</div>
)
EDIT
In the cases where the import depends upon the props, you could simple dynamically import the image within the function itself:
const CollectionItem = ({ item }) => (
import imgSource from `${item.imageUrl}`;
<div className='collection-item'>
<div
className='image'
style={{
backgroundImage: `url(${imgSource})`
}} />
</div>
</div>
)
In the case that it does not work, you can try using require instead
const imgSource = require(item.imageUrl);

How to dismiss a React Modal using createPortal()?

Trying to dismiss a Modal dialog from within the Modal. I'm using ReactDOM.createPortal().
index.html
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<div id="modal"></div>
</body>
ResponseModal.js
import React from "react";
import ReactDOM from "react-dom";
// The gray background
const backdropStyle = {
...
};
// The modal "window"
const modalStyle = {
...
};
const MODAL_ROOT = document.querySelector("#modal");
const JSX_MODAL = ({ children, setShowResponses }) => (
<div style={backdropStyle}>
<div style={modalStyle}>
Child: {children}
<br />
<input
type="button"
value="Dismiss"
onClick={() => {
setShowResponses(false);
}}
/>
</div>
</div>
);
function Modal({ showResponses, ...props }) {
console.log("floop", showResponses);
if (showResponses) {
return ReactDOM.createPortal(JSX_MODAL(props), MODAL_ROOT);
}
return null;
}
export default Modal;
And finally, the containing component:
const LargeCell = ({ SCID, extra, fen, color }) => {
const [showResponses, setShowResponses] = useState(false);
return (
<div
style={{
...
}}
onClick={() => setShowResponses(true)}
>
<SmallCell {...{ SCID, color }} />
<DiagramForCell {...{ fen }} padding="3em"></DiagramForCell>
<span className="diff-text opening-text">{extra.opening.desc}</span>
<ResponsesModal {...{ showResponses, setShowResponses }}>
FLUM!
</ResponsesModal>
</div>
);
};
When I click on the LargeCell div, I see:
However, the dismiss button doesn't work. I'm sure that setShowResponses(false) is called, but there is no re-rendering of the Modal, so it is not dismissed. If I look at the Modal component in Chrome devtools, the state of showResponses still shows true.
So the question is: what is the correct way to dismiss this Modal?
So LargeCell was a table cell component, of which there were multiple. What I did was to push the Modal to the table level, and show/hide it from there:
{showResponses ? (
<ResponsesModal {...{ setShowResponses }}>FLUM!</ResponsesModal>
) : null}
setShowResponses is called by the Dismiss button in the Modal as shown previously.
The disadvantage is that to bring up the modal, each LargeCell needs setShowResponses, also. That prop has to be pushed several levels down. That's okay for now, but I'm starting to wonder if I should use a context.

Access Gatsby Component from a function

I am trying to access a Gatsby component (Anime) from outside of it.
Can not figure out what instance name this would have or how to name it.
Here is my code:
import React from 'react'
import PropTypes from 'prop-types'
import PreviewCompatibleImage from '../components/PreviewCompatibleImage'
import Anime from 'react-anime';
import VisibilitySensor from 'react-visibility-sensor';
function onChange (isVisible) {
console.log('Element is now %s', isVisible ? 'visible' : 'hidden')
}
const FeatureGrid = ({ gridItems }) => (
<div className="columns is-multiline">
<VisibilitySensor onChange={onChange}>
<Anime delay={(e, i) => i * 100}
scale={[.1, .9]}
autoplay={false}>
{gridItems.map(item => (
<div key={item.text} className="column is-3">
<section className="section">
<div className="has-text-centered">
<div
style={{
width: '160px',
display: 'inline-block',
}}
>
<PreviewCompatibleImage imageInfo={item} />
</div>
</div>
<p>{item.text}</p>
</section>
</div>
))}
</Anime>
</VisibilitySensor>
</div>
)
FeatureGrid.propTypes = {
gridItems: PropTypes.arrayOf(
PropTypes.shape({
image: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
text: PropTypes.string,
})
),
}
export default FeatureGrid
I want to get the animation to trigger from the onChange function.
How do I get the name or set the name of the Anime component so I can access it from the function?
Or is there another way I should address this?
Using a Gatsby starter netlify CMS as the base, so extending on their code, but seems that const is not the route I should take.
I want the animation to trigger when it becomes visible.
Any suggestions?
According to the docs react-visibility-sensor :
You can pass a child function, which can be convenient if you don't need to store the visibility anywhere
so maybe instead of using the onchange function you can just pass the isVisible parameter, something like:
<VisibilitySensor>
{({isVisible}) =>
<Anime delay={(e, i) => i * 100}
// the rest of your codes here ...
</Anime>
}
</VisibilitySensor>
Otherwise you can convert this function to a react component and set states, etc..

Resources