-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #725 from sebgroup/develop
minor release: error boundary and translation context
- Loading branch information
Showing
14 changed files
with
465 additions
and
31 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import Docs from "@common/Docs"; | ||
import { ErrorBoundary } from "@sebgroup/react-components/ErrorBoundary"; | ||
import { useDynamicForm } from "@sebgroup/react-components/hooks/useDynamicForm"; | ||
import React from "react"; | ||
|
||
const importString: string = require("!raw-loader!@sebgroup/react-components/ErrorBoundary/ErrorBoundary"); | ||
const code: string = `<ErrorBoundary errorView={<>error view</>}> | ||
<div>lorem ipsum</div> | ||
</ErrorBoundary>`; | ||
|
||
const ErrorBoundaryPage: React.FC = (): React.ReactElement<void> => { | ||
const { | ||
renderForm: renderControls, | ||
state: { controls }, | ||
} = useDynamicForm([ | ||
{ | ||
key: "controls", | ||
items: [{ key: "hasError", label: "hasError", controlType: "Checkbox" }], | ||
}, | ||
]); | ||
|
||
return ( | ||
<Docs | ||
mainFile={importString} | ||
example={ | ||
<div className="w-100 d-flex justify-content-center"> | ||
<ErrorBoundary errorView={<>error view</>}>{controls.hasError ? <div>{new Error()}</div> : <div>lorem ipsum</div>}</ErrorBoundary> | ||
</div> | ||
} | ||
code={code} | ||
controls={<>{renderControls()}</>} | ||
/> | ||
); | ||
}; | ||
|
||
export default ErrorBoundaryPage; |
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import React from "react"; | ||
import { render, unmountComponentAtNode } from "react-dom"; | ||
import { act, Simulate } from "react-dom/test-utils"; | ||
import { ErrorBoundary } from "."; | ||
|
||
describe("Component: ErrorBoundary", () => { | ||
let container: HTMLDivElement = null; | ||
|
||
beforeEach(() => { | ||
container = document.createElement("div"); | ||
document.body.appendChild(container); | ||
}); | ||
|
||
afterEach(() => { | ||
unmountComponentAtNode(container); | ||
container.remove(); | ||
container = null; | ||
}); | ||
|
||
it("Should render correction", () => { | ||
act(() => { | ||
render( | ||
<ErrorBoundary errorView={<div className="error-view">error view</div>}> | ||
<div className="component">some component</div> | ||
</ErrorBoundary>, | ||
container | ||
); | ||
}); | ||
|
||
expect(container.firstElementChild.tagName.toLowerCase()).toEqual("div"); | ||
expect(container.firstElementChild.textContent).toEqual("some component"); | ||
expect(container.querySelector(".error-view")).toBeNull(); | ||
}); | ||
|
||
it("Should render error view when error occurs", () => { | ||
act(() => { | ||
render( | ||
<ErrorBoundary errorView={<div className="error-view">error view</div>}> | ||
<div className="component">{new Error()}</div> | ||
</ErrorBoundary>, | ||
container | ||
); | ||
}); | ||
|
||
expect(container.firstElementChild.tagName.toLowerCase()).toEqual("div"); | ||
expect(container.firstElementChild.textContent).toEqual("error view"); | ||
expect(container.querySelector(".component")).toBeNull(); | ||
}); | ||
}); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import React from "react"; | ||
|
||
interface ErrorBoundaryProps { | ||
/** The error view to be shown */ | ||
errorView: React.ReactNode; | ||
} | ||
|
||
interface ErrorBoundaryState { | ||
hasError: boolean; | ||
} | ||
|
||
export class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> { | ||
constructor(props: ErrorBoundaryProps) { | ||
super(props); | ||
this.state = { hasError: false }; | ||
} | ||
|
||
static getDerivedStateFromError() { | ||
return { hasError: true }; | ||
} | ||
|
||
render() { | ||
if (this.state.hasError) { | ||
return this.props.errorView; | ||
} | ||
|
||
return this.props.children; | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./ErrorBoundary"; |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./translationContext"; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import fetchMock, { enableFetchMocks } from "jest-fetch-mock"; | ||
import React from "react"; | ||
import { render, unmountComponentAtNode } from "react-dom"; | ||
import { act } from "react-dom/test-utils"; | ||
import { TranslationProvider, useTranslationContext } from "./translationContext"; | ||
|
||
enableFetchMocks(); | ||
|
||
const TestContext: React.FC<{ data?: any; translationKey: string }> = ({ data, translationKey }) => { | ||
const { isLoading, t } = useTranslationContext(); | ||
return <div>{isLoading ? <span className="loading-screen">loading...</span> : <p className="translated-text">{t(translationKey, data)}</p>}</div>; | ||
}; | ||
|
||
const MOCK_TRANSLATION_RESULT: any = { result: { content: { title_translation: "title_translation" } } }; | ||
|
||
describe("context: translationContext", () => { | ||
let container: HTMLDivElement = null; | ||
|
||
beforeEach(() => { | ||
fetchMock.resetMocks(); | ||
container = document.createElement("div"); | ||
document.body.appendChild(container); | ||
}); | ||
|
||
afterEach(() => { | ||
unmountComponentAtNode(container); | ||
container.remove(); | ||
container = null; | ||
}); | ||
|
||
it("Should throw error when context used without provider", () => { | ||
expect(() => render(<TestContext translationKey="title_translation" />, container)).toThrowError("useTranslationContext must be used within a TranslationProvider"); | ||
}); | ||
|
||
it("Should show loading screen when fetching translation", async () => { | ||
fetchMock.mockResponseOnce(JSON.stringify(MOCK_TRANSLATION_RESULT)); | ||
await act(async () => { | ||
render( | ||
<TranslationProvider url="https://translation_url"> | ||
<TestContext translationKey="title_translation" /> | ||
</TranslationProvider>, | ||
container | ||
); | ||
expect(container.querySelector(".loading-screen")).toBeDefined(); | ||
}); | ||
expect(container.querySelector(".loading-screen")).toBeNull(); | ||
}); | ||
|
||
it("Should get translation when key exist in translations", async () => { | ||
fetchMock.mockResponseOnce(JSON.stringify(MOCK_TRANSLATION_RESULT)); | ||
await act(async () => { | ||
render( | ||
<TranslationProvider url="https://translation_url"> | ||
<TestContext translationKey="title_translation" /> | ||
</TranslationProvider>, | ||
container | ||
); | ||
}); | ||
expect(container.querySelector(".translated-text").textContent).toEqual("title_translation"); | ||
}); | ||
|
||
it("Should interpolate translation when data exist", async () => { | ||
fetchMock.mockResponseOnce(JSON.stringify({ result: { content: { text_hello: "today is {date}" } } })); | ||
await act(async () => { | ||
render( | ||
<TranslationProvider url="https://translation_url"> | ||
<TestContext translationKey="text_hello" data={{ date: "2020-01-01" }} /> | ||
</TranslationProvider>, | ||
container | ||
); | ||
}); | ||
expect(container.querySelector(".translated-text").textContent).toEqual("today is 2020-01-01"); | ||
}); | ||
|
||
it("Should get empty string when translation does not exist", async () => { | ||
fetchMock.mockResponseOnce(JSON.stringify({})); | ||
await act(async () => { | ||
render( | ||
<TranslationProvider url="https://translation_url"> | ||
<TestContext translationKey="key_unknown" /> | ||
</TranslationProvider>, | ||
container | ||
); | ||
}); | ||
expect(container.querySelector(".translated-text").textContent).toEqual(""); | ||
}); | ||
|
||
it("Should map custom translation path when provided", async () => { | ||
fetchMock.mockResponseOnce(JSON.stringify({ response: MOCK_TRANSLATION_RESULT })); | ||
await act(async () => { | ||
render( | ||
<TranslationProvider url="https://translation_url" translationPath="response.result.content"> | ||
<TestContext translationKey="title_translation" /> | ||
</TranslationProvider>, | ||
container | ||
); | ||
}); | ||
expect(container.querySelector(".translated-text").textContent).toEqual("title_translation"); | ||
}); | ||
|
||
it("Should get fallback translation when translation fetch fails", async () => { | ||
fetchMock.mockRejectOnce(); | ||
await act(async () => { | ||
render( | ||
<TranslationProvider url="https://translation_url" fallbackTranslation={{ title_translation: "title_fallbacktranslation" }}> | ||
<TestContext translationKey="title_translation" /> | ||
</TranslationProvider>, | ||
container | ||
); | ||
}); | ||
expect(container.querySelector(".translated-text").textContent).toEqual("title_fallbacktranslation"); | ||
}); | ||
}); |
Oops, something went wrong.