Skip to content

chore: OPTIC-1956: Refactor all component blocks and pages from core to app-common lib #7367

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 7 commits into from
Apr 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions .cursorrules/react.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
description: React coding standards and best practices for the LabelStudio clientside application
globs: web/**/*.tsx, web/**/*.jsx
alwaysApply: false
---

# React Best Practices

## Project Structure
- All frontend code lives in the `web` directory
- Main application code is in `web/apps/labelstudio`
- Shared libraries are in `web/libs`
- Follow the established directory structure:
- `components/`: Reusable UI components
- `pages/`: Top-level page components
- `utils/`: Utility functions
- `hooks/`: Custom React hooks
- `atoms/`: Jotai atom state definitions
- `providers/`: Context providers
- `services/`: API and other services
- `types/`: TypeScript type definitions
- `assets/`: Static assets

## Component Structure
- Use functional components over class components
- Keep components small and focused
- Extract reusable logic into custom hooks
- Use composition over inheritance
- Implement proper prop types with TypeScript
- Split large components into smaller, focused ones
- Follow a consistent file organization pattern:
```
component-name/
component-name.tsx
component-name.module.scss
component-name.test.tsx
index.ts
```

## Hooks
- Follow the Rules of Hooks
- Use custom hooks for reusable logic
- Keep hooks focused and simple
- Avoid useEffect unless absolutely required
- Use appropriate dependency arrays in useEffect
- Implement cleanup in useEffect when needed
- Avoid nested hooks

## State Management
- Use useState for local component state
- Use Jotai atoms and not the Context API for shared state
- Implement atomWithReducer for complex state logic
- Implement atomWithQuery for any API requests for data
- Keep state as close to where it's used as possible
- Avoid prop drilling through proper state management
- Only use Jotai as the single source of truth of global state management

## Performance
- Implement proper memoization (useMemo, useCallback)
- Use React.memo for expensive components
- Avoid unnecessary re-renders
- Implement proper lazy loading
- Use proper key props in lists
- Profile and optimize render performance

## Tooling
- Use Biome for code linting and formatting
- Follow CSS/SCSS linting rules defined in .stylelintrc.json
- Use TypeScript for type safety
- Keep bundle size in check by monitoring imports

## Forms
- Use controlled components for form inputs
- Implement proper form validation
- Handle form submission states properly
- Show appropriate loading and error states
- Use form libraries for complex forms
- Implement proper accessibility for forms

## Error Handling
- Implement Error Boundaries
- Handle async errors properly
- Show user-friendly error messages
- Implement proper fallback UI
- Log errors appropriately
- Handle edge cases gracefully

## Testing
- Write unit tests for components
- Implement integration tests for complex flows
- Use React Testing Library
- Test user interactions
- Test error scenarios
- Implement proper mock data

## Accessibility
- Ensure components meet WCAG 2.1 AA standards
- Use semantic HTML elements
- Implement proper ARIA attributes
- Ensure keyboard navigation
- Test with screen readers
- Handle focus management
- Provide proper alt text for images

## Code Organization
- Use proper file naming conventions which is kebab-case ie. ListItem -> `list-item.tsx`
- Prefer one component per folder, but group related components together when necessary, and ensure there is only one component per file.
- Component folders should have a SCSS `.module.scss` with the name of the component kebab-case ie. ListItem -> `list-item.module.scss`
- Implement proper directory structure
- UI components live within `web/libs/ui`
- Application components that are shared across applications such as certain page-level blocks live within `web/libs/app-common`
- Code in `web/apps` can only import code from `web/libs` and `web/libs` cannot import from `web/apps`
- Code in `web/libs/app-common` can only import code from other `web/libs` or `web/apps`. No other `web/libs` can import from `web/libs/app-common`
- Keep atoms in a global atoms folder with the name of the file matching the entity or intent of state
- Add all components and their states to Storybook by co-locating the story file next to the component file ie. `list-item.stories.tsx`
- Use the `@humansignal/ui` package for UI components
- Use the `@humansignal/icons` package for icons
- Use the `@humansignal/core` package for core utilities/functions
- Use the `@humansignal/app-common` package for application components

## Best Practices
- No cyclic imports
- Use proper imports/exports
- Follow established import ordering
- Compose components rather than extending them
- Keep components focused on a single responsibility
- Document complex logic with clear comments
- Follow the project's folder structure and naming conventions
- Prefer controlled components over uncontrolled ones
81 changes: 81 additions & 0 deletions .cursorrules/tailwind.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
description: Tailwind CSS and UI component best practices for the LabelStudio clientside application
globs: **/*.css, web/**/*.tsx, web/**/*.jsx, web/**/*.scss, web/libs/ui/tailwind.config.js, web/libs/ui/tailwind.config.ts
---

# Tailwind CSS Best Practices

## Component Styling
- Use utility classes over custom CSS
- Group related utilities with @apply when needed
- Use custom component styles as a SCSS module only when necessary to improve the readability and maintainability of the components
- Use proper responsive design utilities
- Use proper state variants
- Keep component styles consistent

## Tokens
- All tokens from Figma are programatically generated and defined in the `web/libs/ui/src/tokens/tokens.scss` file
- Tokens can be regenerated by exporting from Figma using the plugin `Figma Variable Exporter` and replacing the contents of `web/design-tokens.json` with the newly exported json file then using the `cd web/ && yarn design-tokens` command
- When regenerating tokens, ensure that the project root level `make fmt-all` command is run to ensure all the files are linted and formatted properly
- Do not use any of the tokens not defined through the Figma Variable Exporter plugin and established in the `web/libs/ui/src/tokens/tokens.scss` file
- Do not use any of the default tailwind css classes, only use the ones defined through the `web/libs/ui/src/tokens/tokens.js` file

## Layout
- Use semantic spacing utilities ie. `p-tight` instead of `p-200` or `--spacing-tight` instead of `--spacing-200`
- Use Flexbox and Grid utilities effectively
- Use container queries when needed
- Implement proper responsive breakpoints
- Use proper padding and margin utilities
- Implement proper alignment utilities

## Typography
- Use semantic typography utilities ie. `text-body-medium` instead of `text-16` or `--font-size-body-medium` instead of `--font-size-16`
- Use proper font size utilities
- Implement proper line height
- Use proper font weight utilities
- Configure custom fonts properly
- Use proper text alignment
- Implement proper text decoration

## Colors
- Use semantic color naming only which ensures dark mode compatibility ie. `bg-primary-background` instead of `bg-grape-000` or `--color-primary-background` instead of `--color-grape-000`
- Implement proper color contrast
- Use opacity utilities effectively
- Configure custom colors properly
- Use proper gradient utilities
- Implement proper hover states

## Components
- Use shadcn/ui components when available from `web/libs/ui/src/shad`
- Only use shadcn/ui components re-exported from `web/libs/ui` not the raw ones defined in `web/libs/ui/src/shad`
- The import of these components in other libs and apps should be `@humansignal/ui`
- Compose components properly
- Keep component variants consistent
- Implement proper animations
- Use proper transition utilities
- Keep accessibility in mind
- UI components should be defined and exported from `web/libs/ui` and follow the best practices of Code Organization

## Responsive Design
- Use mobile-first approach
- Implement proper breakpoints
- Use container queries effectively
- Handle different screen sizes properly
- Implement proper responsive typography
- Use proper responsive spacing

## Performance
- Use proper purge configuration
- Minimize custom CSS
- Use proper caching strategies
- Implement proper code splitting
- Optimize for production
- Monitor bundle size

## Best Practices
- Follow naming conventions
- Keep styles organized
- Use proper documentation
- Implement proper testing
- Follow accessibility guidelines
- Use proper version control
57 changes: 57 additions & 0 deletions .cursorrules/typescript.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
description: TypeScript coding standards and best practices for the LabelStudio clientside application
globs: web/**/*.ts, web/**/*.tsx, web/**/*.d.ts
---

# TypeScript Best Practices

## Type System
- Prefer interfaces over types for object definitions
- Use type for unions, intersections, and mapped types
- Avoid using `any`, prefer `unknown` for unknown types
- Use strict TypeScript configuration
- Leverage TypeScript's built-in utility types
- Use generics for reusable type patterns

## Naming Conventions
- Use PascalCase for type names and interfaces
- Use camelCase for variables and functions
- Use UPPER_CASE for constants
- Use descriptive names with auxiliary verbs (e.g., isLoading, hasError)
- Prefix interfaces for React props with 'Props' (e.g., ButtonProps)

## Code Organization
- Keep type definitions close to where they're used
- Export types and interfaces from dedicated type files when shared
- Use barrel exports (index.ts) for organizing exports
- Place shared types in a `types` directory
- Co-locate component props with their components

## Functions
- Use explicit return types for public functions
- Use arrow functions for callbacks and methods
- Implement proper error handling with custom error types
- Use function overloads for complex type scenarios
- Prefer async/await over Promises

## Best Practices
- Enable strict mode in tsconfig.json
- Use readonly for immutable properties
- Leverage discriminated unions for type safety
- Use type guards for runtime type checking
- Implement proper null checking
- Avoid type assertions unless necessary

## Error Handling
- Create custom error types for domain-specific errors
- Use Result types for operations that can fail
- Implement proper error boundaries
- Use try-catch blocks with typed catch clauses
- Handle Promise rejections properly

## Patterns
- Use the Builder pattern for complex object creation
- Implement the Repository pattern for data access
- Use the Factory pattern for object creation
- Leverage dependency injection
- Use the Module pattern for encapsulation
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ff } from "@humansignal/core";
import { SampleDatasetSelect } from "@humansignal/core/blocks/SampleDatasetSelect/SampleDatasetSelect";
import { SampleDatasetSelect } from "@humansignal/app-common/blocks/SampleDatasetSelect/SampleDatasetSelect";
import { IconError, IconFileUpload, IconInfo, IconTrash, IconUpload } from "@humansignal/icons";
import { Badge } from "@humansignal/shad/components/ui/badge";
import { cn as scn } from "@humansignal/shad/utils";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import "./PeopleInvitation.scss";
import { PeopleList } from "./PeopleList";
import "./PeoplePage.scss";
import { SelectedUser } from "./SelectedUser";
import { TokenSettingsModal } from "@humansignal/core/blocks/TokenSettingsModal";
import { TokenSettingsModal } from "@humansignal/app-common/blocks/TokenSettingsModal";
import { IconPlus } from "@humansignal/icons";
import { useToast } from "@humansignal/ui";
import { InviteLink } from "./InviteLink";
Expand Down
2 changes: 1 addition & 1 deletion web/apps/labelstudio/src/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { HomePage } from "./Home/HomePage";
import { OrganizationPage } from "./Organization";
import { ModelsPage } from "./Organization/Models/ModelsPage";
import { FF_HOMEPAGE, isFF } from "../utils/feature-flags";
import { pages } from "@humansignal/core";
import { pages } from "@humansignal/app-common";
import { ff } from "@humansignal/core";

export const Pages = [
Expand Down
12 changes: 12 additions & 0 deletions web/libs/app-common/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"presets": [
[
"@nx/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}
7 changes: 7 additions & 0 deletions web/libs/app-common/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# app-common

This library was generated with [Nx](https://nx.dev).

## Running unit tests

Run `nx test app-common` to execute the unit tests via [Jest](https://jestjs.io).
11 changes: 11 additions & 0 deletions web/libs/app-common/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable */
export default {
displayName: "app-common",
preset: "../../jest.preset.js",
transform: {
"^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "@nx/react/plugins/jest",
"^.+\\.[tj]sx?$": ["babel-jest", { presets: ["@nx/react/babel"] }],
},
moduleFileExtensions: ["ts", "tsx", "js", "jsx"],
coverageDirectory: "../../coverage/libs/app-common",
};
9 changes: 9 additions & 0 deletions web/libs/app-common/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@humansignal/app-common",
"version": "0.0.0",
"license": "MIT",
"private": true,
"dependencies": {},
"main": "src/index.ts",
"files": ["src/lib", "src/pages"]
}
30 changes: 30 additions & 0 deletions web/libs/app-common/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "app-common",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/app-common/src",
"projectType": "library",
"tags": [],
"targets": {
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["libs/app-common/**/*.{ts,tsx,js,jsx}"]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
"options": {
"jestConfig": "libs/app-common/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { useAtomValue } from "jotai";
import { settingsAtom, TOKEN_SETTINGS_KEY } from "@humansignal/core/pages/AccountSettings/atoms";
import { settingsAtom, TOKEN_SETTINGS_KEY } from "@humansignal/app-common/pages/AccountSettings/atoms";
import { queryClientAtom } from "jotai-tanstack-query";

import { Form, Input, Toggle } from "apps/labelstudio/src/components/Form";
import { Button } from "apps/labelstudio/src/components/Button/Button";
import type { AuthTokenSettings } from "@humansignal/core/pages/AccountSettings/types";
import type { AuthTokenSettings } from "@humansignal/app-common/pages/AccountSettings/types";
import { type ChangeEvent, useState } from "react";

export const TokenSettingsModal = ({
Expand Down
3 changes: 3 additions & 0 deletions web/libs/app-common/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import * as pages from "./pages";

export { pages };
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { IconLaunch, IconFileCopy, Label } from "@humansignal/ui";
import styles from "./PersonalAccessToken.module.scss";
import { atomWithMutation, atomWithQuery } from "jotai-tanstack-query";
import { atom, useAtomValue } from "jotai";
import { useCopyText } from "../../../lib/hooks/useCopyText";
import { useCopyText } from "@humansignal/core/lib/hooks/useCopyText";

/**
* FIXME: This is legacy imports. We're not supposed to use such statements
Expand Down
Loading
Loading