Couldn't find story matching - React + Storybook - reactjs

I am unable to display a story using next.js.
Other stories of the app work fine. This one doesn't. Please I have been banging my head against the wall for 3 hours.
import { ComponentMeta, ComponentStory } from '#storybook/react';
import React from 'react';
import { mockAvailability } from './SpaceAvailability.test';
import SpaceAvailability from './index';
export default {
title: 'SpaceAvailability',
component: SpaceAvailability,
} as ComponentMeta<typeof SpaceAvailability>;
const Template: ComponentStory<typeof SpaceAvailability> = (args) => (
<SpaceAvailability {...args} />
);
export const WeekStartsMonday = Template.bind({});
WeekStartsMonday.args = {
availability: mockAvailability,
weekStartsOnMonday: true,
};
export const WeekStartsSunday = Template.bind({});
WeekStartsMonday.args = {
availability: mockAvailability,
weekStartsOnMonday: false,
};
If I preview the story I get:

Related

EditorJS is not showing in NextJS even it is loaded through SSR:false

so I am integrating EditorJs with the NextJs app I have done the initialization in the console it shows Editojs is ready but on the screen, it is not visible
can anyone please tell me what I am doing wrong I am sharing my code below
Editor.js
import { createReactEditorJS } from 'react-editor-js'
import { EditorTools } from './EditorTools';
import React, { useEffect } from 'react'
const Editor = () => {
const ReactEditorJS = createReactEditorJS();
return (
<div>
<ReactEditorJS holder="customEditor" tools={EditorTools}>
<div id="customEditor" />
</ReactEditorJS>
</div>
)
}
export default Editor
EditorTools.js
import Header from '#editorjs/header';
export const EditorTools = {
header: {
class: Header,
config: {
placeholder: 'Let`s write an awesome story! ✨',
},
},
};
Create.js
import React from 'react'
import dynamic from 'next/dynamic';
const EditorJSNoSSRWrapper = dynamic(import('../../../components/Editor/Editor'), {
ssr: false,
loading: () => <p>Loading ...</p>,
});
const create = () => {
return (
<div>
<EditorJSNoSSRWrapper />
</div>
)
}
export default create

Jest throws "Types of property 'id' are incompatible" error when it is trying to use stories from storybook using typescript

So..I am trying to use stories for my unit test with jest + RTL to minimize duplication (explained here) and the test throws "Types of property 'id' are incompatible" error when I pass the args that is also used in my story.
I am currently using below packages as my testing suite.
"#storybook/react": "^6.5.9",
"#storybook/testing-react": "^1.2.4",
"#testing-library/react": "^13.3.0",
"jest": "^28.1.1",
// and others...
Below is my test.
import * as React from 'react';
import { screen, render } from '#testing-library/react';
import { Default as Checkbox } from '../../stories/AccountCheckbox.stories';
const renderCheckbox = () => {
render(<Checkbox {...Checkbox.args} />); // the point where it throws an type error that says 'id is incompatible'.
}
describe('<AccountCheckbox />', () => {
test('should display account information', () => {
renderCheckbox();
expect(screen.getByTestId('example-test-id')).toBeInTheDocument();
});
});
Below is my story.
import React from 'react';
import { ComponentMeta, ComponentStory } from '#storybook/react';
import AccountCheckbox from '../components/AccountCheckbox';
import { StoryAppWrapper } from '../../.storybook/wrapper/StoryAppWrapper';
export default {
title: 'AccountCheckbox',
component: AccountCheckbox,
decorators: [(Story) => <StoryAppWrapper>{Story()}</StoryAppWrapper>]
} as ComponentMeta<typeof AccountCheckbox>;
const Template: ComponentStory<typeof AccountCheckbox> = (args) => (
<AccountCheckbox {...args} />
);
export const Default = Template.bind({});
// These are the args I am trying to utilize in my test as well.
Default.args = {
id: 'example-id',
testId: 'example-test-id',
label: 'Account 1',
selected: false,
onChange: () => { },
};
Below is the type for the checkbox I declared.
export type CheckboxProps = {
id: string;
testId: string;
label: string;
selected: boolean;
onChange: any;
};
Below is the actual code.
import * as React from 'react';
import { Checkbox } from 'custom-lib';
import { CheckboxProps } from '../../types/types';
const AccountCheckbox = ({
id,
testId,
label,
selected,
onChange,
}: CheckboxProps): JSX.Element => {
return (
<Checkbox
id={id}
data-testid={testId}
label={label}
state={selected ? 'true' : 'false'}
onChange={onChange}
/>
);
};
export default AccountCheckbox;
Can anyone pinpoint what I've missed within these codes?
You're trying to render the Story, not the Checkbox itself. Why not import the checkbox directly, but use the story args too?
import * as React from 'react';
import { screen, render } from '#testing-library/react';
import AccountCheckbox from '../components/AccountCheckbox';
import { Default as AccountCheckboxStory } from '../../stories/AccountCheckbox.stories';
const renderCheckbox = () => {
render(<AccountCheckbox {...AccountCheckboxStory.args} />);
}
describe('<AccountCheckbox />', () => {
test('should display account information', () => {
renderCheckbox();
expect(screen.getByTestId('example-test-id')).toBeInTheDocument();
});
});
You may need to adjust paths to the Story/AccountCheckbox as I'm not quite sure what your directory structure looks like.
Update
Going from your comment and the link, it seems that they are using some form of a compositional function to wrap the exported stories and return them as components. So in your case, would you not do this:
import React from 'react';
import { render, screen } from '#testing-library/react';
import { composeStories } from '#storybook/testing-react';
import * as stories from '../../stories/AccountCheckbox.stories';
const { Default: AccountCheckBoxDefault } = composeStories(stories);
test('renders profile page', async () => {
render(<AccountCheckBoxDefault />);
// ... some assertions here
});
describe('<AccountCheckbox />', () => {
test('should display account information', () => {
renderCheckbox();
expect(screen.getByTestId('example-test-id')).toBeInTheDocument();
});
});
I don't think it's actually necessary to pass in the args, as those args should be used by the story itself.

ReactJS not re-rendering after context changed

I have a basic app here and I'm trying to config React Context properly but it isn't working. My goal is to render PlayScreen with the content of currentStage inside the React Context. Game changes the context but App keeps rendering PlayScreen with the "welcome" string, instead of "won" or "lost".
Also, I know that gameContext.js is for autocompletion but I added "welcome" there to have a first default state. Somehow I couldn't find a way to set up that very first "welcome" context when App is rendered for the first time.
I tried feeding PlayScreen with the context itself and didn't work, and now I tried setting a state with it but it doesn't work either (even when using useEffect and having the context as a dependency).
So I have two questions, what am I doing wrong? and, my way to set up the "welcome" default state is wrong? If so, how can I do it? Thanks.
gameContext.js
import React from 'react';
const GameContext = React.createContext({
currentStage: 'welcome',
playerStage: (stage) => {},
});
export default GameContext;
GameProvider.jsx
import React, { useReducer, useMemo } from 'react';
import PropTypes from 'prop-types';
import GameContext from './gameContext';
const defaultState = {
currentStage: '',
};
const gameReducer = (state, action) => {
if (action.type === 'STAGE') {
return {
currentStage: action.playerStage,
};
}
return defaultState;
};
const GameProvider = ({ children }) => {
const [gameState, dispatchGameAction] = useReducer(gameReducer, defaultState);
const playerStageHandler = (playerStage) => {
dispatchGameAction({
type: 'STAGE',
playerStage,
});
};
const gameContext = useMemo(
() => ({
currentStage: gameState.currentStage,
playerStage: playerStageHandler,
}),
[gameState.currentStage]
);
return (
<GameContext.Provider value={gameContext}>{children}</GameContext.Provider>
);
};
GameProvider.propTypes = {
children: PropTypes.node.isRequired,
};
export default GameProvider;
App.jsx
import React, { useContext, useState, useEffect } from 'react';
import GameProvider from './store/GameProvider';
import GameContext from './store/gameContext';
import PlayScreen from './components/PlayScreen';
import Settings from './components/Settings';
import Game from './components/Game';
const App = () => {
const gameContext = useContext(GameContext);
const [stage, setStage] = useState(gameContext.currentStage);
useEffect(() => {
setStage(gameContext.currentStage);
}, [gameContext]);
const [currentScreen, setCurrentScreen] = useState({
playScreen: true,
settings: false,
game: false,
});
const changeScreenHandler = (newScreen) => {
switch (newScreen) {
case 'playScreen':
setCurrentScreen({
playScreen: true,
settings: false,
game: false,
});
break;
case 'settings':
setCurrentScreen({
playScreen: false,
settings: true,
game: false,
});
break;
case 'game':
setCurrentScreen({
playScreen: false,
settings: false,
game: true,
});
break;
default:
break;
}
};
return (
<GameProvider>
{currentScreen.playScreen && (
<PlayScreen stage={stage} onChangeScreen={changeScreenHandler} />
)}
{currentScreen.settings && (
<Settings onChangeScreen={changeScreenHandler} />
)}
{currentScreen.game && <Game onChangeScreen={changeScreenHandler} />}
</GameProvider>
);
};
export default App;
PlayScreen.jsx
import PropTypes from 'prop-types';
const PlayScreen = ({ stage, onChangeScreen }) => {
const clickHandler = () => {
onChangeScreen('settings');
};
return (
<div>
<h1>{stage}</h1>
<button type="button" onClick={clickHandler}>
Go
</button>
</div>
);
};
PlayScreen.propTypes = {
stage: PropTypes.string.isRequired,
onChangeScreen: PropTypes.func.isRequired,
};
export default PlayScreen;
Game.jsx
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import GameContext from '../store/gameContext';
const Game = ({ onChangeScreen }) => {
const gameContext = useContext(GameContext);
const wonHandler = () => {
onChangeScreen('playScreen');
gameContext.playerStage('won');
};
const lostHandler = () => {
onChangeScreen('playScreen');
gameContext.playerStage('lost');
};
return (
<div>
<h1>GAME RUNNING</h1>
<button type="button" onClick={wonHandler}>
won
</button>
<button type="button" onClick={lostHandler}>
lost
</button>
</div>
);
};
Game.propTypes = {
onChangeScreen: PropTypes.func.isRequired,
};
export default Game;
You are consuming the GameContext above the GameProvider. The context shouldn't be available because App isn't being provided the context by the GameProvider.
Try moving everything underneath GameProvider into its own component and consume the context there.

Jest / Enzyme not recognizing props

I am trying to write a test for a React functional component that uses Redux and Hooks.
I am using Jest with Enzyme for testing.
For Reference:
This is the functional component being tested:
import React from 'react';
import {useDispatch, useSelector} from "react-redux";
import * as actions from '../../actions/actions';
import { Button, Icon } from "#material-ui/core";
export const EditBatchHeaderComponent = (props) => {
const dispatch = useDispatch();
const { selectedBatch } = props;
const { batchName } = selectedBatch;
return (
<div className="edit-header-container">
<Button disableRipple onClick={() => {dispatch(actions.unSelectBatch())} }>
<Icon>arrow_back</Icon>
</Button>
<span>Edit Batch</span>
<span>{batchName}</span>
</div>
);
};
This is component's container:
import React from 'react';
import { BatchHeaderComponent } from './BatchHeaderComponent';
import { BatchTableComponent } from './BatchTableComponent';
import { EditBatchComponent } from './EditBatchComponent';
import {useSelector} from "react-redux";
import {EditBatchHeaderComponent} from "./EditBatchHeaderComponent";
export const BatchManagementComponent = () => {
const { selectedBatch } = useSelector(state => state.batchManagementReducer);
if (selectedBatch.length) {
return (
<div className="component-container">
<EditBatchHeaderComponent selectedBatch={selectedBatch} />
<EditBatchComponent selectedBatch={selectedBatch} />
</div>
);
}
return (
<div className="component-container">
<BatchHeaderComponent />
<BatchTableComponent />
</div>
);
};
This is the default state of the reducer:
{
sorting: {
order: '',
orderBy: ''
},
searchBy: 'batchName',
searchText: '',
filterByStatus: '--',
filterByType: '--',
waiting: false,
batchData: [],
selectedBatch: {
batchName: '',
},
}
This is the test file that is failing to recognize the props:
import React from 'react';
import { EditBatchHeaderComponent } from '../../../components/batchManagement/EditBatchHeaderComponent';
import configureStore from '../../../store';
import {Provider} from "react-redux";
import Enzyme, { mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import {Button} from "#material-ui/core";
Enzyme.configure({ adapter: new Adapter() });
describe('EditBatchHeaderComponent', () => {
it('mounts to the DOM successfully', () => {
const wrapper = mount(<Provider store={configureStore()}>
<EditBatchHeaderComponent />
</Provider>);
expect(wrapper.find(EditBatchHeaderComponent)).toBeDefined();
});
it('deselects the account and closes when the back button is clicked', () => {
const props = {selectedBatch: {batchName: 'INFORM'}, dispatch: jest.fn()};
const obj = {};
const wrapper = mount(
<Provider store={configureStore()}>
<EditBatchHeaderComponent {...props} />
</Provider>
);
console.log(wrapper.find(EditBatchHeaderComponent).props());
wrapper.find(Button).first().simulate('click');
expect(wrapper.find(EditBatchHeaderComponent)).toEqual(obj);
});
});
This is the error text provided by the test suite:
FAIL src/spec/components/batchManagement/EditBatchHeaderComponent.test.js (7.182s)
● EditBatchHeaderComponent › mounts to the DOM successfully
TypeError: Cannot read property 'batchName' of undefined
8 | const dispatch = useDispatch();
9 | const { selectedBatch } = props;
> 10 | const { batchName } = selectedBatch;
| ^
11 | return (
12 | <div className="edit-header-container">
13 | <Button disableRipple onClick={() => {dispatch(actions.unSelectBatch())} }>
I have run a nearly identical test on a similar component that runs and covers the code appropriately.
I can't seem to figure out why the props aren't being recognized.
Any assistance would be greatly appreciated, thanks.

React - frontend component test with Jest

I've just written test file for my component, at the moment it's very rudimentary.. I'm quite inexperience in written test for frontend. I ran yarn test to this test file and it failed miserably..
Here is the message:
Unable to find an element with the text: Please review your billing details...
This is what I have so far for my test:
import React from 'react';
import { render, cleanup, waitForElement } from 'react-testing-library';
// React Router
import { MemoryRouter, Route } from "react-router";
import Show from './Show';
test('it shows the offer', async () => {
const { getByText } = render(
<MemoryRouter initialEntries={['/booking-requests/20-A1-C2/offer']}>
<Route
path="/booking-requests/:booking_request/offer"
render={props => (
<Show {...props} />
)}
/>
</MemoryRouter>
);
//displays the review prompt
await waitForElement(() => getByText('Please review your billing details, contract preview and Additions for your space. Once you’re happy, accept your offer'));
//displays the confirm button
await waitForElement(() => getByText('Confirm'));
});
and this is the component:
// #flow
import * as React from 'react';
import i18n from 'utils/i18n/i18n';
import { Btn } from '#appearhere/bloom';
import css from './Show.css';
import StepContainer from 'components/Layout/DynamicStepContainer/DynamicStepContainer';
const t = i18n.withPrefix('client.apps.offers.show');
const confirmOfferSteps = [
{
title: t('title'),
breadcrumb: t('breadcrumb'),
},
{
title: i18n.t('client.apps.offers.billing_information.title'),
breadcrumb: i18n.t('client.apps.offers.billing_information.breadcrumb'),
},
{
title: i18n.t('client.apps.offers.confirm_pay.title'),
breadcrumb: i18n.t('client.apps.offers.confirm_pay.breadcrumb'),
},
];
class Show extends React.Component<Props> {
steps = confirmOfferSteps;
renderCtaButton = (): React.Element<'Btn'> => {
const cta = t('cta');
return <Btn className={css.button} context='primary'>
{cta}
</Btn>
};
renderLeftContent = ({ isMobile }: { isMobile: boolean }): React.Element<'div'> => (
<div>
<p>{t('blurb')}</p>
{!isMobile && this.renderCtaButton()}
</div>
);
renderRightContent = () => {
return <div>Right content</div>;
};
render() {
const ctaButton = this.renderCtaButton();
return (
<StepContainer
steps={this.steps}
currentStep={1}
ctaButton={ctaButton}
leftContent={this.renderLeftContent}
rightContent={this.renderRightContent}
footer={ctaButton}
/>
);
}
}
export default Show;
what am I missing? Suggestions what else to add to my test file would be greatly appreciated!

Resources