Skip to content

Instantly share code, notes, and snippets.

@theer1k
Last active September 20, 2022 17:12
Show Gist options
  • Save theer1k/01198111f72bff068beb84ca5d2adc92 to your computer and use it in GitHub Desktop.
Save theer1k/01198111f72bff068beb84ca5d2adc92 to your computer and use it in GitHub Desktop.
Componser
import { Context, createContext, useContext } from 'react';
import { render, screen } from '@/test/testUtils';
import { ComponentWithChildren, composer } from '.';
type ContextTuple = [Context<string>, ComponentWithChildren];
const createMockContext = (value: string): ContextTuple => {
const MockContext: Context<string> = createContext('');
const MockContextProvider: ComponentWithChildren = ({ children }) => (
<MockContext.Provider value={value}>{children}</MockContext.Provider>
);
return [MockContext, MockContextProvider];
};
const [MockContext1, MockContextProvider1] = createMockContext('context 1');
const [MockContext2, MockContextProvider2] = createMockContext('context 2');
const NestedContext = createContext('');
const NestedContextProvider: ComponentWithChildren = ({ children }) => {
const nestedValue = useContext(MockContext1);
return (
<NestedContext.Provider value={nestedValue}>
{children}
</NestedContext.Provider>
);
};
const MockComponent = () => {
const contextValue1 = useContext(MockContext1);
const contextValue2 = useContext(MockContext2);
const nestedContextValue = useContext(NestedContext);
return (
<>
<p>{contextValue1}</p>
<p>{contextValue2}</p>
{nestedContextValue && (
<p data-testid="nestedValue">{nestedContextValue}</p>
)}
</>
);
};
describe('composer', () => {
it('Should pass multiple context values to its children', () => {
const ComposedProvider = composer(
MockContextProvider1,
MockContextProvider2,
);
render(
<ComposedProvider>
<MockComponent />
</ComposedProvider>,
);
const contextValue1 = screen.getByText('context 1');
const contextValue2 = screen.getByText('context 2');
expect(contextValue1).toBeInTheDocument();
expect(contextValue2).toBeInTheDocument();
});
it('Should render multiple contexts from left to right', () => {
const ComposedProvider = composer(
MockContextProvider1,
MockContextProvider2,
NestedContextProvider,
);
render(
<ComposedProvider>
<MockComponent />
</ComposedProvider>,
);
const contextValue1 = screen.getAllByText('context 1');
const contextValue2 = screen.getByText('context 2');
const nestedValue = screen.queryByTestId('nestedValue');
expect(contextValue1).toHaveLength(2);
expect(contextValue2).toBeInTheDocument();
expect(nestedValue).toBeInTheDocument();
});
it('Should fail to render contexts in the wrong order', () => {
const ComposedProvider = composer(
NestedContextProvider,
MockContextProvider1,
MockContextProvider2,
);
render(
<ComposedProvider>
<MockComponent />
</ComposedProvider>,
);
const contextValue1 = screen.getByText('context 1');
const contextValue2 = screen.getByText('context 2');
const nestedValue = screen.queryByTestId('nestedValue');
expect(contextValue1).toBeInTheDocument();
expect(contextValue2).toBeInTheDocument();
expect(nestedValue).not.toBeInTheDocument();
});
});
import { ReactNode } from 'react';
export type ComponentWithChildren = (props: {
children: ReactNode;
}) => JSX.Element;
export const composer = (...providers: ComponentWithChildren[]) => {
const ComposedComponents: ComponentWithChildren = ({ children }) => {
return (
<>
{providers.reduceRight(
(child, Provider) => (
<Provider>{child}</Provider>
),
children,
)}
</>
);
};
return ComposedComponents;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment