-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/complete list logic and implement tests #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
am1rb
wants to merge
15
commits into
develop
Choose a base branch
from
feature/complete_List_logic_and_implement_tests
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
9f2e5be
install react-dom and some required packages to run tests
am1rb 0b825d3
use dot-object to read key from rows in ListRows component and fix so…
am1rb 5292d5f
implement tests for ListRows component
am1rb 87e493c
install dot-object module and change the testEnviroment
am1rb 67f1dac
add a new test to check the type of dataKey property
am1rb 9c6072c
throw custom errors for `the dataKey does not found state` and `the t…
am1rb 59d7116
move the extend-expect down
am1rb 218ff0a
implement a lot of tests for List component
am1rb 38d7b2c
add a new test to check firstChild and lastChild props
am1rb bfb6652
implement a new test to check the Container prop
am1rb 331f4df
change the type names to meaningful names
am1rb b01fa61
change the type names to meaningful names in ListRows component
am1rb c784a2c
change the EmptyComponentProps name to BaseEmptyProps
am1rb 44bf551
fix the Container typing issue
am1rb 163dd9f
export the List component from the main index.ts file
am1rb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
module.exports = { | ||
preset: 'ts-jest', | ||
testEnvironment: 'node', | ||
}; | ||
testEnvironment: 'jsdom', | ||
}; |
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
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
import React, { ReactNode } from "react"; | ||
import { render } from "@testing-library/react"; | ||
import "@testing-library/jest-dom/extend-expect"; | ||
import List, { Props as ListProps } from "./index"; | ||
|
||
interface TestRowProps { | ||
id: number; | ||
label: string; | ||
} | ||
|
||
function TestRow({ id, label }: TestRowProps) { | ||
return ( | ||
<div> | ||
{id} - {label} | ||
</div> | ||
); | ||
} | ||
|
||
interface TestEmptyProps { | ||
className?: string; | ||
message: string; | ||
} | ||
|
||
function TestEmpty({ message }: TestEmptyProps) { | ||
return ( | ||
<> | ||
<span>custom message:</span> {message} | ||
</> | ||
); | ||
} | ||
|
||
const sharedProps: ListProps<TestRowProps, TestRowProps> = { | ||
RowComponent: TestRow, | ||
rows: [ | ||
{ id: 10, label: "A" }, | ||
{ id: 11, label: "B" }, | ||
{ id: 12, label: "C" } | ||
] | ||
}; | ||
|
||
describe("The List component tests", () => { | ||
test("It should render custom EmptyComponent and message", () => { | ||
const { queryByText } = render( | ||
<List<TestRowProps, TestRowProps, TestEmptyProps> | ||
{...sharedProps} | ||
rows={[]} | ||
EmptyComponent={TestEmpty} | ||
EmptyProps={{ message: "No Data" }} | ||
/> | ||
); | ||
expect(queryByText("custom message:")).toBeInTheDocument(); | ||
}); | ||
|
||
test("It should not render EmptyComponent if hasEmpty is false", () => { | ||
const { queryByTestId } = render( | ||
<List {...sharedProps} rows={[]} hasEmpty={false} /> | ||
); | ||
expect(queryByTestId("empty-component")).toBeNull(); | ||
}); | ||
|
||
test("It should render firstChild and lastChild at correct positions", () => { | ||
const firstChild = <span data-testid="first-child" />; | ||
const lastChild = <span data-testid="last-child" />; | ||
const { getByTestId } = render( | ||
<List {...sharedProps} firstChild={firstChild} lastChild={lastChild} /> | ||
); | ||
expect(getByTestId("root").firstChild).toBe(getByTestId("first-child")); | ||
expect(getByTestId("root").lastChild).toBe(getByTestId("last-child")); | ||
}); | ||
|
||
test("It should render custom container", () => { | ||
function CustomContainer({ children }: { children: ReactNode }) { | ||
return ( | ||
<section data-testid="custom-root"> | ||
{children} | ||
</section> | ||
); | ||
} | ||
const { getByTestId } = render( | ||
<List {...sharedProps} Container={CustomContainer} /> | ||
); | ||
|
||
expect(getByTestId("custom-root")).toBeInTheDocument(); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,43 +1,57 @@ | ||
import React, { ComponentType, memo, ReactNode } from "react"; | ||
import React, { ComponentType, memo, ReactNode, ElementType } from "react"; | ||
import ListRows, { Props as ListRowsProps } from "../ListRows"; | ||
import Empty, { Props as EmptyProps } from "../Empty"; | ||
import Empty, { Props as BaseEmptyProps } from "../Empty"; | ||
|
||
export interface Props<T, U = T, V extends EmptyProps = EmptyProps> | ||
extends ListRowsProps<T, U> { | ||
export interface Props< | ||
DataProps, | ||
RowProps = DataProps, | ||
EmptyProps extends BaseEmptyProps = BaseEmptyProps | ||
> extends ListRowsProps<DataProps, RowProps> { | ||
className?: string; | ||
EmptyComponent?: ComponentType<V>; | ||
EmptyProps?: V; | ||
Container?: ElementType<{ className?: string; children: ReactNode }>; | ||
EmptyComponent?: ComponentType<EmptyProps>; | ||
EmptyProps?: EmptyProps; | ||
hasEmpty?: boolean; | ||
firstChild?: ReactNode; | ||
lastChild?: ReactNode; | ||
} | ||
|
||
function BList<T, U = T, V extends EmptyProps = EmptyProps>({ | ||
function BList< | ||
DataProps, | ||
RowProps = DataProps, | ||
EmptyProps extends BaseEmptyProps = BaseEmptyProps | ||
>({ | ||
className, | ||
Container = "div", | ||
EmptyComponent = Empty, | ||
EmptyProps, | ||
hasEmpty = true, | ||
firstChild, | ||
lastChild, | ||
rows, | ||
...other | ||
}: Props<T, U, V>) { | ||
}: Props<DataProps, RowProps, EmptyProps>) { | ||
return ( | ||
<div className={className}> | ||
<Container data-testid="root" className={className}> | ||
{firstChild} | ||
<ListRows rows={rows} {...other} /> | ||
{hasEmpty && rows.length === 0 && ( | ||
<EmptyComponent {...(EmptyProps as V)} /> | ||
<EmptyComponent | ||
data-testid="empty-component" | ||
{...(EmptyProps as EmptyProps)} | ||
/> | ||
)} | ||
{lastChild} | ||
</div> | ||
</Container> | ||
); | ||
} | ||
|
||
const OList = memo(BList); | ||
|
||
export default function List<T, U = T, V extends EmptyProps = EmptyProps>( | ||
props: Props<T, U, V> | ||
) { | ||
export default function List< | ||
DataProps, | ||
RowProps = DataProps, | ||
EmptyProps extends BaseEmptyProps = BaseEmptyProps | ||
>(props: Props<DataProps, RowProps, EmptyProps>) { | ||
return <OList {...props} />; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import React from "react"; | ||
import { render } from "@testing-library/react"; | ||
import "@testing-library/jest-dom/extend-expect"; | ||
import ListRows, { Props as ListRowsProps } from "./index"; | ||
|
||
interface TestRowProps { | ||
person: { code: number }; | ||
name: string; | ||
family: string; | ||
id: unknown; | ||
} | ||
|
||
function TestRow({ person: { code }, name, family }: TestRowProps) { | ||
return ( | ||
<div> | ||
{code} - {name} {family} | ||
</div> | ||
); | ||
} | ||
|
||
const sharedProps: ListRowsProps<TestRowProps> = { | ||
RowComponent: TestRow, | ||
rows: [ | ||
{ person: { code: 11 }, name: "Sara", family: "Doe", id: 11 }, | ||
{ person: { code: 12 }, name: "John", family: "Doe", id: 12 } | ||
], | ||
dataKey: "person.code" | ||
}; | ||
|
||
const originalError = console.error; | ||
afterEach(() => (console.error = originalError)); | ||
|
||
describe("The ListRows component tests", () => { | ||
test("It should throw an Error if the dataKey does not exist", () => { | ||
console.error = jest.fn(); | ||
try { | ||
render(<ListRows {...sharedProps} dataKey="invalid.key" />); | ||
} catch (e) { | ||
expect(e.message).toBe("The `invalid.key` property does not exist"); | ||
} | ||
}); | ||
|
||
test("It should throw an Error if the type of dataKey property is not number or string", () => { | ||
const keys = [ | ||
{ key: 11, error: false }, | ||
{ key: "11", error: false }, | ||
{ key: true, error: true }, | ||
{ key: () => {}, error: true }, | ||
{ key: new Date(), error: true }, | ||
{ key: { test: { a: "b" } }, error: true } | ||
]; | ||
|
||
console.error = jest.fn(); | ||
|
||
keys.forEach(record => { | ||
let hasError = false; | ||
try { | ||
render( | ||
<ListRows | ||
{...sharedProps} | ||
RowProps={row => ({ ...row, id: record.key })} | ||
dataKey="id" | ||
/> | ||
); | ||
} catch (e) { | ||
hasError = | ||
e.message === "The type of `id` property must be string or number"; | ||
} | ||
|
||
expect(hasError).toBe(record.error); | ||
}); | ||
}); | ||
|
||
test("It should render rows correctly", () => { | ||
const { queryByText } = render(<ListRows {...sharedProps} />); | ||
expect(queryByText("11 - Sara Doe")).toBeInTheDocument(); | ||
expect(queryByText("12 - John Doe")).toBeInTheDocument(); | ||
}); | ||
}); |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,44 @@ | ||
import React, { memo, ComponentType, Key } from "react"; | ||
import dot from "dot-object"; | ||
|
||
export interface Props<T, U = T> { | ||
rows: T[]; | ||
RowComponent: ComponentType<U>; | ||
RowProps?: (row: T) => U; | ||
export interface Props<DataProps, RowProps = DataProps> { | ||
rows: DataProps[]; | ||
RowComponent: ComponentType<RowProps>; | ||
RowProps?: (row: DataProps) => RowProps; | ||
dataKey?: string; | ||
} | ||
|
||
function BListRows<T, U = T>({ | ||
function BListRows<DataProps, RowProps = DataProps>({ | ||
rows, | ||
RowComponent, | ||
RowProps, | ||
dataKey = "id" | ||
}: Props<T, U>) { | ||
}: Props<DataProps, RowProps>) { | ||
return ( | ||
<> | ||
{rows.map(row => { | ||
// const key = dot.pick(dataKey, row); // dot-object | ||
const key = row[dataKey]; | ||
const rowProps = RowProps ? RowProps(row) : row; | ||
const key = dot.pick(dataKey, rowProps); | ||
|
||
if (typeof key !== "string" && typeof key !== "number") { | ||
throw new Error("Invalid row key"); | ||
if (key === undefined) { | ||
throw new Error("The `" + dataKey + "` property does not exist"); | ||
} else if (typeof key !== "string" && typeof key !== "number") { | ||
throw new Error( | ||
"The type of `" + dataKey + "` property must be string or number" | ||
); | ||
} | ||
|
||
const rowProps = RowProps ? RowProps(row) : row; | ||
return ( | ||
<RowComponent | ||
key={(row[dataKey] as unknown) as Key} | ||
{...(rowProps as U)} | ||
/> | ||
<RowComponent key={(key as unknown) as Key} {...(rowProps as RowProps)} /> | ||
); | ||
})} | ||
</> | ||
); | ||
} | ||
|
||
const OListRows = memo(BListRows); | ||
export default function ListRows<T, U = T>(props: Props<T, U>) { | ||
export default function ListRows<DataProps, RowProps = DataProps>( | ||
props: Props<DataProps, RowProps> | ||
) { | ||
return <OListRows {...props} />; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default as ListRows } from "./components/ListRows"; | ||
export { default as List } from "./components/List"; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.