How to test function component with a react-data-grid - reactjs

I have created a data grid with react using React-data-grid (link)
My code is as follows:
import React, { useState } from "react";
import ReactDataGrid from 'react-data-grid';
const ROW_COUNT = 20;
const MIN_WIDTH = 100;
const defaultColumnProperties = {
resizable: true,
sortable: true
};
const columns = [
{
key: "eventTypeNameI18n",
name: "Type",
},
{
key: "nameI18n",
name: "Name",
width: 160
},
{
key: "dateCreated",
name: "Time",
width: 220
},
{
key: "locationNameI18n",
name: "Location",
width: 200
}
].map(col => ({...col, ...defaultColumnProperties}));
const sortRows = (initialRows, sortColumn, sortDirection) => rows => {
const comparer = (a, b) => {
if (sortDirection === "ASC") {
return a[sortColumn] > b[sortColumn] ? 1 : -1;
}
else if (sortDirection === "DESC") {
return a[sortColumn] < b[sortColumn] ? 1 : -1;
}
};
return sortDirection === "NONE" ? initialRows : [...rows].sort(comparer);
};
function DataGrid({initialRows}) {
const [rows, setRows] = useState(initialRows);
return (
<ReactDataGrid
id="EventDataGrid"
columns={columns}
rowGetter={i => rows[i]}
rowsCount={ROW_COUNT}
minColumnWidth={MIN_WIDTH}
onGridSort={(sortColumn, sortDirection) =>
setRows(sortRows(initialRows, sortColumn, sortDirection))
}
/>
);
}
export default DataGrid;
I am new to writing unit tests and have been writing very basic unit tests as of recent. I am wondering what is the best way to test the onGridSort method using Jest/Enzyme
In my tests I currently have the following:
import React from 'react';
import ReactDOM from 'react-dom';
import Enzyme, {mount} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import EventsGrid from '../EventsGrid';
import sampleData from './sampleData/transformedEventSample';
Enzyme.configure({adapter: new Adapter()});
describe('Tests for <EventDataGrid/>', () => {
it('sort data grid by ascending ', () => {
const wrapper = mount(<EventsGrid initialRows={sampleData}/>);
const instance = wrapper.instance();
jest.spyOn(instance, 'sortRows');
const column = 'eventTypeNameI18n';
const sortDirection = 'ASC';
wrapper.find('#EventDataGrid').at(1).prop('onGridSort')(column, sortDirection);
expect(instance.sortRows).toHaveBeenCalled();
});
it('sort data grid by descending', () => {
const wrapper = mount(<EventsGrid initialRows={sampleData}/>);
const instance = wrapper.instance();
jest.spyOn(instance, 'sortRows');
const column = 'eventTypeNameI18n';
const sortDirection = 'DESC';
wrapper.find('#EventDataGrid').at(1).prop('onGridSort')(column, sortDirection);
expect(instance.sortRows).toHaveBeenCalled();
});
});
I want to test to make sure that the grid is ordered correctly (ordered by ascending/descending)

Related

React Quill - social media link to embed media on rich text editor

I want the react quill rich text editor able to convert link into social media, like this
link: https://www.tiktok.com/#epicgardening/video/7055411162212633903
My RTE Code
import { useCallback, useMemo, useEffect } from 'react';
import ImageResize from 'quill-image-resize-module-react';
import ReactQuill, { Quill } from 'react-quill';
import { message } from 'antd';
import { uploadFiles } from 'utils';
import 'react-quill/dist/quill.bubble.css';
import 'react-quill/dist/quill.snow.css';
import './styles.scss';
Quill.register('modules/imageResize', ImageResize);
const RichTextEditor = ({
editorState,
onChange,
readOnly = false,
setLoading = () => {}
}) => {
window.Quill = Quill;
let quillRef = null; // Quill instance
let reactQuillRef = null; // ReactQuill component
useEffect(() => {
attachQuillRefs();
}, []);
const attachQuillRefs = useCallback(() => {
if (typeof reactQuillRef.getEditor !== 'function') return;
quillRef = reactQuillRef.getEditor();
}, []);
const imageHandler = () => {
const input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', 'image/*');
input.click();
input.onchange = async () => {
const file = input.files[0];
if (file.size > 1500000)
return message.error('Image size exceeded 1.5Mb');
setLoading({ image: true });
const formData = new FormData();
formData.append('image', file);
const fileName = file.name;
const imgUrl = await uploadFiles(file, quillRef);
const range = quillRef.getSelection();
quillRef.insertEmbed(range.index, 'image', imgUrl, 'user');
let existingDelta = quillRef.getContents();
const indexOf = existingDelta.ops.findIndex((eachOps) => {
return eachOps.insert?.image === imgUrl;
});
const selectedOps = existingDelta.ops[indexOf];
if (indexOf !== -1) {
selectedOps.attributes = {};
selectedOps.attributes = { alt: fileName };
}
quillRef.setContents(existingDelta);
setLoading({ image: false });
};
};
const modules = useMemo(
() => ({
toolbar: {
container: [
[{ header: [1, 2, 3, 4, 5, 6, false] }],
['bold', 'italic', 'underline'],
[{ list: 'ordered' }, { list: 'bullet' }],
[{ align: [] }],
['link', 'image'],
['clean'],
[{ color: [] }]
],
handlers: {
image: imageHandler
}
},
imageResize: {
modules: ['Resize', 'DisplaySize']
}
}),
[]
);
return (
<div className="react-quill-wrapper">
<ReactQuill
readOnly={readOnly}
theme={readOnly ? 'bubble' : 'snow'}
ref={(e) => {
reactQuillRef = e;
}}
value={editorState}
modules={modules}
placeholder="Add content of your article!"
onChange={onChange}
/>
</div>
);
};
export { RichTextEditor };
const [editorState, setEditorState] = useState('');
<RichTextEditor
editorState={editorState}
onChange={setEditorState}
setLoading={setLoading}
/>
called by parent like this
I've been working on this for almost a week, I really need help
I expected to have an string HTML output, like this library or image above
My attempts:
Get the social media url typed by user based on link, use that typed link to determined what social media, and use react-social-media-embed to give me an output link image above.
I belive(maybe) that the output from react-social-media-embed is jsx, and I need to convert it to html, and parsed it to string.

How to hide these elements before the user uses the search

There is a Main component, which has 4 separate components. It is necessary that these components are not visible before the user does not use the search.
The first component is responsible for displaying the weather graph, and the second for displaying the map. I do not know how to hide these two components specifically.
first component 1
import React, { useContext, useState, useEffect } from 'react';
import Chart from 'react-apexcharts';
import { Context } from '../../contex';
import './weather-graph.scss';
import { useTranslation } from 'react-i18next';
const WeatherGrapth = () => {
const { t } = useTranslation()
const {dailyForecast} = useContext(Context);
const [category, setCategory] = useState([])
const [data, setData] = useState([])
useEffect(() => {
const day = [];
const temp =[];
dailyForecast.forEach((d) => {
const unixTimestamp = d.dt;
const getTemp = Math.round(d.temp.day)
let getDay = new Date(unixTimestamp * 1000).getDate();
day.push(getDay)
temp.push(getTemp)
})
setCategory(day)
setData(temp)
}, [dailyForecast]);
return(
<>
{dailyForecast.temp &&
<div className="graph__container">
<h3 className="graph__title">{t("weekly_foreacst")}</h3>
<Chart options={{
chart: {
id: 'weather-graph'
},
xaxis: {
categories: category,
title: {
text: [t("date")],
},
},
yaxis: {
title: {
text: [t("temperature")],
},
},
}}
series={[{
name: 'temp',
data: data
}]} type="line" height={'349px'} />
</div>
}
</>
)
}
export default WeatherGrapth;
second component 2
import React, { useEffect } from 'react';
import './weather-map.scss';
import {API_KEY} from './../../apis/config';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import 'leaflet-openweathermap/leaflet-openweathermap.css';
import 'leaflet-openweathermap';
import { useTranslation } from 'react-i18next';
const WeatherMap = () => {
const { t } = useTranslation();
useEffect(() => {
const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18, attribution: 'copyright OpenStreetMap contributors' });
const clouds = L.OWM.clouds({showLegend: false, opacity: 0.5, appId: `${API_KEY}`});
const cloudscls = L.OWM.cloudsClassic({showLegend: false,appId: `${API_KEY}`});
const precipitation = L.OWM.precipitation({showLegend: false, appId: `${API_KEY}`});
const precipitationcls = L.OWM.precipitationClassic({showLegend: false,appId: `${API_KEY}`});
const rain = L.OWM.rain({showLegend: false,appId: `${API_KEY}`});
const raincls = L.OWM.rainClassic({showLegend: false,appId: `${API_KEY}`});
const snow = L.OWM.snow({showLegend: false,appId: `${API_KEY}`});
const pressure = L.OWM.pressure({showLegend: false,appId: `${API_KEY}`});
const pressurecntr = L.OWM.pressureContour({showLegend: false,appId: `${API_KEY}`});
const temp = L.OWM.temperature({showLegend: false,appId: `${API_KEY}`});
const wind = L.OWM.wind({showLegend: false,appId: `${API_KEY}`});
const map = L.map('map', { center: new L.LatLng(53.9, 27.5667), zoom: 5, layers: [osm] });
const baseMaps = { "OSM Standard": osm };
const overlayMaps = {
[t("clouds")]: clouds,
[t('cloudscls')]: cloudscls,
[t('precipitation')]: precipitation,
[t('precipitationcls')]: precipitationcls,
[t('rain')]: rain,
[t('raincls')]: raincls,
[t('snow')]: snow,
[t('pressure')]: pressure,
[t('pressurecntr')]: pressurecntr,
[t('temp')]: temp,
[t('wind')]: wind,
};
const layerControl = L.control.layers(baseMaps, overlayMaps,{collapsed:window.innerWidth < 768}).addTo(map);
}, []);
return(
<div className="weathermap-container">
<div id="map" style={{height: '260pt', borderRadius:'20px'}} className="map-weather"></div>
</div>
)
}
export default WeatherMap;
You can achieve that by passing down a prop
For instance
return (
<>
<div className="main-container">
{prop.visible ?
<CardWeather />
<Forecast/>
<WeatherGrapth/>
<WeatherMap/>
: ""
}
</div>
<div className="pr">weather app</div>
</>
)
}
export default Main;```
So to make it visible just pass in
```visible={true} ```
when calling the function

Testing rxjs subscription in react using jest

I am trying to test the below code
Slider.tsx
import React, { useEffect, useState } from "react";
import { ISlide } from "./ISlide";
import styled, { css } from "styled-components";
import { BehaviorSubject, Observable, Subject, timer } from "rxjs";
import { repeatWhen, take, takeUntil, takeWhile, tap } from "rxjs/operators";
interface ISliderProps {
...
}
const SliderWrapper = styled.ul`
...
`;
const SliderItem = styled.li`
...
`;
export const Slider = (props: ISliderProps) => {
const {
slides,
activeSlide = 0,
onActiveItemChange,
itemsPerPage = 1,
delay = 5000,
interval = 5000,
loop = true
} = props;
const [currentActiveSlide, setCurrentActiveSlide] = useState(activeSlide);
const sliderCount = Math.ceil(slides.length / itemsPerPage);
const timerStart$: Observable<null> = new BehaviorSubject(null);
const timerStop$: Observable<null> = new Subject<null>();
const timer$ = timer(delay, interval).pipe(
takeWhile(() => loop),
takeUntil(timerStop$),
repeatWhen(() => timerStart$)
);
useEffect(() => {
const timerSubscription = timer$.subscribe({
next: (i) => {
const nextSlide = i >= sliderCount ? i % sliderCount : i;
onActiveItemChange(nextSlide);
setCurrentActiveSlide(nextSlide);
}
});
return () => timerSubscription.unsubscribe();
}, []);
const handleSliderItemClick = (i: number) => {
setCurrentActiveSlide(i);
timerStop$.next(null);
timer(delay).pipe(
take(1),
tap(timerStart$.next(null))
)
}
return (
<>
<SliderWrapper>
{currentActiveSlide}
{slides.map(({ id }, i) =>
<SliderItem
key={id}
aria-label={"scroll page number " + id}
onClick={() => handleSliderItemClick(i)}
active={i === currentActiveSlide}
/>)}
</SliderWrapper>
</>
);
};
Below is my test
import React from "react";
import { Slider } from "../../../components/slider/Slider";
import renderer, { act } from "react-test-renderer";
import { ISlide } from "../../../components/slider/ISlide";
it("renders without crashing", done => {
const slides: ISlide[] = [
{ id: 1 }, { id: 2 }, { id: 3 },
{ id: 4 }, { id: 5 }, { id: 6 },
{ id: 7 }, { id: 8 }
];
act(() => {
const component = renderer.create(<Slider itemsPerPage={3} slides={slides} activeSlide={0} />);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
done();
});
});
I have called done but the test keeps returning a warning
Jest did not exit one second after the test run has completed.
As expected the problem is in the subscription
● Timeout
67 |
68 | useEffect(() => {
> 69 | const timerSubscription = timer$.subscribe({
| ^
70 | next: (i) => {
71 | const nextSlide = i >= sliderCount ? i % sliderCount : i;
72 | onActiveItemChange(nextSlide);
How do I unsubscribe from this subscription?

GSAP animate elements in an array fetched from the server

I would like to animate each element in an array with TweenMax.staggerFrom. At the moment I created this working sample
import React, { useRef, useEffect createRef } from 'react';
import { TweenMax } from 'gsap';
const AnimateView = () => {
const data = [
{ title: 'Title 1', value: 1000 },
{ title: 'Title 2', value: 1100 },
{ title: 'Title 3', value: 1200 },
];
const elementsRef = useRef(data.map(() => createRef()));
useEffect(() => {
const elements = elementsRef.current.map(el => el.current);
TweenMax.staggerFrom(elements, 1, { scale: 0 }, 0.3);
}, []);
return (
<>
{data.map((item, index) => (
<div ref={elementsRef.current[index]}>{item.title}</div>
))}
</>
);
};
export default AnimateView;
The difference is that I want data array to be fetched from the server. I cannot figure out why in elementsRef I'm getting no attached refs. Below you can check what I wanted to achieve.
import React, { useRef, useEffect createRef } from 'react';
import { TweenMax } from 'gsap';
import { connect } from 'react-redux';
import { fetchData } from 'actions';
const AnimateView = ({ combineFetching, income }) => {
const elementsRef = useRef(income.length && income.map(() => createRef()));
useEffect(() => {
const elements = elementsRef.current.map(el => el.current);
TweenMax.staggerFrom(elements, 1, { scale: 0 }, 0.3);
}, []);
return (
<>
<button onClick={fetchData}>Click</button>
{income.map((item, index) => (
<div ref={elementsRef.current[index]}>{item.title}</div>
))}
</>
);
};
const mapDispatchToProps = state => ({
income: state.budget.income,
});
export default connect(
mapDispatchToProps,
{ fetchData },
)(AnimateView);
On Click I want to fetch data from database and animate each element in the array.

React-Data-Grid MulitSelect Filter

Can someone explain what setFilters is doing here I don't understand how it's declared and what it's doing. I'm trying to implement react-data-grid. I can get one column to filter but when I select another it overwrites the previously save filter selection.
If someone has an example of setFilter I would really appreciate it.
import React, { useState } from "react";
import ReactDOM from "react-dom";
import ReactDataGrid from "react-data-grid";
import { Toolbar, Data } from "react-data-grid-addons";
import createRowData from "./createRowData";
import "./styles.css";
const defaultColumnProperties = {
filterable: true,
width: 120
};
const selectors = Data.Selectors;
const columns = [
{
key: "street",
name: "Street"
},
{
key: "zipCode",
name: "ZipCode"
},
{
key: "date",
name: "Date"
},
{
key: "jobTitle",
name: "Job Title"
},
{
key: "catchPhrase",
name: "Catch Phrase"
},
{
key: "jobArea",
name: "Job Area"
},
{
key: "jobType",
name: "Job Type"
}
].map(c => ({ ...c, ...defaultColumnProperties }));
const ROW_COUNT = 50;
const handleFilterChange = filter => filters => {
const newFilters = { ...filters };
if (filter.filterTerm) {
newFilters[filter.column.key] = filter;
} else {
delete newFilters[filter.column.key];
}
return newFilters;
};
function getRows(rows, filters) {
return selectors.getRows({ rows, filters });
}
function Example({ rows }) {
const [filters, setFilters] = useState({});
const filteredRows = getRows(rows, filters);
return (
<ReactDataGrid
columns={columns}
rowGetter={i => filteredRows[i]}
rowsCount={filteredRows.length}
minHeight={500}
toolbar={<Toolbar enableFilter={true} />}
onAddFilter={filter => setFilters(handleFilterChange(filter))}
onClearFilters={() => setFilters({})}
/>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Example rows={createRowData(50)} />, rootElement);

Resources