From 5b85c4bf25c149a23ec673c414ec9ecd526e0801 Mon Sep 17 00:00:00 2001 From: Dmytro Kirpa Date: Mon, 23 Feb 2026 12:32:42 +0100 Subject: [PATCH] docs: add Agents monorepo instructions update contributing guidelines --- AGENTS.md | 236 ++++++++++++++++++++++++++++++++++++++++++++++++ CONTRIBUTING.md | 13 +-- 2 files changed, 243 insertions(+), 6 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..83722df7 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,236 @@ +# AGENTS.md — Coding Agent Guide for fluentui-contrib + +Nx monorepo of `@fluentui-contrib/*` packages extending Microsoft's Fluent UI v9 design system. +Package manager: **Yarn 4** (`yarn@4.12.0`). Build orchestration: **Nx 22**. Node: `^20.19.0 || ^22.12.0`. + +## Environment Setup + +Use Corepack to ensure the expected Yarn version is active: + +```sh +corepack enable +corepack prepare yarn@4.12.0 --activate +yarn --version +node --version +``` + +## Build / Lint / Test Commands + +All commands run from the **repo root**. Never `cd` into a package folder. + +```sh +# Run a target for a specific package ( = folder name under packages/) +yarn nx run :build # Build a package (SWC-based) +yarn nx run :test # Run all Jest tests for a package +yarn nx run :lint # Lint a package +yarn nx run :type-check # TypeScript type checking +yarn nx run :storybook # Start Storybook dev server + +# Run a single test file +yarn nx run :test -- --testPathPattern="ComponentName.test" + +# Run a single test by name +yarn nx run :test -- --testNamePattern="should render correctly" + +# Run affected targets (compares against main branch) +yarn nx affected --target=build +yarn nx affected --target=test +yarn nx affected --target=lint + +# Format check/write +yarn nx format:check --base=main +yarn nx format:write --base=main + +# Playwright component tests (only packages that have them) +yarn nx run :component-test + +# Dependency version consistency +yarn check-dependencies +``` + +## Project Structure + +``` +packages// + src/ + index.ts # Package entry point — explicit named exports (no `export *`) + components// + .tsx # Component definition + .types.ts # Props, State, Slots types + .styles.ts # makeStyles + style hook + .test.tsx # Co-located Jest tests + use.ts # State/logic hook + render.tsx # Render function (Fluent v9 pattern) + index.ts # Component barrel exports + project.json # Nx project config with targets + jest.config.ts # SWC-based Jest config + tsconfig.json # Solution-style TS config + tsconfig.lib.json # Library build config + tsconfig.spec.json # Test config (commonjs, jest+node types) + eslint.config.js # Extends root config + .swcrc # SWC compiler options +``` + +## Code Style + +### TypeScript + +- **Strict mode** is enabled globally (`tsconfig.base.json` sets `"strict": true`) +- **Explicit return types** on all exported functions (`@typescript-eslint/explicit-module-boundary-types: error`); relaxed in test files +- **No `export *` in package index.ts** — use explicit named exports (enforced by `@rnx-kit/no-export-all`) +- Component-level barrel files may use `export *` or `export type *` +- Prefer `type` keyword for type-only re-exports: `export type { FooProps } from './Foo.types'` + +### Formatting (Prettier) + +- **Single quotes** (`singleQuote: true`) +- 2-space indentation (`.editorconfig`) +- Insert final newline, trim trailing whitespace +- Line length guideline: 120 characters (VS Code ruler) + +### Imports + +Order imports in groups separated by blank lines: + +1. React: `import * as React from 'react'` (always namespace import for React) +2. Third-party libraries (`@dnd-kit/*`, etc.) +3. Fluent UI (`@fluentui/react-components`, `@fluentui/react-jsx-runtime`, etc.) +4. Internal/relative imports + +### Naming Conventions + +- **Components**: PascalCase (`DraggableDialog`, `ChatMessage`) +- **Hooks**: `use` (e.g., `useKeytipRef`, `useDraggableDialog`) +- **Fluent v9 hooks/render fns**: `_unstable` suffix (`useChat_unstable`, `renderChat_unstable`) +- **Types**: `Props`, `State`, `Slots` +- **Style hooks**: `useStyles_unstable` +- **CSS class names constant**: `ClassNames` (e.g., `chatMessageClassNames`) +- **CSS class prefix**: `fui-` (e.g., `fui-Chat`, `fui-ChatMessage__body`) +- **Display names**: Always set (`Component.displayName = 'ComponentName'`) +- **Files**: PascalCase for components (`ComponentName.tsx`), camelCase for hooks (`useHookName.ts`) + +### Restricted Globals + +Direct use of browser globals is **banned** in source files (eslint `no-restricted-globals`): +`window`, `document`, `navigator`, `location`, `performance`, `fetch`, +`setTimeout`/`setInterval`/`clearTimeout`/`clearInterval`, +`requestAnimationFrame`/`cancelAnimationFrame`, +`IntersectionObserver`, `MutationObserver`, `ResizeObserver`, `matchMedia`, +`getComputedStyle`, `customElements`, `devicePixelRatio`. + +Access these via Fluent UI's `useFluent_unstable()` or equivalent context-based utilities. +This restriction is **turned off** in test/spec files. + +Also avoid: `cancelIdleCallback`, `requestIdleCallback`, `setImmediate`, `clearImmediate`. + +### Restricted Types + +These React types are banned (eslint `@typescript-eslint/no-restricted-types`): + +- `React.RefAttributes` → use `RefAttributes` (direct import) +- `JSX.Element` / `React.JSX.Element` → use `JSXElement` +- `JSX.IntrinsicElements` / `React.JSX.IntrinsicElements` → banned + +## Testing + +### Jest Unit Tests + +- Test files: `.test.tsx` or `.test.ts`, **co-located** with source (not in `__tests__/`) +- Use `describe`/`it` blocks (not bare `test()`) +- Rendering: `import { render, screen } from '@testing-library/react'` +- Hooks: `import { renderHook, act } from '@testing-library/react'` +- User interaction: `import userEvent from '@testing-library/user-event'` +- Mocking: `jest.fn()`, `jest.mock()`, `jest.spyOn()` +- Test environment: jsdom (configured in jest preset) + +### Test Scope Policy + +- Start with the smallest relevant target, e.g. `yarn nx run :test` or a single `--testPathPattern` +- If behavior might impact other packages, run affected tests: `yarn nx affected --target=test` +- For purely type-level/API changes, run `yarn nx run :type-check` +- Don’t expand to whole-repo runs unless needed to validate the change + +### React Version Compatibility + +When changing shared hooks/types/utilities, validate compatibility in the versioned test apps: + +- `apps/react-17-tests` +- `apps/react-18-tests` +- `apps/react-19-tests` + +### Playwright Component Tests + +- File: `.component-browser-spec.tsx` with companion `Example.component-browser-spec.tsx` +- Import from `@playwright/experimental-ct-react` +- Use `test`/`expect` from Playwright (not Jest) +- Run with: `yarn nx run :component-test` + +## Component Patterns + +### Fluent v9 Slot-Based Pattern (preferred for complex components) + +```tsx +// useComponent.ts — state hook +export const useComponent_unstable = (props: ComponentProps, ref: React.Ref): ComponentState => { ... }; + +// Component.styles.ts — style hook +export const useComponentStyles_unstable = (state: ComponentState): ComponentState => { ... }; + +// renderComponent.tsx — render function (uses Fluent JSX runtime) +/** @jsxRuntime classic */ +/** @jsx createElement */ +import { createElement } from '@fluentui/react-jsx-runtime'; +export const renderComponent_unstable = (state: ComponentState) => { ... }; + +// Component.tsx — composing everything +export const Component: ForwardRefComponent = React.forwardRef((props, ref) => { + const state = useComponent_unstable(props, ref); + useComponentStyles_unstable(state); + return renderComponent_unstable(state); +}); +Component.displayName = 'Component'; +``` + +### Simple Wrapper Pattern (for lightweight components) + +```tsx +export const Component: React.FC = React.memo((props) => { ... }); +Component.displayName = 'Component'; +``` + +## Commits & Change Management + +### Semantic Commits + +Use [Conventional Commits](https://www.conventionalcommits.org/) format: + +``` +(): +``` + +Common types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`, `perf`, `ci`. +Scope is typically the package name (e.g., `feat(react-keytips): add shortcut display`). + +### Changesets (Beachball) + +- Uses **Beachball** for versioning (independent per-package) +- **Before submitting a PR**, run `yarn change` to generate a changeset file describing your changes +- CI will fail (`npx beachball check`) if a changeset is missing for packages with source changes +- npm scope: `@fluentui-contrib/` +- Files ignored for changeset requirements are defined in `beachball.config.js` (`ignorePatterns`) + +## Scaffolding New Code + +```sh +# New package +yarn nx generate @fluentui-contrib/nx-plugin:library + +# New component in existing package +yarn nx generate @fluentui-contrib/nx-plugin:component + +# Add storybook to package +yarn nx generate @fluentui-contrib/nx-plugin:configure-storybook + +# Add Playwright component testing +yarn nx generate @fluentui-contrib/nx-plugin:playwright-component-configuration +``` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d2c6475c..8e89eb99 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,19 +16,20 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio ### Prerequisites -Make sure you have at least Node.js v18: +Make sure you have Node.js `^20.19.0 || ^22.12.0`: ```sh node -v -v18.0.0 +v20.19.0 ``` -This repo runs with Yarn v1, please install make sure to install it, since all other steps will assume -that you have this dependency installed. +This repo uses Yarn v4 via Corepack. Enable Corepack and activate the pinned Yarn version: ```sh -npm install -g yarn@1 +corepack enable +corepack prepare yarn@4.12.0 --activate +yarn -v ``` ### Setup the repo @@ -39,7 +40,7 @@ install dependencies. ```sh git clone git@github.com:/fluentui-contrib.git cd fluentui-contrib -yarn +yarn install ``` Add the main repo as a git remote so you can pull/rebase your fork with our latest updates: