|
| 1 | +# CORD Front-End Development Guidelines |
| 2 | + |
| 3 | +This document provides guidelines for developers working on the CORD Field front-end project, a UI built with React, Material-UI (MUI) v5, and GraphQL, using Yarn and Razzle. The front-end connects to the CORD API v3. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The CORD Field front-end enables [describe functionality briefly, e.g., field data management; replace with specific details if available]. It connects to the CORD API v3 by default at `http://localhost:3000`. For a complete setup guide, see the [cord-docs wiki](https://github.com/SeedCompany/cord-docs/wiki#new-hire-on-boarding). |
| 8 | + |
| 9 | +## Build/Configuration Instructions |
| 10 | + |
| 11 | +### Prerequisites |
| 12 | + |
| 13 | +1. NodeJS (current version or LTS recommended, >= 18.x as per `package.json`). |
| 14 | + - Check version with `node -v`. If compilation errors occur (e.g., `Buffer` object missing `blob` property), upgrade NodeJS. |
| 15 | +2. Corepack enabled (`corepack enable`). |
| 16 | +3. Yarn (managed via Corepack). |
| 17 | +4. Local CORD API v3 server running (see [Connecting to the API Server](https://github.com/SeedCompany/cord-api-v3)). |
| 18 | + |
| 19 | +### Setup |
| 20 | + |
| 21 | +1. Install dependencies: |
| 22 | + |
| 23 | + ```bash |
| 24 | + yarn install |
| 25 | + ``` |
| 26 | + |
| 27 | +2. Generate GraphQL schema and TypeScript types: |
| 28 | + |
| 29 | + ```bash |
| 30 | + yarn gql-gen |
| 31 | + ``` |
| 32 | + |
| 33 | +3. Set up environment variables: |
| 34 | + - Copy `.env.example` to `.env.local` and configure as needed (e.g., API endpoint). |
| 35 | + |
| 36 | +### Development |
| 37 | + |
| 38 | +1. Start the development server: |
| 39 | + |
| 40 | + ```bash |
| 41 | + yarn start |
| 42 | + ``` |
| 43 | + |
| 44 | +2. For production build: |
| 45 | + |
| 46 | + ```bash |
| 47 | + yarn build |
| 48 | + ``` |
| 49 | + |
| 50 | +3. Clean build artifacts: |
| 51 | + ```bash |
| 52 | + yarn clean |
| 53 | + ``` |
| 54 | + |
| 55 | +### Connecting to the API Server |
| 56 | + |
| 57 | +By default, the front-end connects to the local CORD API v3 server at `http://localhost:3000`. To use a different API server, create an `.env.local` file: |
| 58 | + |
| 59 | +```dotenv |
| 60 | +RAZZLE_API_BASE_URL=http://your-api-server-host.com:3000 |
| 61 | +``` |
| 62 | + |
| 63 | +## Testing Information |
| 64 | + |
| 65 | +### Testing Framework |
| 66 | + |
| 67 | +The project uses Jest and React Testing Library for testing: |
| 68 | + |
| 69 | +- Unit tests: Located in `__tests__` folders alongside source code in `src/components` and `src/scenes`. |
| 70 | +- End-to-end (E2E) tests: Located in `test` directory (if applicable). |
| 71 | + |
| 72 | +### Running Tests |
| 73 | + |
| 74 | +1. Run unit tests: |
| 75 | + |
| 76 | + ```bash |
| 77 | + yarn test |
| 78 | + ``` |
| 79 | + |
| 80 | +2. Run tests with coverage: |
| 81 | + ```bash |
| 82 | + yarn test:coverage |
| 83 | + ``` |
| 84 | + |
| 85 | +### Writing Tests |
| 86 | + |
| 87 | +#### Unit Tests |
| 88 | + |
| 89 | +Unit tests should be placed in `__tests__` folders alongside the component or hook, with a `.test.tsx` or `.spec.tsx` extension. For example: |
| 90 | + |
| 91 | +- `src/components/form/UserForm.tsx` → `src/components/form/__tests__/UserForm.test.tsx` |
| 92 | +- `src/hooks/useUser.ts` → `src/hooks/__tests__/useUser.test.ts` |
| 93 | + |
| 94 | +Example unit test: |
| 95 | + |
| 96 | +```tsx |
| 97 | +// src/components/form/__tests__/UserForm.test.tsx |
| 98 | +import { render, screen } from '@testing-library/react'; |
| 99 | +import { Form } from 'react-final-form'; |
| 100 | +import { UserForm } from '../UserForm'; |
| 101 | + |
| 102 | +describe('UserForm', () => { |
| 103 | + it('renders name field', () => { |
| 104 | + render(<Form onSubmit={jest.fn()} render={() => <UserForm onSubmit={jest.fn()} />} />); |
| 105 | + expect(screen.getByLabelText('Name')).toBeInTheDocument(); |
| 106 | + }); |
| 107 | +}); |
| 108 | +``` |
| 109 | + |
| 110 | +## Additional Development Information |
| 111 | + |
| 112 | +### Code Style |
| 113 | + |
| 114 | +The project uses ESLint and Prettier for code formatting and linting: |
| 115 | + |
| 116 | +- Run `yarn lint` to check and fix linting issues. |
| 117 | +- Run `yarn format` to format code using Prettier. |
| 118 | +- Enforce TypeScript strict mode (`tsconfig.json` with `strict: true`). |
| 119 | + |
| 120 | +### GraphQL |
| 121 | + |
| 122 | +The project uses Apollo Client for GraphQL: |
| 123 | + |
| 124 | +- Queries, mutations, and fragments are defined in `src/api` (e.g., `*.ts` or `*.graphql`). |
| 125 | +- Use `yarn gql-gen` to generate TypeScript types for queries/mutations. |
| 126 | +- Only access properties defined in generated types or `src/api/schema.graphql`. |
| 127 | + |
| 128 | +### Project Structure |
| 129 | + |
| 130 | +- `src/`: Source code |
| 131 | + - `src/api/`: GraphQL queries, mutations, Apollo Client setup, and API-related files. |
| 132 | + - `src/common/`: Utility TypeScript files (types, interfaces, Yup validation schemas). |
| 133 | + - `src/components/`: Reusable React components (mostly TSX, some with GraphQL files), with subfolders (e.g., `form/` for Final Form components). |
| 134 | + - `src/hooks/`: Custom React hooks (TypeScript). |
| 135 | + - `src/scenes/`: Application-specific, non-reusable components (mostly TSX, some with GraphQL files), with subfolders. |
| 136 | + - `src/server/`: Razzle server configuration files. |
| 137 | + - `src/theme/`: MUI theme configuration files. |
| 138 | + |
| 139 | +### Coding Standards |
| 140 | + |
| 141 | +- Use camelCase for variables, functions, and methods; ensure names are descriptive. |
| 142 | +- Use PascalCase for React components, classes, and enums. |
| 143 | +- Use kebab-case for new folders and files. |
| 144 | +- Use single quotes for strings, 2 spaces for indentation. |
| 145 | +- Prefer arrow functions for callbacks, async/await for GraphQL queries/mutations. |
| 146 | +- Use `const` for constants, minimize `let` usage (e.g., except in try/catch). |
| 147 | +- Use destructuring for objects/arrays, template literals for strings. |
| 148 | +- Follow SOLID principles for modular, reusable, maintainable code. |
| 149 | +- Avoid code duplication, deeply nested statements, hard-coded values. |
| 150 | +- Use constants/enums instead of magic numbers/strings. |
| 151 | +- Avoid mutations (non-GraphQL): |
| 152 | + - Prefer `const` over `let`. |
| 153 | + - Use spread syntax (e.g., `{ ...object, foo: 'bar' }`) instead of modifying objects. |
| 154 | +- Use strict TypeScript: |
| 155 | + - Define all object shapes in `src/common` or generated GraphQL types in `src/api`. |
| 156 | + - Use optional chaining (`?.`) or type guards for safe property access. |
| 157 | + |
| 158 | +### React Guidelines |
| 159 | + |
| 160 | +- Use `key` attribute only for dynamic lists (e.g., `Array.map`). |
| 161 | +- Minimize `useEffect`; prefer other hooks/patterns. |
| 162 | +- Avoid `event.preventDefault()` unless necessary. |
| 163 | +- For new small components: |
| 164 | + - Pass most props to wrapping components for reusability. |
| 165 | + - Match designs or comment stop-gap versions. |
| 166 | +- Avoid unnecessary HTML elements for styling (e.g., `<MyCard sx={{ m: 1 }} />` instead of `<Box sx={{ m: 1 }}><MyCard /></Box>`). |
| 167 | +- Use optional chaining (`?.`) or type guards for object properties. |
| 168 | + |
| 169 | +### Form Development |
| 170 | + |
| 171 | +- Use custom form components with Final Form and react-final-form in `src/components/form`: |
| 172 | + - Use `Form`, `Field`, and custom inputs. |
| 173 | + - Define TypeScript interfaces in `src/common`. |
| 174 | + - Use `react-final-form` hooks (e.g., `useForm`, `useField`). |
| 175 | + - Ensure form data properties match defined interfaces. |
| 176 | +- Integrate MUI v5 components (`TextField`, `Select`, etc.): |
| 177 | + - Use `sx` prop, referencing `src/theme`. |
| 178 | + - Pass `sx` and `className` props for flexibility. |
| 179 | +- Use Yup for validation: |
| 180 | + - Ensure TypeScript compatibility. |
| 181 | + |
| 182 | +### CSS Guidelines |
| 183 | + |
| 184 | +- Use `sx` prop for styling; avoid StyledComponents or `makeStyles`. |
| 185 | +- Reference `src/theme` for spacing (1-8), palette colors, typography variants. |
| 186 | +- Comment if using magic numbers/colors/fonts. |
| 187 | +- Ensure styles are minimal, necessary, and responsive across screen sizes. |
| 188 | +- Use `// ai edge-case` for justified deviations (e.g., non-theme colors). |
| 189 | +- **Parent components own layout styles**: Apply layout-related styles (e.g., centering, alignment, spacing) to parent components, not children, to ensure encapsulation and reusability. |
| 190 | + - Example (Correct): |
| 191 | + ```tsx |
| 192 | + // ai example Parent-owned centering |
| 193 | + // src/components/CenteredCard.tsx |
| 194 | + import { Box, Card } from '@mui/material'; |
| 195 | + export const CenteredCard = () => ( |
| 196 | + <Box sx={{ display: 'flex', justifyContent: 'center', p: 2 }}> |
| 197 | + <Card sx={{ width: 300 }}>Content</Card> |
| 198 | + </Box> |
| 199 | + ); |
| 200 | + ``` |
| 201 | + _Why_: Parent `<Box>` controls centering, keeping `<Card>` reusable. |
| 202 | + - Example (Incorrect): |
| 203 | + ```tsx |
| 204 | + // ai anti-pattern Child-owned centering |
| 205 | + // src/components/CenteredCard.tsx |
| 206 | + import { Box, Card } from '@mui/material'; |
| 207 | + export const CenteredCard = () => ( |
| 208 | + <Box sx={{ p: 2 }}> |
| 209 | + <Card sx={{ margin: 'auto', width: 300 }}>Content</Card> |
| 210 | + </Box> |
| 211 | + ); |
| 212 | + ``` |
| 213 | + _Why_: `<Card>` assumes parent’s layout, reducing reusability. |
| 214 | +- **Order control styles before appearance styles**: In `sx` prop declarations, list control styles (e.g., `display`, `padding`, `width`) before appearance styles (e.g., `color`, `background`, `fontSize`) for readability. |
| 215 | + - Example (Correct): |
| 216 | + ```tsx |
| 217 | + // ai example Ordered sx styles |
| 218 | + // src/components/StyledBox.tsx |
| 219 | + import { Box } from '@mui/material'; |
| 220 | + export const StyledBox = () => ( |
| 221 | + <Box |
| 222 | + sx={{ |
| 223 | + display: 'flex', |
| 224 | + justifyContent: 'center', |
| 225 | + padding: 2, |
| 226 | + width: '100%', |
| 227 | + backgroundColor: 'primary.main', |
| 228 | + color: 'white', |
| 229 | + borderRadius: 1, |
| 230 | + fontSize: 16, |
| 231 | + }} |
| 232 | + > |
| 233 | + Content |
| 234 | + </Box> |
| 235 | + ); |
| 236 | + ``` |
| 237 | + _Why_: Control styles (`display`, `justifyContent`, `padding`, `width`) precede appearance styles (`backgroundColor`, `color`, `borderRadius`, `fontSize`), enhancing readability. |
| 238 | + - Example (Incorrect): |
| 239 | + ```tsx |
| 240 | + // ai anti-pattern Disordered sx styles |
| 241 | + // src/components/StyledBox.tsx |
| 242 | + import { Box } from '@mui/material'; |
| 243 | + export const StyledBox = () => ( |
| 244 | + <Box |
| 245 | + sx={{ |
| 246 | + color: 'white', |
| 247 | + display: 'flex', |
| 248 | + backgroundColor: 'primary.main', |
| 249 | + padding: 2, |
| 250 | + fontSize: 16, |
| 251 | + justifyContent: 'center', |
| 252 | + borderRadius: 1, |
| 253 | + width: '100%', |
| 254 | + }} |
| 255 | + > |
| 256 | + Content |
| 257 | + </Box> |
| 258 | + ); |
| 259 | + ``` |
| 260 | + _Why_: Mixed style order reduces readability and maintainability. |
| 261 | + |
| 262 | +### API Guidelines |
| 263 | + |
| 264 | +- Avoid over-simplification in queries; consider second-order consequences. |
| 265 | +- Ensure `src/api` components have consistent interfaces matching designs. |
| 266 | +- Only access properties in generated GraphQL types; use type guards for dynamic data. |
| 267 | + |
| 268 | +### Version Control |
| 269 | + |
| 270 | +- Create branches: `type/MondayTicketID-description` (e.g., `enhancement/1234-new-profile-page`). |
| 271 | +- Write commit messages: |
| 272 | + - First line: `[TicketID] - [Short Title] - Imperative verb` (e.g., `0654 - IRP - Remove/move Impact Report Date`). |
| 273 | + - Optional body: Explain why the change is needed. |
| 274 | +- Create PRs for single changes: |
| 275 | + - Link Monday ticket in description. |
| 276 | + - Use bite-sized, logical commits. |
| 277 | + - Demo functionality to reviewers. |
| 278 | + - Check entire PR for applicability. |
| 279 | + - Verify no incorrect property access; properties must match defined types. |
| 280 | + |
| 281 | +### Security Practices |
| 282 | + |
| 283 | +- Sanitize user inputs to prevent XSS. |
| 284 | +- Use Apollo Client’s error handling in `src/api`. |
| 285 | +- Implement role-based access control (RBAC) via GraphQL context. |
| 286 | +- Configure secure cookies (`HttpOnly`, `Secure`, `SameSite=Strict`) in `src/server`. |
| 287 | +- Enforce Content Security Policy (CSP) in `src/server`. |
| 288 | +- Validate external data (APIs, databases) for expected formats. |
| 289 | +- Check dependencies with Snyk before adding via Yarn. |
| 290 | + |
| 291 | +### Common Errors to Avoid |
| 292 | + |
| 293 | +- **Accessing Non-Existent Properties**: |
| 294 | + - Never assume properties exist without type verification. |
| 295 | + - Reference TypeScript interfaces in `src/common` or generated GraphQL types in `src/api`. |
| 296 | + - Use optional chaining (`?.`) or type guards (e.g., `if ('foo' in obj)`). |
| 297 | + - Example (Correct): |
| 298 | + ```tsx |
| 299 | + // ai type-safety Safe property access with optional chaining |
| 300 | + interface User { |
| 301 | + name?: string; |
| 302 | + } |
| 303 | + const user: User = {}; |
| 304 | + const name = user?.name ?? 'Unknown'; |
| 305 | + ``` |
| 306 | + _Why_: Prevents runtime errors by checking property existence. |
| 307 | + - Example (Incorrect): |
| 308 | + ```tsx |
| 309 | + // ai anti-pattern Assumes non-existent property |
| 310 | + const user = {}; |
| 311 | + const name = user.name; // Error: Property 'name' does not exist |
| 312 | + ``` |
| 313 | + _Why_: Causes runtime errors due to unverified property access. |
| 314 | + |
| 315 | +### Type Checking |
| 316 | + |
| 317 | +Run `yarn type-check` to check for TypeScript errors without compiling the code. |
| 318 | + |
| 319 | +### Debugging |
| 320 | + |
| 321 | +- Use browser dev tools for React component debugging. |
| 322 | +- Enable Razzle’s server-side logging in `src/server` for SSR issues. |
| 323 | +- Use Jest debugger for tests: `yarn test --selectProjects Unit` with debugger attached. |
| 324 | + |
| 325 | +### Tagged Comments |
| 326 | + |
| 327 | +- Use `// ai tag` to mark code for reference: |
| 328 | + - `example`: Best practice or model code. |
| 329 | + - `edge-case`: Necessary deviation from standards. |
| 330 | + - `best-practice`: Adherence to coding standards. |
| 331 | + - `anti-pattern`: Code to avoid (pending refactor). |
| 332 | + - `todo`: Needs improvement or refactoring. |
| 333 | + - `workaround`: Temporary fix for a limitation. |
| 334 | + - `performance`: Optimized code. |
| 335 | + - `security`: Security-critical code. |
| 336 | + - `test`: Exemplary test case. |
| 337 | + - `design-alignment`: Matches or deviates from design specs. |
| 338 | + - `type-safety`: Safe property access. |
| 339 | +- Optionally add notes after the tag (e.g., `// ai example Reusable form with theme-based styling`). |
| 340 | +- Search tags with `git grep "ai "` to collect examples. |
0 commit comments