Skip to content

Commit 217028b

Browse files
authored
chore: OPTIC-1956: Refactor all component blocks and pages from core to app-common lib (#7367)
1 parent d08a2bd commit 217028b

35 files changed

+413
-9
lines changed

.cursorrules/react.mdc

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
---
2+
description: React coding standards and best practices for the LabelStudio clientside application
3+
globs: web/**/*.tsx, web/**/*.jsx
4+
alwaysApply: false
5+
---
6+
7+
# React Best Practices
8+
9+
## Project Structure
10+
- All frontend code lives in the `web` directory
11+
- Main application code is in `web/apps/labelstudio`
12+
- Shared libraries are in `web/libs`
13+
- Follow the established directory structure:
14+
- `components/`: Reusable UI components
15+
- `pages/`: Top-level page components
16+
- `utils/`: Utility functions
17+
- `hooks/`: Custom React hooks
18+
- `atoms/`: Jotai atom state definitions
19+
- `providers/`: Context providers
20+
- `services/`: API and other services
21+
- `types/`: TypeScript type definitions
22+
- `assets/`: Static assets
23+
24+
## Component Structure
25+
- Use functional components over class components
26+
- Keep components small and focused
27+
- Extract reusable logic into custom hooks
28+
- Use composition over inheritance
29+
- Implement proper prop types with TypeScript
30+
- Split large components into smaller, focused ones
31+
- Follow a consistent file organization pattern:
32+
```
33+
component-name/
34+
component-name.tsx
35+
component-name.module.scss
36+
component-name.test.tsx
37+
index.ts
38+
```
39+
40+
## Hooks
41+
- Follow the Rules of Hooks
42+
- Use custom hooks for reusable logic
43+
- Keep hooks focused and simple
44+
- Avoid useEffect unless absolutely required
45+
- Use appropriate dependency arrays in useEffect
46+
- Implement cleanup in useEffect when needed
47+
- Avoid nested hooks
48+
49+
## State Management
50+
- Use useState for local component state
51+
- Use Jotai atoms and not the Context API for shared state
52+
- Implement atomWithReducer for complex state logic
53+
- Implement atomWithQuery for any API requests for data
54+
- Keep state as close to where it's used as possible
55+
- Avoid prop drilling through proper state management
56+
- Only use Jotai as the single source of truth of global state management
57+
58+
## Performance
59+
- Implement proper memoization (useMemo, useCallback)
60+
- Use React.memo for expensive components
61+
- Avoid unnecessary re-renders
62+
- Implement proper lazy loading
63+
- Use proper key props in lists
64+
- Profile and optimize render performance
65+
66+
## Tooling
67+
- Use Biome for code linting and formatting
68+
- Follow CSS/SCSS linting rules defined in .stylelintrc.json
69+
- Use TypeScript for type safety
70+
- Keep bundle size in check by monitoring imports
71+
72+
## Forms
73+
- Use controlled components for form inputs
74+
- Implement proper form validation
75+
- Handle form submission states properly
76+
- Show appropriate loading and error states
77+
- Use form libraries for complex forms
78+
- Implement proper accessibility for forms
79+
80+
## Error Handling
81+
- Implement Error Boundaries
82+
- Handle async errors properly
83+
- Show user-friendly error messages
84+
- Implement proper fallback UI
85+
- Log errors appropriately
86+
- Handle edge cases gracefully
87+
88+
## Testing
89+
- Write unit tests for components
90+
- Implement integration tests for complex flows
91+
- Use React Testing Library
92+
- Test user interactions
93+
- Test error scenarios
94+
- Implement proper mock data
95+
96+
## Accessibility
97+
- Ensure components meet WCAG 2.1 AA standards
98+
- Use semantic HTML elements
99+
- Implement proper ARIA attributes
100+
- Ensure keyboard navigation
101+
- Test with screen readers
102+
- Handle focus management
103+
- Provide proper alt text for images
104+
105+
## Code Organization
106+
- Use proper file naming conventions which is kebab-case ie. ListItem -> `list-item.tsx`
107+
- Prefer one component per folder, but group related components together when necessary, and ensure there is only one component per file.
108+
- Component folders should have a SCSS `.module.scss` with the name of the component kebab-case ie. ListItem -> `list-item.module.scss`
109+
- Implement proper directory structure
110+
- UI components live within `web/libs/ui`
111+
- Application components that are shared across applications such as certain page-level blocks live within `web/libs/app-common`
112+
- Code in `web/apps` can only import code from `web/libs` and `web/libs` cannot import from `web/apps`
113+
- 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`
114+
- Keep atoms in a global atoms folder with the name of the file matching the entity or intent of state
115+
- Add all components and their states to Storybook by co-locating the story file next to the component file ie. `list-item.stories.tsx`
116+
- Use the `@humansignal/ui` package for UI components
117+
- Use the `@humansignal/icons` package for icons
118+
- Use the `@humansignal/core` package for core utilities/functions
119+
- Use the `@humansignal/app-common` package for application components
120+
121+
## Best Practices
122+
- No cyclic imports
123+
- Use proper imports/exports
124+
- Follow established import ordering
125+
- Compose components rather than extending them
126+
- Keep components focused on a single responsibility
127+
- Document complex logic with clear comments
128+
- Follow the project's folder structure and naming conventions
129+
- Prefer controlled components over uncontrolled ones

.cursorrules/tailwind.mdc

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
---
2+
description: Tailwind CSS and UI component best practices for the LabelStudio clientside application
3+
globs: **/*.css, web/**/*.tsx, web/**/*.jsx, web/**/*.scss, web/libs/ui/tailwind.config.js, web/libs/ui/tailwind.config.ts
4+
---
5+
6+
# Tailwind CSS Best Practices
7+
8+
## Component Styling
9+
- Use utility classes over custom CSS
10+
- Group related utilities with @apply when needed
11+
- Use custom component styles as a SCSS module only when necessary to improve the readability and maintainability of the components
12+
- Use proper responsive design utilities
13+
- Use proper state variants
14+
- Keep component styles consistent
15+
16+
## Tokens
17+
- All tokens from Figma are programatically generated and defined in the `web/libs/ui/src/tokens/tokens.scss` file
18+
- 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
19+
- 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
20+
- 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
21+
- 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
22+
23+
## Layout
24+
- Use semantic spacing utilities ie. `p-tight` instead of `p-200` or `--spacing-tight` instead of `--spacing-200`
25+
- Use Flexbox and Grid utilities effectively
26+
- Use container queries when needed
27+
- Implement proper responsive breakpoints
28+
- Use proper padding and margin utilities
29+
- Implement proper alignment utilities
30+
31+
## Typography
32+
- Use semantic typography utilities ie. `text-body-medium` instead of `text-16` or `--font-size-body-medium` instead of `--font-size-16`
33+
- Use proper font size utilities
34+
- Implement proper line height
35+
- Use proper font weight utilities
36+
- Configure custom fonts properly
37+
- Use proper text alignment
38+
- Implement proper text decoration
39+
40+
## Colors
41+
- 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`
42+
- Implement proper color contrast
43+
- Use opacity utilities effectively
44+
- Configure custom colors properly
45+
- Use proper gradient utilities
46+
- Implement proper hover states
47+
48+
## Components
49+
- Use shadcn/ui components when available from `web/libs/ui/src/shad`
50+
- Only use shadcn/ui components re-exported from `web/libs/ui` not the raw ones defined in `web/libs/ui/src/shad`
51+
- The import of these components in other libs and apps should be `@humansignal/ui`
52+
- Compose components properly
53+
- Keep component variants consistent
54+
- Implement proper animations
55+
- Use proper transition utilities
56+
- Keep accessibility in mind
57+
- UI components should be defined and exported from `web/libs/ui` and follow the best practices of Code Organization
58+
59+
## Responsive Design
60+
- Use mobile-first approach
61+
- Implement proper breakpoints
62+
- Use container queries effectively
63+
- Handle different screen sizes properly
64+
- Implement proper responsive typography
65+
- Use proper responsive spacing
66+
67+
## Performance
68+
- Use proper purge configuration
69+
- Minimize custom CSS
70+
- Use proper caching strategies
71+
- Implement proper code splitting
72+
- Optimize for production
73+
- Monitor bundle size
74+
75+
## Best Practices
76+
- Follow naming conventions
77+
- Keep styles organized
78+
- Use proper documentation
79+
- Implement proper testing
80+
- Follow accessibility guidelines
81+
- Use proper version control

.cursorrules/typescript.mdc

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
description: TypeScript coding standards and best practices for the LabelStudio clientside application
3+
globs: web/**/*.ts, web/**/*.tsx, web/**/*.d.ts
4+
---
5+
6+
# TypeScript Best Practices
7+
8+
## Type System
9+
- Prefer interfaces over types for object definitions
10+
- Use type for unions, intersections, and mapped types
11+
- Avoid using `any`, prefer `unknown` for unknown types
12+
- Use strict TypeScript configuration
13+
- Leverage TypeScript's built-in utility types
14+
- Use generics for reusable type patterns
15+
16+
## Naming Conventions
17+
- Use PascalCase for type names and interfaces
18+
- Use camelCase for variables and functions
19+
- Use UPPER_CASE for constants
20+
- Use descriptive names with auxiliary verbs (e.g., isLoading, hasError)
21+
- Prefix interfaces for React props with 'Props' (e.g., ButtonProps)
22+
23+
## Code Organization
24+
- Keep type definitions close to where they're used
25+
- Export types and interfaces from dedicated type files when shared
26+
- Use barrel exports (index.ts) for organizing exports
27+
- Place shared types in a `types` directory
28+
- Co-locate component props with their components
29+
30+
## Functions
31+
- Use explicit return types for public functions
32+
- Use arrow functions for callbacks and methods
33+
- Implement proper error handling with custom error types
34+
- Use function overloads for complex type scenarios
35+
- Prefer async/await over Promises
36+
37+
## Best Practices
38+
- Enable strict mode in tsconfig.json
39+
- Use readonly for immutable properties
40+
- Leverage discriminated unions for type safety
41+
- Use type guards for runtime type checking
42+
- Implement proper null checking
43+
- Avoid type assertions unless necessary
44+
45+
## Error Handling
46+
- Create custom error types for domain-specific errors
47+
- Use Result types for operations that can fail
48+
- Implement proper error boundaries
49+
- Use try-catch blocks with typed catch clauses
50+
- Handle Promise rejections properly
51+
52+
## Patterns
53+
- Use the Builder pattern for complex object creation
54+
- Implement the Repository pattern for data access
55+
- Use the Factory pattern for object creation
56+
- Leverage dependency injection
57+
- Use the Module pattern for encapsulation

web/apps/labelstudio/src/pages/CreateProject/Import/Import.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ff } from "@humansignal/core";
2-
import { SampleDatasetSelect } from "@humansignal/core/blocks/SampleDatasetSelect/SampleDatasetSelect";
2+
import { SampleDatasetSelect } from "@humansignal/app-common/blocks/SampleDatasetSelect/SampleDatasetSelect";
33
import { IconError, IconFileUpload, IconInfo, IconTrash, IconUpload } from "@humansignal/icons";
44
import { Badge } from "@humansignal/shad/components/ui/badge";
55
import { cn as scn } from "@humansignal/shad/utils";

web/apps/labelstudio/src/pages/Organization/PeoplePage/PeoplePage.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import "./PeopleInvitation.scss";
1313
import { PeopleList } from "./PeopleList";
1414
import "./PeoplePage.scss";
1515
import { SelectedUser } from "./SelectedUser";
16-
import { TokenSettingsModal } from "@humansignal/core/blocks/TokenSettingsModal";
16+
import { TokenSettingsModal } from "@humansignal/app-common/blocks/TokenSettingsModal";
1717
import { IconPlus } from "@humansignal/icons";
1818
import { useToast } from "@humansignal/ui";
1919
import { InviteLink } from "./InviteLink";

web/apps/labelstudio/src/pages/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { HomePage } from "./Home/HomePage";
33
import { OrganizationPage } from "./Organization";
44
import { ModelsPage } from "./Organization/Models/ModelsPage";
55
import { FF_HOMEPAGE, isFF } from "../utils/feature-flags";
6-
import { pages } from "@humansignal/core";
6+
import { pages } from "@humansignal/app-common";
77
import { ff } from "@humansignal/core";
88

99
export const Pages = [

web/libs/app-common/.babelrc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"presets": [
3+
[
4+
"@nx/react/babel",
5+
{
6+
"runtime": "automatic",
7+
"useBuiltIns": "usage"
8+
}
9+
]
10+
],
11+
"plugins": []
12+
}

web/libs/app-common/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# app-common
2+
3+
This library was generated with [Nx](https://nx.dev).
4+
5+
## Running unit tests
6+
7+
Run `nx test app-common` to execute the unit tests via [Jest](https://jestjs.io).

web/libs/app-common/jest.config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/* eslint-disable */
2+
export default {
3+
displayName: "app-common",
4+
preset: "../../jest.preset.js",
5+
transform: {
6+
"^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "@nx/react/plugins/jest",
7+
"^.+\\.[tj]sx?$": ["babel-jest", { presets: ["@nx/react/babel"] }],
8+
},
9+
moduleFileExtensions: ["ts", "tsx", "js", "jsx"],
10+
coverageDirectory: "../../coverage/libs/app-common",
11+
};

web/libs/app-common/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@humansignal/app-common",
3+
"version": "0.0.0",
4+
"license": "MIT",
5+
"private": true,
6+
"dependencies": {},
7+
"main": "src/index.ts",
8+
"files": ["src/lib", "src/pages"]
9+
}

web/libs/app-common/project.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "app-common",
3+
"$schema": "../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "libs/app-common/src",
5+
"projectType": "library",
6+
"tags": [],
7+
"targets": {
8+
"lint": {
9+
"executor": "@nx/eslint:lint",
10+
"outputs": ["{options.outputFile}"],
11+
"options": {
12+
"lintFilePatterns": ["libs/app-common/**/*.{ts,tsx,js,jsx}"]
13+
}
14+
},
15+
"test": {
16+
"executor": "@nx/jest:jest",
17+
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
18+
"options": {
19+
"jestConfig": "libs/app-common/jest.config.ts",
20+
"passWithNoTests": true
21+
},
22+
"configurations": {
23+
"ci": {
24+
"ci": true,
25+
"codeCoverage": true
26+
}
27+
}
28+
}
29+
}
30+
}

web/libs/core/src/blocks/TokenSettingsModal/index.tsx renamed to web/libs/app-common/src/blocks/TokenSettingsModal/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { useAtomValue } from "jotai";
2-
import { settingsAtom, TOKEN_SETTINGS_KEY } from "@humansignal/core/pages/AccountSettings/atoms";
2+
import { settingsAtom, TOKEN_SETTINGS_KEY } from "@humansignal/app-common/pages/AccountSettings/atoms";
33
import { queryClientAtom } from "jotai-tanstack-query";
44

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

1010
export const TokenSettingsModal = ({

web/libs/app-common/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import * as pages from "./pages";
2+
3+
export { pages };

web/libs/core/src/pages/AccountSettings/sections/PersonalAccessToken.tsx renamed to web/libs/app-common/src/pages/AccountSettings/sections/PersonalAccessToken.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { IconLaunch, IconFileCopy, Label } from "@humansignal/ui";
22
import styles from "./PersonalAccessToken.module.scss";
33
import { atomWithMutation, atomWithQuery } from "jotai-tanstack-query";
44
import { atom, useAtomValue } from "jotai";
5-
import { useCopyText } from "../../../lib/hooks/useCopyText";
5+
import { useCopyText } from "@humansignal/core/lib/hooks/useCopyText";
66

77
/**
88
* FIXME: This is legacy imports. We're not supposed to use such statements

0 commit comments

Comments
 (0)