diff --git a/.cursor/rules/ui5-knowledge-base.mdc b/.cursor/rules/ui5-knowledge-base.mdc new file mode 100644 index 0000000000000..fc82a344c04b5 --- /dev/null +++ b/.cursor/rules/ui5-knowledge-base.mdc @@ -0,0 +1,29 @@ +--- +description: UI5 Web Components conventions and guardrails. Consult before editing component code. +globs: + - packages/**/*.ts + - packages/**/*.tsx + - packages/**/*.css + - packages/**/cypress/specs/**/*.cy.tsx +alwaysApply: false +--- + +# UI5 Web Components — AI Knowledge Base + +Detailed conventions, guardrails, and good/bad examples live in the AI Knowledge Base at +`ai-knowledge-base/`. It is the authority on how to write code here. + +Before editing this file, read `ai-knowledge-base/INDEX.md`, find your task in its routing table, +and open the **one** reference file it names — do not read every file. Only the rules that apply to +your task cost context. + +Non-negotiables (full rationale in `ai-knowledge-base/INDEX.md`): + +- Never run the full test suite. Run one spec: `yarn test:cypress:single cypress/specs/.cy.tsx`. +- Enums are type-only: `import type`, declare as `` `${X}` ``, compare against string literals. +- Select by attribute, never tag name: `[ui5-button]` in CSS, `querySelector`, and specs. +- No `instanceof` against a UI5 class. Use `createInstanceChecker` with a duck-typing marker. +- `@query` refs are for method calls like `.focus()`; set child state through the template. +- Make the smallest change that solves the problem. No opportunistic cleanup. + +When done, check your work: `yarn ts`, `yarn lint`, and `yarn lint:scope`. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 4d7f455b12637..5758cab220ea5 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -2,6 +2,10 @@ When reviewing pull requests to UI5 Web Components project, follow these guidelines: +## AI Knowledge Base + + Detailed conventions, guardrails, and good/bad examples live in the **AI Knowledge Base** at [`ai-knowledge-base/`](../ai-knowledge-base/). When a change touches a `.ts`, `.tsx`, or `.css` file under `packages/`, a Cypress spec, or a `ui5-*` component/property/slot/event/design token/ARIA attribute, consult [`ai-knowledge-base/INDEX.md`](../ai-knowledge-base/INDEX.md) and open the one reference file its routing table names for the area under review. Review the diff against those rules. + ## Commit Message and PR Title Validation Check that commit messages and PR titles follow the [Conventional Commits](https://conventionalcommits.org) specification as outlined in [Conventions and guidelines](../docs/5-contributing/02-conventions-and-guidelines.md): diff --git a/AGENTS.md b/AGENTS.md index 12c85d30c81e0..a411bd51289fd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,6 +2,22 @@ This file provides guidance for AI coding assistants (Claude, Copilot, Cursor, etc.) when working with this repository. +## Knowledge Base — read this first + +Detailed conventions, guardrails, and good/bad examples live in the **AI Knowledge Base** at +`ai-knowledge-base/`. It is the authority on how to write code here. Load it **on demand**, +not up front: it has a routing table that points you to the one reference file your task needs. + +**Read `ai-knowledge-base/INDEX.md` before you edit code whenever any of these is true:** + +- You add, change, review, or test a `.ts`, `.tsx`, or `.css` file under `packages/`. +- You write or debug a Cypress spec. +- The task names a `ui5-*` component, a property, slot, event, design token, or ARIA attribute. + +Then open **only** the reference file its routing table names for your task — do not read every file. +If your work is purely conversational, a docs-only edit, or a question that touches no component code, +you do not need it. + ## Project Overview UI5 Web Components is an enterprise-grade, framework-agnostic web components library implementing SAP Fiori design. It's a Yarn-based monorepo using Lerna and Yarn Workspaces. @@ -28,6 +44,7 @@ packages/ ### Root Level (Always run from repo root) ```bash yarn start # Start dev server with watch mode (recommended for development) +yarn ts # TypeScript type-check (use this to verify compilation) yarn build # Full production build yarn lint # Lint all packages ``` @@ -71,19 +88,9 @@ Remove `.only` before committing and run the full test file for final verificati ## Component Development -For detailed component architecture, development rules, and testing patterns, see [`packages/base/AGENTS.md`](./packages/base/AGENTS.md). - -### Quick Reference - -| Rule | Bad | Good | -|------|-----|------| -| Enum imports | `import Enum from "..."` | `import type Enum from "..."` | -| Enum types | `prop: Enum` | `prop: \`${Enum}\`` | -| Enum values | `Enum.Value` | `"Value"` | -| DOM queries | `querySelector("ui5-tag")` | `querySelector("[ui5-tag]")` | -| CSS selectors | `ui5-tag { }` | `[ui5-tag] { }` | -| Type checks | `instanceof Component` | `isInstanceOfComponent(el)` | -| DOM mutation | `this._ref.value = x` | Template: `` | +For component architecture, coding rules, testing patterns, and the full bad/good examples, read the +AI Knowledge Base and follow its routing table (see "Knowledge Base" above). Package-specific +base-class detail lives in [`packages/base/AGENTS.md`](./packages/base/AGENTS.md). ## Commit Message Format diff --git a/ai-knowledge-base/INDEX.md b/ai-knowledge-base/INDEX.md new file mode 100644 index 0000000000000..553bf7ead9848 --- /dev/null +++ b/ai-knowledge-base/INDEX.md @@ -0,0 +1,114 @@ +--- +name: ui5-knowledge-base +description: Conventions and patterns for writing UI5 Web Components code in this repository. Use when adding, changing, reviewing, or testing any .ts, .tsx, or .css file under packages/, when writing a Cypress spec, or when a task names a ui5-* component, property, slot, event, design token, or ARIA attribute. +--- + +# UI5 Web Components — AI Knowledge Base + +Guidance for any AI coding tool (Claude, Cursor, Copilot, or other). This is a reference bundle, not a +tool-specific plugin: read the **one** file the routing table names for your task, and nothing more. + +Find your task in the routing table and open the one file it names. + +## Non-negotiables + +1. Never run the full test suite. Run one spec: `yarn test:cypress:single cypress/specs/.cy.tsx`. +2. Enums are type-only: `import type X`, declare the property as `` `${X}` ``, compare against string literals. +3. Select by attribute, never by tag name: `[ui5-button]` in CSS, in `querySelector`, and in specs. +4. No `instanceof` against a UI5 class. Use `createInstanceChecker` with a duck-typing marker. +5. `@query` refs exist to call methods like `.focus()`. Set child state through the template instead. +6. Make the smallest change that solves the problem. No opportunistic cleanup, no drive-by refactors. + +## Routing table + +| Your task | Open | +|-----------|------| +| Changing component code, and you are not sure what applies | `references/core-rules.md` | +| Reviewing a diff, or checking your own work before declaring done | `references/core-rules.md` (Reviewing a diff) | +| Adding or changing a property, slot, event, or public method | `references/api-design.md` | +| Understanding how a component is assembled, or which file does what | `references/component-anatomy.md` | +| Creating a brand-new component | `references/new-component.md` | +| Writing, fixing, or debugging a Cypress test | `references/testing.md` | +| CSS, theming, design tokens, RTL, `::part`, `:host` | `references/theming-and-css.md` | +| Keyboard navigation, ARIA, focus, screen reader output | `references/accessibility.md` | +| Any text a user can see | `references/i18n.md` | +| Unnecessary re-renders, slow interaction, invalidation | `references/performance.md` | +| A test that passes locally and fails in CI | `references/testing.md`, then `references/performance.md` | +| Build fails with a documentation error | `references/api-design.md` (CEM validation) | + +## Commands + +### From the repository root + +| Command | What it does | +|---------|--------------| +| `yarn ts` | `tsc -b` across all packages. This is the type-check | +| `yarn generate` | Compiles `.css` and `.properties` into `src/generated/**`. Required on a clean checkout before `yarn ts` | +| `yarn lint` | ESLint across all packages | +| `yarn lint:scope` | Fails on tag-name selectors in `src/**/*.css` and `querySelector("ui5-...")` in `src/**/*.ts` | +| `yarn start` | Runs `generate` once, builds the Cypress helper packages, then starts the dev server with per-package watchers | + +### From a package folder + +```bash +cd packages/main +yarn test:cypress:single cypress/specs/Button.cy.tsx # one spec +yarn test:cypress:open # interactive runner +yarn lint +yarn lint:scope +``` + +## Debugging + +Start at the top and stop as soon as you have an answer: read the neighbouring component, then +`yarn ts`, then one spec with `.only`, then `yarn start` and a test page. + +From the root dev server the page is `packages/main/test/pages/.html`; from a package-level +`yarn start` it is `test/pages/.html`. The port is chosen at startup — read it from the console. + +In the DevTools console: + +```js +const el = document.querySelector("[ui5-button]"); +el.getDomRef() // the component's root shadow element +el.isUI5Element // confirm it upgraded +``` + +When a spec fails, in order: read the assertion rather than the stack; rerun that case with `.only`; +confirm the component upgraded, since one missing from `bundle.esm.ts` mounts with an empty shadow +root; check for async focus (`references/testing.md`); then throttle the CPU. If it still +fails under throttling it is a product bug, not a test bug. + +One thing accounts for most interaction bugs here: handlers that read `event.target` break when the +event crosses a shadow boundary — use `composedPath()` instead. + +| Build symptom | Cause | +|---------------|-------| +| `Missing default value for 'x'` | A public property without `@default` | +| `Type 'X' is used to describe a public API but is not exported` | Add it to `export type { ... }` | +| `Boolean properties must be initialzed to false` | Invert the property name (the typo is in the real message) | +| Scope lint failure | A tag-name selector in CSS or TS | +| Cannot find `./generated/...` | Run `yarn generate` | +| Unresolved i18n constant | The key is in the properties file but not in `i18n-defaults.ts` | + +## Checking your work + +Before declaring a task done: `yarn ts` for types, `yarn lint` for style, and `yarn lint:scope` to +catch tag-name selectors and `querySelector("ui5-...")`. Then re-read `references/core-rules.md` +(Reviewing a diff) and confirm your change respects each non-negotiable above. + +## When sources disagree + +1. `references/` in this skill governs how you write new code. The rules are absolute even where + existing code disagrees; each one records how much legacy code violates it so you can recognise + the violation rather than copy it. +2. Neighbouring source code governs structure and idiom — file layout, naming, how a template is put + together. Copy its shape, not its rule violations. `Button.ts` is the reference component and it + still uses runtime enum imports. + +## Keeping this accurate + +Add a learning here only when it is a repeatable rule that applies beyond one component. One-off +incidents don't belong here. + +Cite files without line numbers and state rules without counts — both go stale on unrelated commits. diff --git a/ai-knowledge-base/SKILL.md b/ai-knowledge-base/SKILL.md new file mode 100644 index 0000000000000..0052b4a7effc6 --- /dev/null +++ b/ai-knowledge-base/SKILL.md @@ -0,0 +1,12 @@ +--- +name: ui5-knowledge-base +description: Conventions and patterns for writing UI5 Web Components code in this repository. Use when adding, changing, reviewing, or testing any .ts, .tsx, or .css file under packages/, when writing a Cypress spec, or when a task names a ui5-* component, property, slot, event, design token, or ARIA attribute. +--- + +# UI5 Web Components — AI Knowledge Base (Claude skill entry) + +This is the Claude-discoverable entry point. The canonical, tool-neutral index — routing table, +non-negotiables, commands, and debugging — lives in [`INDEX.md`](./INDEX.md) next to this file. + +Read `INDEX.md`, find your task in its routing table, and open the **one** reference file it names. +Do not read every file. If the task touches no component code, you do not need this bundle. diff --git a/ai-knowledge-base/references/accessibility.md b/ai-knowledge-base/references/accessibility.md new file mode 100644 index 0000000000000..db19565d0c3af --- /dev/null +++ b/ai-knowledge-base/references/accessibility.md @@ -0,0 +1,187 @@ +# Accessibility + +## The accessibility API + +Interactive components expose this surface. Match the names exactly. + +| Property | Type | Purpose | +|----------|------|---------| +| `accessibleName` | string | Direct label text | +| `accessibleNameRef` | string | Space-separated IDs of elements whose text forms the label | +| `accessibleDescription` | string | Longer description | +| `accessibleDescriptionRef` | string | IDs of elements forming the description | +| `accessibleRole` | `` `${SomeRole}` `` | Overrides the default ARIA role | +| `accessibilityAttributes` | `AccessibilityAttributes` | `expanded`, `hasPopup`, `controls`, `role`, `ariaLabel`, ... | + +Components opt into a subset — not every one carries all six. `accessibleDescriptionRef` in particular is absent from many (Button, Link, Icon). + +`AccessibilityAttributes` is a real type, not a free-form object. Narrow it per component with `Pick`: `type ButtonAccessibilityAttributes = Pick`. Not every `*AccessibilityAttributes` type is a `Pick`, though — `ListAccessibilityAttributes` is a bespoke nested object (`{ growingButton?: { name?, description? } }`), unrelated to the base type. Check the actual declaration before assuming. + +### Resolving the texts + +Every helper below comes from `@ui5/webcomponents-base/dist/util/AccessibilityTextsHelper.js`. `getEffectiveAriaLabelText(el)` returns the `accessibleNameRef` texts, else `accessibleName`. It does **not** resolve native `