Skip to content

goooseman/sample-chat-frontend

Repository files navigation

Sample React Chat

A real-time chat application based on React. Written from scratch without any boilerplates and has a minimum set of 3rd-party dependencies.

Netlify Status Coverage Status Tests status Linters status Dependencies status Dev dependencies status

Application link | Storybook

Logo

How does it work?

This chat connects to a sample-chat-backend using a WebSocket (or long-polling as a fallback) connection as a transport. API documentation.

The server does not have authentication and the username can be changed at any moment, but we need a consistent way to check if a message is the current user's or not. For this purpose, a persistent userId is generated on the initial run of the application and saved to the localStorage of the browser. If you want to "register" yourself as a new user, just use the "Reset settings" button in the settings.

There are several settings in the application: username, theme and etc. All those settings are stored in the localStorage and will not be lost on a browser refresh.

Warning The application restricts you to see the chat until you specify the username.

Features

  • Multi-language
    • left-to-right and right-to-left support (e.g. Arabic or Hebrew)
    • pluralization (for different languages with difficult pluralization rules, like Russian and Hebrew)
    • Default locale is taken from your browser's language (if application supports it) or fallbacks to English
  • Two themes (light and dark)
  • Clock settings (12 hours or 24 hours)
  • Sending messages by CTRL+ENTER
  • Settings persistence using localStorage
  • Unread count in the NavBar
  • Title is changed, when there are unread messages
  • NavBar blinks, when there are unread messages
  • If a link is sent, it becomes clickable
  • If an image link is sent, the image is shown (try to paste the following message: Check this out: https://source.unsplash.com/600x300?girl)
  • If a youtube link is sent, the youtube video is shown (try to paste the following message: Check this out: https://www.youtube.com/watch?v=BMUiFMZr7vk)
  • Searching the messages (implemented using a finite state machine, take a look at src/utils/gstate and src/pages/Chat/ChatPage.container.tsx)

Quick start

To view the project just open the application link in your browser!

  • npm start to start the project in development mode
  • npm run start:local to start the project in development mode and use localhost:8090 as a backend URL. Use it, if you have an instance of sample-chat-backend running on your machine.
  • docker-compose up to start the project locally and start sample-chat-backend automatically. Use it to preview the project without internet access.
  • npm run storybook to start storybook in development mode
  • npm run build:prod to build the project in production mode to the /dist folder. Use npm run serve:app to serve this folder locally with HTTPS support.
  • npm run build:storybook to build the storybook in production mode to the /storybook-static folder. Use npm run serve:storybook to serve this folder locally with HTTPS support.
  • npm test to run unit and integration tests with jest.
  • npm run lint:check to check the project using all available linters. This command should be a part of your CI setup.

NodeJS version is specified at .nvmrc. NPM version is specified at the engines section of package.json. Dependencies can not be installed with npm version lower to prevent unnecessary package-lock.json git conflicts, but can be opted-out by deleting .npmrc.

Browser compatibility

The project is compatible with IE11.

To change browser compatibility just edit the .browserlistrc file in the root of the project.

Developer tools

Typescript 🤓

Why Typescript? | Typescript in 5 minutes | Advanced types

The project is built with Typescript using a strict config. If a 3rd-party library is added to the project, which does not contain Typescript typing, a type definition should be written in @types/${libName}/index.d.ts. Don't forget to open a PR in the library's GitHub or in Definetely Typed repository. Let's make the JS community type-safe together!

The project is not type-checked during development run-time, because it should be made on the CI server.

Storybook 📖

Check it out! It's really cool!

Storybook tutorial | Component Driven Development

What is Component Driven Development?

It’s a process that builds UIs from the “bottom-up” starting with components and ending with screens. CDD helps you scale the amount of complexity you’re faced with as you build out the UI. -- learnstorybook.com

Storybook helps you to develop View components in isolation.

The project has storybook, which is configured:

  • to show automatic documentation of the component. The prop descriptions are taken from Typescript props and the component subtitle is taken from jsdoc of the component (take a look at Button component for an example).
  • to show a component in different CSS themes.
  • to show a component in different languages (especially useful to test component in rtl layout for such languages as Arabic or Hebrew).
  • to show accessibility problems of the component.
  • to show a component in different viewports.

It is recommended to publish the storybook, so the developers will be able to have live documentation on the components of the project.

Integration tests 🕵️‍♂️

Guiding Principles | How to know what to test | Five factor testing | Principles of Automated Testing

The more your tests resemble the way your software is used, the more confidence they can give you. -- Kent C. Dodds

Write tests. Not too many. Mostly integration. -- Kent C. Dodds

The project includes utils/render.tsx, which is a drop-in replacement for @testing-library/react, but with support of:

Additionally, the project contains __utils__/renderWithRouter.tsx to write tests for containers with some redirection logic.

When to write a test:

  • React Smart containers should have business logic integration tests.
  • React View components usually do not require tests. But there are some exceptions:
    • Regression bug which happened because of a prop has not been passed to an underlying component, e.g. Button component, which does not forward onClick prop to the <button /> element. In this case a regression test can be written (example).
    • A component, which contains some view-related business logic, e.g. Button which prevents double click within 500ms.
  • Any non-React code containing business logic.

Do not do it:

  • Do not test implementation details. Those tests can be broken during the refactoring of the code and become false negative. It will require the developer to fix the test even if the specification of the system did not change.
  • Do not test private methods. This is a more specific case of the previous bad practice, but it is better to be written explicitly.
  • Do not use testing-only selectors (if possible) in UI tests and frontend integration tests. To avoid using them the developer should use text values of HTML elements or ARIA attributes instead of using data-testid.
  • Do not try to reach 100% code coverage and do not encourage developers to do so. Code coverage does not tell if the code is working according to the specification.

i18n 🌍

The project uses react-targem with gettext-utils to provide i18n support.

  • Locales are stored as .po files, which are easy to be edited by translators using tools like POEdit.
  • To make your development workflow more fluent, an open-source Weblate system can be installed and configured to provide translators with an easy-to-use system, which is automatically synced with the git repository.
  • .po files store information about pluralization rules for every language.
  • After translation strings are added or removed, the .po files are updated automatically on commit using lint-staged.
  • Webpack is not able to import .po files directly, so webpack-shell-plugin-next is used to build src/i18n/translation.json file automatically on each build. In the future this can be improved by creating a webpack loader.
  • dir="ltr" or dir="rtl" is added automatically to the root div automatically to support right-to-left languages. If you need to support one of those languages make sure to follow RTL CSS Guidelines.
  • To add a new language support, just open src/i18n/template.pot in POEdit and create a new locale file from it. Save it to src/i18n folder and update src/config/locales.ts file.
  • Storybook is configured to display components in different languages and different directions.

Styles 🎨

The project uses CSS instead of those fancy CSS-in-JS solutions. There are a lot of disadvantages in CSS, such as:

  • Lack of isolation. Solved by using css-modules.
  • Lack of variables. Solved by using postcss-custom-properties as a fallback for browsers, which does not support custom properties.
  • Lack of theming. Solved by setting different root-level custom CSS properties. That works only in supported browsers, but that is a matter of graceful degradation.
  • CSS-naming-convention is not camelCase, so is not so comfortable to be used in JS/TS code. Solved by using css-loader's localsConvention option.

Linters 🚨

Linters are not your enemies, they help developers to avoid mistakes and to follow common specifications. This project contains the following linters:

Linters are useless if they are not integrated with the development workflow. This project provides you with the following integrations:

  • VSCode users should install the recommended extensions. Files are fixed on every save automatically. Refer to VSCode section.
  • husky is used to check every commit message with commitlint.
  • husky and lint-staged are used to automatically run linters across staged files on every git commit.
  • Use npm run lint:check to check all the codebase at once. Configure this command to be run on your CI server.
  • Use npm run lint:write to check all the codebase and fix issues automatically (if possible). Use it after config change.

VS Code 💻

The project is configured to be used in VS Code.

It contains .vscode/extensions.json file with a list of recommended extensions. Just open the extensions tab in your editor, and choose to show recommended extensions. Install those to have incredible development experience including all the linters support and amazing CSS modules autocompletion.

It also contains helpful code snippets to scaffold new React components. Just start typing reactComponent in a new file to create a new component, reactComponentStorybook to create a new storybook and reactComponentIndex to create an index file.

The debugger is configured to run a specific test, by pressing F5 when a .spec file is open. But a debugger gives an additional overhead, so a task to just run a specific test is added to run tests faster when debugging is not needed. Unfortunately, there is no comfortable shortcut to run a task in VS Code, but you can configure it yourself by specifying a keyboard shortcut (e.g. F6) for Tasks: Rerun Last Task command.

Production deps 💼

The project contains webpack and other build-time dependencies listed as production ones. This is not a mistake and done on a purpose.

It is common for a frontend project to list only dependencies, which are used inside a production bundle (e.g. react, react-dom, @material-ui or others) as production ones. But actually a frontend project is bundled before being distributed. So no dependencies are required during run-time.

On the other hand, there are "true" development dependencies, like storybook-related ones, testing-related ones, linters and etc. We do not need them on the CD server, which builds the application before it is being distributed. We need those only on developers' machines.

So the solution is pretty straightforward - to list everything, which is required to build the project as production dependencies. To have an ability to install only those ones on the CD server and save some time.

No HMR? ♻️

The project does not contain HMR in dev mode (npm start). HMR is mostly useful for component design development, which should be done in Storybook, which supports HMR out-of-the-box. But if you miss it, feel free to open a PR!