Skip to content

Commit 6468867

Browse files
authored
Merge pull request #497 from qoretechnologies/codex/add-documentation-using-docusaurus
Remove docs favicon asset
2 parents 7dcb01a + 9606dab commit 6468867

File tree

35 files changed

+16718
-7020
lines changed

35 files changed

+16718
-7020
lines changed

.github/copilot-instructions.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# ReQore AI Coding Agent Instructions
2+
3+
## Project Overview
4+
5+
ReQore is a **themeable React component library** for the Qorus platform. It provides 40+ UI components (Button, Table, Modal, Drawer, etc.) that share a unified design system with consistent theming, sizing, and effect systems.
6+
7+
**Key Facts:**
8+
9+
- **Framework:** React 18 + TypeScript (strict mode)
10+
- **Build:** TypeScript compilation to `/dist`, exports both `.js` and `.d.ts`
11+
- **Styling:** styled-components with theme-driven values (no CSS modules)
12+
- **State Management:** zustand + React Context (use-context-selector)
13+
- **Testing:** Jest + React Testing Library (tests in `__tests__/`)
14+
- **Documentation:** Storybook (dev stories in `src/stories/`) + Docusaurus (docs site)
15+
16+
## Architecture Essentials
17+
18+
### General Development Practices
19+
20+
# General
21+
22+
- Focus is first on user experience and performance, complexity and tech debt secondary
23+
- Follow existing code patterns for new components; refer to similar components for guidance
24+
- Check if a helper or utility already exists before writing a new one
25+
26+
# TypeScript
27+
28+
- Use TypeScript with strict typing; define prop interfaces for each component with `I` prefix for interfaces and `T` prefix for types
29+
30+
# UI / UX
31+
32+
- Always make sure to create reusable components
33+
- Always use named exports for React components
34+
- There can be multiple React components in one file if it makes sense
35+
- Use styled-components for styling; define style interfaces for styled components
36+
- Always componentize styles with styled-components; avoid inline styles except for dynamic cases
37+
- Use functional components with React hooks
38+
- For React, always wrap components in `memo()` unless there's a specific reason not to
39+
- For React, always wrap callbacks in `useCallback()` unless there's a specific reason not to
40+
- For React, always memoize computed values in `useMemo()` unless there's a specific reason not to
41+
42+
# Testing
43+
44+
- Run tests after changes, run `yarn precheck` after feature completions
45+
- Write a unit test if it makes sense for the change you have made, but Storybook tests will always have higher priority
46+
47+
### Component Structure
48+
49+
- **Folder:** `src/components/{ComponentName}/` contains only `index.tsx` (and occasionally `backdrop.tsx` for overlay components)
50+
- **Pattern:** Each component is a `memo()` wrapped functional component with TypeScript interfaces
51+
- **Exports:** Named exports (e.g., `export const ReqoreButton`) from component files; re-exported in `src/index.tsx`
52+
53+
### Theme System (`src/constants/theme.ts` + hooks)
54+
55+
- **Global theming:** `useReqoreTheme()` hook provides `IReqoreTheme` (colors, text, main background, intents)
56+
- **Dynamic theming:** Components read theme via hooks, enabling runtime theme switching
57+
- **Intents:** Type-safe intent system (e.g., `'primary' | 'secondary' | 'success' | 'danger'`) maps to theme colors
58+
- **Custom themes:** Merge with `DEFAULT_THEME`; see `ThemeProvider.tsx`
59+
60+
### Sizing System (`src/constants/sizes.ts`)
61+
62+
- **Size types:** `'micro' | 'tiny' | 'small' | 'normal' | 'big' | 'huge' | 'massive'`
63+
- **Pattern:** Maps like `SIZE_TO_PX`, `SIZE_TO_NUMBER`, `CONTROL_TEXT_FROM_SIZE` convert size enums to pixel/CSS values
64+
- **Usage:** Most interactive components accept `size?: TSizes` prop; pass through to nested styled components
65+
66+
### Effect System (`src/components/Effect/`)
67+
68+
- **Purpose:** Provides gradient, blur, shadow effects applied via `StyledTextEffect`
69+
- **Props:** `IWithReqoreEffect` mixin (gradient, blur, shadow, opacity) on interactive components
70+
- **Color types:** `TReqoreHexColor`, `TReqoreRgbaColor`, `TReqoreMultiTypeColor` used consistently
71+
72+
### Global Context (`src/context/ReqoreContext.tsx` + `ReqoreProvider.tsx`)
73+
74+
- **Manages:** Modals, notifications, z-index stack, mobile breakpoints, animations toggle, tooltips
75+
- **Methods:** `addModal()`, `removeModal()`, `addNotification()`, `confirmAction()` (confirmation dialog)
76+
- **Mobile detection:** `isMobile`, `isTablet`, `isMobileOrTablet` flags from `useMedia()` hook
77+
- **ESC handling:** Modals/popovers close on ESC via `escClosableModals` stack; `closeModalsOnEscPress` toggle
78+
79+
## Development Workflows
80+
81+
### Quick Start
82+
83+
```bash
84+
yarn install
85+
yarn storybook # Dev mode on http://localhost:6007
86+
yarn docs:dev # Docusaurus dev server
87+
yarn test:watch # Jest watch mode
88+
yarn lint # ESLint check
89+
yarn build # TypeScript compilation
90+
```
91+
92+
### Pre-commit Checks
93+
94+
- `yarn precheck` runs: lint → test → build (production)
95+
- `pre-push` hook enforces same checks before push (see `package.json`)
96+
- Line length: 100 characters (enforced by eslint)
97+
98+
### Testing Patterns
99+
100+
- **Setup:** `__tests__/setup.js` disables console debug/info/error
101+
- **Wrapper:** Always wrap tests with `<ReqoreUIProvider><ReqoreLayoutContent><ReqoreContent>...</ReqoreContent></ReqoreLayoutContent></ReqoreUIProvider>`
102+
- **Selectors:** Use CSS classes like `.reqore-button`, `.reqore-icon` (added via `className` prop)
103+
- **Example:** See [button.test.tsx](__tests__/button.test.tsx)
104+
105+
### Storybook Stories
106+
107+
- **Location:** `src/stories/` (e.g., `Collection.stories.tsx`)
108+
- **Pattern:** ArgTypes for props, canvas controls, visual testing via Chromatic
109+
- **Command:** `yarn build-storybook` builds static site
110+
111+
## Code Patterns & Conventions
112+
113+
### Component Prop Interfaces
114+
115+
- **Extend mixins:** `IWithReqoreSize`, `IWithReqoreEffect`, `IWithReqoreLoading`, `IWithReqoreReadOnly`
116+
- **Naming:** Props interface is `IReqore{ComponentName}Props`; style interface is `IReqore{ComponentName}Style`
117+
- **Optional theme:** Style interfaces accept `theme: IReqoreTheme` for styled-components access
118+
- **Readonly context:** Use `readonly` keyword on context properties (immutability)
119+
120+
### Styled Components Pattern
121+
122+
```tsx
123+
const StyledButton = styled.button<IReqoreButtonStyle>`
124+
// Use props.theme from ReqoreTheme
125+
background: ${({ theme, intent }) => theme.intents?.[intent]?.color};
126+
color: ${({ theme }) => getReadableColor(theme)};
127+
// Conditional styles via css helper
128+
${({ disabled }) =>
129+
disabled &&
130+
css`
131+
opacity: 0.5;
132+
`}
133+
`;
134+
```
135+
136+
### Hooks Usage
137+
138+
- **Theme:** `useReqoreTheme()` returns `IReqoreTheme`
139+
- **Context props:** `useReqoreProperty('propertyName')` for context values (avoids consuming entire context)
140+
- **Local refs:** `useCombinedRefs()` for forwarding + internal refs; `useOutsideClick()` for popover clicks
141+
- **Auto-focus:** `useAutoFocus()` for modal/drawer focus management
142+
143+
### Color Helpers (`src/helpers/colors.ts`)
144+
145+
- **Text contrast:** `getReadableColor(theme)` returns light or dark based on theme.main
146+
- **Gradients:** `getGradientMix()` blends multiple colors for effect gradients
147+
- **Hex↔RGBA:** `hexAToRGBA()`, `getRGBAFromHex()` for color space conversions
148+
- **Lightness:** `changeLightness()`, `changeDarkness()` for theme-aware color adjustments
149+
150+
### Animation Config
151+
152+
- **Spring:** `SPRING_CONFIG` for bounce/smooth animations (via `@react-spring/web`)
153+
- **Disabled:** `SPRING_CONFIG_NO_ANIMATIONS` when `animations.buttons: false`
154+
- **Usage:** Wrap animated components with `<animated>` from react-spring
155+
156+
## Key Integration Points
157+
158+
### Modal & Notification Flow
159+
160+
1. **Add modal:** `context.addModal(modalElement, id?)` returns modal ID
161+
2. **Close:** `context.removeModal(id)` + optional confirmation dialog
162+
3. **Notifications:** `context.addNotification({...})` queues toast; auto-removes after `duration`
163+
4. **Portal:** All modals/notifications render via `customPortalId` or default DOM portal
164+
165+
### Responsive Breakpoints
166+
167+
- **Mobile:** `isMobile` (width ≤ 480px)
168+
- **Tablet:** `isTablet` (width ≤ 1024px)
169+
- **Usage:** Conditional rendering in components; affects dropdown/menu positioning
170+
171+
### Icon System (`src/types/icons.ts`)
172+
173+
- **Type:** `IReqoreIconName` (string literal of all icon names)
174+
- **Render:** `<ReqoreIcon name="iconNameHere" />` or component prop `icon="iconNameHere"`
175+
- **Sizing:** Icon size auto-scales with component size via `ICON_FROM_SIZE`
176+
177+
### Collections & Paging
178+
179+
- **Collection:** Renders arrays with optional sorting/filtering; used in Table, MultiSelect
180+
- **Paging:** `useReqorePaging()` hook handles offset/limit; `<ReqorePaging>` component for controls
181+
- **Pattern:** See [Collection.tsx](src/components/Collection/) and [Paging.tsx](src/containers/Paging.tsx)
182+
183+
## Common Pitfalls & Solutions
184+
185+
| Issue | Solution |
186+
| -------------------------------------- | -------------------------------------------------------------------------------------------- |
187+
| Styled component props not typed | Add `<IComponentStyle>` generic; ensure mixin interfaces extend properly |
188+
| Theme not applying | Verify `ReqoreUIProvider` wraps component tree; check `useReqoreTheme()` call |
189+
| ESC key ignored in modal | Ensure `closeModalsOnEscPress: true` in provider; modal must be in `escClosableModals` stack |
190+
| Icon not rendering | Verify icon name in `IReqoreIconName`; check icon imports in `Icon/` component |
191+
| Tests fail with "React is not defined" | Verify `setup.js` is loaded; jsx: "react-jsx" in tsconfig.json |
192+
| Animation janky on slow devices | Offer `animations.buttons: false` toggle in theme/context; use `SPRING_CONFIG_NO_ANIMATIONS` |
193+
194+
## File Reference
195+
196+
| Path | Purpose |
197+
| ----------------- | --------------------------------------------------------------- |
198+
| `src/index.tsx` | Main export barrel; re-exports all public components |
199+
| `src/constants/` | Global enums, size maps, theme defaults, animation configs |
200+
| `src/helpers/` | Color math, utility functions (no React) |
201+
| `src/hooks/` | Custom React hooks (theme, context, DOM utilities) |
202+
| `src/containers/` | Provider components (ReqoreProvider, ThemeProvider, UIProvider) |
203+
| `src/context/` | Context definitions (ReqoreContext, ThemeContext) |
204+
| `src/types/` | Global TypeScript interfaces (icons, global prop mixins) |
205+
| `__tests__/` | Jest tests; mirrors `src/` structure |
206+
| `src/stories/` | Storybook stories for visual development |
207+
208+
## When Adding New Components
209+
210+
1. **Create folder:** `src/components/{ComponentName}/index.tsx`
211+
2. **Define interfaces:** `IReqore{ComponentName}Props` + `IReqore{ComponentName}Style`
212+
3. **Use mixins:** Extend `IWithReqoreEffect`, `IWithReqoreSize` as needed
213+
4. **Apply theme:** Use `useReqoreTheme()` and styled-components `theme` prop
214+
5. **Export:** Add named export to `src/index.tsx`
215+
6. **Test:** Add test file in `__tests__/{ComponentName}.test.tsx` with UIProvider wrapper
216+
7. **Story:** Create `src/stories/{ComponentName}.stories.tsx` with argTypes
217+
218+
## Documentation
219+
220+
- **Storybook:** Run `yarn storybook` for interactive component playground
221+
- **Docusaurus:** Run `yarn docs:dev` for user guides + API docs
222+
- **TypeDoc:** `yarn docs:api` generates API reference from JSDoc comments
223+
- **Inline:** Use JSDoc comments on public props/methods for IDE tooltips

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@ dist
33
output.json
44
tests.json
55
coverage
6-
storybook-static
6+
storybook-static
7+
.docusaurus
8+
build
9+
docs/api

__tests__/dropdown.test.tsx

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,74 @@ test('Renders <Dropdown /> and updates its items when state changes, does not cl
389389

390390
expect(document.querySelectorAll('.reqore-popover-content').length).toBe(1);
391391
});
392+
393+
test('Renders <Dropdown /> with nested items, closes only on leaf item click', () => {
394+
jest.useFakeTimers();
395+
const onItemSelect = jest.fn();
396+
397+
act(() => {
398+
render(
399+
<ReqoreUIProvider>
400+
<ReqoreLayoutContent>
401+
<ReqoreContent>
402+
<ReqoreDropdown
403+
onItemSelect={onItemSelect}
404+
items={[
405+
{
406+
label: 'Parent Item',
407+
value: 'parent',
408+
items: [
409+
{
410+
label: 'Child Item 1',
411+
value: 'child1',
412+
},
413+
{
414+
label: 'Child Item 2',
415+
value: 'child2',
416+
},
417+
],
418+
},
419+
{
420+
label: 'Leaf Item',
421+
value: 'leaf',
422+
},
423+
]}
424+
/>
425+
</ReqoreContent>
426+
</ReqoreLayoutContent>
427+
</ReqoreUIProvider>
428+
);
429+
jest.advanceTimersByTime(1);
430+
});
431+
432+
// Open the dropdown
433+
fireEvent.click(document.querySelector('.reqore-button')!);
434+
jest.advanceTimersByTime(1);
435+
436+
expect(document.querySelectorAll('.reqore-popover-content').length).toBe(1);
437+
expect(document.querySelectorAll('.reqore-menu-item').length).toBe(2);
438+
439+
// Click on parent item with sub-items - should NOT close
440+
fireEvent.click(document.querySelectorAll('.reqore-menu-item')[0]);
441+
jest.advanceTimersByTime(1);
442+
443+
expect(document.querySelectorAll('.reqore-popover-content').length).toBe(1);
444+
expect(document.querySelectorAll('.reqore-menu-item').length).toBe(2);
445+
expect(onItemSelect).not.toHaveBeenCalled();
446+
447+
// Now we should see child items
448+
expect(document.querySelectorAll('.reqore-menu-item')[0].textContent).toContain('Child Item 1');
449+
450+
// Click on leaf item - should close
451+
fireEvent.click(document.querySelectorAll('.reqore-menu-item')[0]);
452+
jest.advanceTimersByTime(1);
453+
454+
expect(onItemSelect).toHaveBeenCalledWith(
455+
expect.objectContaining({
456+
label: 'Child Item 1',
457+
value: 'child1',
458+
}),
459+
expect.anything()
460+
);
461+
expect(document.querySelectorAll('.reqore-popover-content').length).toBe(0);
462+
});

0 commit comments

Comments
 (0)