Last active
February 20, 2024 02:37
-
-
Save j-/1c0c6b7461b65bff20887135f2ef0eea to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { render, screen } from '@testing-library/react'; | |
import React, { createContext } from 'react'; | |
import { ComposeProviders } from './ComposeProviders'; | |
const DummyContextA = createContext<string>(''); | |
const DummyContextB = createContext<string>(''); | |
const DummyContextC = createContext<string>(''); | |
const DummyApp: React.FC = () => ( | |
<dl data-testid='dummy-app'> | |
<dt>Context A</dt> | |
<DummyContextA.Consumer> | |
{(value) => <dd data-testid='context-a'>{value}</dd>} | |
</DummyContextA.Consumer> | |
<dt>Context B</dt> | |
<DummyContextB.Consumer> | |
{(value) => <dd data-testid='context-b'>{value}</dd>} | |
</DummyContextB.Consumer> | |
<dt>Context C</dt> | |
<DummyContextC.Consumer> | |
{(value) => <dd data-testid='context-c'>{value}</dd>} | |
</DummyContextC.Consumer> | |
</dl> | |
); | |
it('can be given no providers', () => { | |
render( | |
<ComposeProviders> | |
<DummyApp /> | |
</ComposeProviders> | |
); | |
expect(screen.getByTestId('dummy-app')).toBeInTheDocument(); | |
expect(screen.getByTestId('context-a')).toHaveTextContent(/^$/); | |
expect(screen.getByTestId('context-b')).toHaveTextContent(/^$/); | |
expect(screen.getByTestId('context-c')).toHaveTextContent(/^$/); | |
}); | |
it('can be given empty providers array', () => { | |
render( | |
<ComposeProviders providers={[]}> | |
<DummyApp /> | |
</ComposeProviders> | |
); | |
expect(screen.getByTestId('dummy-app')).toBeInTheDocument(); | |
expect(screen.getByTestId('context-a')).toHaveTextContent(/^$/); | |
expect(screen.getByTestId('context-b')).toHaveTextContent(/^$/); | |
expect(screen.getByTestId('context-c')).toHaveTextContent(/^$/); | |
}); | |
it('can be given one provider', () => { | |
render( | |
<ComposeProviders | |
providers={[<DummyContextB.Provider key={0} value='Banana' />]} | |
> | |
<DummyApp /> | |
</ComposeProviders> | |
); | |
expect(screen.getByTestId('dummy-app')).toBeInTheDocument(); | |
expect(screen.getByTestId('context-a')).toHaveTextContent(/^$/); | |
expect(screen.getByTestId('context-b')).toHaveTextContent(/^Banana$/); | |
expect(screen.getByTestId('context-c')).toHaveTextContent(/^$/); | |
}); | |
it('can be given multiple providers', () => { | |
render( | |
<ComposeProviders | |
providers={[ | |
<DummyContextA.Provider key={0} value='Apple' />, | |
<DummyContextB.Provider key={1} value='Banana' />, | |
<DummyContextC.Provider key={2} value='Cherry' />, | |
]} | |
> | |
<DummyApp /> | |
</ComposeProviders> | |
); | |
expect(screen.getByTestId('dummy-app')).toBeInTheDocument(); | |
expect(screen.getByTestId('context-a')).toHaveTextContent(/^Apple$/); | |
expect(screen.getByTestId('context-b')).toHaveTextContent(/^Banana$/); | |
expect(screen.getByTestId('context-c')).toHaveTextContent(/^Cherry$/); | |
}); | |
it('applies providers in order', () => { | |
render( | |
<ComposeProviders | |
providers={[ | |
<DummyContextC.Provider key={0} value='Car' />, | |
<DummyContextA.Provider key={1} value='Apple' />, | |
<DummyContextB.Provider key={2} value='Banana' />, | |
<DummyContextC.Provider key={3} value='Cherry' />, | |
]} | |
> | |
<DummyApp /> | |
</ComposeProviders> | |
); | |
expect(screen.getByTestId('dummy-app')).toBeInTheDocument(); | |
expect(screen.getByTestId('context-a')).toHaveTextContent(/^Apple$/); | |
expect(screen.getByTestId('context-b')).toHaveTextContent(/^Banana$/); | |
expect(screen.getByTestId('context-c')).toHaveTextContent(/^Cherry$/); | |
}); | |
it('ignores any providers which are not valid', () => { | |
render( | |
<ComposeProviders | |
providers={[ | |
// @ts-expect-error Boolean is not React element | |
true, | |
<DummyContextA.Provider key={0} value='Apple' />, | |
// @ts-expect-error Number is not React element | |
123, | |
// @ts-expect-error Null is not React element | |
null, | |
]} | |
> | |
<DummyApp /> | |
</ComposeProviders> | |
); | |
expect(screen.getByTestId('dummy-app')).toBeInTheDocument(); | |
expect(screen.getByTestId('context-a')).toHaveTextContent(/^Apple$/); | |
expect(screen.getByTestId('context-b')).toHaveTextContent(/^$/); | |
expect(screen.getByTestId('context-c')).toHaveTextContent(/^$/); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Children, cloneElement, isValidElement } from 'react'; | |
export type ComposeProvidersProps = { | |
providers?: React.ReactElement[]; | |
}; | |
/** | |
* Use this to define providers in a flat structure. Will wrap all children in | |
* the providers given, and in the order they are given. | |
* | |
* @example | |
* | |
* return ( | |
* <ComposeProviders providers={[ | |
* <StoreProvider key="store" store={store} />, | |
* <ThemeProvider key="theme" theme={dark} />, | |
* <AnalyticsProvider key="analytics" dataLayer={window.dataLayer} />, | |
* ]}> | |
* Consume providers here | |
* </ComposeProviders> | |
* ); | |
*/ | |
export const ComposeProviders: React.FC<ComposeProvidersProps> = ({ | |
children, | |
providers = [], | |
}) => ( | |
<> | |
{Children.toArray(providers) | |
.filter(isValidElement) | |
.reduceRight( | |
(children, provider) => cloneElement(provider, {}, children), | |
children | |
)} | |
</> | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment