Render Context for React Component Previews and Tests

as
ashanfernando1 year ago

'Compositions' (a.ka. 'component previews') are a the rendering of a component, in different variations.

It often happens that your compositions require a common context, such as a theme provider, a router, or an auth provider. In such cases, you can wrap every composition individually, or add it as a constant wrapper for all compositions, using the same env.

However, it often happens that your unit tests require the same context your compositions use. This blog post will show you how to create a common render context for both your compositions and unit tests.

You can find the complete code example as shown below;

Create a common render context

Run the following command to create a React Context component:

$bit
Copiedcopy

In our example, the implementation of the render context is as follows:

/* @filename:  my-render-context.tsx */

export function MyRenderContext({ children }: MyRenderContextProps) {
  return (
    <ThemeProvider theme={theme}>
    <Box sx={{ p: 5 }}>
      <Box sx={{ display: "flex", justifyContent: "flex-end" }}/>
      {children}
    </Box>
    </ThemeProvider>
  );
}
CopiedCopy

Step 1: Add it to the env mounter preview

Create a React env (if you don't have one already) and add the render context component as a wrapper for all your component previews.

/* @filename:  my-env/mounter.tsx */

import { MyRenderContext } from '@learnbit-react/common-render-context.render.my-render-context';
export default createMounter(MyRenderContext) as any;
CopiedCopy

See our demo env here. Learn more about changing your React components preview in the Component previews docs.

Step 2: Add it to your component tests

To use the same render context for your unit tests, pass the render context component as a wrapper to the react-testing-library render function.

/* @filename:  demo/button.spec.tsx */

import { render } from "@testing-library/react";
import { SampleButton } from "./button.composition";

it('renders with the correct text', () => {
  const { getByText } = render(<MyRenderContext><SampleButton /></MyRenderContext>);
  const rendered = getByText('Click Me!');
  expect(rendered).toBeTruthy();
});

it('should render with the theme color', () => {
  const { getByText } = render(<MyRenderContext><SampleButton /></MyRenderContext>);
  const rendered = getByText('Click Me!');
  const styles = window.getComputedStyle(rendered);
  const primaryColor = 'rgb(44, 0, 195)';
  expect(styles.backgroundColor).toBe(primaryColor);
});
CopiedCopy

Though, this approach is simpler to implement and easy to understand. However, we need to repeatedly add the wrapper for all our test cases. Let's look at a way to avoid this repetition.

Alternative: Create a custom testing library

Run the following to create a component that extends the react-testing-library function, and customizes it to use the render context component as a wrapper.

$bit
Copiedcopy

The following is the implementation of the test renderer in our example:

my-testing-library.tsx
main.ts
import React from "react";
import { RenderResult, render as _render } from "@testing-library/react";
import { MyRenderContext } from "@learnbit-react/common-render-context.render.my-render-context";

export function render(children: React.JSX.Element): RenderResult {
  return _render(<MyRenderContext>{children}</MyRenderContext>);
}
CopiedCopy

In each test case you can use the render function as follows:

/* @filename:  demo/button.spec.tsx */

import { render } from "@learnbit-react/common-render-context.my-testing-library";
import { SampleButton } from "./button.composition";

it("should render with the correct text", () => {
  const { getByText } = render(<SampleButton />);
  const rendered = getByText("Click Me!");
  expect(rendered).toBeTruthy();
});

it("should render with the theme color", () => {
  const { getByText } = render(<SampleButton />);
  const rendered = getByText("Click Me!");
  const styles = window.getComputedStyle(rendered);
  const primaryColor = "rgb(44, 0, 195)";
  expect(styles.backgroundColor).toBe(primaryColor);
});
CopiedCopy

You can use the custom testing library for all your unit tests. Add it to your component templates to avoid repetition.