React testing with Typescript: Passing array of objects as props - reactjs

I am trying to pass an array of objects to mock component data for testing like so:
const mockPackage = {
id: '1232-1234-12321-12321',
name: 'Mock Package',
price: 8.32,
description: 'Mock description',
globalProduct: {
imageUrl: 'http://imageurl.com',
},
};
const mockPackageData = {
name: 'Package',
packages: [mockPackage],
};
beforeEach(() => {
component = render(
<SuiteContextProvider>
<PackageCard
showDetail={{ display: true, selectedIndex: 1, hideOthers: false }}
handleShowDetail={handleShowDetail}
packageData={mockPackageData}
/>
</SuiteContextProvider>,
);
});
However, I receive the following error:
The component that receives the data destructures the packageData like so:
export interface Package {
id: string;
name: string;
price: number;
description: string;
globalProduct: {
imageUrl: string;
};
}
export interface PackageData {
name: string;
packages: [];
}
type Props = {
packageData: PackageData;
handleShowDetail: (data: DefaultPackageProps) => void;
showDetail: {
display: boolean;
selectedIndex: number | null;
hideOthers: boolean;
};
};
const PackageCard: React.FC<Props> = ({ packageData, handleShowDetail, showDetail }: Props) => {
return (
<>
{packageData.packages.map((packageInfo: Package, index: number) => {
const {
id,
name,
price,
description,
globalProduct: { imageUrl },
} = packageInfo;

Your PackageData defintion should be
export interface PackageData {
name: string;
packages: Package[];
}
You current code packages: []; declares packages to must be an empty array that is why you get the type '0' error.

Related

How to define typescript interface for array on objects

Here is the data from the server.
I want to display it with map on UI.
Here is the interface I did -
export interface IHistory {
reports: Readonly<{
readonly id?: string,
status?: Status,
readonly created_at?: Date,
}>
}[];
The map I'm doing:
{props.history!.map((index, idx) => {}
The error:
TypeError: Cannot read properties of null (reading 'map')
What am I doing wrong?
I want to display only the reports.
Added -
Interface -
export interface IHistory {
reports: Array<{
id?: string;
status?: Status;
created_at?: string;
}>;
};
const [ hitoryState, setHistoryState ] = useState<IHistory | null>(null);
useEffect(() => {
backendAPIAxios.get('/history')
.then((response: AxiosResponse<IHistoryResponse>) => {
if (!response.data) {
return alert('Failed to get history');
}
setHistoryState(() => response.data);
})
.catch((e: AxiosError) => {
// alert(`Failed to get history with error: ${e}`);
});
}, [setHistoryState])
console.log(props.history!.reports.map((hist) => <p>{hist.created_at}</p>))
This is the error I'm getting:
You are making IHistory an array of reports objects, when reports is the field with the array. Also, created_at will likely be a string and not a date if it's being returned from the backend.
type Status = "fair" | "unfair";
interface IHistory {
reports: Array<{
id?: string;
status?: Status;
created_at?: string;
}>;
};
const backendHistory: IHistory = {
reports: [
{ id: "123", status: "fair", created_at: new Date().toISOString() },
{ id: "456", status: "unfair", created_at: new Date().toISOString() },
]
};
const result = backendHistory.reports.map(({ id }, _idx) => id);
console.log("result", result);
React code:
import React from "react";
type Status = "fair" | "unfair";
interface IHistory {
reports: Array<{
id?: string;
status?: Status;
created_at?: string;
}>;
}
async function fakeFetch(): Promise<IHistory> {
const backendHistory: IHistory = {
reports: [
{ id: "123", status: "fair", created_at: new Date().toISOString() },
{ id: "456", status: "unfair", created_at: new Date().toISOString() }
]
};
return new Promise((resolve) =>
setTimeout(() => resolve(backendHistory), 1000)
);
}
export default function App() {
const [backendHistory, setBackendHistory] = React.useState<IHistory>();
React.useEffect(() => {
let isAlive = true;
(async function () {
const result = await fakeFetch();
if (isAlive) {
setBackendHistory(result);
}
})();
return () => {
isAlive = false;
};
}, []);
return (
<div className="App">
<h1>Backend History</h1>
{backendHistory ? (
backendHistory.reports.map((hist) => <p>{hist.id}</p>)
) : (
<span>loading</span>
)}
</div>
);
}

Fullcalendar event component typescript test

Started to use fullcalendar and I am trying to make a test to test my event component that gets rendered for an event. The problem I am running into is EventContentArg. I getting typescript errors that my event property is missing a bunch of things that are not really related for the test to render. How can I fix the following error:
Type '{ id: string; start: Date; end: Date; title: string; backgroundColor: string; borderColor: string; editable: boolean; textColor: string; resourceId: string; extendedProps: { type: { id: number; name: string; }; status: { ...; }; displayProperties: { ...; }; }; }' is missing the following properties from type 'EventApi': _context, _def, _instance, setProp, and 26 more.ts(2740)
main.d.ts(1289, 5): The expected type comes from property 'event' which is declared here on type 'EventContentArg'
Here is part of my component
import { EventContentArg } from '#fullcalendar/react';
export const EventContent = (event: EventContentArg) => {
const { backgroundColor, timeText, view } = event,
{ end, extendedProps, start, title } = event.event,
{ displayProperties } = extendedProps,
slotDuration = view.getOption('slotDuration') as ISlotDuration,
slotDurationNumber = Math.floor(slotDuration.milliseconds / 60000),
appointmentLength = end!.getTime() - start!.getTime(),
duration = Math.floor(appointmentLength / 60000);
return (
<div>My Component</div>
)
}
Here is my test
import { render, screen } from '#testing-library/react';
import React from 'react';
import { EventContentArg } from '#fullcalendar/react';
import { EventContent } from '.';
import { events } from '../../actions/mockData';
import { prettyDate } from './EventContent.stories';
test('event renders with all expected data', () => {
let event = events[2];
let timeText = `${prettyDate(event.start)} - ${prettyDate(event.end)}`;
let args: EventContentArg = {
backgroundColor: 'rgb(133,33,51)',
event,
timeText: timeText,
view: {
getOption: () => {
return {
milliseconds: 900000,
};
},
},
};
render(<EventContent {...args} />);
});
Here is the events from the mockData
export const events = [
...,
{
id: '3',
start: new Date(thirdStart),
end: new Date(thirdEnd),
title: 'Hair Coloring', // This would be like the service name
backgroundColor: 'rgb(133,33,51)', // Should be based on event status
borderColor: '#d2d2d2', // For now should be same color as bg
editable: false,
textColor: '#2d2d2d',
resourceId: '2',
extendedProps: {
type: {
id: 1,
name: 'Appointment',
},
status: {
id: 2,
name: 'Scheduled',
},
displayProperties: {
attendeeDetails: 'with Kylo Ren',
resourceDetails: 'Chair 2',
eventIcons: [],
},
},
},
...,
];

How to pass entire props to a child component with TypeScript & Next.js?

I want to pass entire objects of contents in Interface Props to RichText.tsx.
Below is what I tried;
interface Props {
title: string;
contents: {
content: [
{
content: [
{
nodeType: string;
value: string;
}
];
nodeType: string;
data: { key: string };
}
];
};
}
const PostDetail: React.FC<Props> = ({ title, contents }) => {
return (
<PageLayout>
<h1>{title}</h1>
<RichText content={contents} />
</PageLayout>
);
};
The result is that RichText component received the object like this;
{
content: [
{
nodeType: string;
value: string;
}
];
nodeType: string;
data: { key: string };
}
But I want the component to have the object in this style...
{
content: [
{
content: [
{
nodeType: string;
value: string;
}
];
nodeType: string;
data: { key: string };
}
];
}
I thought this was because I specified the key "content" when passing props, so I tried using spread operator to pass the props as shown below.
const PostDetail: React.FC<Props> = ({ title, contents }) => {
return (
<PageLayout>
<h1>{title}</h1>
<RichText {...contents} />
</PageLayout>
);
};
However, I got an error as a result: Object(...) is not a function.
Any idea?
[Edited]
When I console.log(contents) in PostDetail, the result is like this,
Seems like even in contents, the key 'content' already disappears...

Property 'customProperty' does not exist on type 'X[]'. Typescript React. TS2339

I'm trying to move a directory component into redux, but I keep getting this error. The type is basically an array of objects. If I change the type from 'Section' to any, then the sections prop in mapStateToProps complains that 'no overload matches this call'.
Property 'sections' does not exist on type '({ title: string; imageURL: string; id: number; linkURL: string; size?: undefined; } | { title: string; imageURL: string; size: string; id: number; linkURL: string; })[]'
Directory.tsx
import React from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { selectDirectorySections, Section } from '../../redux/directory/directorySelectors';
import MenuItem, { IMenuItem } from '../menuItem/MenuItem';
import './Directory.scss';
interface IMenuItems extends IMenuItem {
id:number
};
const Directory = ({ sections }:Section) => {
return (
<div className='directory-menu'>
{sections.map(({ id, ...otherSectionProps }:IMenuItems) => (
<MenuItem key={id} {...otherSectionProps} />
))}
</div>
);
};
const mapStateToProps = createStructuredSelector({
sections: selectDirectorySections
});
export default connect(mapStateToProps)(Directory);
directorySelectors.ts
import { RootState } from '../rootReducer';
import { createSelector } from 'reselect';
const selectDirectory = (state:RootState) => state.directory;
export const selectDirectorySections = createSelector(
[selectDirectory],
directory => directory.sections
);
export type Section = ReturnType<typeof selectDirectorySections>;
directoryReducer.js
const INITIAL_STATE = {
sections: [
{
title: 'hats',
imageURL: 'https://i.ibb.co/cvpntL1/hats.png',
id: 1,
linkURL: 'hats'
}...
]
};
const directoryReducer = (state=INITIAL_STATE, action) => {
switch(action.type) {
default:
return state;
};
};
export default directoryReducer;
In this code:
const Directory = ({ sections }:Section) => {
return (
<div className='directory-menu'>
{sections.map(({ id, ...otherSectionProps }:IMenuItems) => (
<MenuItem key={id} {...otherSectionProps} />
))}
</div>
);
};
you're trying to destructure the property sections from an object of type Section. Section is defined as:
export const selectDirectorySections = createSelector(
[selectDirectory],
directory => directory.sections
);
export type Section = ReturnType<typeof selectDirectorySections>;
So it has the type that directory.sections has. From the name directory.sections, and from the error message, that's an array:
Property 'sections' does not exist on type '({ title: string; imageURL: string; id: number; linkURL: string; size?: undefined; } | { title: string; imageURL: string; size: string; id: number; linkURL: string; })[]'
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^
Arrays don't have a sections property (unless you add one, which is usually not a good idea).
You'll need to modify how you define Section, or change your code using it to use it as an array (e.g., by looping, etc.).

How to pass `props` to another component in TypeScript

I have this component :
interface listvote {
items: any[]
}
const ListVote: React.FC<listvote> = props => {
useEffect(() => {
console.log(items)
})
return (
<VoteResult
id={props.id}
img={props.img}
name={props.name}
score={props.score}
/>
)
}
export default ListVote
It receives this Array as a prop:
<ListVote items={dataListVote} />
const dataListVote = [
{
id: 1,
img:
'sampleimage.com',
name: 'samplename',
score: 50,
},
{
id: 2,
img:
'sampleimage.com',
name: 'samplename',
score: 80,
},
]
There is another component inside ListVote :
interface voteresult {
items: any[]
}
const VoteResult: React.FC<voteresult> = props => {
useEffect(() => {
console.log(props)
})
return <h1>hello</h1>
}
export default VoteResult
The problem is when I try to pass the same array to another component inside ListVote component, it throws this error:
Type 'PropsWithChildren<listvote>' is missing the following properties from type 'any[]': length, pop, push, concat, and 28 more.
Based on your usage:
<VoteResult
id={props.id}
img={props.img}
name={props.name}
score={props.score}
/>
VoteResult does not accept items as a prop. So your type is incorrect:
interface voteresult {
items: any[]
}
Correct Type
The simplest type to get rid of the error:
interface voteresult {
id: any;
img: any;
name: any;
score: any;
}
Home component :
const dataListVote = [
{
id: 1,
img:
'sampleimage.com',
name: 'samplename',
score: 50,
},
{
id: 2,
img:
'sampleimage.com',
name: 'samplename',
score: 80,
},
]
<ListVote items={dataListVote} />
ListVote COmponent:
interface listvote {
items: any[]
}
const ListVote: React.FC<listvote> = props => {
useEffect(() => {
console.log(typeof props)
})
return <VoteResult items={props} />
}
export default ListVote
VoteResult Component :
interface listvote {
items: any[]
}
const VoteResult: React.FC<voteresult> = props => {
useEffect(() => {
console.log(props)
})
return <h1>hello</h1>
}
export default VoteResult
Error :
Type 'PropsWithChildren<listvote>' is missing the following properties from type 'any[]': length, pop, push, concat, and 28 more.

Resources