From 8ea9cbafa6dde23b968ffd47662970d5cc9fff0b Mon Sep 17 00:00:00 2001 From: Chirag Madlani <12962843+chirag-madlani@users.noreply.github.com> Date: Thu, 20 Aug 2026 12:26:05 +0530 Subject: [PATCH 1/2] docs(ui): define domain/feature folder structure and file naming conventions The UI tree has grown to 5,305 files with 77 top-level component folders (667 in common/ alone) and 526 flat utils, with no documented grouping scheme. File naming has forked: 830 bare .tsx vs 456 .component.tsx, 411 .interface.ts vs 4 .types.ts. Document a layer-first structure that keeps components/, pages/, rest/, utils/, hooks/ as top-level layers and adds domain/feature grouping inside each, so `governance/glossary` is one coordinate across layers. Keeping layers at the top preserves the custom ESLint rules in eslint-rules/, which resolve a file's layer from the first path segment after src/. Also correct three stale claims in the handbook: - src/routes/ and src/store/ do not exist; routers live in components/AppRouter/ and Zustand stores in src/hooks/ - index.ts barrels were recommended in two places despite the no-internal-barrel-imports rule reporting them - the assets/icons/index.ts icon barrel is the exact case that rule exists to prevent Co-Authored-By: Claude Opus 5 (1M context) --- .../main/resources/ui/DEVELOPER_HANDBOOK.md | 365 +++++++++++++----- 1 file changed, 271 insertions(+), 94 deletions(-) diff --git a/openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md b/openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md index a5b35daed35a..3cd0f7916dfb 100644 --- a/openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md +++ b/openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md @@ -2,118 +2,281 @@ ## 1. Folder Structure +The top-level folders are **layers** (`components`, `pages`, `rest`, `utils`, …). Inside each layer, +code is grouped by **domain**, then by **feature**. The same domain and feature names repeat in every +layer, so `governance/glossary` is one coordinate you can use to find anything about glossaries. + ``` @src/ │ -├── assets/ # Static files (images, fonts, icons, etc.) -├── components/ # Reusable UI components (buttons, modals, etc.) -│ └── [ComponentName]/ -│ ├── index.tsx -│ ├── [ComponentName].tsx -│ ├── [ComponentName].test.tsx -│ ├── [ComponentName].style.less -│ └── [ComponentName].types.ts # (if needed, see below) -├── constants/ # App-wide constants and enums -├── context/ # React context providers -├── hooks/ # Custom React hooks -├── interface/ # TypeScript interfaces and types (shared) -├── pages/ # Top-level pages/views (mapped to routes) -│ └── [PageName]/ -│ ├── index.tsx -│ ├── [PageName].tsx -│ ├── [PageName].test.tsx -│ ├── [PageName].style.less -│ └── [PageName].types.ts # (if needed) -├── routes/ # Route definitions and helpers -├── store/ # State management (Redux, Zustand, etc.) -├── api/ # API service functions and clients -├── utils/ # Utility/helper functions -├── styles/ # Global styles, variables, mixins (LESS) -├── App.tsx # Main app component -├── index.tsx # Entry point -└── ... # Other config files, setup, etc. +├── components/ # Reusable UI components +│ ├── discovery/ # domain +│ │ ├── explore/ # feature +│ │ │ ├── ExploreTree/ # component +│ │ │ └── ExploreSearchBar/ +│ │ ├── search/ +│ │ ├── my-data/ +│ │ └── data-marketplace/ +│ ├── governance/ +│ │ ├── glossary/ +│ │ │ ├── GlossaryList/ +│ │ │ ├── GlossaryHeader/ +│ │ │ └── GlossaryTermCard/ +│ │ ├── classification/ +│ │ ├── domain/ +│ │ └── data-product/ +│ ├── observability/ +│ │ ├── data-quality/ +│ │ ├── incident-manager/ +│ │ ├── profiler/ +│ │ └── alerts/ +│ ├── insights/ +│ │ ├── data-insight/ +│ │ └── kpi/ +│ ├── platform/ +│ │ ├── settings/ +│ │ ├── services/ +│ │ ├── ingestion/ +│ │ └── auth/ +│ ├── lineage/ # cross-cutting — belongs to no single domain +│ ├── data-contract/ +│ ├── entity/ +│ ├── activity-feed/ +│ └── common/ # generic, domain-agnostic UI +│ +├── pages/ # Route-level views +│ ├── governance/ +│ │ └── glossary/ +│ │ ├── GlossaryListPage/ +│ │ └── GlossaryDetailPage/ +│ ├── discovery/ +│ ├── observability/ +│ ├── insights/ +│ ├── platform/ +│ └── lineage/ +│ +├── rest/ # API clients +│ ├── governance/ +│ │ ├── glossary.api.ts +│ │ └── classification.api.ts +│ └── … # same domains +│ +├── utils/ # Helpers +│ ├── governance/ +│ │ ├── glossary.utils.ts +│ │ └── glossary.utils.test.ts +│ ├── … # same domains +│ └── common/ # date, string, formatting +│ +├── hooks/ +│ ├── governance/ +│ │ └── useGlossaryList.ts +│ └── common/ +│ +├── constants/ # grouped by domain, plus common/ +├── interface/ # grouped by domain, plus common/ +├── context/ # React context providers (note: a stray `contexts/` also +│ # exists — use `context/`, fold the other in) +├── generated/ # generated from openmetadata-spec — never hand-edit +├── assets/ # images, fonts, icons +├── locale/ # i18n resources +├── styles/ # global styles, variables, mixins (LESS) +├── App.tsx +└── index.tsx +``` + +One feature, fully expanded across the layers: + +``` +components/governance/glossary/GlossaryList/ +pages/governance/glossary/GlossaryListPage/ +rest/governance/glossary.api.ts +utils/governance/glossary.utils.ts +hooks/governance/useGlossaryList.ts +constants/governance/glossary.constants.ts +interface/governance/glossary.interface.ts +``` + +**Trade-off to be aware of:** because layers stay at the top, one feature spans several trees — adding +a glossary field can touch four folders. That is the price of keeping the core layers stable and the +lint layer-rules working (see §6). Keep the domain and feature names identical across layers so the +files are still trivially greppable. + +## 2. Domains + +Five domains, matching the product surface: + +| Domain | Owns | +|---|---| +| `discovery` | Explore, search, my data, data marketplace, knowledge center | +| `governance` | Glossary, classification, domains, data products, policies, roles, certification | +| `observability` | Data quality, incident manager, profiler, test library, alerts, service insights | +| `insights` | Data insight, KPI, web analytics | +| `platform` | Settings, services, ingestion, auth, bots, applications, personas, customization | + +**Cross-cutting features sit at the domain level, not inside a domain** — `lineage/`, +`data-contract/`, `entity/`, `activity-feed/`. + +Use one test to decide: **if two domains would both reasonably claim a feature, it has no domain** — +put it at the domain level. Lineage is discovery *and* governance *and* observability, so it stays +flat. This is what stops every new folder from reopening a taxonomy argument. + +Do not add a sixth domain without team agreement — the value of the scheme is that the same five +names appear in every layer. + +## 3. File Naming Conventions + +One stem per unit; the suffix carries the role. Fuzzy-finding `GlossaryList` returns a set of files +that label themselves. + +| File | Holds | +|---|---| +| `GlossaryList.tsx` | the component | +| `GlossaryList.types.ts` | props and local types | +| `GlossaryList.utils.ts` | pure logic, no React, no JSX | +| `GlossaryList.constants.ts` | literals, enums | +| `GlossaryList.style.less` | component-scoped styles | +| `GlossaryList.test.tsx` | unit test | +| `GlossaryList.mock.ts` | test fixtures | + +Casing: + +- **kebab-case** for domain and feature folders — they are paths, not things: `governance/`, + `data-contract/` +- **PascalCase** for component folders and component files — the import name then matches the + filename with no mental mapping: `GlossaryList/GlossaryList.tsx` +- **camelCase** for non-component modules: `useGlossaryList.ts`, `glossary.api.ts`, + `glossary.utils.ts` +- **UPPER_CASE** for constant values (not filenames): `API_URL` + +Never rename a file by case alone. macOS is case-insensitive by default and git will not track it +cleanly — rename via an intermediate name if you must. + +> Legacy files use `.component.tsx` and `.interface.ts`. New code uses the table above. The +> divergence is intentional: the suffix tells you at a glance whether a file has been migrated. + +## 4. Component Structure + +Each component gets a folder containing only the files it needs — do not scaffold empty ones: + +``` +components/governance/glossary/GlossaryList/ + GlossaryList.tsx + GlossaryList.types.ts + GlossaryList.utils.ts + GlossaryList.test.tsx + GlossaryList.style.less ``` -## 2. File Naming Conventions +- **Keep business logic out of the component.** Data shaping, filtering, and derivation go in + `*.utils.ts` as pure functions — no React, no JSX. They are then unit-testable without rendering. +- **No `index.ts` barrel files inside a component folder.** A barrel re-export pulls every sibling + module into the graph and defeats tree-shaking; the `no-internal-barrel-imports` lint rule reports + it. Import the deep path instead. -- Use PascalCase for components, pages, and their folders: `UserProfile`, `UserProfile.tsx` -- Use camelCase for hooks and utility functions: `useFetchData.ts`, `formatDate.ts` -- Use UPPER_CASE for constants: `API_URL` -- Test files: `[ComponentName].test.tsx` or `[util].test.ts` -- Styles: `[ComponentName].style.less` for component-scoped styles -- Types: `[ComponentName].types.ts` for component-specific types (see below) +## 5. Component-Specific Types -## 3. Component Structure +- Simple, single-use types: define at the top of the component file. +- Complex or reused types: `[ComponentName].types.ts` beside the component. +- Types shared across a whole feature: `interface/[domain]/[feature].interface.ts`. +- **Never redefine an API type** — import it from `generated/`, which is generated from + `openmetadata-spec/` (see `.claude/rules/schema-first.md`). -Each component/page should have its own folder containing: -- Main component file (`.tsx`) -- Test file (`.test.tsx`) -- Styles (`.style.less`) -- Index file for clean imports -- Types file (`.types.ts`) if types are complex or reused +## 6. Imports -Example: +**Within a feature, use relative imports.** + +```ts +import { formatTerms } from './GlossaryList.utils'; +import { GlossaryHeader } from '../GlossaryHeader/GlossaryHeader'; ``` -components/ - Button/ - Button.tsx - Button.test.tsx - Button.style.less - Button.types.ts - index.ts + +If you need more than one `../`, the file is probably in the wrong feature. Reach for an absolute +import rather than climbing. + +**Across features or layers, use absolute imports from the layer root.** + +```ts +import { GlossaryList } from 'components/governance/glossary/GlossaryList/GlossaryList'; +import { getGlossaryTerms } from 'rest/governance/glossary.api'; +import { formatTerms } from 'utils/governance/glossary.utils'; ``` -## 4. Component-Specific Types +> **Setup required.** Absolute imports currently resolve in Jest only +> (`jest.config.js` → `moduleDirectories: ['node_modules', 'src']`). Vite and `tsc` do not resolve +> them yet — `tsconfig.json` has no `baseUrl`, and `vite.config.ts` aliases only `@`. Before using +> absolute imports in application code, add `"baseUrl": "./src"` to `tsconfig.json` and matching +> entries to the Vite `resolve.alias` block. Until then, application code must keep using relative +> paths; only tests can use the absolute form. + +Enforced import rules (custom ESLint plugins in `eslint-rules/`, run on every PR): -- For simple or small components, define types/interfaces at the top of the component file. -- For complex components or when types are reused elsewhere, create a separate file: - - `[ComponentName].types.ts` -- **Rule of Thumb:** - - If the type is only used in one file and is simple, keep it in the same file. - - If the type is reused or complex, use a separate `.types.ts` file. +- **No app-internal barrel imports** — import the deep path, not an `index.ts`. +- **Pages must not import other pages.** Move shared code down into components, hooks, interfaces, or + pure utilities. +- **REST clients and hooks must not import UI.** Dependencies point one way — `pages` → `components` + → `hooks`/`rest` → `utils`. +- **Pure utilities must not contain JSX** or import React, pages, or REST. +- **Routers must lazy-load pages** with `import()`, never a static import. +- **No lodash default or namespace imports** — `import { isEmpty } from 'lodash';`. -## 5. API Layer +These rules derive a file's layer from the first path segment after `src/`, which is why the layer +folders stay at the top level. -- All API calls go in `@src/api/` -- Group by resource (e.g., `userApi.ts`, `projectApi.ts`) -- Use TypeScript interfaces from `@src/interface/` -- Keep API logic separate from UI logic +## 7. API Layer -## 6. State Management +- All API calls live in `@src/rest/`, grouped by domain: `rest/governance/glossary.api.ts` +- One file per feature, not per endpoint +- Request and response types come from `generated/` +- Keep API logic free of UI concerns — no React, no toasts, no navigation -- Use `@src/store/` for global state (Redux, Zustand, etc.) -- Organize by feature/domain -- Keep actions, reducers, and selectors together +## 8. State Management -## 7. Hooks +- Zustand stores for global state. They live in `@src/hooks/` alongside other hooks, not in a separate + `store/` folder — `useApplicationStore`, `useSearchStore`, `useDomainStore`, `useWelcomeStore` +- New stores go in `hooks/[domain]/`, named `use[Thing]Store.ts` +- Keep component state local with `useState` when possible +- Context providers for feature-shared state live in `@src/context/` -- Place all custom hooks in `@src/hooks/` -- Prefix with `use` (e.g., `useAuth.ts`) -- Keep hooks generic and reusable +## 9. Hooks -## 8. Interfaces & Types +- Feature hooks: `hooks/[domain]/useThing.ts` +- Generic hooks: `hooks/common/` +- Always prefix with `use`; return typed objects +- Hooks must not import UI components -- All shared types/interfaces go in `@src/interface/` -- Group by domain or feature -- Import types from here in components, API, and store +## 10. Interfaces & Types -## 9. Testing +- Shared types go in `@src/interface/[domain]/` +- Component-local types stay in `[ComponentName].types.ts` (see §5) +- API types always come from `generated/` — never hand-written + +## 11. Testing - Use Jest + React Testing Library - Test files live next to the code they test - Cover components, hooks, utils, and API logic -- **IMPORTANT**: Always write unit tests for utility functions in `@src/utils/` +- **IMPORTANT**: Always write unit tests for utility functions - Every utility function should have corresponding test coverage - Test edge cases, error conditions, and expected behavior - - Place test files next to the utility: `utilName.test.ts` + - Place test files next to the utility: `glossary.utils.test.ts` +- Because business logic lives in `*.utils.ts` (§4), most logic is testable without rendering — prefer + that over deep component tests - Follow [this](./playwright/PLAYWRIGHT_DEVELOPER_HANDBOOK.md) guide for e2e testing. -## 10. Routing +## 12. Routing -- Define all routes in `@src/routes/` -- Use a central file for route paths and lazy loading +- Routers live in `@src/components/AppRouter/`, which is the app's routing composition root — there is + no `src/routes/` folder +- Each feature that owns a route subtree gets its own router there: `GlossaryRouter`, `DomainRouter`, + `ClassificationRouter`, `SettingsRouter`, `EntityRouter` +- Route path constants go in `constants/[domain]/[feature].routes.ts` +- Route modules must load pages with `React.lazy` + `import()`, never a static import — enforced by + the `no-eager-page-imports` rule. Pass every lazy component to a helper from + `components/AppRouter/withSuspenseFallback`, or render it in a real `Suspense` boundary with an + explicit `fallback` -## 11. Assets +## 13. Assets - Place images, SVGs, fonts, etc. in `@src/assets/` - Organize by type or feature @@ -122,12 +285,12 @@ components/ - ✅ Good: `` - This allows flexible sizing via CSS/props without hardcoded dimensions -## 12. Styles +## 14. Styles - Use LESS style for component styles (`.style.less`) - Place global styles, variables, and mixins in `@src/styles/` as `.less` files -## 13. Translations +## 15. Translations - **i18n Translation Rule**: DO NOT use i18n translation functions outside of React components - Only use `t()` from `useTranslation()` hook inside components @@ -136,7 +299,7 @@ components/ - ✅ Good: Store the key `message: 'label.name'`, then translate in component using `t('label.name')` - This prevents initialization order issues and keeps non-component code pure -## 14. UI Library (@openmetadata/ui-core-components) and Icons (@untitledui/icons) +## 16. UI Library (@openmetadata/ui-core-components) and Icons (@untitledui/icons) - Primary UI library: `@openmetadata/ui-core-components`, built on Untitled UI patterns with `react-aria-components` as the accessibility foundation. - Icon source: @untitledui/icons. @@ -150,13 +313,13 @@ Usage guidance: - Use CSS custom properties (design tokens) defined in `openmetadata-ui-core-components` for colors instead of hardcoded values. - Use `.style.less` only for component-specific static styles/layout that predate the Tailwind migration. - Icons: - - Import icons directly to keep bundle size small. Example: - - import { IconName } from '@untitledui/icons'; - - Create a single re-export file for commonly used icons: - - @src/assets/icons/index.ts — re-export the subset of icons used across app to centralize changes. + - Import icons directly to keep bundle size small: `import { IconName } from '@untitledui/icons';` - For rarely used icons, import directly where needed to enable tree-shaking. - - Wrap icons in an Icon component if you need consistent sizing, color, or additional behavior: - - @src/components/ui/Icon/Icon.tsx + - If you need consistent sizing, color, or extra behavior, wrap icons in a shared component at + `@src/components/common/Icon/Icon.tsx` (create it when the need arises — it does not exist yet). + - Do **not** create an `assets/icons/index.ts` re-export barrel. It is the exact case the + `no-internal-barrel-imports` rule exists to prevent: one import pulls the whole icon set into the + module graph and defeats tree-shaking. - Accessibility and ARIA: - Rely on `react-aria-components`' accessible primitives (already wired into `openmetadata-ui-core-components`). - Ensure icons used as interactive elements have accessible names or aria-hidden when decorative. @@ -164,16 +327,15 @@ Usage guidance: - Prefer named imports to maintain tree-shaking icon packages. - Keep the icon re-export file limited to used icons; avoid exporting entire icon packs. -## 15. General Rules +## 17. General Rules - Keep files small and focused - Prefer composition over inheritance -- Use TypeScript everywhere -- Write clear, descriptive comments and JSDoc where needed -- Use absolute imports from `@src/` (configure `tsconfig.json` paths) +- Use TypeScript everywhere; never `any` +- Write clear, descriptive comments and JSDoc where needed — explain *why*, never restate the code - Keep third-party code and wrappers in a separate folder if needed -## 16. Forms +## 18. Forms - New forms use the `react-hook-form` + `react-aria` stack from `@openmetadata/ui-core-components` (`getField`/`FieldProp`/`FieldTypes`/`HookForm`/`FormFields`): a form is `FieldProp[]` config objects + RHF state + a pure values→payload transform. - Full reference: [docs/formutils.md](./docs/formutils.md) — field types, validation, composition, escape hatches, and the drawer caller pattern. @@ -181,4 +343,19 @@ Usage guidance: --- +## Adding a new feature — checklist + +1. Pick the domain (§2). If two domains claim it, put it at the domain level. +2. Create the folders you need, in each layer you touch: + - `components/[domain]/[feature]/[ComponentName]/` + - `pages/[domain]/[feature]/[PageName]/` + - `rest/[domain]/[feature].api.ts` + - `utils/[domain]/[feature].utils.ts` +3. Name files by role (§3): `.tsx`, `.types.ts`, `.utils.ts`, `.test.tsx`. +4. Put business logic in `*.utils.ts` and unit-test it. +5. Import API types from `generated/` — never redeclare them. +6. Run `yarn ui-checkstyle:changed` and `npx tsc --noEmit`. + +--- + This handbook ensures a clean, scalable, and consistent codebase for all developers. From 6c562c3aaaadfcab59796f3e646e8df7e678cb73 Mon Sep 17 00:00:00 2001 From: Auto-revert Test Date: Fri, 21 Aug 2026 10:33:49 +0530 Subject: [PATCH 2/2] docs: wire the UI folder-structure handbook into the AI-facing config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEVELOPER_HANDBOOK.md was referenced only from the UI README, so no agent-facing config pointed at it. Worse, five AI-facing docs mandated the legacy naming (`ComponentName.component.tsx` / `ComponentName.interface.ts`) that the handbook supersedes for new code, so an agent following the rules and an agent following the handbook would produce different filenames. Point every agent entry point at the handbook and reconcile the naming guidance: - .claude/rules/frontend-react.md — auto-loads on UI *.{ts,tsx}; now carries the domain/feature placement rule and the new naming table - CLAUDE.md — added to "Repo coding conventions" (AGENTS.md symlinks it) - docs/index.md — indexed in the UI reference table - skills/openmetadata-workflow/SKILL.md — the UI component row now reads the handbook first - skills/agents/frontend-reviewer.md — reviews placement and naming - .github/copilot-instructions.md, openmetadata-ui/UI_PR_REVIEW_GUIDELINES.md Every one states that legacy `.component.tsx`/`.interface.ts` files must NOT be renamed, so reviewers don't generate rename churn across the 5,305 existing files. check_harness.py reports the same 5 pre-existing warnings, no new ones; frontend-react.md is 94 lines, under the 100-line rule budget. Co-Authored-By: Claude Opus 5 (1M context) --- .claude/rules/frontend-react.md | 18 ++++++++++----- .github/copilot-instructions.md | 26 ++++++++++++++++++---- CLAUDE.md | 5 +++++ docs/index.md | 1 + openmetadata-ui/UI_PR_REVIEW_GUIDELINES.md | 19 ++++++++++++---- skills/agents/frontend-reviewer.md | 19 ++++++++++++++-- skills/openmetadata-workflow/SKILL.md | 2 +- 7 files changed, 73 insertions(+), 17 deletions(-) diff --git a/.claude/rules/frontend-react.md b/.claude/rules/frontend-react.md index 571279ec88e3..ca1cfda31ce0 100644 --- a/.claude/rules/frontend-react.md +++ b/.claude/rules/frontend-react.md @@ -8,15 +8,21 @@ paths: "openmetadata-ui/src/main/resources/ui/**/*.{ts,tsx}" Applies to UI `*.{ts,tsx}`. Styling/tokens are in `frontend-styling.md`; component-library choice in `component-library.md`; strings/i18n in `i18n.md`; Playwright in `frontend-playwright.md`. For the **formatting procedure** invoke the `ui-checkstyle` skill — do not hand-edit formatting. -Compliant reference: `openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedCardNew/ActivityFeedcardNew.component.tsx` -with its interface in a sibling `*.interface.ts`; forms reference -`openmetadata-ui/src/main/resources/ui/docs/formutils.md`. +**Folder structure and file naming for new code: +`openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md`** — read it before creating any new +file. Forms reference `openmetadata-ui/src/main/resources/ui/docs/formutils.md`. ## Component patterns -- **File naming**: components `ComponentName.component.tsx`, interfaces `ComponentName.interface.ts` - (props interfaces live in the `.interface.ts` file). *(Adherence is partial across the tree; follow - it for new files.)* +- **Folder structure**: layers stay top-level (`components/`, `pages/`, `rest/`, `utils/`, `hooks/`); + inside each, group by `domain/feature/` — `components/governance/glossary/GlossaryList/`. Domains: + `discovery`, `governance`, `observability`, `insights`, `platform`; cross-cutting features + (`lineage`, `data-contract`, `entity`, `activity-feed`) sit at the domain level, not inside one. +- **File naming (new code)**: one stem, suffix = role — `GlossaryList.tsx`, `.types.ts`, `.utils.ts`, + `.constants.ts`, `.style.less`, `.test.tsx`, `.mock.ts`. Pure logic goes in `*.utils.ts` (no React, + no JSX) so it is testable without rendering. + *(Legacy uses `.component.tsx`/`.interface.ts` — don't rename; the suffix marks migration status.)* +- **No `index.ts` barrel** inside a component folder — `no-internal-barrel-imports` reports it. - **Functional components only** — no class components. - **State**: `useState` with proper typing; multiple loading states as one object (`useState>({})`). diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5cb1039b99da..c4d45759bd3a 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -568,12 +568,27 @@ make unit_ingestion_dev_env # For Python changes **IMPORTANT: When reviewing UI pull requests, you MUST follow the comprehensive guidelines in [/openmetadata-ui/UI_PR_REVIEW_GUIDELINES.md](../openmetadata-ui/UI_PR_REVIEW_GUIDELINES.md) and [/openmetadata-ui/src/main/resources/ui/playwright/PLAYWRIGHT_DEVELOPER_HANDBOOK.md](../openmetadata-ui/src/main/resources/ui/playwright/PLAYWRIGHT_DEVELOPER_HANDBOOK.md)** +**Folder structure and file naming are specified in +[/openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md](../openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md)** +— consult it for any newly added file under `ui/src/`. + +#### Structure & Naming +- ✅ **REQUIRE**: new files placed as `///` — layers are `components/`, + `pages/`, `rest/`, `utils/`, `hooks/`; domains are `discovery`, `governance`, `observability`, + `insights`, `platform`. Cross-cutting features (`lineage`, `data-contract`, `entity`, + `activity-feed`) sit at the domain level. +- ✅ **REQUIRE**: new files named with one stem + role suffix — `GlossaryList.tsx`, `.types.ts`, + `.utils.ts`, `.constants.ts`, `.style.less`, `.test.tsx`, `.mock.ts` +- ❌ **REJECT**: an `index.ts` barrel inside a component folder (`no-internal-barrel-imports`) +- ⚠️ **DO NOT** ask for existing `.component.tsx` / `.interface.ts` files to be renamed — the suffix + marks migration status + ### Critical UI Standards to Enforce #### Type Safety (Zero Tolerance) - ❌ **REJECT**: Any use of `any` type in TypeScript - ✅ **REQUIRE**: Proper type imports from `generated/` or `@rjsf/utils` -- ✅ **REQUIRE**: Defined interfaces for all component props in `.interface.ts` files +- ✅ **REQUIRE**: Defined types for all component props — `.types.ts` for new files, `.interface.ts` in legacy ones #### Internationalization (Zero Tolerance) - ❌ **REJECT**: Any hardcoded string literals in UI components @@ -599,9 +614,12 @@ make unit_ingestion_dev_env # For Python changes - ✅ **REQUIRE**: Navigation with `useNavigate`, not direct history manipulation #### File Naming (Must Follow) -- ✅ **REQUIRE**: Components named as `ComponentName.component.tsx` -- ✅ **REQUIRE**: Interfaces named as `ComponentName.interface.ts` -- ✅ **REQUIRE**: Custom hooks prefixed with `use` and placed in `src/hooks/` +- ✅ **REQUIRE**: new files use one stem + role suffix — `GlossaryList.tsx`, `GlossaryList.types.ts`, + `GlossaryList.utils.ts`, `GlossaryList.constants.ts`, `GlossaryList.style.less`, + `GlossaryList.test.tsx`, `GlossaryList.mock.ts` +- ⚠️ **DO NOT** ask for existing `.component.tsx` / `.interface.ts` files to be renamed — the suffix + marks migration status +- ✅ **REQUIRE**: Custom hooks prefixed with `use` and placed in `src/hooks//` ### PR Review Checklist diff --git a/CLAUDE.md b/CLAUDE.md index 2b91e4c346ef..d187e12ef32a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -139,6 +139,11 @@ on functionality over education. Do not add unnecessary blank lines between pros for repositories, Factory/Registry for dispatch, Strategy/Adapter/Observer, the ingestion Source→Sink pipeline, …) with the canonical class to copy each from. Extend the established pattern rather than inventing a parallel one. +- `openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md` — **the UI folder structure and file + naming spec.** Read before creating any new file under `openmetadata-ui/.../ui/src/`. Layers stay + top-level (`components/`, `pages/`, `rest/`, `utils/`, `hooks/`) and are grouped inside by + `domain/feature/`; new files use one stem with a role suffix (`GlossaryList.tsx`, `.types.ts`, + `.utils.ts`, `.test.tsx`). Legacy `.component.tsx`/`.interface.ts` files stay as they are. ### Skills (invoke by name; procedures, not rules) diff --git a/docs/index.md b/docs/index.md index 445be0f526f1..ea199ddd0477 100644 --- a/docs/index.md +++ b/docs/index.md @@ -72,6 +72,7 @@ verdict cites an artifact that was checked to still exist): | Doc | Purpose | Read when | Modified | Freshness | |---|---|---|---|---| +| `openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md` | **UI folder structure + file naming spec.** Layers (`components/`, `pages/`, `rest/`, `utils/`, `hooks/`) stay top-level, grouped inside by `domain/feature/`; five domains (`discovery`, `governance`, `observability`, `insights`, `platform`) with cross-cutting features at the domain level. New files use one stem + role suffix (`GlossaryList.tsx`, `.types.ts`, `.utils.ts`, `.test.tsx`); legacy uses `.component.tsx`/`.interface.ts`. Also covers imports, barrels, routing and state locations | **Before creating any new file under `ui/src/`**, or when deciding where code belongs | 2026-08-21 | CURRENT | | `openmetadata-ui/src/main/resources/ui/specs/` | **Machine-readable design system** (41 files). `README.md` declares two stacks — **go-forward = UntitledUI + Tailwind (`tw:`)**, **legacy (deprecated) = Ant Design + Less** — plus `foundations/*` (color, spacing, typography, radius, elevation, motion), `tokens/*` (Tailwind-utility + master token reference), `untitled/*` (go-forward component specs), and legacy `components/*` | **Before writing or modifying any UI code** — start at `specs/README.md`, then the `foundations`/`tokens` and the `untitled/.md` (or legacy `components/*`) spec for what you touch | 2026-07-27 | CURRENT ⁶ | | `openmetadata-ui/src/main/resources/ui/docs/colors.md` | Semantic color-token system (`tw:bg-primary`, `tw:text-fg-*`, `tw:border-*`) with light/dark values + the mandatory `ring`→`border` migration (§2.3.1) | Before writing/reviewing any Tailwind color class or dark-mode styling, or when tempted to use `ring-*` or a raw hex | 2026-07-23 | CURRENT | | `openmetadata-ui/src/main/resources/ui/docs/formutils.md` | The modern react-hook-form + react-aria form stack (`FieldProp`, `getField`/`FormFields`/`HookForm`) vs the legacy antd `@utils/formUtils` API | Before building/modifying any UI form — which API to use, and wiring to `useFormDrawerWithHook` + a pure transform | 2026-07-15 | CURRENT | diff --git a/openmetadata-ui/UI_PR_REVIEW_GUIDELINES.md b/openmetadata-ui/UI_PR_REVIEW_GUIDELINES.md index cf10e9c391f0..f4802870fd6a 100644 --- a/openmetadata-ui/UI_PR_REVIEW_GUIDELINES.md +++ b/openmetadata-ui/UI_PR_REVIEW_GUIDELINES.md @@ -6,17 +6,28 @@ This document outlines the standards and best practices that must be followed wh - [ ] **No `any` types**: Code must never use `any` type - use proper types or `unknown` with type guards - [ ] **Proper type imports**: All types imported from existing definitions (e.g., `RJSFSchema` from `@rjsf/utils`, types from `generated/`) -- [ ] **Interface definitions**: All component props have defined interfaces in `.interface.ts` files +- [ ] **Prop types defined**: All component props have declared types — `.types.ts` for new files, `.interface.ts` in legacy ones - [ ] **Type assertions**: Avoid type assertions unless absolutely necessary and well-justified - [ ] **Discriminated unions**: Use discriminated unions for action types and state variants ## React Component Standards ### File Structure and Naming -- [ ] **Component files**: Named as `ComponentName.component.tsx` -- [ ] **Interface files**: Named as `ComponentName.interface.ts` + +Full spec: [`src/main/resources/ui/DEVELOPER_HANDBOOK.md`](src/main/resources/ui/DEVELOPER_HANDBOOK.md). + +- [ ] **Placement**: new files live at `///` — layers are `components/`, + `pages/`, `rest/`, `utils/`, `hooks/`; domains are `discovery`, `governance`, `observability`, + `insights`, `platform`. Cross-cutting features (`lineage`, `data-contract`, `entity`, + `activity-feed`) sit at the domain level +- [ ] **New file names**: one stem + role suffix — `GlossaryList.tsx`, `.types.ts`, `.utils.ts`, + `.constants.ts`, `.style.less`, `.test.tsx`, `.mock.ts` +- [ ] **Legacy files untouched**: do not ask for existing `.component.tsx` / `.interface.ts` files to + be renamed — the suffix marks migration status +- [ ] **No `index.ts` barrel** inside a component folder (`no-internal-barrel-imports`) +- [ ] **Business logic in `*.utils.ts`**: pure functions, no React or JSX, unit-testable without rendering - [ ] **Functional components only**: No class components -- [ ] **Custom hooks**: Prefixed with `use`, placed in `src/hooks/`, return typed objects +- [ ] **Custom hooks**: Prefixed with `use`, placed in `src/hooks//`, return typed objects ### Component Implementation - [ ] **State management**: Uses `useState` with proper typing diff --git a/skills/agents/frontend-reviewer.md b/skills/agents/frontend-reviewer.md index 876fddbf1d8e..979c45b00045 100644 --- a/skills/agents/frontend-reviewer.md +++ b/skills/agents/frontend-reviewer.md @@ -223,9 +223,24 @@ Beyond CI checks, review for these patterns: - Avoid type assertions (`as Type`) unless absolutely necessary - Use discriminated unions for action types and state variants -### 10. Component Patterns +### 10. Structure, Naming & Component Patterns + +Full spec: `openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md` — read it before reviewing +any newly added file. + +- **Placement**: layers stay top-level (`components/`, `pages/`, `rest/`, `utils/`, `hooks/`), grouped + inside by `domain/feature/` — `components/governance/glossary/GlossaryList/`. Domains are + `discovery`, `governance`, `observability`, `insights`, `platform`; cross-cutting features + (`lineage`, `data-contract`, `entity`, `activity-feed`) sit at the domain level. Flag a new file + dropped straight into `components/` or `utils/` without a domain folder. +- **File naming (new files)**: one stem, suffix = role — `GlossaryList.tsx`, `.types.ts`, `.utils.ts`, + `.constants.ts`, `.style.less`, `.test.tsx`, `.mock.ts`. Do NOT flag legacy + `.component.tsx`/`.interface.ts` files or ask for them to be renamed — the suffix marks migration + status, and case-only/suffix renames churn history. +- **Business logic in `*.utils.ts`** as pure functions (no React, no JSX) so it is unit-testable + without rendering. +- **No `index.ts` barrel** inside a component folder — `no-internal-barrel-imports` reports it. - Functional components only, no class components -- File naming: `ComponentName.component.tsx`, interfaces in `ComponentName.interface.ts` - `useCallback` for event handlers passed to children - `useMemo` for expensive computations - `useEffect` with correct dependency arrays — no missing deps, no over-fetching diff --git a/skills/openmetadata-workflow/SKILL.md b/skills/openmetadata-workflow/SKILL.md index 29caa3eef895..6bfe805a6dd4 100644 --- a/skills/openmetadata-workflow/SKILL.md +++ b/skills/openmetadata-workflow/SKILL.md @@ -19,7 +19,7 @@ This skill is loaded automatically at session start. It ensures you follow the r | Bug fix | `/systematic-debugging` then `/tdd` (write regression test) then `/verification` | | New API endpoint | `/planning` then `/tdd` then `/test-enforcement` (must include integration test) | | New connector | `/connector-standards` then `/connector-building` then `/test-enforcement` | -| UI component | `/ui-core-components` + `/react-best-practices` + `/composition-patterns` **before writing**, then `/tdd`, then `/web-design-guidelines` **after**, then `/test-enforcement` (must include Jest + Playwright if user-facing) | +| UI component | **Read `openmetadata-ui/src/main/resources/ui/DEVELOPER_HANDBOOK.md` first** (folder structure + file naming for any new file), then `/ui-core-components` + `/react-best-practices` + `/composition-patterns` **before writing**, then `/tdd`, then `/web-design-guidelines` **after**, then `/test-enforcement` (must include Jest + Playwright if user-facing) | | UI a11y / UX / design audit | `/web-design-guidelines` | | React perf work (re-renders, waterfalls, bundle) | `/react-best-practices` | | Code review / PR review | `/code-review` then `/test-enforcement` |