@bloomreach/react-banana-ui
v1.35.0
Published
A TypeScript React component library of primitive and common UI components used across Bloomreach apps.
Maintainers
Keywords
Readme
React Banana UI
A TypeScript React component library of primitive and common UI components used across Bloomreach apps.
Table of Contents
- Viewing Available Components
- Guide from Figma Design to the React Banana UI Components
- Using library in a project
- Development
- Branching types
- Commit Message Format
- Creating a new component
- Storybook
- Autogen
- Testing
- Releasing
- Contributing Guidelines
- Code Quality Standards
Viewing Available Components
Visit the library Storybook to view the latest available components.
Guide from Figma Design to the React Banana UI Components
Use the following guide to translate a Figma design into a functional page using RBUI components.
Using library in a project
Install the library as a dependency in the project
npm i --save --save-exact @bloomreach/react-banana-uiThis package requires React 17/18 as peer dependencies:
- react (peer)
- react-dom (peer)
- @types/react (optional peer in TS projects)
Components
To use components in your application, import them as named exports:
import { Button } from '@bloomreach/react-banana-ui';
function MyComponent() {
return <Button onClick={() => console.log('Button clicked!')}>Click Me</Button>;
}Prefer named imports for effective tree shaking.
Styles & Fonts
To make the library's styles and fonts available in your project, follow these steps:
Import the main stylesheet: Add the following line to your main application's style file (e.g.,
main.scssorindex.css):/* main.scss */ @import '@bloomreach/react-banana-ui/style.css'; /* ... other styles ... */Font Family: The
htmlandbodytags' font families are already defined within the library's styles, ensuring consistent typography across your application without additional configuration.
IMPORTANT
Avoid directly using --rbui-... and/or --banana-... CSS custom properties in your application styles. These variables are intended for internal library use only. Always use the library components to ensure proper styling and adherence to the design system.
Development
This section outlines the necessary steps and commands for developing with the React Banana UI library.
Prerequisites
Ensure you have the following installed:
- Node.js: >=18
- NPM: >=9
Getting Started
Install Dependencies: Run the following command to install all project dependencies:
npm ciThis command performs a clean installation of dependencies, ensuring consistency across environments.
Run Storybook: Start the Storybook development server (alias of
npm run storybook) to view and develop components in isolation:npm run startStorybook will open at http://localhost:6006, and any changes saved in the source code will automatically reflect in the Storybook UI.
Generate New Components: To create a new component with the standard file structure, use the component generation script:
npm run generateFollow the prompts to set up your new component. More details can be found in the Creating a new component section.
Build
To build the library for production, run the following command:
npm run buildThis command compiles the TypeScript code, bundles the components, and generates the necessary output files for distribution. The built files will be located in the dist/ directory.
To build the Storybook for deployment, run the following command:
npm run storybook:buildThis command compiles the Storybook stories and generates a static Storybook build, which can be deployed to a web server. The output will be in the storybook-static/ directory.
Docker Services
We use Docker Compose to manage various services required for local development and testing. The docker-compose.yml file defines these services.
- Playwright E2E Testing: The
e2eservice indocker-compose.ymlsets up a Playwright test environment within a Docker container, ensuring consistent end-to-end test execution across different environments. You can run these tests usingnpm run e2e. - SonarQube Analysis: The
sonarservice provides a local SonarQube instance for static code analysis. This helps in identifying code quality and security vulnerabilities early in the development cycle. You can start this service and perform analysis usingnpm run sonar(after configuring SonarQube locally). Ensure theSONAR_TOKENenvironment variable is set.
Branching types
To manage and automate complex release workflow based on multiple Git branches and distribution channels, we utilize the semantic-release library. Refer to the documentation for comprehensive details. Our current setup is streamlined but designed for future extensibility.
The current branch types are explained below.
- The
masterbranch - is a permanent branch, where releases happen for the latest versions. All versions from that branch will be published to the npm registry with alatestdist-tag; - The
nextbranch should be used when we want to develop an important feature, which is a breaking change. Considering the scope of this feature we want to make it available, at first, only to a limited amount of users in order to get feedback or run tests. Once we get that feedback we can make improvements and ultimately make the new feature available to all users by merging changes into themasterbranch; Releases from thenextbranch will be published to the npm registry with anextdist-tag; - The maintenance branches (i.e.
2.xto maintain releasev2). The maintenance branch is a type of branch that allows publishing releases with a semantic version on top of the codebase of an old release. This is useful when you need to provide fixes or features to users who cannot upgrade to the last version of the package; - Feature branches (like
feature/BR-XX-my-awesome-feature,fix/BR-XX-that-is-life) or any other kind of branch to commit small units of work that do not produce releases (until merged into one of the branches above). These branches are ignored from now on as they don’t have to trigger releases;
The following changes should be done to change or extend the current setup:
- Add a new release/maintenance/pre-release branch to the
semantic-releaseconfiguration in./release.config.cjs, check the documentation for more details; - Update the GitLab pipeline
releasejob to allow running it with the new branch, check./.gitlab-ci.yml; - Verify that the new branch is marked as
protectedin the repository settings.
Find more examples of possible release workflows in the official semantic-release documentation.
Commit Message Format
To have an automated release process the git commit messages must follow the following format. This specification following Conventional Commits. It provides an easy set of rules for creating an explicit commit history. This convention dovetails with semantic version, by describing the features, fixes, and breaking changes made in commit messages.
Each commit message consists of a header, a body, and a footer.
<header>
<BLANK LINE>
<body>
<BLANK LINE>
<footer>The header is mandatory and must conform to the Commit Message Header format.
The body is optional. When the body is present it must be at least 20 characters long and must conform to the Commit Message Body format.
The footer is optional. The Commit Message Footer format describes what the footer is used for and the structure it must have.
The commit contains the following structural elements:
fix: a commit of the type fix patches a bug in your codebase (this correlates withPATCHin Semantic Versioning).feat: a commit of the type feat introduces a new feature to the codebase (this correlates withMINORin Semantic Versioning).BREAKING CHANGE: a commit that has a footer BREAKING CHANGE:, or appends a!after thetype/scope, introduces a breaking API change (correlating withMAJORin Semantic Versioning). A BREAKING CHANGE can be part of commits of any type.- types other than
fix: andfeat: are allowed, see types. - footers other than BREAKING CHANGE: may be provided and follow a convention similar to git trailer format.
Additional types are not mandated by the Conventional Commits specification, and have no implicit effect in Semantic Versioning (unless they include a BREAKING CHANGE).
Commit Message Header
<type>(<scope>): <short summary>
│ │ │
│ │ └─⫸ Summary in present tense. Not capitalized. No period at the end.
│ │
│ └─⫸ Commit Scope: Right now not used but will be expanded in the near future.
│
└─⫸ Commit Type: build|chore|ci|docs|feat|fix|perf|refactor|style|test.The <type> and <summary> fields are mandatory, the (<scope>) field is optional and not used now.
Type
Must be one of the following:
build- changes that affect the build system or external dependencies; Hidden from the release notes;chore- changes which are not fit to the rest of the types. Hidden from the release notes;ci- changes to our CI configuration files and scripts. Hidden from the release notes;docs- documentation only changes. Hidden from the release notes;feat- a new feature. Appeared in the release notes under sectionFeatures;fix- a bug fix. Appeared in the release notes under sectionBug Fixes;perf- a code change that improves performance. Appeared in the release notes under sectionPerformance Improvements;refactor- a code change that neither fixes a bug nor adds a feature. Hidden from the release notes;revert- revert of the previous changes. Appeared in the release notes under sectionReverts;style- changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc). Hidden from the release notes;test- adding missing tests or correcting existing tests. Hidden from the release notes.
If the prefix is feat, fix, perf or revert, it will appear in the release notes. However if there is any BREAKING CHANGE, the commit will always appear in the changelog.
Other prefixes are up to your discretion. Suggested prefixes are build, ci, docs ,style, refactor, style, test and chore for non-changelog related tasks.
Scope
Not used currently.
Summary
Use the summary field to provide a succinct description of the change:
- use the imperative, present tense: "change" not "changed" nor "changes"
- don't capitalize the first letter
- no dot (.) at the end
Commit Message Body
Just as in the summary, use the imperative, present tense: "fix" not "fixed" nor "fixes".
Explain the motivation for the change in the commit message body. This commit message should explain why you are making the change. You can include a comparison of the previous behavior with the new behavior in order to illustrate the impact of the change.
Commit Message Footer
The footer can contain information about breaking changes and is also the place to reference Jira tickets, and other MRs that this commit closes or is related to.
For example:
BREAKING CHANGE: <breaking change summary>
<BLANK LINE>
<breaking change description + migration instructions>
<BLANK LINE>
<BLANK LINE>
<Closes|Fixes> BR-<issue number>Breaking Change section should start with the phrase BREAKING CHANGE: followed by a summary of the breaking change, a blank line, and a detailed description of the breaking change that also includes migration instructions.
Similarly, a Deprecation section should start with DEPRECATED: followed by a short description of what is deprecated, a blank line, and a detailed description of the deprecation that also mentions the recommended update path.
Revert commits
If the commit reverts a previous commit, it should begin with revert: , followed by the header of the reverted commit.
The content of the commit message body should contain:
- information about the SHA of the commit being reverted in the following format:
This reverts commit <SHA>, - a clear description of the reason for reverting the commit message.
Creating a new component
To create a new component, run the following command
npm run generateand then follow the instructions to create the all required sets of files for the new component. It will create the following files:
component-name.tsx // Main component file
component-name.scss // Component styles
component-name.spec.tsx // Unit tests
component-name.stories.tsx // Storybook stories
component-name.qa.stories.tsx // E2E tests stories
component-name.test.tsx // E2E testsNote: The script doesn't create the index.ts file inside ./src/components/<group> folder. It should be created manually or updated already existing index.ts file.
Storybook
The Storybook is used to develop and view components in isolation. The storybook files should end with *.stories.tsx and be placed close to the component or module which is going to be documented for all new or rewritten components. The ./stories folder should locate only generic information about the library itself and additionally, complex stories involving multiple components.
The old components stories are located in the ./stories folder. They are going to be removed soon. In case the new implementation is ready but the old one is still available to prevent breaking changes, please, mark the old stories as DEPRECATED to group them, see for example: ./stories/components/buttons.
Autogen
Icons
The icon files and exports are auto-generated using the script at scripts/gen-icons. If new icons are added in the banana-theme project, run npm run generate:icons to regenerate the icon exports.
Note: running the generator deletes and recreates src/components/foundations/icon/icons/.
Testing
Comprehensive testing is crucial for maintaining the quality and stability of the React Banana UI library. We employ a two-level testing strategy: unit tests for isolated logic and end-to-end (E2E) tests for overall component behavior in a real browser environment, including visual regression. Each new or modified component is expected to have thorough test coverage.
Unit tests
We use the Vitest framework for unit testing. Unit test files should end with *.spec.ts(x) and be co-located with the component or module being tested. When writing unit tests:
- Focus on isolated logic: Test individual functions, components, or modules in isolation, mocking dependencies as needed.
- Cover edge cases: Ensure your tests cover boundary conditions, invalid inputs, and error states.
- Assert component behavior: Verify that components render correctly, respond to user interactions, and manage their state as expected.
Common unit test commands:
npm test
npm run test:watch
npm run test:ui
npm run test:coverageEnd-to-end tests
We utilize the Playwright framework for end-to-end testing. These tests are co-located with their respective components (files ending with *.test.ts(x)) and run against dedicated Storybook stories (*.qa.stories.ts(x)). The primary goal of E2E tests is to eliminate manual QA efforts and ensure high-quality components for end-users. Key aspects of our E2E testing strategy include:
- Comprehensive Scenarios: Each component should be covered with E2E tests that simulate various user interactions, component states, and different scenarios.
- Visual Regression Testing: This is a critical part of our E2E suite, ensuring that visual changes are intentional and do not introduce unexpected regressions. When visual failures occur and are deemed correct, update the snapshots using
npm run e2e:update. - Balancing Coverage and Performance: While extensive coverage is vital, E2E tests can be slow. Strive for a balance between comprehensive test scenarios and efficient test execution. Prioritize the quality of components, and always consider performance optimizations for CI.
Run e2e tests
- Use the command
npm run e2eto start end-to-end tests inside the Docker container (thedockeranddocker composeshould be installed on your machine). - When tests are done check the Playwright output in the console if some of the tests failed use the following command to open the Playwright report:
npm run e2e:report - For a local run without Docker, use
npm run e2e:internal(builds Storybook then runs Playwright).
Update failed visual regression tests
- If the failure appeared in the visual regression tests and those changes are correct, please update the existing reference images with the following command:
npm run e2e:update. The Playwright will generate new correct reference images which should be committed to the repository. - Important: Playwright snapshots should always be generated within the Docker environment (
npm run e2e:updatewill use Docker by default). This ensures consistency with the Linux environment used in the GitLab pipeline, preventing discrepancies caused by operating system-specific rendering differences.
The e2e tests configuration for CI and local development
E2E tests are executed in a Docker container, both locally and in CI, leveraging the official Playwright Docker image. This approach ensures a consistent and predictable testing environment across all development and integration stages. The CI setup mirrors the local docker-compose.yml configuration.
Configuration Synchronization
It is crucial to keep the CI setup for the e2e tests job (defined in .gitlab-ci.yml) synchronized with the local Docker Compose configuration (docker-compose.yml). Any changes made to one should be carefully applied to the other to maintain environment consistency and avoid unexpected test failures.
Contributing Guidelines
We welcome contributions to the React Banana UI library! To ensure a smooth and collaborative development process, please adhere to the following guidelines:
- Commit Message Format: All commit messages must follow the Conventional Commits specification. This helps automate releases and generate accurate changelogs. Please review the Commit Message Format section for detailed instructions.
- Testing: Before submitting any changes, ensure your code is thoroughly tested. This includes both Unit Tests for isolated logic and End-to-end Tests for comprehensive component behavior and visual regression. Refer to the Testing section for detailed guidance on writing and running tests.
- Code Quality: Maintain high code quality by adhering to the project's linting, formatting, and security best practices. (Refer to the
always_applied_workspace_rulesin the documentation for more details.) - Development Workflow: Follow the established Development workflow for installing dependencies, running Storybook, and generating new components.
- Branching: Understand and follow our Branching types for feature development, bug fixes, and releases.
Releasing
The release of the new version is going to be done automatically when the changes reach one of the release branches and commit messages contain one of the structural elements.
To preview the possible release version and release notes based on already existing commits run the following command
npm run release:dry-runSee MIGRATION.md for migration guidance and RELEASE_NOTES.md for detailed changes between versions.
GitLab Pipeline
The GitLab pipeline automates the build, test, and deployment processes for the React Banana UI library. It consists of several stages, each designed to ensure code quality, functionality, and proper release management.
- Build Stage: This stage is responsible for compiling the TypeScript code, bundling the components, and generating the necessary output files for distribution. It also builds the Storybook for deployment.
- Test Stage: In this stage, unit tests and end-to-end (E2E) tests are executed to ensure the functionality and visual integrity of the components.
- Lint Stage: This stage performs linting and formatting checks on the codebase to enforce code quality standards.
- Type Check Stage: TypeScript type checking is performed in this stage to catch any type-related issues.
- Deploy Stage: This final stage is responsible for publishing the new version of the library to npm and updating the release notes, provided all previous stages pass successfully.
The GitLab pipeline runs all the checks and if the library is in good order, the following automated steps occur:
- The release notes will be updated according to commit messages history;
- The library version will be bumped;
- A new commit will be created with the updated version and release notes;
- A new Git tag will be created in the repository;
- The new version of the library will be published to npm;
After these automated steps, a few manual actions are required:
- Announce the new release in the slack channel #react-banana-ui-component-library :tada:;
- Mark the new version as
releasedin the releases section in Jira once the ticket is merged and closed; - Take a break and then pick up the next challenge :muscle:!
Code Quality Standards
This section outlines the code quality standards enforced in the React Banana UI library, covering linting, formatting, security, and performance. Adhering to these standards ensures maintainable, secure, and performant code.
Linting & Formatting
- ESLint Configuration: We use a comprehensive ESLint configuration to enforce code quality and consistency. Refer to the
eslint.config.jsfile for detailed rules. - Import Organization: Maintain a structured import order for readability and consistency. Generally, external libraries come first, followed by internal utilities, components, types, and finally, styles.
- Prettier Settings: Our codebase adheres to a strict Prettier formatting configuration. Ensure your code is formatted using the provided scripts (
npm run format/npm run format:check). - Quality Scripts: Utilize
npm run lint(npm run lint:fixfor auto-fixing),npm run format(npm run format:checkfor checking), andnpm run check:typesfor type validation.
Git Hooks (Husky)
We utilize Husky to manage Git hooks, ensuring that certain scripts are run automatically before commits or pushes. This helps maintain code quality and consistency across the repository.
- Pre-commit hook: Runs
npm run lintandnpm run formatto check for linting errors and enforce code formatting before a commit is created. This ensures that only properly formatted and linted code is committed to the repository. - Pre-push hook: Runs
npm run check:typesto perform TypeScript type checking before code is pushed to the remote repository. This helps catch type-related issues early in the development cycle.
Security Best Practices
- Input Validation: Use specific types (avoid
any), union types for variants (e.g.,primary | secondary), and avoiddangerouslySetInnerHTML. SanitizeclassNameinputs to allow only trusted class combinations. - Event Handler Security: Prevent default actions when components are loading or disabled. Safely call optional callbacks using optional chaining (e.g.,
onClick?.(event)). - XSS Prevention: Avoid
dangerouslySetInnerHTML, leverage React's built-in escaping, validate SVG props, and use trusted sources for assets. - Dependency Security: Regularly run
npm run security-audit, adhere to specified Node.js and npm version requirements, control React peer dependencies, and commitpackage-lock.json.
Performance Guidelines
- Component Optimization: Memoize expensive components (
memo) and calculations (useMemo), and useCallback for event handlers to prevent unnecessary re-renders. - Bundle Optimization: Prefer named imports (e.g.,
import { Button } from ...) for effective tree shaking and modular utility imports. - Efficient Patterns: Memoize class computations with
useMemo, avoid inline object creation for styles, and use controlled value hooks. - Memory Management: Implement cleanup functions in
useEffectfor subscriptions and event listeners to prevent memory leaks.
CSS Performance
- Efficient Selectors: Favor single-class selectors (e.g.,
.rbui-button--primary) over complex, nested selectors. Use design tokens (e.g.,var(--banana-text-primary)) and avoid deep nesting (max 3 levels).
Performance Budgets
- Bundle size: Aim for minimal bundle sizes, typically <50KB per component and <100KB for core components.
- Build Optimization: Externalize peer dependencies to reduce bundle size.
- Asset Optimization: Optimize SVG assets for efficient loading.
Common Pitfalls to Avoid
Avoid inline arrow functions for onClick, inline style objects, and unmemoized expensive calculations.
