0.0.1-alpha.1 • Published 2 years ago

mainboard-core-test2 v0.0.1-alpha.1

Weekly downloads
-
License
MIT
Repository
-
Last release
2 years ago

MainboardUI Documentation v1.1

This Markdown file contains all the essential details about our project. It contains information about the bugs we encountered and the standards we are going to maintain.

As a developer working on this project, you must adhere to all the standards set. This will ensure consistent code for everyone to work with.


Standard Practices:

The Project Structure

The project structure for our library separates out component code from the code that tests the components and the visual documentation using Storybook.

Global Level Code

Code pieces that we need throughout the project. These mostly include helper functions, config files and types

Top Level Code

Includes the code directly under the src/ directly. We have placed our global level components here. Global level components are intended to be directly used independent of any other component.

Module Level Code

A module is a collection of files that combine to create one complex component. For instance, to create a Header component, you need to have specific wrappers, and drawer. These components do not make sense on their own so we include it as a module-level component and not global.

These ideas extend not to components; but utilities as well. As seen below, for module utilities we use _utils instead of @/utils at the global level.

Depiction for our project structure:

library
│   tsconfig.json
│   package.json
|   .storybook
|   jest.config.ts
│   // other config files...
|
└─── src
│   │   global.d.ts // declarations
│   │   types.ts
|   |   index.ts
|   |   other global files needed throughout project...
|   |
|   |
|   |
|   |─── @/utils (global level @/utils)
|   |       <filename>.ts(x?) per helper
|   |
|   |
│   └─── Component
│   |   │   Global-Level-Component.tsx
|   |   |   index.ts
|   |   |   types.ts
|   |
|   |   // other similar Components...
|   |
│   └─── Module
│       │   Module-Level-Component-1.tsx
│       │   Module-Level-Component-2.tsx
│       │   Module-Level-Component-3.tsx
|       |   types.ts
|       |   _utils (module level @/utils)
|               <filename>.ts(x?) per helper
|
|       // other similar Modules...
|
└─── tests
|    |  test-setup.ts
|    |  helper.tsx
|    |
│    └─── Component
|    |      Component.test.tsx
|    |
│    └─── Module
|    |      Component-1.test.tsx
|    |      Component-2.test.tsx
|    |
|
|    // tests for other components or modules
|
└─── stories
|    |  types.ts
|    |  _utils
|    |      <filename>.ts(x?) per helper
|    |
|    | // follow similar structure as in tests/ and src/
|
└─── @/utils
         <filename>.ts(x?) per helper

Remember that for each module in src/ there is a index.ts that is meant to export all the components and needed helper's (if any) from that module. Then from the root of src/ we have index.ts that is meant to export everything from the core/root.


TypeScript Developer Experience

We rely heavily on TypeScript. Everything should be typed properly with no any's. This ensures consistency and Storybook documentations are better as well.

React Component Declaration

You must use React.FC<Props> to create a component. This works most consistently with our custom withStyles HOC explained in the styling section.

Creating JSDOCs for each component and each of its props

This is essential. Storybook uses JSDocs to document the the Docs page on Storybook.

Storybook

Export and use unstyled component in Storybook

In <component>.stories.tsx, in the default exported object, the component field should be the unstyled component (without the withStyles HOC wrapped). This ensures props are documented in Storybook.

The component structure

Stay consistent with the code project structure in the component structure in Storybook.

Testing

Before making unit test rebase on REACTMOD-125

It's important for waitFor and test coverage working.

The data-attribute for testing

By default we use:

  • data-testid for unit-testing with React Testing Library.
  • data-cy for testing with Cypress.

Async testing

For async testing it is important to wrap function with async and wrap waitFor with await. In case if waitFor without async/await it is ignore result of expect test.

// ✅ correct way!
test('Example',async()=>{
  await waitFor(()=>expect()
})

Mobile screen testing

For screen resize use resizeTo function.

const resizeTo = (width, height)=>{
  Object.assign(window, {
    innerWidth: width,
    innerHeight: height,
    outerWidth: width,
    outerHeight: height,
  }).dispatchEvent(new window.Event('resize'));
};

For components which use useMediaQuery. window.matchMedia needs to be reassigned , follow instruction from https://mui.com/components/use-media-query/#basic-media-query.

Styling

How our styling is going to work:

We use Material-UI's createStyles to define the styles for the components. We have created a special withStyles HOC that internally uses Material-UI's withStyles. The purpose of our withStyles is to give a displayName to the component which is then used in Storybook docs.

Theme Overrides:

Material-UI provides theme overrides for its components. We are going to use that for our components as well. Theme overrides would allow us to inject styles for components' classes straight from an API call.

We have typed the custom theme overrides object for our custom classes but we are looking to improve and automate it.

Material-UI's Theme overrides bug:

Material-UI has a bug where if:

  • The classes defined in createStyles depend on props for the component, instead of wrapping the whole object in a callback that accepts props, manually set each property in that class based on props:

      const styles = createStyles({
        // ❌ wrong, theme override would not work!
        Appbar: ({ backgroundColor }: BaseHeaderProps) => ({
          backgroundColor
        }),
    
        // ✅ this is the way to go!
        Appbar: {
          backgroundColor: props => props.backgroundColor;
        },
    });

Then the theme overrides would not take place.

There is a complication encountered with this approach which we are still trying to fix. When trying to modify classes at certain breakpoints, we cannot use the breakpoint prop passed to the component.

As an example below, the HeaderLogoWrapper class needs to change depending on the breakpoint set. It is not possible to adhere to our fix/workaround as shown above. Instead you would have to wrap the whole class object in a callback to access the breakpoint

HeaderLogoWrapper: (props: { breakpoint: Breakpoint; drawerPosition: 'left' | 'right' }) => ({
      [theme.breakpoints.down(props.breakpoint)]: {
        order: props => (props.drawerPosition === 'left' ? 1 : 0),
      },
    }),

We will use this approach unless we find a better way.


Workflow

Out workflow uses Bitbucket to version control our code, and Jira to organize and assign tasks to developers. Jira and Bitbucket allow a tight and seamless integration.

We have a master branch which is the production code.

We have a dev branch which is a staging branch.

For features, bugfixes and other reasons, we create a specific branch from the dev branch. The naming convention is typically <issue-type>/jira-issue-title.

Steps to take

You will be assigned a task on Jira under the To do swimlane. You are expected to create a Developement branch from the dev branch on the core repository. Now, move the issue to the Development swimlane.

You will implement the feature, or fix the bug on your branch. Once ready to commit, push the code to your branch and open a PR. Now, move the issue to the Testing swimlane.

Your PR will run the build pipeline on Bitbucket which has a preview link to Storybook. Make sure everything looks good.

Make sure to assign reviewers to your PR. Ala will merge the PR if all is well.

Once the PR is created, go back to the Jira Issue and mention Ala or Osama in the comments.

Possible problems

Once you create a branch you will notice that yarn storybook generates an error. This is because a jest add-on expoects the .jest-test-results.json to be present which is in project's .gitignore.

If you encouter this problem, please run yarn test:generate-output before running Storybook.


Updating branches with Git

Since we have a base dev branch and all the feature branches are created from this, we often face syncing issues amongst the branches. For example, a developer will push their feature branch onto dev while other developers are still working on their branches.

To re-sync your feature branch with the updated dev branch, we make use of git rebase. Rebase strategy allows us to maintain clean commit history.

The Git Workflow

For every feature, create a branch from the latest dev branch.

Develop your feature, and once you develop it, make sure it is leveled with the dev branch. For this, checkout to the local dev branch on your machine, and run git pull. Now checkout to your feature branch and run git rebase dev. Resolve any conflicts while re-anchoring your feature branch with the head of the dev branch.

Once resolved, create a PR. Now, it is Ala's job or anyone who is responsible to merge it to dev to rebase dev branch to this feature branch. Simply checkout to dev and run git rebase <feature-branch> to put the feature branch's commits on top of the local dev branch. Then push the changes.

Checkout this video to understand the exact workflow we are using.

Note: Once you rebase your feature branch, you usually have to use git push -f to forcefully push your changes.


This documentation is constantly being maintained and improved.

Changelog

  • v1.1

    • Include Git Workflow.
    • Update the project's directory structure.