Hello! This is an invoicing application build with React JS and styled-components. The application is used to manage invoices and allows the user to create, read, update, filter by status and delete invoices. There is an option in the app to switch between a dark and a light theme. All transitions are smoothly displayed by using Framer Motion library to create animations.
- Invoices can be created either as drafts or as pending. Clicking "Save as Draft" allow the user to leave any form field blank and set the status to "draft". Clicking "Save & Send" require all forms fields to be filled in, and set the status to "pending". The created invoice receives a unique ID. Each ID consists of the 2 random uppercased letters followed by 4 random numbers.
- When saving changes to an invoice, all fields are required when the "Save Changes" button is clicked. If the user clicks "Cancel", any unsaved changes reset.
- If the invoice being edited is a "draft" and the "Save Changes" button is clicked the status update to "pending". All fields are required at this stage.
- Clicking the "Mark as Paid" button change the invoice's status to "paid". This option is displayed only when the invoice status is "pending".
- Users receive a confirmation modal when trying to delete or change status of invoice because both of those actions are irreversible.
- Clicking the sun/moon button toggle light and dark mode.
- The
TabandShift+Tabkeys will cycle through the focus trap's tabbable elements but will not leave the focus trap when modal or form is open. - Invoices list can be filtered by status (draft/pending/paid) by using "Filter by status" button.
- Keep track of any changes with
localStorage.
- Live site URL to see live version.
- Frontend Mentor challenges allow you to improve your skills in a real-life workflow.
The first time I used useReducer hook to manage application state. I noticed that my state logic getting more complex as the few elements of my state relies on the value of another element of my state. Together with useReducer, the useContext hook turned out to be handy. It allowed me to create common data that can be accessed throughout the component hierarchy without passing the props down manually to each level which in turn allowed me to avoid Prop drilling (also called "threading") that is the process you have to go through to get data to parts of the React Component tree.
In order to create a theme switcher and provide colors for components I used styled-components <ThemeProvider> wrapper component. By leveraging the theme provider I only need to write my styles in one single object and invoke them in any component that is a descendant of that provider.
When creating the form I learned what Controlled Component is. In HTML, form elements such as <input>, <textarea>, and <select> typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state property of components, and only updated with setState(). Then the React component that renders a form also controls what happens in that form on subsequent user input.
To make application more ADA compliant (which means the website should be entirely accessible using just keyboard) I prevent focus go outside the modal once the modal is opened. In this case, the focus trap turns on when the form or modal with invoice deletion/status change is opened. I did this by using a simple function that is invoking on the keydown event. Function saves to the variable all focusable elements in modal container and check if the event.key is Tab. If so and it encounters the last focusable element, select the first element. The same logic corresponds to Shift + Tab but with a reverse action. Esc key closes the modal. In order to create an accessible modal I followed this great tutorial that follow the WAI-ARIA Practices.
It was by far the largest and most comprehensive project I have done so far. It showed me how important it is to plan so that you don't have to change things that previously worked well in the middle of the project. A valuable lesson!
- ReactJS
- Styled-components
- Framer Motion API
- react-datepicker
- Webpack
- Mobile first
- Semantic HTML5 markup
- I used ReactJS library to create an app. React is a declarative, efficient, and flexible JavaScript library for building user interfaces. It lets you compose complex UIs from small and isolated pieces of code called “components”.
- To style my app I used styled-components library. Styled Components are one of the new ways to use CSS in modern JavaScript. It is the meant to be a successor of CSS Modules, a way to write CSS that's scoped to a single component, and not leak to any other element in the page. Also allows you to write plain CSS in your components without worrying about class name collisions.
- To animate the pages transitions and modals I used Framer Motion API. Framer Motion is an open source, production-ready library that's designed for creating creative animations. In order to support users who have enabled their device’s Reduced Motion setting and make accessible animations I used
useReducedMotionhook. Based on whetheruseReducedMotionreturnstrueor not we're passing different values toanimate. That replace position transitions with opacity. - Added prefers-color-scheme CSS media feature which is used to detect if the user has requested a light or dark color theme and save it to Local Storage. I made it with window interface
matchMedia()method. It returns a newMediaQueryList objectthat can then be used to determine if the document matches the media query string. In this case prefers-color-scheme. - Implemented focus trap inside modal to make it ADA compliant. Focus trap in short prevent our focus go outside the modal once the modal is opened.
- Learned about the optional chaining operator (
?.) that enables you to read the value of a property located deep within a chain of connected objects without having to check that each reference in the chain is valid. The?.operator is like the.chaining operator, except that instead of causing an error if a reference is nullish (nullorundefined). - Handle 404 routes in React Router and provide a fallback component for displaying an imfamous
404 Page Not Founderror to the user. Try to enter a page that doesn't exist - like 'invoice-tediko.netlify.app/gotcha' - For Invoice Date picker I used React Date Picker package. This is a simple and reusable Datepicker component for React.
:focus-visiblepseudo class. This selector only indicate focus when it is helpful to the user - such as in cases where the user interacts with the page via a keyboard or some other non-pointing device. It isn't supported by Safari yet, but there is simple workaround.- To create this project I used webpack. More specifically i used
laravel mixwhich is a wrapper for webpack and targets the 80% usecase.
To run this project, clone it and install it locally using npm:
$ git clone [email protected]:tediko/invoice-app.git
$ cd invoice-app
$ npm install
Use npm to build and compile assets in a local environment:
$ npm run build
Watch assets for changes and rebuild your bundle each time you update a file with:
$ npm run mix-watch
or
$ npx mix watch
- DOCS - ReactJS
- VIDEO - ReactJS tutorial by The Net Ninja
- DOCS- Styled-components
- LINK - useReducer hook
- LINK - useContext hook
- LINK - Prop Drilling "threading"
- LINK - ThemeProvider
- DOCS - Forms/Controlled Component
- LINK - Accessible modal
- DOCS - Optional chaining operator
- LINK - React Date Picker
- LINK - Prettier
- DOCS - Framer Motion API
- DOCS - Framer Motion - useReducedMotion hook
- DOCS - prefer-color-sheme
- LINK - webpack
