Helpful(?) tips
- When debugging, consider eliminating (or confirming) the per-asset cache (#4835) as the source of the problem as early as possible. Do so by forcing
refresh = trueinloadAsset. For a list of issues related to this caching, refer to https://github.com/kobotoolbox/kpi/issues?q=%234835. We intend to replace this caching mechanism, but probably not until 2025. [internal discussion]
Code style
Our goals:
- TypeScript (and everything typed)
- React functional components and hooks
- CSS Modules
More details
General:
- Use Prettier on all new and modified code.
- Treat it as imperfect cleanup and tidy up the code (so it’s easier to read) after it runs.
- Be aware that older code may not conform.
- Use an editor that respects
.editorconfig.
Dependencies:
- Use React functional components and hooks.
- For state, use React’s
useState,useReducer,useContext. When working with Reflux, we are ok with converting it to MobX if it may be a lot easier than converting to React’s state management. - Avoid dependencies when practical.
- Prefer
fetch*(seeapi.tsfile) overJQuery.ajax. - Prefer ES6 built-ins over
lodash.
- Prefer
- Use
classnamesasimport cx from 'classnames';to set class names conditionally.
File architecture:
- Organize code by feature, route or some other logical division. Use directory structures like
/settings/emailsinstead of/components. - Some directories we use:
js/hooks- for custom hooks that are shared between multiple components in different routes/featuresjs/components/common- for simple general components like button or checkbox
- Prefer one React component per file.
- Prefer smaller files. Avoid splitting into multiple files if it would be harder to work with/understand the divided code.
- Include type of file in filename. Such as:
foo.interface.tsfoo.reducer.tsfoo.actions.tsfoo.module.scssfoo.component.tsxuseFoo.hook.tsfoo.types.tsfoo.utils.tsfoo.store.tsfoo.tests.ts
- Move TypeScript types and interfaces to separate file if it would be beneficial (e.g. long complex interface requires a lot of scrolling to get to the actual component code).
- Include Storybook stories or tests as sibling file (e.g.
button.stories.tsxnext tobutton.component.tsx). - Avoid deep relative paths in imports. Do not import
../../../foo/bar/far.ts.
JS:
- Use TypeScript. If modifying a non-TypeScript file, update it to TypeScript.
- Use
isSomething/hasSomethingnaming convetion for booleans. Some common ones we often use:isLoading- means waiting for some async fetch of data (may switch value back and forth).isFirstLoadComplete- means that all the data necessary for displaying a component was gathered, and the UI was displayed to the user (we also haveisInitialisedin our codebase, but it’s not as precise, and thus deprecated)
- Comments are good, we like comments. It’s good to write a short description of a component or utlity function, or to explain some complex code step-by-step.
- Bonus: if you’re trying to understand some code for the first time, it’s probably a perfect moment to add some comments for the next adventurer :).
- We use JSDoc comments to describe classes, functions, variables, and properties. We use regular comments for everything else. Many editors understand and use JSDoc comments internally and thus are helpful.
- Use
t()for every Front-end facing static string./kpi/jsapp/js/i18nMissingStrings.es6file holds all the strings we want to translate but don’t appear in our Front-end code.
- At minimum write tests for utility functions.
CSS:
- Use CSS modules. Do not use BEM style class names, unless appropriate for complex or global CSS. Do not use the deprecated
makeBemutility. - We use autoprefixer, so no need to add prefixes manually.
- Avoid adding new colors to stylesheets. We have an existing list of all available colors. If the design contains a color that is very similar to existing one - use that color. If it’s completely new color, please discuss adding it to the list with Design Team or Front-End Lead.
Common components
These are Buttons, Checkboxes, and many other common/atomic UI elements. The aim is to write DRY code and build beautiful, consistent UI. We gather them all in js/components/common directory, but you can see them all in action at our storybook.
Please:
- Make sure you know what we have.
- Avoid creating custom one-shots that are very similar to existing common components.
- Adding new option to common component (size, color, etc.) shouldn’t be done lightly.
- Make sure to update the story file for the component if you ever add that option.
- If in doubt, please refer to Design Team or Front-End Lead.
Workflow
We follow Cleanup First™ methodology. It boils down to making a cleanup-only PR before starting any work. A step-by-step guide would be:
- Think about which files your changes will touch.
- Make a PR that includes only cleanup1 changes in those files.
- Make a new branch for the actual changes from the cleanup branch.
- If you end up needing to cleanup more files as you work, make a new cleanup PR.
- If you end up cleaning more files than needed, it’s ok.
That way we get less merge conflicts, and less complex PRs to review.
1 by “cleanup” we mean stylistic changes, applying linter and prettier, moving things around, renaming, etc.
Adding new icons
Some notes first:
- we generate an icons font from
svgfiles using webfonts-generator package - the source of the icons is the Sketch file from our Google Drive:
/Kobo Product Design/Design Current/04. Design/Comps Sketch/Icons.sketch - all the icons need to have the same artboard size (
webfonts-generatorrequirement) - after kpi#4109 is done, this will be much simpler 😇
Steps to take when adding new icon:
- Open
Icons.sketch - Create new artboard
Icons/<icon-name>with the same size as the other. Make sure it is exportable (easiest way is to clone an existing one) - Draw the icon or drag&drop existing SVG file, make sure the paths are combined and the color is black (
#000000) - Export all icons
- Copy new icon from
Icons/<icon-name>.svgtojsapp/svg-iconsin KPI repository - Run the new
svgfile through ImageOptim.app or some alternative non-destructive SVG compressor - Run
npm run generate-icons - Use your new icon, e.g.
<Icon name='alert'/>(or through some other component that uses icons internally)
Getting translations
Getting latest translations strings on local environment requires few steps:
- In your
kpidirectory, navigate into/locale - Do a
git pull - In your
kobo-installdirectory, enter container:./run.py -cf exec kpi bash - Run
./manage.py compilemessages