diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000000..f6f035bce00 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,289 @@ +# CLAUDE.md + +This file provides guidance to Claude Code when working with code in this repository. + +## Overview + +UI5 Web Components for React is a React wrapper library for SAP's UI5 Web Components. It provides Fiori-compliant React components by wrapping native UI5 Web Components with React-specific implementations. The project is a monorepo managed with Lerna and Yarn workspaces. + +## Critical: Working with Web Components + +### Event Handling (MUST KNOW) + +Web components emit **CustomEvents** with data in the `detail` property: + +```tsx +// ❌ WRONG - won't work with web components +const handleChange = (e) => { + console.log(e.selectedOption); // undefined! + console.log(e.value); // undefined! +}; + +// ✅ CORRECT - access via e.detail +const handleChange = (e) => { + console.log(e.detail.selectedOption); + console.log(e.detail.value); +}; +``` + +For enriching events with additional data (internal use): + +```tsx +import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/internal/utils'; + +onLoadMore(enrichEventWithDetails(e, { rowCount, totalRowCount })); +``` + +### Ref Handling (MUST KNOW) + +**Never use `useRef()` directly for forwarded refs** - use `useSyncRef()`: + +```tsx +import { useSyncRef } from '@ui5/webcomponents-react-base/internal/hooks'; + +const MyComponent = forwardRef((props, ref) => { + // ✅ CORRECT - syncs forwarded ref with internal ref + const [componentRef, internalRef] = useSyncRef(ref); + + return
; +}); +``` + +### Slot Props (MUST KNOW) + +When passing custom React components to slot props, they **must forward the `slot` prop**: + +```tsx +// ❌ WRONG - slot won't render correctly +const CustomHeader = ({ children }) =>
{children}
; + +// ✅ CORRECT - forwards slot prop to root element +const CustomHeader = ({ children, slot }) =>
{children}
; + +// Usage +Title} />; +``` + +**Note:** React Portals are NOT supported in slot props (will log warning). + +### CSS and Shadow DOM Styling + +Web component internals are in Shadow DOM. Use `::part()` pseudo-element: + +```css +/* ❌ WRONG - can't pierce shadow DOM */ +.myButton button { + background: red; +} + +/* ✅ CORRECT - use ::part() for shadow DOM */ +.myCheckbox::part(root) { + display: flex; + width: unset; +} + +.actionSheet::part(content) { + padding: 0.1875rem 0.375rem; +} +``` + +Use SAP CSS variables directly: + +```css +.myComponent { + background: var(--sapBackgroundColor); + color: var(--sapTextColor); + border-radius: var(--sapElement_BorderCornerRadius); + font-family: var(--sapFontFamily); + + /* Semantic colors */ + --success: var(--sapPositiveColor); + --error: var(--sapNegativeColor); + --warning: var(--sapCriticalColor); + --info: var(--sapInformativeColor); +} +``` + +Internal component variables use `--_ui5wcr*` prefix: + +```tsx +const style = { + '--_ui5wcr-AnalyticalTableRowHeight': `${rowHeight}px`, +} as CSSProperties; +``` + +### SSR Compatibility + +Components must work in both SPA and SSR environments. Wrappers use `suppressHydrationWarning` because web component internal state doesn't serialize consistently. + +## Core Architecture + +### Base Package Imports + +The `@ui5/webcomponents-react-base` package uses subpath exports. Always use the specific subpath, not the root import: + +```tsx +// ❌ WRONG - don't use root import +import { useSyncRef } from '@ui5/webcomponents-react-base'; + +// ✅ CORRECT - use subpath exports +import { useI18nBundle, useViewportRange } from '@ui5/webcomponents-react-base/hooks'; +import { + useSyncRef, + useStylesheet, + useIsRTL, + useIsomorphicLayoutEffect, + useCurrentTheme, +} from '@ui5/webcomponents-react-base/internal/hooks'; +import { debounce, throttle, enrichEventWithDetails } from '@ui5/webcomponents-react-base/internal/utils'; +import { Device } from '@ui5/webcomponents-react-base/Device'; +``` + +**Available subpath exports:** + +- `./hooks` - Public hooks: `useI18nBundle`, `useViewportRange` +- `./internal/hooks` - Internal hooks: `useSyncRef`, `useStylesheet`, `useIsRTL`, `useIsomorphicLayoutEffect`, `useCurrentTheme` +- `./internal/utils` - Utilities: `debounce`, `throttle`, `enrichEventWithDetails` +- `./Device` - Device detection utilities +- `./types` - TypeScript types + +### Package Structure + +| Package | npm Name | Description | +| ------------------ | ------------------------------------- | --------------------------------------------- | +| `main` | `@ui5/webcomponents-react` | React wrappers + custom components | +| `base` | `@ui5/webcomponents-react-base` | Core utilities, hooks, wrapper infrastructure | +| `charts` | `@ui5/webcomponents-react-charts` | Chart components (recharts-based) | +| `compat` | `@ui5/webcomponents-react-compat` | Legacy components | +| `cli` | `@ui5/webcomponents-react-cli` | Wrapper generation, codemods | +| `cypress-commands` | `@ui5/webcomponents-cypress-commands` | Testing utilities | +| `ai` | `@ui5/webcomponents-ai-react` | AI component wrappers | + +### Main Package Structure + +- `src/webComponents/`: **Auto-generated** wrappers (Button, Input, Dialog, etc.) +- `src/components/`: **Custom** React components (AnalyticalTable, ObjectPage, FilterBar) +- `src/enums/`: TypeScript enums +- `src/i18n/`: Internationalization + +### Wrapper Architecture + +The `withWebComponent` HOC (`packages/base/src/internal/wrapper/withWebComponent.tsx`): + +```typescript +withWebComponent( + tagName: string, // 'ui5-button' + regularProperties: string[], // String/number attributes + booleanProperties: string[], // Boolean attributes (React 18 vs 19 handling) + slotProperties: string[], // Named slots + eventProperties: string[] // Custom events +) +``` + +**React version handling:** + +- **React 19+**: Native JSX event attributes, direct boolean passing +- **React 18**: Manual `addEventListener`, conditional boolean attributes + +## Working in This Repo + +Use **yarn** (not pnpm). For tools, use project binaries via yarn (e.g., `yarn cypress`, `yarn eslint`, `yarn tsc`). + +**Run prettier on edited files after changes.** + +**Testing single files:** Try `yarn test` first. If that doesn't work, use `yarn cypress run --spec `. If still stuck, ask. + +```bash +yarn start # Storybook (localhost:6006) +yarn test # Cypress component tests +yarn lint # ESLint +yarn prettier:all # Format all files +``` + +## Testing Patterns + +### Data Attributes + +Components use `data-*` attributes for functional purposes (not just testing). Use existing attributes for selectors: + +```tsx +cy.get('[data-component-name="AnalyticalTableContainer"]'); +cy.get('[data-row-index="0"]'); +``` + +**Do not create data attributes solely for testing.** + +### Cypress Commands + +`packages/cypress-commands` provides commands for interacting with web components in tests (typing into inputs, selecting options, etc.). + +## TypeScript Patterns + +### Component Interface Pattern + +```typescript +// Three-tier pattern used consistently: +interface ButtonAttributes { + /* web component props */ +} +interface ButtonDomRef extends Required, Ui5DomRef { + // DOM methods like focus(), click() +} +interface ButtonPropTypes extends ButtonAttributes, Omit { + // React additions: slots, children +} +``` + +### Slot Type + +```typescript +badge?: UI5WCSlotsNode; // ReactNode | ReactNode[] +``` + +## Internationalization + +```tsx +import { useI18nBundle } from '@ui5/webcomponents-react-base/hooks'; + +const i18nBundle = useI18nBundle('@ui5/webcomponents-react'); +const text = i18nBundle.getText(CLEAR); + +// Also available for web components bundle +const wcBundle = useI18nBundle('@ui5/webcomponents'); +``` + +Import assets for translations: + +```typescript +import '@ui5/webcomponents-react/dist/Assets.js'; +``` + +Test with URL param: `?sap-ui-language=de` + +## Breaking Changes + +If a change could be considered breaking, inform the user. + +## Accessibility + +See [UI5 Web Components Accessibility](https://ui5.github.io/webcomponents/docs/advanced/accessibility/). + +**Web component wrappers:** Accessibility handled by UI5 Web Components. Wrappers pass through `accessibleName`, `accessibleNameRef`, ARIA props. + +**Custom components** (AnalyticalTable, ObjectPage, FilterBar, etc.) combine web components with standard HTML elements (`div`, `span`, etc.). These require manual accessibility implementation: + +- ARIA roles/attributes on standard HTML elements +- Keyboard navigation +- Screen reader announcements +- Focus management + +## Abstract Components + +Components marked with `@abstract` in JSDoc (e.g., `SuggestionItem`, `Tab`, `WizardStep`, `ToolbarButton`) are placeholders that pass props to their parent's shadow root. They don't render their own DOM. + +**Implications:** + +- **Not stylable** - `style`, `className` have no visual effect +- **Native HTML attributes** - `title`, `data-*`, etc. apply to the placeholder, not the rendered element +- **Native events** - registered on the placeholder element, not the actual rendered component +- **`getDomRef()`** - returns the parent's DOM element representing this item, not the placeholder itself diff --git a/packages/ai/CLAUDE.md b/packages/ai/CLAUDE.md new file mode 100644 index 00000000000..a2992e785f0 --- /dev/null +++ b/packages/ai/CLAUDE.md @@ -0,0 +1,54 @@ +# @ui5/webcomponents-ai-react + +React wrappers for `@ui5/webcomponents-ai` - AI-focused UI5 Web Components. + +> **Experimental:** This package is under active development. APIs may change. +> +> **Horizon themes only** - not compatible with other SAP themes. + +## Documentation + +For component APIs, props, and usage examples, see the official UI5 Web Components AI documentation: + +**https://sap.github.io/ui5-webcomponents/components/ai/** + +## Installation + +```bash +npm install @ui5/webcomponents-ai-react @ui5/webcomponents-ai +``` + +## Available Components + +| Component | Description | +| ------------- | ---------------------------------- | +| `Button` | Multi-state AI action button | +| `ButtonState` | State definition for Button | +| `PromptInput` | Input for AI prompts | +| `Input` | Input with AI Writing Assistant | +| `TextArea` | TextArea with AI Writing Assistant | + +## Import Pattern + +```tsx +import { Button } from '@ui5/webcomponents-ai-react/Button'; +import { ButtonState } from '@ui5/webcomponents-ai-react/ButtonState'; +import { PromptInput } from '@ui5/webcomponents-ai-react/PromptInput'; +import { Input } from '@ui5/webcomponents-ai-react/Input'; +import { TextArea } from '@ui5/webcomponents-ai-react/TextArea'; +``` + +## Event Handling + +Same pattern as other UI5 Web Components - event data is in `e.detail`: + +```tsx + { + const goingBack = e.detail.backwards; + }} + onItemClick={(e) => { + const item = e.detail.item; + }} +/> +``` diff --git a/packages/ai/package.json b/packages/ai/package.json index 2d1c8760df0..da134d1ef45 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -62,6 +62,6 @@ "files": [ "dist", "CHANGELOG.md", - "NOTICE.txt" + "CLAUDE.md" ] } diff --git a/packages/base/CLAUDE.md b/packages/base/CLAUDE.md new file mode 100644 index 00000000000..a762a59fba7 --- /dev/null +++ b/packages/base/CLAUDE.md @@ -0,0 +1,124 @@ +# @ui5/webcomponents-react-base + +Core utilities, hooks, and infrastructure for UI5 Web Components React wrappers. This package is primarily used internally by `@ui5/webcomponents-react` but exports useful hooks and utilities for consumers. + +## Import Pattern + +Subpath imports are recommended for clarity: + +```tsx +// ✅ Recommended - subpath imports +import { useI18nBundle, useViewportRange } from '@ui5/webcomponents-react-base/hooks'; +import * as Device from '@ui5/webcomponents-react-base/Device'; +import { ThemingParameters } from '@ui5/webcomponents-react-base/ThemingParameters'; + +// Also works - root import +import { useI18nBundle, useViewportRange } from '@ui5/webcomponents-react-base'; +``` + +## Public API + +### Hooks (`@ui5/webcomponents-react-base/hooks`) + +#### `useI18nBundle(bundleName: string)` + +Access internationalization bundles for translated text. + +```tsx +import { useI18nBundle } from '@ui5/webcomponents-react-base/hooks'; + +function MyComponent() { + const i18n = useI18nBundle('@ui5/webcomponents-react'); + const label = i18n.getText('BUTTON_LABEL'); + // With placeholders: i18n.getText('ITEMS_SELECTED', 5, 20) -> "5 of 20 items selected" + + return {label}; +} +``` + +#### `useViewportRange()` + +Get current viewport size range for responsive layouts. + +```tsx +import { useViewportRange } from '@ui5/webcomponents-react-base/hooks'; + +function ResponsiveComponent() { + const range = useViewportRange(); + // Returns: 'Phone' | 'Tablet' | 'Desktop' | 'LargeDesktop' + + return range === 'Phone' ? : ; +} +``` + +### Device Utilities (`@ui5/webcomponents-react-base/Device`) + +Re-exports from `@ui5/webcomponents-base/dist/Device.js` plus additional resize/orientation handlers. + +```tsx +import * as Device from '@ui5/webcomponents-react-base/Device'; + +// From @ui5/webcomponents-base - check capabilities +Device.supportsTouch(); + +// Resize handling +Device.attachResizeHandler((windowSize) => { + console.log(windowSize.width, windowSize.height); +}); +Device.detachResizeHandler(handler); + +// Orientation handling +Device.getOrientation(); // { landscape: boolean, portrait: boolean } +Device.attachOrientationChangeHandler((orientation) => { + console.log(orientation.landscape, orientation.portrait); +}); +Device.detachOrientationChangeHandler(handler); + +// Media range queries +Device.getCurrentRange(); // Current viewport range +Device.attachMediaHandler(handler); +Device.detachMediaHandler(handler); +``` + +### ThemingParameters (`@ui5/webcomponents-react-base/ThemingParameters`) + +TypeScript-friendly mappings for SAP theming CSS variables. Useful for **inline styles** or **CSS-in-JS** solutions. + +```tsx +import { ThemingParameters } from '@ui5/webcomponents-react-base/ThemingParameters'; + +// CSS-in-JS / inline styles +const style = { + backgroundColor: ThemingParameters.sapBackgroundColor, // 'var(--sapBackgroundColor)' + color: ThemingParameters.sapTextColor, // 'var(--sapTextColor)' + borderRadius: ThemingParameters.sapElement_BorderCornerRadius, + fontFamily: ThemingParameters.sapFontFamily, +}; + +// Semantic colors +ThemingParameters.sapPositiveColor; // success/positive +ThemingParameters.sapNegativeColor; // error/negative +ThemingParameters.sapCriticalColor; // warning +ThemingParameters.sapInformativeColor; // info +``` + +**For standard CSS files**, use the CSS variables directly (e.g., `var(--sapBackgroundColor)`). + +See the full list of available CSS variables: https://experience.sap.com/fiori-design-web/theming-base-content/ + +### Types (`@ui5/webcomponents-react-base/types`) + +```tsx +import type { UI5WCSlotsNode } from '@ui5/webcomponents-react-base/types'; + +// UI5WCSlotsNode - Type for slot content (ReactNode | ReactNode[]) +``` + +## Internal APIs + +The following exports exist for sharing between packages of this library. **They are not intended for consumer use** and may change without notice: + +- `./internal/hooks` - Internal hooks (`useSyncRef`, `useStylesheet`, `useIsRTL`, `useCurrentTheme`, `useIsomorphicLayoutEffect`) +- `./internal/utils` - Internal utilities (`debounce`, `throttle`, `enrichEventWithDetails`) +- `./internal/types` - Internal types (`CommonProps`, `Ui5CustomEvent`, `Ui5DomRef`) +- `./withWebComponent` - HOC for wrapping web components diff --git a/packages/base/package.json b/packages/base/package.json index 248ac96bbfe..40016140d20 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -105,8 +105,7 @@ "files": [ "dist", "CHANGELOG.md", - "LICENSE", - "NOTICE.txt", + "CLAUDE.md", "README.md" ] } diff --git a/packages/charts/CLAUDE.md b/packages/charts/CLAUDE.md new file mode 100644 index 00000000000..96142ff9fb3 --- /dev/null +++ b/packages/charts/CLAUDE.md @@ -0,0 +1,241 @@ +# @ui5/webcomponents-react-charts + +Chart components for data visualization built on [Recharts](https://recharts.org/). Provides SAP Fiori-styled chart components with automatic theming. + +> **Important:** Charts are custom-built **without** defined design specifications! They use the Fiori color palette, but functionality and especially **accessibility may not meet standard app requirements**. + +## Installation + +```bash +npm install @ui5/webcomponents-react-charts +``` + +## Setup + +For SSR/Next.js, import the styles and set `staticCssInjected` on ThemeProvider: + +```tsx +import '@ui5/webcomponents-react/styles.css'; +import '@ui5/webcomponents-react-charts/styles.css'; + + + +; +``` + +## Import Pattern + +Subpath imports are recommended for tree-shaking: + +```tsx +// ✅ Recommended - tree-shakeable +import { BarChart } from '@ui5/webcomponents-react-charts/BarChart'; +import { LineChart } from '@ui5/webcomponents-react-charts/LineChart'; +import { PieChart } from '@ui5/webcomponents-react-charts/PieChart'; + +// Also works - root import +import { BarChart, LineChart, PieChart } from '@ui5/webcomponents-react-charts'; +``` + +## Available Charts + +| Component | Use Case | +| ---------------------- | ---------------------------------------------------- | +| `BarChart` | Horizontal bar comparison | +| `ColumnChart` | Vertical column comparison | +| `ColumnChartWithTrend` | Column chart with trend line | +| `LineChart` | Trends over time | +| `PieChart` | Part-to-whole ratios | +| `DonutChart` | Part-to-whole with center space | +| `ComposedChart` | Mix bars, lines, areas | +| `BulletChart` | Compare actual vs target | +| `RadarChart` | Multi-dimensional comparison | +| `RadialChart` | Radial visualization | +| `ScatterChart` | X/Y/Z point relationships (different data structure) | + +Each chart has a corresponding `Placeholder` component for loading states. + +**Deprecated:** + +- `TimelineChart` - Will be removed in v3 without replacement + +## Core Data Structure + +Charts use a **dimensions + measures** data model: + +```tsx +import { BarChart } from '@ui5/webcomponents-react-charts/BarChart'; + +const dataset = [ + { month: 'Jan', revenue: 4000, profit: 2400 }, + { month: 'Feb', revenue: 3000, profit: 1398 }, + { month: 'Mar', revenue: 5000, profit: 3200 }, +]; + +; +``` + +### ScatterChart - Different Data Structure + +ScatterChart uses a nested data structure with multiple datasets: + +```tsx +import { ScatterChart } from '@ui5/webcomponents-react-charts/ScatterChart'; + +const dataset = [ + { + label: 'America', + color: 'var(--sapChart_OrderedColor_1)', + opacity: 0.7, + data: [ + { users: 120, sessions: 200, volume: 302 }, + { users: 20, sessions: 230, volume: 392 }, + ], + }, + { + label: 'Europe', + data: [{ users: 50, sessions: 100, volume: 200 }], + }, +]; + +; +``` + +## Colors + +Charts use SAP theming by default (`--sapChart_OrderedColor_1` through `--sapChart_OrderedColor_12`): + +```tsx +// Custom colors +measures={[ + { accessor: 'revenue', color: 'var(--sapPositiveColor)' }, + { accessor: 'profit', color: '#FF6B6B' } +]} + +// Conditional highlighting (BarChart, ColumnChart, BulletChart) +measures={[{ + accessor: 'revenue', + highlightColor: (value, measure, dataElement) => { + if (value > 5000) return 'var(--sapPositiveColor)'; + if (value < 2000) return 'var(--sapNegativeColor)'; + return measure.color; + } +}]} +``` + +## Event Handling + +```tsx + { + console.log(e.detail.payload); + console.log(e.detail.activePayloads); + }} + onDataPointClick={(e) => { + const { value, dataKey, payload, dataIndex } = e.detail; + }} + onLegendClick={(e) => { + console.log(e.detail.dataKey); + }} +/> +``` + +## ComposedChart - Mixed Chart Types + +Use ComposedChart to combine bars, lines, and areas: + +```tsx +import { ComposedChart } from '@ui5/webcomponents-react-charts/ComposedChart'; + +; +``` + +## Stacked Charts + +```tsx +measures={[ + { accessor: 'revenue', stackId: 'stack1' }, + { accessor: 'profit', stackId: 'stack1' } +]} +``` + +## Loading States + +```tsx +import { BarChart } from '@ui5/webcomponents-react-charts/BarChart'; +import { BarChartPlaceholder } from '@ui5/webcomponents-react-charts/BarChartPlaceholder'; + +; +``` + +## Responsive Behavior + +Charts default to `width: 100%` and `height: 400px`, so they render out of the box without container configuration. + +**Important:** If you override chart dimensions with relative values (e.g., `height: '100%'`), the container must have fixed dimensions. Charts won't render correctly in containers that derive size from their children: + +```tsx +// ❌ Won't work - chart uses 100% height but parent has no fixed height +
+ +
+ +// ✅ Works - container has fixed height for percentage-based chart +
+ +
+ +// ✅ Works - using default dimensions (no container height needed) +
+ +
+``` + +## Limitations + +**Critical:** + +- Charts are **custom-built without defined design specifications** - they use the Fiori color palette, but functionality and especially **accessibility may not meet standard app requirements** +- `accessibilityLayer` is **experimental** and only supports categorical/horizontal charts with tooltips +- `legendPosition: "middle"` is **not supported** for: ColumnChartWithTrend, DonutChart, PieChart + +**Data:** + +- Measure values must be **numbers** (not strings) +- ScatterChart uses a different dataset format (nested `data` array per series) + +**Sizing:** + +- When using relative dimensions (e.g., `height: '100%'`), containers must have fixed dimensions +- Default: `width: 100%`, `height: 400px` + +**Styling:** + +- 12-color palette cycles after 12 measures + +**Deprecated:** + +- `legendHorizontalAlign` - use `legendConfig.align` instead +- `TimelineChart` - will be removed in v3 without replacement diff --git a/packages/charts/package.json b/packages/charts/package.json index 6e00a9b27ea..c6d7b8fd9f4 100644 --- a/packages/charts/package.json +++ b/packages/charts/package.json @@ -141,6 +141,6 @@ "files": [ "dist", "CHANGELOG.md", - "NOTICE.txt" + "CLAUDE.md" ] } diff --git a/packages/cli/CLAUDE.md b/packages/cli/CLAUDE.md new file mode 100644 index 00000000000..28d9faaa381 --- /dev/null +++ b/packages/cli/CLAUDE.md @@ -0,0 +1,92 @@ +# @ui5/webcomponents-react-cli + +CLI for generating React wrappers from UI5 Web Components and code-mods for migrations. + +## Installation + +```bash +npm install -D @ui5/webcomponents-react-cli +``` + +## Wrapper Generation + +Generate React component wrappers from any UI5 Web Components package. This is how `@ui5/webcomponents-react` wraps the official UI5 Web Components. + +See [Bring Your Own Web Components](https://ui5.github.io/webcomponents-react/v2/?path=/docs/knowledge-base-bring-your-own-web-components--docs) for full documentation. + +```bash +npx @ui5/webcomponents-react-cli create-wrappers \ + --packageName your-package-name \ + --out ./src/components +``` + +**Requirements:** + +- Package must contain a Custom Element Manifest (`custom-elements.json`) + +**Options:** + +| Option | Description | +| --------------------------- | -------------------------------------------------- | +| `--packageName` | npm package containing web components (required) | +| `--out`, `-o` | Output directory for generated wrappers (required) | +| `--additionalComponentNote` | Custom note to add to generated component JSDoc | + +## Codemods + +### `v2` - Migrate from v1 to v2 + +Automates v1 to v2 migration. See [Migration Guide](https://ui5.github.io/webcomponents-react/v2/?path=/docs/migration-guide--docs#codemod). + +```bash +npx @ui5/webcomponents-react-cli codemod --transform v2 \ + --src ./src \ + --typescript # omit for JavaScript projects +``` + +**What it transforms:** + +- Component renames (`StandardListItem` → `ListItemStandard`, `Badge` → `Tag`) +- Prop renames (`busy` → `loading`, `mode` → `selectionMode`, `placementType` → `placement`) +- Event renames (`onAfterClose` → `onClose`, `onAfterOpen` → `onOpen`) +- Enum value changes (`ValueState.Error` → `ValueState.Negative`) +- Enum import path updates +- Removed prop cleanup + +**Limitations:** + +- Handles ~90% of changes automatically +- Some changes require manual review (e.g., inverted logic for `showCancelButton` → `hideCancelButton`) +- Complex prop merges need manual attention (e.g., `itemsPerPageS/M/L` → `itemsPerPage`) + +### `export-maps` - Convert to Subpath Imports + +Convert root imports to tree-shakeable subpath imports. See [FAQ](https://ui5.github.io/webcomponents-react/v2/?path=/docs/knowledge-base-faq--docs#why-use-direct-imports-via-package-export-maps). + +```bash +npx @ui5/webcomponents-react-cli codemod --transform export-maps \ + --src ./src \ + --typescript # omit for JavaScript projects +``` + +**Before:** + +```tsx +import { Button, Input, Dialog } from '@ui5/webcomponents-react'; +``` + +**After:** + +```tsx +import { Button } from '@ui5/webcomponents-react/Button'; +import { Input } from '@ui5/webcomponents-react/Input'; +import { Dialog } from '@ui5/webcomponents-react/Dialog'; +``` + +**Supported packages:** + +- `@ui5/webcomponents-react` +- `@ui5/webcomponents-react-base` +- `@ui5/webcomponents-react-charts` +- `@ui5/webcomponents-ai-react` +- `@ui5/webcomponents-react-compat` diff --git a/packages/cli/package.json b/packages/cli/package.json index 6e8c34c996a..155dd87ffdd 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -30,6 +30,7 @@ }, "files": [ "dist", + "CLAUDE.md", "patches" ], "dependencies": { diff --git a/packages/compat/package.json b/packages/compat/package.json index e8941497b9c..8d4b727abe1 100644 --- a/packages/compat/package.json +++ b/packages/compat/package.json @@ -118,8 +118,6 @@ "files": [ "dist", "CHANGELOG.md", - "LICENSE", - "NOTICE.txt", "README.md" ] } diff --git a/packages/cypress-commands/CLAUDE.md b/packages/cypress-commands/CLAUDE.md new file mode 100644 index 00000000000..95f837f8d7d --- /dev/null +++ b/packages/cypress-commands/CLAUDE.md @@ -0,0 +1,118 @@ +# @ui5/webcomponents-cypress-commands + +Cypress commands and queries for testing UI5 Web Components in React applications. + +> **Note:** This package is extensible - PRs welcome for additional commands/queries. + +## Installation + +```bash +npm install -D @ui5/webcomponents-cypress-commands +``` + +## Setup + +Import in your Cypress support file (e.g., `cypress/support/component.ts`): + +```typescript +import '@ui5/webcomponents-cypress-commands'; +``` + +For TypeScript, add the type reference: + +```typescript +/// +``` + +## Commands + +### Input Components + +```typescript +// Type into ui5-input, ui5-combobox, ui5-multi-combobox, etc. +cy.get('[ui5-input]').typeIntoUi5Input('Hello World'); + +// Clear input +cy.get('[ui5-input]').clearUi5Input(); + +// Type with delay (for components needing time to initialize, e.g., ComboBox suggestions) +cy.get('[ui5-combobox]').typeIntoUi5InputWithDelay('Hello', 500); + +// Type into ui5-textarea +cy.get('[ui5-textarea]').typeIntoUi5TextArea('Multi-line text'); +``` + +### Toggle Components + +```typescript +cy.get('[ui5-checkbox]').toggleUi5Checkbox(); +cy.get('[ui5-switch]').toggleUi5Switch(); +cy.get('[ui5-radio-button]').clickUi5RadioButton(); +``` + +### Select/Dropdown Components + +```typescript +// Open dropdown +cy.get('[ui5-select]').openDropDownByClick(); +cy.get('[ui5-combobox]').openDropDownByClick(); +cy.get('[ui5-multi-combobox]').openDropDownByClick(); + +// Click dropdown item by text (popover must be open) +cy.get('[ui5-select]').clickDropdownMenuItemByText('Option 2'); +cy.get('[ui5-combobox]').clickDropdownMenuItemByText('Item 1'); + +// Click chained dropdown item +cy.get('[ui5-option]').clickDropdownMenuItem(); +cy.get('[ui5-mcb-item]').clickDropdownMenuItem(); +``` + +### List Components + +```typescript +// Click list item by text +cy.get('[ui5-list]').clickUi5ListItemByText('List Item'); +cy.clickUi5ListItemByText('List Item'); // Also works without chaining +``` + +### Popups + +```typescript +// Close any popup with Escape key +cy.closeUi5PopupWithEsc(); +``` + +## Queries + +### TabContainer + +```typescript +// Find tab by text (chain to ui5-tabcontainer) +cy.get('[ui5-tabcontainer]').findUi5TabByText('Tab 1'); + +// Find sub-tab popover button +cy.get('[ui5-tabcontainer]').findUi5TabOpenPopoverButtonByText('Tab 1.1'); +``` + +### Toolbar + +```typescript +// Find toolbar button by text +cy.findToolbarButtonByText('Button Text'); +cy.get('[ui5-toolbar]').findToolbarButtonByText('Button Text'); + +// Get internal button element +cy.findToolbarButtonByText('Button Text', { queryShadowButton: true }); +``` + +## Deprecated Commands + +- `clickUi5SelectOptionByText` → use `clickDropdownMenuItemByText` +- `clickUi5SelectOption` → use `clickDropdownMenuItem` + +## Important Notes + +- **Popover must be open** before clicking dropdown items +- **`ui5-multi-combobox`** requires ~500ms delay before popover is available +- **`ui5-select`** uses keyboard Enter instead of click due to shadow DOM limitations +- Use **attribute selectors** (`[ui5-input]`) not tag names (`ui5-input`) diff --git a/packages/cypress-commands/package.json b/packages/cypress-commands/package.json index d57426c436a..b6acc4edb3a 100644 --- a/packages/cypress-commands/package.json +++ b/packages/cypress-commands/package.json @@ -39,6 +39,7 @@ "access": "public" }, "files": [ - "dist" + "dist", + "CLAUDE.md" ] } diff --git a/packages/main/CLAUDE.md b/packages/main/CLAUDE.md new file mode 100644 index 00000000000..a2a10c3361e --- /dev/null +++ b/packages/main/CLAUDE.md @@ -0,0 +1,605 @@ +# @ui5/webcomponents-react + +React wrappers for SAP UI5 Web Components plus custom Fiori-compliant React components. + +## Installation + +```bash +npm install @ui5/webcomponents-react @ui5/webcomponents @ui5/webcomponents-fiori +``` + +## Setup + +Wrap your application with `ThemeProvider`: + +```tsx +import { ThemeProvider } from '@ui5/webcomponents-react'; + +function App() { + return ( + + + + ); +} +``` + +**ThemeProvider Props:** + +- `children` - Application content +- `staticCssInjected` - Set to `true` when you've imported static CSS (required for SSR/Next.js) + +**SSR/Next.js Setup:** + +```tsx +// layout.tsx or _app.tsx +import '@ui5/webcomponents-react/styles.css'; + +{children}; +``` + +## TypeScript + +### Type Import Patterns + +The package provides its own types AND re-uses types from `@ui5/webcomponents`. Follow these patterns: + +**Component Props and DOM Refs - import from `@ui5/webcomponents-react`:** + +```tsx +// ✅ Props types and DomRef types - from the React package +import type { ButtonPropTypes, ButtonDomRef } from '@ui5/webcomponents-react/Button'; +import type { DialogPropTypes, DialogDomRef } from '@ui5/webcomponents-react/Dialog'; +import type { SideNavigationPropTypes } from '@ui5/webcomponents-react'; + +// Usage +const buttonRef = useRef(null); + +// Event handlers - use the prop type's event property (includes detail types) +const handleClick: ButtonPropTypes['onClick'] = (e) => { + // e.detail is fully typed +}; +const handleSelectionChange: SideNavigationPropTypes['onSelectionChange'] = (e) => { + console.log(e.detail.item.text); // fully typed +}; +``` + +**Enums and Type Constants - import from `@ui5/webcomponents`:** + +```tsx +// ✅ Enums/types - from the web components package directly +import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js'; +import ListItemType from '@ui5/webcomponents/dist/types/ListItemType.js'; +import ValueState from '@ui5/webcomponents-base/dist/types/ValueState.js'; +import MessageStripDesign from '@ui5/webcomponents/dist/types/MessageStripDesign.js'; +import NavigationLayoutMode from '@ui5/webcomponents-fiori/dist/types/NavigationLayoutMode.js'; + +// Usage + + + +``` + +### Complete Example + +```tsx +import { useState, useRef } from 'react'; +import { Button, Dialog, SideNavigation, SideNavigationItem } from '@ui5/webcomponents-react'; +import type { ButtonPropTypes, DialogDomRef, SideNavigationPropTypes } from '@ui5/webcomponents-react'; +import ButtonDesign from '@ui5/webcomponents/dist/types/ButtonDesign.js'; +import homeIcon from '@ui5/webcomponents-icons/dist/home.js'; + +function MyComponent() { + const dialogRef = useRef(null); + const [open, setOpen] = useState(false); + + const handleClick: ButtonPropTypes['onClick'] = (e) => { + setOpen(true); + }; + + const handleSelectionChange: SideNavigationPropTypes['onSelectionChange'] = (e) => { + console.log(e.detail.item.text); + }; + + return ( + <> + + setOpen(false)}> + Content + + + + + + ); +} +``` + +## Side-Effect Imports (Icons, Assets, Features) + +UI5 Web Components require certain side-effect imports to function. These register components/assets at runtime. + +**SSR/Next.js Critical:** These imports must be in **client components** (`'use client'` directive). In server components, they do nothing. + +### Icon Imports + +Icons must be imported before use. **Use named imports for better maintainability:** + +```tsx +// ✅ Recommended - named imports +import addIcon from '@ui5/webcomponents-icons/dist/add.js'; +import deleteIcon from '@ui5/webcomponents-icons/dist/delete.js'; +import editIcon from '@ui5/webcomponents-icons/dist/edit.js'; + +// Use with the imported variable + + + +// ❌ Avoid - side-effect imports with magic strings +import '@ui5/webcomponents-icons/dist/add.js'; + +``` + +### Asset Imports (i18n/Theming) + +Required for translations and theme switching: + +```tsx +// Standard installation (includes fiori package assets) +import '@ui5/webcomponents-react/dist/Assets.js'; + +// Minimal installation (without @ui5/webcomponents-fiori) +import '@ui5/webcomponents/dist/Assets.js'; +import '@ui5/webcomponents-react/dist/json-imports/i18n.js'; +``` + +Test translations with URL param: `?sap-ui-language=de` + +### Feature Imports + +Optional features that must be imported before use: + +```tsx +// Form support - required for native form submission +import '@ui5/webcomponents/dist/features/FormSupport.js'; + +// Must be imported BEFORE any other imports +``` + +See [UI5 Web Components Features](https://ui5.github.io/webcomponents/docs/advanced/using-features/) for all available features. + +## Import Pattern + +Use subpath imports for tree-shaking (especially in development): + +```tsx +// ✅ Recommended - tree-shakeable +import { Button } from '@ui5/webcomponents-react/Button'; +import { Input } from '@ui5/webcomponents-react/Input'; +import { Dialog } from '@ui5/webcomponents-react/Dialog'; +import { AnalyticalTable } from '@ui5/webcomponents-react/AnalyticalTable'; + +// ❌ Avoid - imports entire bundle +import { Button, Input, Dialog } from '@ui5/webcomponents-react'; +``` + +**Convert existing imports:** + +```bash +npx @ui5/webcomponents-react-cli codemod --transform export-maps --src ./src +``` + +## Critical: Event Handling + +Web components emit **CustomEvents** with data in `e.detail`: + +```tsx +// ❌ WRONG - won't work +const handleChange = (e) => { + console.log(e.selectedOption); // undefined! +}; + +// ✅ CORRECT - use e.detail +const handleChange = (e) => { + console.log(e.detail.selectedOption); +}; + +// Common event patterns + setValue(e.target.value)} /> + { + if (e.key.startsWith('Arrow')) { + e.stopPropagation(); + } + }} +/> +``` + +#### Scrolling Methods + +Available on DOM ref: + +- `scrollTo(offset, align?)` - Scroll to pixel offset +- `scrollToItem(index, align?)` - Scroll to row index +- `horizontalScrollTo(offset, align?)` +- `horizontalScrollToItem(index, align?)` + +--- + +### ObjectPage + +**Limitation:** In iframes, smooth scrolling is disabled. + +**Toggling header programmatically:** + +```tsx +const objectPageRef = useRef(null); +objectPageRef.current.toggleHeaderArea(); // toggle +objectPageRef.current.toggleHeaderArea(true); // snap +objectPageRef.current.toggleHeaderArea(false); // expand +``` + +**Fullscreen section (TabBar mode only):** + +```tsx + + {/* Content */} + +``` + +**Note:** Using multiple sections with `height: 100%` will break your layout. + +--- + +### FilterBar + +#### Identifying Event Origin + +When filters should only update from FilterBar (not FiltersDialog), check the dataset: + +```tsx +const handleInput = (e) => { + const firedInFilterBar = !!e.currentTarget.parentElement.dataset.inFilterBar; + const firedInFiltersDialog = !!e.currentTarget.parentElement.dataset.inFiltersDialog; +}; +``` + +#### Reordering + +Enable with `enableReordering={true}`. Handle `onFiltersDialogSave` to persist order: + +```tsx +const handleFiltersDialogSave = (e) => { + setOrderedFilterKeys(e.detail.reorderedFilterKeys); + setVisibleFilters(e.detail.selectedFilterKeys); +}; +``` + +--- + +### VariantManagement + +#### Custom Input Validation + +Prevent internal save logic by marking the event as invalid: + +```tsx +const handleSaveViewInput = (e) => { + if (!e.target.value.match(/^[a-z0-9\s]+$/i)) { + e.isInvalid = true; // prevents save + setValueState(ValueState.Negative); + } else { + e.isInvalid = false; + setValueState(undefined); + } +}; + +Error message
, + onInput: handleSaveViewInput, + }} +> + Variant Name +; +``` + +--- + +### MessageView + +**Navigation pattern in Dialog/Popover:** + +Don't set `showDetailsPageHeader`. Instead, listen to `onItemSelect` and use `navigateBack()`: + +```tsx +const messageViewRef = useRef(null); +const [isOnDetailsPage, setIsOnDetailsPage] = useState(false); + + messageViewRef.current.navigateBack()} + header={ + { + setIsOnDetailsPage(false); + messageViewRef.current.navigateBack(); + }} + /> + ) + } + /> + } +> + setIsOnDetailsPage(true)} /> +; +``` + +--- + +### SplitterLayout + +**Reset options via `options` prop:** + +| Property | Description | +| ------------------------- | --------------------------------------- | +| `resetOnSizeChange` | Reset when container size changes | +| `resetOnChildrenChange` | Reset when children change | +| `resetOnCustomDepsChange` | Custom dependency list to trigger reset | diff --git a/packages/main/package.json b/packages/main/package.json index 321463cff73..0ec84616eaf 100644 --- a/packages/main/package.json +++ b/packages/main/package.json @@ -844,8 +844,7 @@ "dist", "wrappers", "CHANGELOG.md", - "LICENSE", - "NOTICE.txt", + "CLAUDE.md", "README.md" ] }