Jest: How to test that a component has a certain ref attribute? - jestjs

JSX
const MyComp = ({ myDOMElRef }) => <div id="x" ref={ myDOMElRef } />
test
const mockedElRef = 'whatever';
// ... set up stuff
instance=shallow(<MyComp ref={ mockedElRef } />
expect(instance).toHaveProp('ref', mockedElRef);
Ref is a special prop so the test fails. How to check the div with id x has ref as passed into MyComp?

React 16+
const domElement = instance.get(0);
expect(domElement.ref).toBe(mockedElRef);
Note: Up until React 15 it was possible to use instance() instead of get(0) on the wrapper returned from shallow().

Related

how to render svg icon in nuxt 3

I am using nuxt 3 and Compositions API.
I get such a nesting, how to get rid of the extra svg tag?
I would also like to receive svg attributes whenever possible and change, for example, fill
template
<template>
<div>
<component :is="render"></component>
</div>
</template>
Script
import { h } from "vue";
const { data, pending, error, refresh } = await useFetch(svgURL);
const getDataVal = data.value
const SvgToRaw = await getDataVal.text();
const render = () => {
return h("svg", {
class: "bar",
innerHTML: SvgToRaw,
});
};
Chrome Dev Tools
I tried to create a virtual DOM tree and get an HTML element from there, not text, but I think this is a bad solution
import hv from "virtual-dom/h";
import diff from "virtual-dom/diff";
import patch from "virtual-dom/patch";
import createElement from "virtual-dom/create-element";
const betaRender = hv("span", { innerHTML: svgString });
var rootNode = createElement(betaRender);
var patches = diff(rootNode);
return patches[0].vNode.innerHTML
SSR support is important to me so I can't use standard tools

tiptap ReactNodeViewRenderer how to render the original view

I'm using tiptap and trying to extend the Paragraph node to wrap some extra stuff around its view. I used <NodeViewWrapper> and <NodeViewContent> as the guides said.
const ParagraphWrapper = () => {
return (
<NodeViewWrapper>
<NodeViewContent />
</NodeViewWrapper>
)
}
const ParagraphExt = Paragraph.extend({
addNodeView() {
return ReactNodeViewRenderer(ParagraphWrapper)
}
})
export default function App() {
const editor = useEditor({
extensions: [
Document,
Text,
ParagraphExt, // <<<< text-align was not rendered
// Paragraph, // <<<< This worked
TextAlign.configure({
types: ["paragraph"]
}),
],
content: `<p style="text-align: center">This is a paragraph</p>`,
})
return (
<>
<EditorContent editor={editor} />
<pre>{JSON.stringify(editor?.getJSON?.(), null, 2)}</pre>
</>
);
}
However, this seems to render the node from scratch. Thus, other extensions, such as textAlign no longer works.
I only need to wrap a thin layer around whatever was rendered originally. How do I do that?
Code Sandbox
You still get access to the attrs being passed to the node which is available in props. You can use that info to style your rendered content as you wish.
const ParagraphWrapper = (props) => {
const textAlign = props.node.attrs.textAlign;
return (
<NodeViewWrapper>
<NodeViewContent style={{textAlign}} />
</NodeViewWrapper>
);
};

How can I render dynamically imported react components?

let's consider a list of modules imported arbitrarily as so :
/**
* modules is a list of absolute paths to modules exporting react components
*/
const getAllComponents = async(modules) => {
const components = [];
modules.forEach((moduleName) => {
try {
const module = await import(moduleName);
components.push(module.default);
}catch(err) {
console.warn(err.message)
}
})
return components;
}
and a parent react component in project and a random component exported from a disk based module:
// my-component.js
function MyComponent({moduleNames}) {
const [components, setComponents] = useState([]);
useEffect(() => getAllComponents.then(setComponents), []);
// rendering a random component assuming it exists
const RenderedComponent = components[0];
return (
<div>
{/* failling here: */}
<RenderedComponent />
</div>
)
}
// a-random-component.js (disk based module whose path is in moduleNames in above component props)
function RandomComponent() {
return (<div>propless component</div>)
}
I get the following error when compiling:
Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
Check the render method of `MyComponent`.
Is there a way to render arbitrary component in the dom without having to use the static import statement ?

Passing useState value through parent component using react hooks (getting = undefined)

I'm working in a project already began that's using react class version. I plan to work with react hooks, so to don't refactor all the classes, as I write new codes, I'm trying to mix those react versions (idk if it's a good idea and I should refactor all).
I'm creating a list with pagination and search. The pagination and search are in an unique component.
To this component a need pass the search character value input by user, and here is where I'm facing problem. In other words, I need pass a value to the parent component.
Code is below:
useState hook:
const [search, setSearch] = useState('');
Filter component, that change the search value:
const Filter = () => {
return (
<Card>
<Form.Group label="Filtro">
<Grid.Row gutters="xs">
<Grid.Col>
<Form.Input
name='search'
placeholder='Filtro'
autoFocus
value={search}
onChange={e => setSearch(e.target.value)}
/>
</Grid.Col>
<Grid.Col auto>
<Button
color="success"
icon="search"
onClick={filtrar}
>
</Button>
</Grid.Col>
</Grid.Row>
</Form.Group>
</Card>
);
}
function getSearchDB() {
setSearch((search) => {
return search;
})
}
Pagination component, that receive the props:
<Pagination
baseUrl={'vehicles/toUse'}
updateState={setStateDB}
getSearch={getSearchDB}
fields={'license_plate'}
/>
Printing search value pass through Pagination component:
console.log(this.props.getSearch()) //print undefined
OBS: updateState={setStateDB} is working fine.
Things done to make this work (no success):
In getSearch={getSearchDB} directly pass search value. Result: this.props.getSearch() print undefined
Defined getSearchDB() to be like:
function getSearchDB() {
return search;
}
Result: this.props.getSearch() print undefined.
Is there a way to put it to work?
Guys, let me know if the post is confusion or the English is poorly written.
Instead of passing down a function that returns search, why not just pass down search itself as a prop?
<Pagination
search={search}
const Pagination = (props) => {
console.log(props.search);
add :
<Pagination
search={search}
/>
In component Pagination :
const Pagination = ({search}) => {
console.log(search);
return {
//...
}
}

TypeError: Cannot read property 'count' of undefined

I am a novice MERN stack developer.
I am trying to calculate the number of pages for pagination. The info object prints in console.log. However, when I try to use it in the for loop I get an error.
Can someone please explain what's the React logic or flow behind this? I have had issues with this multiple times but, could fix it with conditional rendering. But, somehow I wasn't able to fix this and I don't seem to understand the logic of how the flow in react is.
App Component :
const App = () => {
const [episodes, setEpisodes] = useState({});
const [loading, setLoading] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [episodesPerPage, setEpisodesPerPage] = useState(10);
useEffect(() => {
const fetchEpisodes = async () => {
setLoading(true);
const res = await axios.get('https://rickandmortyapi.com/api/episode/');
setEpisodes(res.data);
setLoading(false);
};
fetchEpisodes();
}, []);
console.log(episodes.info);
return (
<div>
<div id='header'>
<h1>Rick & Morty</h1>
<h2>Episodes</h2>
</div>
<div>
<h3>All Episodes</h3>
<EpisodeList episodeList={episodes.results} loading={loading} />
<Pagenation info={episodes.info} />
</div>
</div>
);
};
export default App;
Pagenation Component:
const Pagenation = ({ info }) => {
const pageNumbers = [];
console.log(info);
for (let i = 1; i <= Math.ceil(info.count / 20); i++) {
pageNumbers.push(i);
}
return (
<nav aria-label='...'>
<ul class='pagination pagination-lg'>
{pageNumbers.map((number) => {
return (
<li class='page-item active' aria-current='page'>
<span class='page-link'>
{number}
<span class='sr-only'>(current)</span>
</span>
</li>
);
})}
</ul>
</nav>
);
};
Conditional rendering can be the solution here as well.
episodes is initially an empty object, so episodes.info is initially undefined. This means you cannot access a property on info without checking if it exists first because you know already that it will be undefined at the beginning.
A simple solution might look like this:
{episodes.info && <Pagenation info={episodes.info} />}
You could also move the conditional into the Pagenation component to be something like this:
if (info) {
for (let i = 1; i <= Math.ceil(info.count / 20); i++) {
pageNumbers.push(i);
}
}
Regardless of your strategy to avoid the error, the core of the issue is that you have data that is loaded after the component mounts. This means you need to account for that data being missing for at least one render.

Resources