See the live version - User table.
This project is a user table interface with features such as fetching user data from an API, filtering, sorting, and pagination. The project uses technologies like React, Redux Toolkit, Styled Components, and TypeScript.
- The user data is fetched from a remote API using
axios
. - This fetching process is managed by Redux Toolkit’s
createAsyncThunk
to handle asynchronous requests within the Redux store. - A loading spinner is displayed while the data is being fetched, and an error message is shown if the request fails.
- The project uses Redux Toolkit to manage the state of the users, sorting options, filtering, pagination, and more.
- Users can sort the table by different columns such as
Name
,Username
,E-mail
, orPhone Number
. - The sorting feature supports ascending and descending orders.
- The table allows users to filter by
Name
,Username
,E-mail
, andPhone Number
through input fields provided in the table header. - The filtering is done locally, meaning that once the user data is fetched, the data is filtered on the client side for optimal performance.
- The table supports pagination, allowing users to specify how many users they want to see per page.
- Users can navigate through pages, and the pagination state is stored in Redux to persist the current page and items per page settings.
- The UI is styled using Styled Components, allowing scoped and dynamic styling.
- It also leverages CSS variables for consistent theming and maintainable styles.
- Jest and React Testing Library are set up to enable unit and integration testing for the application.
- Tests cover key components, Redux actions, and asynchronous operations like fetching and displaying user data.
Example
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
test('shows spinner while loading and displays user data after loading', async () => {
mockedAxios.get.mockImplementationOnce(() => new Promise(resolve => setTimeout(() => resolve({ data }), 1000)));
jest.useFakeTimers();
renderWithProviders(<App />);
expect(screen.getByText(/user list/i)).toBeInTheDocument();
expect(screen.getByTestId('spinner')).toBeInTheDocument();
jest.advanceTimersByTime(1000);
await screen.findByText(/Zenon Zenonkiewicz/i);
expect(screen.queryByTestId('spinner')).not.toBeInTheDocument();
jest.useRealTimers();
});
test('handles API failure correctly and displays an error message', async () => {
mockedAxios.get.mockRejectedValueOnce({
response: {
status: 500,
data: { message: 'Internal Server Error' },
},
});
renderWithProviders(<App />);
expect(screen.getByText(/user list/i)).toBeInTheDocument();
expect(await screen.findByText(/An unknown error occurred/i)).toBeInTheDocument();
});
test('displays correct number of users after changing items per page', async () => {
const mockUsers: t.User[] = [
// mocked users
]
mockedAxios.get.mockResolvedValue({ data: mockUsers });
const { store } = renderWithProviders(<App />);
await waitFor(() => {
expect(screen.getByText(/Alice Thompson/i)).toBeInTheDocument();
});
const itemsPerPageSelect = screen.getByLabelText(/Items per page:/i);
fireEvent.change(itemsPerPageSelect, { target: { value: '3' } });
await waitFor(() => {
const userRows = screen.getAllByRole('row');
expect(userRows).toHaveLength(4); // 1 header row + 3 user rows
});
expect(store.getState().usersData.itemsPerPage).toBe(3);
const nextPageButton = screen.getByTestId('next-page-button');
fireEvent.click(nextPageButton);
store.dispatch(setPage(2));
await waitFor(() => {
expect(screen.getByText(/Diana Prince/i)).toBeInTheDocument();
screen.debug();
expect(screen.queryByText(/Alice Thompson/i)).not.toBeInTheDocument();
});
});
-
Clone the repository:
git clone https://github.com/yakksiek/task-users-table.git
-
Navigate into the project directory:
cd task-users-table
-
Install the dependencies:
npm install
-
Run the development server:
npm run dev
Find me on LinkedIn or Instagram