diff --git a/.claude/skills/vibe-breaking-change/SKILL.md b/.claude/skills/vibe-breaking-change/SKILL.md new file mode 100644 index 0000000000..7600228356 --- /dev/null +++ b/.claude/skills/vibe-breaking-change/SKILL.md @@ -0,0 +1,598 @@ +--- +name: vibe-breaking-change +description: Implements Vibe Design System breaking changes with full workflow automation including component updates, migration guide updates, codemod generation, testing, and PR creation. Use when implementing breaking changes to Vibe components that require coordinated updates across the design system. +--- + +# Vibe Breaking Change Implementation + +## Overview + +Implements breaking changes to Vibe Design System components following the established workflow with automated validation, documentation updates, and codemod generation where applicable. + +## When to Use + +**Use this skill when:** +- Implementing breaking API changes to Vibe components +- Deprecating component props or methods +- Changing component behavior that affects dependent components +- Updating component interfaces that require migration documentation +- Making changes that need coordinated updates across the design system + +**Do NOT use for:** +- Non-breaking enhancements or bug fixes +- Internal refactoring that doesn't affect public APIs +- Style-only changes without behavioral impact +- Emergency hotfixes that bypass normal workflow + +## Quick Reference + +| Phase | Actions | Validation | +|-------|---------|------------| +| 1. Analysis | Identify affected components, dependencies | Component mapping complete | +| 2. Implementation | Apply breaking change, update dependents | Tests pass locally | +| 3. Testing | Run full test suite, update failing tests | All tests green | +| 4. Documentation | Update migration guide, add codemod if deterministic | Documentation complete | +| 5. Cleanup & Delivery | lint:fix, lint, build, test โ†’ commit, push, PR with task link | All checks pass, PR ready for review | + +## Core Workflow + +### Phase 1: Analysis and Planning + +**๐Ÿ” Comprehensive Dependency Analysis:** + +```bash +# 1. Find all imports and usage across packages +grep -r "import.*ComponentName" packages/ +grep -r "ComponentName" packages/ --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" + +# 2. Find specific prop usage being changed (comprehensive search) +grep -r "oldProp=" packages/ --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" +find packages -name "*.tsx" -o -name "*.ts" -o -name "*.jsx" -o -name "*.js" | xargs grep -l "oldProp\|deprecatedMethod" + +# 3. Analyze by package structure +find packages/components -name "*.tsx" | xargs grep -l "ComponentName" # Standalone packages +find packages/core/src -name "*.tsx" | xargs grep -l "ComponentName" # Core package +find packages/docs -name "*.tsx" | xargs grep -l "ComponentName" # Documentation +find packages/mcp -name "*.ts" | xargs grep -l "ComponentName" # MCP examples +``` + +**๐Ÿ“‹ Analysis Checklist:** +1. **Map Dependencies:** + - [ ] Components that import the target component + - [ ] Components that use the target component inline + - [ ] Hook or utility functions that reference the component + - [ ] Documentation and example files + +2. **Assess Impact Scope:** + - [ ] Standalone component packages (packages/components/*) + - [ ] Core package components (packages/core/src/components/*) + - [ ] Supporting packages (docs, mcp, testkit) + - [ ] Test files and stories + +3. **Plan Migration Strategy:** + - [ ] Order of updates (component first, then dependents) + - [ ] TypeScript interface changes needed + - [ ] Codemod feasibility (deterministic vs manual) + - [ ] Documentation updates required + +See `references/dependency-analysis.md` for advanced dependency mapping techniques. + +**๐Ÿ“Š Expected Findings:** +- **20-40 component files** typically need updates for major component changes +- **Multiple package types** - standalone, core, docs, examples +- **Mixed file types** - .tsx, .ts, .jsx, .js all may need updates +- **Hidden dependencies** - MCP tools, test utilities, etc. + +### Phase 2: Implementation + +**๐Ÿ—๏ธ Systematic Update Approach:** + +**Step 1: Source Component Updates** +```typescript +// 1. Update component interface +interface ComponentProps { + // โŒ Remove deprecated props + // oldProp?: string; + // deprecatedMethod?: () => void; + + // โœ… Add new props with better API + newProp?: string; + improvedMethod?: () => void; +} + +// 2. Update component implementation +const Component = ({ newProp, ...props }: ComponentProps) => { + // Implementation with new API +}; +``` + +**Step 2: Internal Dependencies (same package)** +```typescript +// Update hooks, utilities, and helpers in the same package +// Example: Icon component's useIconProps hook +export default function useIconProps({ + label, // โœ… Updated from iconLabel + // iconLabel, // โŒ Removed +}) { + // Implementation +} +``` + +**Step 3: Cross-Package Updates (systematic)** +```bash +# Process 20-40 files systematically +# Group by package for efficient updates: + +# A. Standalone component packages +packages/components/tooltip/src/Tooltip/Tooltip.tsx +packages/components/button/src/Button/Button.tsx + +# B. Core package components (bulk of changes) +packages/core/src/components/AttentionBox/AttentionBox.tsx +packages/core/src/components/Checkbox/Checkbox.tsx +# ... (typically 20-30 files) + +# C. Supporting files +packages/mcp/src/server/tools/list-vibe-icons.ts +packages/docs/src/pages/components/ComponentName/ComponentName.stories.tsx +``` + +**๐Ÿ”„ Implementation Order:** +1. **Component Package** - Update source component and internal dependencies +2. **Individual Packages** - Update standalone packages that use the component +3. **Core Package** - Systematically update all core components (largest effort) +4. **Build Fix** - Address any TypeScript errors revealed by changes +5. **Supporting Files** - Update docs, examples, MCP tools + +**โœ… Implementation Checklist:** +- [ ] Source component interface updated +- [ ] Internal component dependencies updated (hooks, utilities) +- [ ] Standalone packages updated (5-10 files typically) +- [ ] Core package components updated (20-30 files typically) +- [ ] TypeScript build errors resolved +- [ ] Documentation and examples updated +- [ ] All updates maintain semantic consistency + +### Phase 3: Testing and Validation + +```bash +# Run comprehensive tests +yarn workspace @vibe/core test +lerna run test + +# Run specific component tests +yarn workspace @vibe/core test -- Component + +# Update snapshots if needed +yarn workspace @vibe/core test -- --updateSnapshot +``` + +See `references/testing-validation.md` for detailed testing patterns and examples. + +**Testing requirements:** +- All existing tests pass or are updated appropriately +- New tests cover breaking change scenarios +- Integration tests verify dependent components work +- No TypeScript errors across the monorepo + +### Phase 4: Documentation Updates + +#### Migration Guide Update (VIBE4_MIGRATION_GUIDE.md) + +```markdown +## ComponentName API Changes + +### Breaking Changes + +**Removed `oldProp` prop** +- **Before:** `` +- **After:** `` +- **Reason:** Better API consistency and performance + +### Migration Path + +1. Replace `oldProp` with `newProp` in all usages +2. Update prop value format if needed +3. Test component behavior matches expected outcome + +### Codemod Available +```bash +npx @vibe/codemod componentname-old-prop-to-new-prop +``` +``` + +#### Codemod Generation (if deterministic) + +โš ๏ธ **CRITICAL**: Follow established Vibe codemod patterns to avoid common pitfalls. +See `references/codemod-best-practices.md` and `references/codemod-examples.md` for detailed patterns and real examples. + +```typescript +// packages/codemod/transformations/core/v3-to-v4/ComponentName-component-migration.ts +import { + wrap, + getImports, + getComponentNameOrAliasFromImports, + findComponentElements, + migratePropsNames +} from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; + +/** + * ComponentName migration for v3 to v4: + * 1. Rename oldProp1 to newProp1 + * 2. Rename oldProp2 to newProp2 + */ +function transform({ j, root, filePath }: TransformationContext) { + // โœ… Use correct import path detection + const imports = getImports(root, NEW_CORE_IMPORT_PATH); + const componentName = getComponentNameOrAliasFromImports(j, imports, "ComponentName"); + if (!componentName) return; + + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + // โœ… Single efficient call handles all prop renames + elements.forEach(elementPath => { + migratePropsNames(j, elementPath, filePath, componentName, { + oldProp1: "newProp1", + oldProp2: "newProp2", + oldProp3: "newProp3" + }); + }); +} + +export default wrap(transform); +``` + +**Key Pattern Elements:** +- โœ… Use `getImports(root, NEW_CORE_IMPORT_PATH)` not `getCoreImportsForFile()` +- โœ… Use `migratePropsNames()` not non-existent `renameProp()` +- โœ… Single loop with batch prop updates +- โœ… Include `filePath` parameter for error reporting +- โœ… Use established utility imports + +### Phase 5: Cleanup, Validation, and PR Creation + +**โš ๏ธ ABSOLUTE REQUIREMENT: Do NOT commit, push, or create a PR until every validation step below passes with zero errors. A PR with failing CI is not acceptable.** + +**Step 1: Validation gate โ€” run each command, fix failures, repeat until all pass** + +Run these commands **sequentially**. If ANY command fails, fix the issue and **restart from that command**. Do NOT skip ahead. + +```bash +# 1. Fix lint issues across all packages +yarn lint:fix + +# 2. Verify lint passes with zero errors +yarn lint + +# 3. Build all packages โ€” must exit 0 +yarn build + +# 4. Run full test suite โ€” must exit 0 +yarn test +``` + +**If a step fails:** +1. Read the error output carefully +2. Fix the root cause (do not suppress or skip) +3. Re-run **from that step** through the remaining steps +4. Repeat until all 4 steps pass cleanly in sequence + +**Only proceed to Step 2 when lint, build, AND tests all pass with zero errors.** + +**Step 2: Create branch and commit only after all checks pass** + +See `references/pr-templates.md` for PR description, commit message, and migration guide templates. +See `references/workflow-checklist.md` for a comprehensive checklist of all phases. + +**๐Ÿ“‹ Monday.com Task Link:** +Extract the Monday.com task link from the user's original prompt if provided. +The link format is: `https://monday.monday.com/boards//pulses/` +Include this link in the PR description under the "Task Link" section. +If no task link was provided in the original prompt, ask the user for it before creating the PR. + +**Commit and PR titles MUST follow [Conventional Commits](https://www.conventionalcommits.org/).** + +Use the appropriate type based on the nature of the change: +- `feat:` โ€” new feature or capability change +- `fix:` โ€” bug fix +- `refactor:` โ€” refactor without behavior change + +Always include a `BREAKING CHANGE:` footer in the commit body. + +```bash +# Create feature branch from vibe4 +git checkout vibe4 +git pull origin vibe4 +git checkout -b breaking-change/component-name-api-update + +# Commit changes (only after all checks above pass) +git add . +git commit -m "feat(ComponentName): remove oldProp in favor of newProp + +- Remove deprecated oldProp in favor of newProp +- Update all dependent components +- Add migration guide and codemod +- Update tests for new API + +BREAKING CHANGE: ComponentName.oldProp has been removed. +Use ComponentName.newProp instead. + +Co-Authored-By: Claude Opus 4.6 " + +# Push and create PR +git push -u origin breaking-change/component-name-api-update +gh pr create \ + --title "feat(ComponentName): remove oldProp in favor of newProp" \ + --body "## Summary +โ€ข Remove deprecated \`oldProp\` from ComponentName +โ€ข Update all dependent components to use \`newProp\` +โ€ข Add comprehensive migration guide +โ€ข Include codemod for automated migration + +## Breaking Changes +- \`ComponentName.oldProp\` โ†’ \`ComponentName.newProp\` + +## Task Link +[Monday.com Task](https://monday.monday.com/boards//pulses/) + +## Test Plan +- [ ] All component tests pass +- [ ] Dependent components work correctly +- [ ] Codemod transforms existing usage +- [ ] Migration guide tested +- [ ] Build and lint checks pass + +๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code)" +``` + +## Common Patterns + +### Comprehensive Component Updates + +**Pattern: Systematic Cross-Package Updates** +```bash +# Real example from Icon migration (28 files updated): + +# 1. Component package (source) +packages/components/icon/src/Icon/Icon.tsx # Main component +packages/components/icon/src/Icon/hooks/useIconProps.tsx # Internal hooks + +# 2. Related component packages +packages/components/tooltip/src/Tooltip/Tooltip.tsx # Uses Icon +packages/components/icon-button/src/IconButton/IconButton.tsx # Uses Icon internally + +# 3. Core package components (bulk updates) +packages/core/src/components/AttentionBox/AttentionBox.tsx # 2 Icon instances +packages/core/src/components/Checkbox/Checkbox.tsx # 2 Icon instances +packages/core/src/components/Chips/Chips.tsx # 2 Icon instances +# ... 20+ more core components + +# 4. Supporting updates +packages/mcp/src/server/tools/list-vibe-icons.ts # Documentation examples +packages/core/src/components/DatePicker/DatePickerHeader.tsx # Related component fixes +``` + +**๐Ÿ”ง Batch Update Strategy:** +1. Use `replace_all=true` for simple prop renames across files +2. Use `replace_all=false` for context-specific updates +3. Group similar changes together for efficiency +4. Verify build after each logical group of changes + +### Deterministic Changes (Add Codemod) +- Simple prop renames (`iconSize` โ†’ `size`) +- Enum value updates +- Import path changes +- Method signature changes with clear mapping + +### Non-Deterministic Changes (No Codemod) +- Complex behavioral changes requiring human judgment +- Context-dependent prop usage +- Changes requiring business logic updates +- Multi-step migration requiring staged approach + +### Mixed Changes (Partial Codemod + Manual) +- Prop renames (codemod) + related component fixes (manual) +- API changes (codemod) + TypeScript error fixes (manual) +- Interface updates (codemod) + documentation updates (manual) + +### Error Recovery +- If tests fail: Fix broken components, don't skip tests +- If build fails: Check import/export consistency +- If codemod fails: Validate transform logic with test cases +- If PR blocked: Address review feedback before merging + +## โš ๏ธ Common Pitfalls & Lessons Learned + +### Codemod Implementation Issues + +**โŒ Wrong Function Usage:** +```typescript +// WRONG - this function doesn't exist +renameProp(j, elementPath, "oldProp", "newProp"); + +// CORRECT - use established utility +migratePropsNames(j, elementPath, filePath, componentName, { + oldProp: "newProp" +}); +``` + +**โŒ Wrong Import Path:** +```typescript +// WRONG - looks for old package name +const imports = getCoreImportsForFile(root); + +// CORRECT - specify the right import path +const imports = getImports(root, NEW_CORE_IMPORT_PATH); // "@vibe/core" +``` + +**โŒ Inefficient Multiple Loops:** +```typescript +// WRONG - separate loops for each prop +if (isPropExists(j, elementPath, "prop1")) { + // handle prop1 +} +if (isPropExists(j, elementPath, "prop2")) { + // handle prop2 +} + +// CORRECT - single efficient call +migratePropsNames(j, elementPath, filePath, componentName, { + prop1: "newProp1", + prop2: "newProp2", + prop3: "newProp3" +}); +``` + +### JSCodeshift Formatting Artifacts + +**โš ๏ธ Problem:** jscodeshift adds unnecessary parentheses around JSX elements in files that don't even use the target component. + +**Example Artifacts:** +```jsx +// Before codemod +
+ +// After codemod (WRONG!) +(
+``` + +**๐Ÿ”ง Prevention:** +1. **Target specific files** instead of entire directories +2. **Use `--dry` run first** to verify only expected files change +3. **Review all diffs carefully** before committing +4. **Create cleanup commits** if formatting issues slip through + +**๐Ÿ”ง Cleanup Pattern:** +```typescript +// Find files with formatting artifacts +git diff HEAD~1 HEAD --name-only | grep -v "target-component" + +// Manual cleanup needed for unintended changes +``` + +### Documentation Reversion Issues + +**โš ๏ธ Problem:** Linters, formatters, or branch switches can revert documentation changes. + +**๐Ÿ”ง Prevention:** +- **Commit documentation separately** from code changes +- **Verify documentation persists** after all code changes +- **Check git status** before final PR creation +- **Re-add documentation** if reverted by automation + +### Component Package Dependencies + +**โš ๏ธ Problem:** Components in different packages need different import handling. + +```typescript +// Icon is in @vibe/icon but also re-exported from @vibe/core +// Both need to be handled correctly +``` + +**๐Ÿ”ง Solution:** +- Check component package structure first +- Handle both direct imports and re-exports +- Test codemod on actual usage patterns + +### Build System Validation + +**โš ๏ธ Problem:** Not all components build even before changes, causing false positives. + +**๐Ÿ”ง Approach:** +- **Test individual packages** first (`yarn workspace @vibe/icon build`) +- **Focus on changed packages** rather than full monorepo build +- **Separate build issues** from breaking change issues + +### Cross-Package Component Updates + +**โš ๏ธ Problem:** Breaking changes often affect components across multiple packages, not just the main @vibe/core package. + +**Real Example - Icon Migration:** +Icon component is in `@vibe/icon` but used throughout: +- `packages/components/tooltip/` - standalone package using Icon +- `packages/core/src/components/` - 25+ core components using Icon +- `packages/components/icon-button/` - IconButton using Icon internally +- `packages/mcp/` - documentation and examples + +**๐Ÿ”ง Comprehensive Search Strategy:** + +```bash +# 1. Find ALL files with old prop usage across entire monorepo +grep -r "iconType\|iconSize\|iconLabel" packages/ --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" + +# 2. Identify usage by package type +find packages/components -name "*.tsx" -o -name "*.ts" | xargs grep -l "iconType=" +find packages/core/src -name "*.tsx" -o -name "*.ts" | xargs grep -l "iconSize=" + +# 3. Check supporting files (docs, examples, tests) +grep -r "iconLabel=" packages/mcp packages/docs +``` + +**๐Ÿ”ง Update Strategy:** +1. **Component Package First** - Update the source component and its internal hooks +2. **Core Package Components** - Systematically update all core components (20-30 files typical) +3. **Standalone Packages** - Update components that depend on the changed component +4. **Supporting Files** - Update documentation, examples, and MCP tools +5. **Build Verification** - Test each package individually before full build + +### TypeScript Build Errors in Related Components + +**โš ๏ธ Problem:** Breaking changes can reveal existing TypeScript errors in related components. + +**Real Example - IconButton Props:** +```typescript +// DatePickerHeader was using deprecated Button static properties + + +// Fixed to use string literals + +``` + +**๐Ÿ”ง Resolution Approach:** +1. **Isolate the error** - determine if it's related to your breaking change or pre-existing +2. **Check component API** - verify correct prop values for the component +3. **Use string literals** instead of deprecated static enum properties +4. **Test build** after each fix to catch additional issues + +### Documentation and Example Updates + +**โš ๏ธ Problem:** Code examples in documentation and MCP tools need updating with new API. + +**๐Ÿ”ง Required Updates:** +- **MCP Documentation** - Update usage examples with new props +- **Component Stories** - Storybook examples using the component +- **README files** - Any inline code examples +- **Test files** - Update snapshot tests and component tests + +## Quality Gates + +**โš ๏ธ These are hard gates, not suggestions. Do NOT proceed past a gate until every item passes.** + +**Before committing (all must pass with exit code 0):** +- [ ] `yarn lint` โ€” zero errors +- [ ] `yarn build` โ€” zero errors +- [ ] `yarn test` โ€” zero failures +- [ ] Migration guide entry complete + +**Before creating PR:** +- [ ] All of the above still pass after final commit +- [ ] Codemod tested (if applicable) +- [ ] Documentation updated + +## Integration Points + +- **Package Dependencies:** Update `package.json` files as needed +- **TypeScript:** Ensure type consistency across packages +- **Build System:** Verify Rollup configurations handle changes +- **Storybook:** Update stories to reflect new API +- **Documentation:** Sync with component documentation site + diff --git a/.claude/skills/vibe-breaking-change/references/codemod-best-practices.md b/.claude/skills/vibe-breaking-change/references/codemod-best-practices.md new file mode 100644 index 0000000000..536093d03f --- /dev/null +++ b/.claude/skills/vibe-breaking-change/references/codemod-best-practices.md @@ -0,0 +1,273 @@ +# Codemod Best Practices for Vibe Breaking Changes + +## Lessons Learned from Real Implementation + +This guide covers critical lessons learned from implementing the Icon component prop renames codemod, including major pitfalls to avoid. + +## โŒ Common Mistakes to Avoid + +### 1. Using Non-Existent Functions + +**WRONG:** +```typescript +import { renameProp } from "../../../src/utils"; // โŒ Doesn't exist + +elements.forEach(elementPath => { + if (isPropExists(j, elementPath, "oldProp")) { + renameProp(j, elementPath, "oldProp", "newProp"); // โŒ Function doesn't exist + } +}); +``` + +**CORRECT:** +```typescript +import { migratePropsNames } from "../../../src/utils"; // โœ… Real function + +elements.forEach(elementPath => { + migratePropsNames(j, elementPath, filePath, componentName, { + oldProp: "newProp", + anotherOld: "anotherNew" + }); // โœ… Handles all props in one efficient call +}); +``` + +### 2. Wrong Import Path Detection + +**WRONG:** +```typescript +// This only finds "monday-ui-react-core" imports, not "@vibe/core" +const imports = getCoreImportsForFile(root); +``` + +**CORRECT:** +```typescript +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; + +// This finds "@vibe/core" imports correctly +const imports = getImports(root, NEW_CORE_IMPORT_PATH); +``` + +### 3. Inefficient Multiple Loops + +**WRONG:** +```typescript +elements.forEach(elementPath => { + // Separate check for each prop - inefficient! + if (isPropExists(j, elementPath, "iconLabel")) { + renameProp(j, elementPath, "iconLabel", "label"); + } + if (isPropExists(j, elementPath, "iconType")) { + renameProp(j, elementPath, "iconType", "type"); + } + if (isPropExists(j, elementPath, "iconSize")) { + renameProp(j, elementPath, "iconSize", "size"); + } +}); +``` + +**CORRECT:** +```typescript +elements.forEach(elementPath => { + // Single call handles all prop renames efficiently + migratePropsNames(j, elementPath, filePath, componentName, { + iconLabel: "label", + iconType: "type", + iconSize: "size" + }); +}); +``` + +## ๐Ÿ”ง Proven Codemod Pattern + +Based on successful v2-v3 codemods, use this proven pattern: + +```typescript +import { + wrap, + getImports, + getComponentNameOrAliasFromImports, + findComponentElements, + migratePropsNames +} from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; + +function transform({ j, root, filePath }: TransformationContext) { + // 1. Find imports from correct package + const imports = getImports(root, NEW_CORE_IMPORT_PATH); + + // 2. Check if component is imported + const componentName = getComponentNameOrAliasFromImports(j, imports, "ComponentName"); + if (!componentName) return; + + // 3. Find all component elements + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + // 4. Migrate props efficiently + elements.forEach(elementPath => { + migratePropsNames(j, elementPath, filePath, componentName, { + oldProp1: "newProp1", + oldProp2: "newProp2", + oldProp3: "newProp3" + }); + }); +} + +export default wrap(transform); +``` + +## โš ๏ธ JSCodeshift Formatting Artifacts + +### The Problem + +JSCodeshift can add unnecessary parentheses around JSX elements, even in files that **don't use the target component**. + +**Example:** +```jsx +// Original code +
+ Content +
+ +// After codemod (WRONG!) +(
+ Content +
) +``` + +### Root Cause + +JSCodeshift parses and rebuilds the AST, which can introduce formatting changes even when no transforms are applied. + +### Prevention Strategies + +#### 1. Target Specific Files +```bash +# WRONG - transforms entire directory +npx jscodeshift -t transform.js packages/core/src/components/ + +# BETTER - target specific files known to use the component +npx jscodeshift -t transform.js packages/core/src/components/Button/Button.tsx packages/core/src/components/IconButton/IconButton.tsx +``` + +#### 2. Use Dry Run First +```bash +# Always run dry first to see what changes +npx jscodeshift -t transform.js packages/ --dry + +# Review the output carefully +# Only proceed if changes look correct +npx jscodeshift -t transform.js packages/ +``` + +#### 3. Filter by Import Detection +```typescript +function transform({ j, root, filePath }: TransformationContext) { + // Only proceed if file actually imports the component + const imports = getImports(root, NEW_CORE_IMPORT_PATH); + const componentName = getComponentNameOrAliasFromImports(j, imports, "ComponentName"); + if (!componentName) return; // โœ… Exit early, no changes + + // ... rest of transform +} +``` + +### Cleanup Process + +If formatting artifacts slip through: + +```bash +# 1. Identify files with unintended changes +git diff HEAD~1 HEAD --name-only | grep -E "(Checkbox|AvatarGroup|Dropdown)" + +# 2. Review each file +git diff HEAD~1 HEAD -- path/to/file.tsx + +# 3. Manual cleanup +# Remove extra parentheses: (
โ†’
+# Fix indentation as needed + +# 4. Commit cleanup +git add . +git commit -m "fix: remove codemod formatting artifacts" +``` + +## ๐Ÿงช Testing Best Practices + +### Test Development +```typescript +// Cover edge cases in tests +defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should rename all three props" +); + +// Test aliased imports +defineInlineTest( + transform, + {}, + `import { Icon as VibeIcon } from "@vibe/core"; + const element = ;`, + `import { Icon as VibeIcon } from "@vibe/core"; + const element = ;`, + "should work with aliased imports" +); +``` + +### Expected Results +- **7-8 out of 8 tests pass** - indicates working codemod +- **1 formatting test failure** - expected jscodeshift artifact (acceptable) +- **0 tests pass** - indicates broken codemod logic + +## ๐Ÿ“ Documentation Integration + +### Migration Guide Entry +```markdown +## ComponentName API Changes + +### Breaking Changes +**Props Renamed:** +- `oldProp` โ†’ `newProp` +- `anotherOld` โ†’ `anotherNew` + +### Automated Migration +```bash +npx @vibe/codemod component-name-props-update src/ +``` + +### Manual Migration +[Include before/after examples] +``` + +### Changelog Entry +```markdown +#### ComponentName v2.0.0 +- **BREAKING**: Renamed props for better consistency +- **Migration**: Use `npx @vibe/codemod component-name-props-update` +- **Task**: Monday.com #[task-id] +``` + +## ๐Ÿš€ Success Criteria + +A successful codemod implementation should: + +1. **โœ… Follow established patterns** from v2-v3 codemods +2. **โœ… Use correct utility functions** (migratePropsNames, getImports) +3. **โœ… Handle import paths correctly** (NEW_CORE_IMPORT_PATH) +4. **โœ… Be efficient** (single loop, batch prop updates) +5. **โœ… Have comprehensive tests** (7+ passing test cases) +6. **โœ… Minimize formatting artifacts** (targeted file updates) +7. **โœ… Include cleanup process** (for any artifacts that slip through) + +## ๐Ÿ”„ Iteration Process + +1. **RED**: Write failing tests first +2. **GREEN**: Implement minimal working codemod +3. **REFACTOR**: Fix issues, improve efficiency +4. **CLEANUP**: Remove formatting artifacts +5. **DOCUMENT**: Update migration guide and changelog +6. **VALIDATE**: Test on real codebase examples \ No newline at end of file diff --git a/.claude/skills/vibe-breaking-change/references/codemod-examples.md b/.claude/skills/vibe-breaking-change/references/codemod-examples.md new file mode 100644 index 0000000000..32e8519813 --- /dev/null +++ b/.claude/skills/vibe-breaking-change/references/codemod-examples.md @@ -0,0 +1,125 @@ +# Codemod Examples for Vibe Breaking Changes + +## Simple Prop Rename + +```typescript +// Transform: oldProp โ†’ newProp +import type { Transform } from 'jscodeshift'; + +const transform: Transform = (file, api) => { + const j = api.jscodeshift; + const root = j(file.source); + + // Transform JSX prop usage + root + .find(j.JSXElement) + .filter(path => { + const name = path.value.openingElement?.name; + return name && name.type === 'JSXIdentifier' && name.name === 'ComponentName'; + }) + .forEach(path => { + const attributes = path.value.openingElement?.attributes || []; + attributes.forEach(attr => { + if ( + attr.type === 'JSXAttribute' && + attr.name?.type === 'JSXIdentifier' && + attr.name.name === 'oldProp' + ) { + attr.name.name = 'newProp'; + } + }); + }); + + return root.toSource(); +}; + +export default transform; +``` + +## Enum to String Union Migration + +```typescript +// Transform: ComponentName.SIZE.LARGE โ†’ "large" +import type { Transform } from 'jscodeshift'; + +const transform: Transform = (file, api) => { + const j = api.jscodeshift; + const root = j(file.source); + + // Replace enum member access with string literal + root + .find(j.MemberExpression, { + object: { + type: 'MemberExpression', + object: { name: 'ComponentName' }, + property: { name: 'SIZE' } + } + }) + .forEach(path => { + const property = path.value.property; + if (property.type === 'Identifier') { + const stringValue = property.name.toLowerCase(); + j(path).replaceWith(j.literal(stringValue)); + } + }); + + return root.toSource(); +}; + +export default transform; +``` + +## Complex Import Path Update + +```typescript +// Transform: from old package to new package structure +import type { Transform } from 'jscodeshift'; + +const transform: Transform = (file, api) => { + const j = api.jscodeshift; + const root = j(file.source); + + // Update import declarations + root + .find(j.ImportDeclaration) + .filter(path => { + return path.value.source.value?.includes('@vibe/core/components/ComponentName'); + }) + .forEach(path => { + if (path.value.source.type === 'Literal') { + path.value.source.value = '@vibe/component-name'; + } + }); + + return root.toSource(); +}; + +export default transform; +``` + +## Testing Codemod Changes + +```bash +# Test codemod on specific file +npx jscodeshift -t packages/codemod/src/transforms/component-prop-rename.ts packages/core/src/components/Button/__tests__/Button.test.tsx --dry + +# Test on directory +npx jscodeshift -t packages/codemod/src/transforms/component-prop-rename.ts packages/core/src/components/ --dry + +# Apply transformation +npx jscodeshift -t packages/codemod/src/transforms/component-prop-rename.ts packages/core/src/components/ + +# Add to codemod CLI +# Edit packages/codemod/src/index.ts to include new transform +``` + +## Codemod Integration Checklist + +- [ ] Transform handles JSX usage +- [ ] Transform handles TypeScript interfaces +- [ ] Transform handles prop destructuring +- [ ] Transform handles spread props appropriately +- [ ] Test cases cover edge cases +- [ ] Dry run shows expected changes +- [ ] Added to codemod CLI interface +- [ ] Documentation includes usage example \ No newline at end of file diff --git a/.claude/skills/vibe-breaking-change/references/dependency-analysis.md b/.claude/skills/vibe-breaking-change/references/dependency-analysis.md new file mode 100644 index 0000000000..5c37b6145b --- /dev/null +++ b/.claude/skills/vibe-breaking-change/references/dependency-analysis.md @@ -0,0 +1,150 @@ +# Dependency Analysis for Breaking Changes + +## Component Dependency Mapping + +### Finding Direct Dependencies + +```bash +# Find components importing the target component +grep -r "import.*ComponentName" packages/core/src/components/ +grep -r "import.*ComponentName" packages/components/ + +# Find usage in TypeScript files +grep -r "ComponentName" packages/*/src/**/*.tsx +grep -r "ComponentName" packages/*/src/**/*.ts + +# Find usage in test files +grep -r "ComponentName" packages/*/src/**/__tests__/*.tsx +grep -r "ComponentName" packages/*/src/**/__stories__/*.tsx +``` + +### Finding Indirect Dependencies + +```bash +# Check for components that extend or compose target component +grep -r "extends.*ComponentNameProps" packages/ +grep -r "ComponentName.*Props" packages/ + +# Find re-exports +grep -r "export.*ComponentName" packages/*/src/index.ts +grep -r "export.*from.*ComponentName" packages/ +``` + +### Analyzing Package Dependencies + +```bash +# Check package.json dependencies +find packages/ -name "package.json" -exec grep -l "@vibe/component-name\|ComponentName" {} \; + +# Check workspace dependencies +yarn workspace @vibe/core info +yarn workspaces info +``` + +## Impact Assessment Matrix + +| Component | Import Type | Usage Pattern | Breaking Impact | Update Required | +|-----------|-------------|---------------|-----------------|-----------------| +| ButtonGroup | Direct | Props passed through | High | Yes - API change | +| Dialog | Indirect | Used in composition | Medium | Yes - prop update | +| Form | Test only | Test utilities | Low | Maybe - test update | + +## Component Relationship Types + +### Direct Usage +- Component imports and uses target component +- Props passed directly to target component +- **Impact:** High - requires immediate update + +### Composed Usage +- Component wraps or extends target component +- May add additional props or behavior +- **Impact:** Medium - requires careful testing + +### Re-export Usage +- Package re-exports target component +- May not use component directly +- **Impact:** Low - update exports only + +### Test/Story Usage +- Only used in test files or Storybook +- No runtime impact on users +- **Impact:** Minimal - update tests/stories + +## Dependency Update Strategy + +### Phase 1: Core Component Update +1. Update target component with breaking change +2. Update component types and interfaces +3. Ensure component builds and basic tests pass + +### Phase 2: Direct Dependencies +1. Update components with direct imports +2. Update prop passing and usage patterns +3. Fix compilation errors + +### Phase 3: Indirect Dependencies +1. Update composed components +2. Update complex usage patterns +3. Verify behavioral consistency + +### Phase 4: Package Dependencies +1. Update package.json versions if needed +2. Update workspace dependencies +3. Verify cross-package compatibility + +## Validation Commands + +```bash +# Check TypeScript compilation across all packages +lerna run type-check + +# Check for remaining references to old API +grep -r "oldProp" packages/ --include="*.ts" --include="*.tsx" + +# Verify no broken imports +yarn workspace @vibe/core build +lerna run build + +# Check for usage in Storybook +grep -r "oldProp" packages/**/__stories__/ +``` + +## Common Dependency Patterns + +### Button Dependencies +- ButtonGroup, IconButton, SplitButton +- Form components (FieldButton, FormButton) +- Dialog components (DialogButton) + +### Icon Dependencies +- All components using icons +- IconButton, Button with icons +- Menu items, navigation components + +### Layout Dependencies +- Grid system components +- Container components +- Responsive utilities + +### Hook Dependencies +- Components using shared hooks +- Custom hook implementations +- Test utilities using hooks + +## Risk Mitigation + +### High-Risk Changes +- Core utility changes (hooks, constants) +- Base component changes (Button, Icon) +- Type system changes (VibeComponentProps) + +### Medium-Risk Changes +- Layout component changes +- Form component changes +- Complex composite components + +### Low-Risk Changes +- Leaf components (no dependencies) +- Style-only changes +- Test utility changes \ No newline at end of file diff --git a/.claude/skills/vibe-breaking-change/references/pr-templates.md b/.claude/skills/vibe-breaking-change/references/pr-templates.md new file mode 100644 index 0000000000..2e8e6f8b3c --- /dev/null +++ b/.claude/skills/vibe-breaking-change/references/pr-templates.md @@ -0,0 +1,290 @@ +# PR and Documentation Templates + +## PR Template for Breaking Changes + +### PR Title Format + +Follow [Conventional Commits](https://www.conventionalcommits.org/). Use the appropriate type (`feat`, `fix`, `refactor`, etc.): + +``` +(): brief description + +Examples: +feat(Button): remove deprecated size prop +refactor(Dialog): update API for better accessibility +fix(Form): replace onSubmit with onFormSubmit +``` + +### PR Description Template + +```markdown +## Summary +โ€ข Brief description of the breaking change +โ€ข Why this change was necessary +โ€ข Impact on existing users + +## Breaking Changes +**[ComponentName] API Changes:** +- โŒ Removed: `oldProp` prop +- โœ… Added: `newProp` prop with enhanced functionality +- ๐Ÿ”„ Changed: `existingProp` now requires different value format + +## Migration Path + +### Automated Migration (Codemod Available) +```bash +npx @vibe/codemod component-name-api-update +``` + +### Manual Migration +**Before:** +```jsx + +``` + +**After:** +```jsx + +``` + +## Task Link +[Monday.com Task](https://monday.monday.com/boards//pulses/) + +## Testing +- [ ] All component tests pass +- [ ] Integration tests updated and passing +- [ ] Dependent components tested +- [ ] Storybook stories render correctly +- [ ] Codemod tested on real codebase examples +- [ ] Performance impact assessed + +## Documentation +- [ ] Migration guide updated +- [ ] API documentation updated +- [ ] Changelog entry added +- [ ] Codemod documentation added + +## Validation +- [ ] Full monorepo build passes +- [ ] All linting checks pass +- [ ] TypeScript compilation successful +- [ ] No broken imports or exports +- [ ] Manual testing completed + +## Reviewer Notes +- Focus on [specific areas of concern] +- Pay attention to [migration complexity/edge cases] +- Verify [specific functionality still works] + +--- +๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) +``` + +## Commit Message Template + +Follow [Conventional Commits](https://www.conventionalcommits.org/). Use the appropriate type (`feat`, `fix`, `refactor`, etc.): + +``` +(): brief description + +Detailed explanation of what changed and why. +Include context about the problem being solved. + +BREAKING CHANGE: Specific description of what breaks. +Explain the old behavior vs new behavior. + +Migration: +- Old usage: ComponentName.oldProp +- New usage: ComponentName.newProp +- Codemod: npx @vibe/codemod component-migration + +Affects: +- ComponentName API +- Dependent components: ButtonGroup, Dialog +- Documentation: Migration guide updated + +Co-Authored-By: Claude Opus 4.6 +``` + +## Migration Guide Entry Template + +```markdown +## [ComponentName] API Changes + +### Overview +Brief explanation of what changed and why the breaking change was necessary. + +### Breaking Changes + +#### Removed `oldProp` prop +- **Impact**: High - affects all usage +- **Reason**: Simplifies API and improves performance +- **Migration**: Replace with `newProp` + +#### Changed `existingProp` format +- **Impact**: Medium - affects complex usage +- **Reason**: Better type safety and validation +- **Migration**: Convert values using utility function + +### Before and After + +#### Basic Usage +```jsx +// Before + + +// After + +``` + +#### Advanced Usage +```jsx +// Before + handleOldWay(data)} +/> + +// After + handleNewWay(data)} +/> +``` + +### Migration Strategies + +#### Automated Migration +Use the provided codemod for straightforward cases: +```bash +npx @vibe/codemod componentname-api-update src/ +``` + +The codemod handles: +- Simple prop renames +- Basic value transformations +- Import statement updates + +#### Manual Migration +For complex cases requiring human judgment: + +1. **Dynamic prop values**: Review logic and update calculations +2. **Event handlers**: Update to match new event signature +3. **Conditional usage**: Verify new API handles all conditions +4. **Tests**: Update test assertions and mocks + +#### Gradual Migration +For large codebases, consider a phased approach: + +1. **Phase 1**: Update critical components first +2. **Phase 2**: Update high-traffic components +3. **Phase 3**: Update remaining components +4. **Phase 4**: Remove any compatibility shims + +### Common Issues + +#### TypeScript Errors +```typescript +// Error: Property 'oldProp' does not exist + + +// Solution: Use new prop name + +``` + +#### Runtime Errors +```javascript +// Error: onOldEvent is not a function +props.onOldEvent?.(data); + +// Solution: Use new event name +props.onNewEvent?.(data); +``` + +### Validation +After migration, verify: +- [ ] No TypeScript compilation errors +- [ ] Component renders as expected +- [ ] Event handlers work correctly +- [ ] Tests pass +- [ ] No console warnings +``` + +## Changelog Entry Template + +```markdown +### BREAKING CHANGES + +#### ComponentName v2.0.0 +- **BREAKING**: Removed `oldProp` in favor of `newProp` for better API consistency +- **BREAKING**: Changed `existingProp` format from object to string for improved performance +- **Migration**: Use `npx @vibe/codemod componentname-api-update` for automated migration +- **Affects**: All ComponentName usage, ButtonGroup, Dialog components + +##### Migration Example +```jsx +// Before + + +// After + +``` + +See [Migration Guide](./VIBE4_MIGRATION_GUIDE.md#componentname-api-changes) for detailed instructions. +``` + +## Code Review Checklist Template + +```markdown +## Breaking Change Review Checklist + +### API Design +- [ ] Breaking change is necessary and well-justified +- [ ] New API is more intuitive than old API +- [ ] API follows Vibe design system conventions +- [ ] TypeScript types are accurate and helpful + +### Implementation +- [ ] Target component updated correctly +- [ ] All dependent components updated +- [ ] No lingering references to old API +- [ ] Error handling updated appropriately + +### Testing +- [ ] Component tests updated and comprehensive +- [ ] Integration tests cover dependent components +- [ ] Edge cases tested +- [ ] Performance impact assessed + +### Documentation +- [ ] Migration guide entry is clear and complete +- [ ] Code examples work correctly +- [ ] Codemod documented and tested +- [ ] Breaking change clearly flagged + +### Developer Experience +- [ ] TypeScript errors are helpful +- [ ] Runtime errors (if any) are clear +- [ ] Migration path is straightforward +- [ ] Documentation is discoverable + +### Build System +- [ ] All packages build successfully +- [ ] No circular dependencies introduced +- [ ] Import/export structure clean +- [ ] Version bumps appropriate +``` \ No newline at end of file diff --git a/.claude/skills/vibe-breaking-change/references/testing-validation.md b/.claude/skills/vibe-breaking-change/references/testing-validation.md new file mode 100644 index 0000000000..336e05852b --- /dev/null +++ b/.claude/skills/vibe-breaking-change/references/testing-validation.md @@ -0,0 +1,211 @@ +# Testing and Validation for Breaking Changes + +## Testing Strategy + +### 1. Unit Tests +Test individual component behavior with new API + +```typescript +// Before: Test with old API +it('should handle oldProp correctly', () => { + render(); + expect(screen.getByRole('button')).toHaveAttribute('data-old', 'value'); +}); + +// After: Test with new API +it('should handle newProp correctly', () => { + render(); + expect(screen.getByRole('button')).toHaveAttribute('data-new', 'value'); +}); + +// Migration test: Ensure old API no longer works +it('should not accept oldProp', () => { + // TypeScript should prevent this at compile time + // @ts-expect-error - oldProp no longer exists + render(); +}); +``` + +### 2. Integration Tests +Test dependent components work with changes + +```typescript +// Test component that uses the changed component +it('should work with updated ComponentName API', () => { + render( + + + + ); + + // Verify integration works + expect(screen.getByTestId('parent-wrapper')).toBeInTheDocument(); + expect(screen.getByRole('button')).toHaveAttribute('data-new', 'value'); +}); +``` + +### 3. Snapshot Testing +Update snapshots for visual regression testing + +```bash +# Update component snapshots +yarn workspace @vibe/core test -- ComponentName --updateSnapshot + +# Update all affected snapshots +yarn workspace @vibe/core test -- --updateSnapshot + +# Check snapshot diffs are intentional +git diff packages/core/src/components/ComponentName/__tests__/__snapshots__/ +``` + +## Validation Checklist + +### Pre-Implementation Validation +- [ ] Impact analysis complete +- [ ] Dependent components identified +- [ ] Migration strategy defined +- [ ] Breaking change justified and approved + +### Implementation Validation +- [ ] Target component updated correctly +- [ ] TypeScript compilation passes +- [ ] Component tests updated and passing +- [ ] No console errors in development + +### Dependency Validation +- [ ] All dependent components updated +- [ ] Integration tests passing +- [ ] No broken imports or exports +- [ ] Cross-package dependencies resolved + +### Build Validation +```bash +# Full monorepo build +lerna run build + +# Individual package builds +yarn workspace @vibe/core build +yarn workspace @vibe/components build + +# TypeScript validation +lerna run type-check + +# Lint validation +lerna run lint +``` + +### Runtime Validation +```bash +# Storybook validation +yarn storybook +# Manually verify affected stories render correctly + +# Test app validation (if available) +yarn start +# Test breaking changes in demo application +``` + +### Documentation Validation +- [ ] Migration guide updated with clear examples +- [ ] Breaking change documented with before/after +- [ ] Codemod usage documented (if applicable) +- [ ] API documentation updated + +## Test Update Patterns + +### Props Interface Changes +```typescript +// Old interface +interface OldComponentProps { + oldProp?: string; + size?: 'small' | 'medium' | 'large'; +} + +// New interface +interface NewComponentProps { + newProp?: string; + size?: ComponentSize; // enum to string union +} + +// Test updates needed: +// 1. Update prop names in test cases +// 2. Update enum values to string literals +// 3. Add tests for new API behavior +// 4. Remove tests for deprecated functionality +``` + +### Behavior Changes +```typescript +// Test behavioral changes +describe('ComponentName behavior changes', () => { + it('should trigger callback on new event', () => { + const mockCallback = jest.fn(); + render(); + + // Trigger new behavior + fireEvent.click(screen.getByRole('button')); + expect(mockCallback).toHaveBeenCalledWith(/* new signature */); + }); + + it('should not trigger old callback', () => { + // Ensure old behavior is removed + const mockOldCallback = jest.fn(); + // @ts-expect-error - old callback should not exist + render(); + }); +}); +``` + +## Common Testing Pitfalls + +### 1. Incomplete Test Updates +- **Problem:** Tests still use old API but pass due to lenient typing +- **Solution:** Enable strict TypeScript checking in tests +- **Validation:** `yarn workspace @vibe/core test --typecheck` + +### 2. Missing Integration Tests +- **Problem:** Individual components work but integration breaks +- **Solution:** Test component composition scenarios +- **Validation:** Create test cases for typical usage patterns + +### 3. Snapshot Pollution +- **Problem:** Snapshots updated but changes not reviewed +- **Solution:** Review snapshot diffs before committing +- **Validation:** `git diff --no-index old-snapshot new-snapshot` + +### 4. Performance Regressions +- **Problem:** New API performs worse than old API +- **Solution:** Add performance benchmarks for critical components +- **Validation:** Use React DevTools Profiler + +## Automated Validation Scripts + +### Pre-commit Validation +```bash +#!/bin/bash +# scripts/validate-breaking-change.sh + +echo "๐Ÿ” Validating breaking change implementation..." + +# 1. Check TypeScript compilation +echo "Checking TypeScript..." +lerna run type-check || exit 1 + +# 2. Run tests +echo "Running tests..." +lerna run test || exit 1 + +# 3. Check for old API usage +echo "Checking for old API usage..." +if grep -r "oldProp" packages/ --include="*.ts" --include="*.tsx"; then + echo "โŒ Found old API usage" + exit 1 +fi + +# 4. Build validation +echo "Validating build..." +lerna run build || exit 1 + +echo "โœ… Breaking change validation passed" +``` + diff --git a/.claude/skills/vibe-breaking-change/references/workflow-checklist.md b/.claude/skills/vibe-breaking-change/references/workflow-checklist.md new file mode 100644 index 0000000000..c637405ce0 --- /dev/null +++ b/.claude/skills/vibe-breaking-change/references/workflow-checklist.md @@ -0,0 +1,194 @@ +# Complete Breaking Change Workflow Checklist + +## Pre-Implementation Phase + +### Planning and Analysis +- [ ] Breaking change request approved by team +- [ ] Impact analysis completed +- [ ] Alternative solutions considered and ruled out +- [ ] Migration strategy defined +- [ ] Timeline established + +### Dependency Mapping +- [ ] Direct dependencies identified +- [ ] Indirect dependencies mapped +- [ ] Cross-package impacts assessed +- [ ] Test dependencies catalogued +- [ ] Documentation dependencies noted + +## Implementation Phase + +### Code Changes +- [ ] Target component updated with breaking change +- [ ] TypeScript interfaces updated +- [ ] Component logic implements new API +- [ ] Old API removed or deprecated appropriately +- [ ] Component constants updated + +### Dependent Components +- [ ] Direct dependents updated +- [ ] Prop passing updated +- [ ] Event handling updated +- [ ] Composition patterns maintained +- [ ] Integration points verified + +### Type System +- [ ] Component props interfaces updated +- [ ] Export types updated +- [ ] Enum to string union conversions (if applicable) +- [ ] Generic constraints updated +- [ ] Utility type helpers updated + +## Testing Phase + +### Unit Testing +- [ ] Component tests updated for new API +- [ ] Old API tests removed +- [ ] Edge cases covered +- [ ] Error conditions tested +- [ ] Accessibility tests updated + +### Integration Testing +- [ ] Dependent component tests updated +- [ ] Cross-package integration tested +- [ ] Composition scenarios tested +- [ ] Event flow tested +- [ ] State management tested + +### Build and Compilation +- [ ] TypeScript compilation passes +- [ ] All packages build successfully +- [ ] No circular dependencies +- [ ] Import/export consistency verified +- [ ] Bundle size impact assessed + +### Quality Assurance +- [ ] Linting passes +- [ ] Style linting passes +- [ ] Prettier formatting applied +- [ ] No console errors/warnings +- [ ] Performance benchmarks within acceptable range + +## Documentation Phase + +### Migration Guide +- [ ] Clear before/after examples +- [ ] Migration strategies documented +- [ ] Common issues addressed +- [ ] Validation steps provided +- [ ] Timeline for migration suggested + +### API Documentation +- [ ] Component API docs updated +- [ ] Props documentation current +- [ ] Examples reflect new API +- [ ] TypeScript signatures accurate +- [ ] Deprecation notices removed + +### Changelog +- [ ] Breaking change clearly flagged +- [ ] Version bump appropriate +- [ ] Impact description clear +- [ ] Migration instructions included +- [ ] Related issues referenced + +## Codemod Phase (If Applicable) + +### Codemod Development +- [ ] Transform logic implements correctly +- [ ] Edge cases handled +- [ ] TypeScript compatibility maintained +- [ ] JSX patterns covered +- [ ] Test cases comprehensive + +### Codemod Testing +- [ ] Dry run on real examples +- [ ] Before/after comparison reviewed +- [ ] Complex scenarios tested +- [ ] Error handling validated +- [ ] Performance acceptable + +### Codemod Integration +- [ ] Added to codemod CLI +- [ ] Documentation written +- [ ] Usage examples provided +- [ ] Integration tests added +- [ ] Release notes updated + +## Validation Phase + +### Comprehensive Testing +- [ ] Full test suite passes +- [ ] Storybook stories render correctly +- [ ] Example applications work +- [ ] Performance tests pass +- [ ] Accessibility tests pass + +### Build Verification +- [ ] All packages build +- [ ] Linting checks pass +- [ ] Type checking passes +- [ ] Bundle analysis clean +- [ ] No build warnings + +### Manual Testing +- [ ] Component behavior verified in browser +- [ ] Responsive behavior tested +- [ ] Keyboard navigation tested +- [ ] Screen reader compatibility verified +- [ ] Cross-browser testing completed + +## Release Phase + +### Branch and Commit +- [ ] Feature branch created from vibe4 +- [ ] Meaningful commit messages +- [ ] Breaking change flagged in commit +- [ ] Co-authored appropriately +- [ ] Commit history clean + +### Pull Request +- [ ] PR title follows convention +- [ ] Description comprehensive and clear +- [ ] Breaking changes highlighted +- [ ] Migration instructions included +- [ ] Task/issue linked +- [ ] Reviewers assigned + +### Code Review +- [ ] API design reviewed +- [ ] Implementation reviewed +- [ ] Test coverage reviewed +- [ ] Documentation reviewed +- [ ] Migration path validated + +### CI/CD Pipeline +- [ ] All automated tests pass +- [ ] Build artifacts generated +- [ ] Quality gates passed +- [ ] Security scans clean +- [ ] Performance benchmarks acceptable + +## Post-Implementation Phase + +### Merge and Deploy +- [ ] PR approved and merged +- [ ] Branch cleaned up +- [ ] Version tagged appropriately +- [ ] Release notes published +- [ ] Team notified of changes + +### Monitoring +- [ ] Error rates monitored +- [ ] Performance impact tracked +- [ ] User feedback collected +- [ ] Migration adoption tracked +- [ ] Support requests handled + +### Follow-up Actions +- [ ] Migration deadline set and communicated +- [ ] Deprecated API removal scheduled +- [ ] Backward compatibility plan finalized +- [ ] Success metrics defined +- [ ] Lessons learned documented + diff --git a/.cursor/rules/base-components.mdc b/.cursor/rules/base-components.mdc index 4607c0d938..db4f425e7e 100644 --- a/.cursor/rules/base-components.mdc +++ b/.cursor/rules/base-components.mdc @@ -234,7 +234,7 @@ Components support various size variants. Components use CSS custom properties for theming: -- Design tokens from Vibe's styling library (`monday-ui-style`) +- Design tokens from Vibe's styling library (`@vibe/style`) - Automatic dark/light mode support - Consistent spacing and typography scales diff --git a/.cursor/rules/component-internal-structure.mdc b/.cursor/rules/component-internal-structure.mdc index 8c8fdfbb64..04c0cdaeb1 100644 --- a/.cursor/rules/component-internal-structure.mdc +++ b/.cursor/rules/component-internal-structure.mdc @@ -13,7 +13,6 @@ alwaysApply: false 2. **TypeScript**: All components must be written in TypeScript. 3. **`forwardRef`**: Prefer using `React.forwardRef` for all components that might need to expose their underlying DOM element or instance to parent components. The `ref` should be typed directly on the function's second argument. 4. **Props Typing and Destructuring**: Props must be destructured in the component function's first argument, and this argument must be explicitly typed with the component's props type. -5. **No Static Properties**: Never use the `withStaticProps` utility or assign static properties to components. Components should remain without attached static properties. This means avoiding patterns like `MyComponent.colors = {...}`, `MyComponent.sizes = {...}`, and even avoiding `MyComponent.displayName = "..."`. ## File Structure (within the `.tsx` file) diff --git a/.cursor/rules/styling-conventions.mdc b/.cursor/rules/styling-conventions.mdc index 189d341a3c..d01cad5744 100644 --- a/.cursor/rules/styling-conventions.mdc +++ b/.cursor/rules/styling-conventions.mdc @@ -1,5 +1,5 @@ --- -description: "Provides comprehensive guidelines for writing CSS Modules specifically for UI components within the `@vibe/core` library (under `packages/core/src/components/`). This rule covers file naming conventions (e.g., `ComponentName.module.scss`), CSS nesting, mandatory browser compatibility, strategies for adopting newer CSS APIs with fallbacks, the exclusive use of design tokens from `monday-ui-style` (via CSS custom properties like `var(--primary-text-color)`), discouraging mixin usage from `monday-ui-style`, typography best practices (using `Text`/`Heading` components instead of direct font tokens), `camelCase` class naming, and common anti-patterns like avoiding theme-specific styles directly in component SCSS, never using `:global`, and never using `!important`. Activate this rule when working with `.module.scss` files in `packages/core/src/components/` or when styling Vibe core components." +description: "Provides comprehensive guidelines for writing CSS Modules specifically for UI components within the `@vibe/core` library (under `packages/core/src/components/`). This rule covers file naming conventions (e.g., `ComponentName.module.scss`), CSS nesting, mandatory browser compatibility, strategies for adopting newer CSS APIs with fallbacks, the exclusive use of design tokens from `@vibe/style` (via CSS custom properties like `var(--primary-text-color)`), discouraging mixin usage from `@vibe/style`, typography best practices (using `Text`/`Heading` components instead of direct font tokens), `camelCase` class naming, and common anti-patterns like avoiding theme-specific styles directly in component SCSS, never using `:global`, and never using `!important`. Activate this rule when working with `.module.scss` files in `packages/core/src/components/` or when styling Vibe core components." globs: - "packages/core/src/components/**/*.module.scss" alwaysApply: false @@ -62,20 +62,20 @@ This document outlines the rules and best practices for writing styles using SCS ## 3. Design Tokens - **Utilize Design Tokens:** Always prefer design tokens over hardcoded values (e.g., pixel values for spacing, specific hex codes for colors). -- **Source of Tokens:** Design tokens are provided by the `monday-ui-style` package, located in the `packages/style` directory. The available tokens are defined as CSS custom properties within the theme classes (e.g., `.default-app-theme`, `.dark-app-theme`, `.black-app-theme`) in the `packages/style/dist/index.css` file. This file serves as the source of truth for all available tokens. +- **Source of Tokens:** Design tokens are provided by the `@vibe/style` package, located in the `packages/style` directory. The available tokens are defined as CSS custom properties within the theme classes (e.g., `.default-app-theme`, `.dark-app-theme`, `.black-app-theme`) in the `packages/style/dist/index.css` file. This file serves as the source of truth for all available tokens. - Tokens are typically used as CSS custom properties: `var(--token-name)`. - Example: `color: var(--primary-text-color);`, `margin-bottom: var(--space-8);` - **Semantic Usage:** Use tokens semantically. For instance, use a background color token for backgrounds, a text color token for text, and spacing tokens for margins/paddings, according to their intended purpose. - **Pixel Units for Non-Token Values:** When a design token is not available or suitable for a specific dimension (e.g., a unique border width, a specific icon size not covered by spacing tokens), always use pixel (`px`) units. Do not use `em`, `rem`, percentages, or other relative units for these cases unless explicitly part of a design token's definition or a well-justified, specific requirement. - **Prefer New Spacing System:** For layout spacing (margins, paddings, gaps, etc.), prioritize the new numeric spacing tokens (e.g., `var(--space-2)`, `var(--space-4)`, `var(--space-8)`). These should be used instead of the legacy named spacing tokens (e.g., `var(--spacing-xs)`, `var(--spacing-small)`, `var(--spacing-medium)`). Strive to use the new system in all new code or in any css code you're asked to refactor. -## 4. Mixins from `monday-ui-style` +## 4. Mixins from `@vibe/style` -- **Discourage Use of Mixins:** While some existing components might use mixins imported from `monday-ui-style` (e.g., `@import "~monday-ui-style/dist/mixins";` and usages like `@include focus-style();`), the preference is to **avoid using these mixins for new development or when refactoring existing components.** +- **Discourage Use of Mixins:** While some existing components might use mixins imported from `@vibe/style` (e.g., `@import "~@vibe/style/dist/mixins";` and usages like `@include focus-style();`), the preference is to **avoid using these mixins for new development or when refactoring existing components.** ## 5. Typography -- **Use Vibe Components for Typography:** Do not directly use font family, font size, font weight, or line height tokens from `monday-ui-style` within your `.module.scss` files. +- **Use Vibe Components for Typography:** Do not directly use font family, font size, font weight, or line height tokens from `@vibe/style` within your `.module.scss` files. - Instead, leverage the `Text` and `Heading` components from `@vibe/core` (`packages/core/src/components/Typography/Text` and `packages/core/src/components/Typography/Heading`). These components are already configured to apply the correct typography design tokens according to supplied props. ## 6. Class Naming Conventions diff --git a/.cursor/templates/package-separation/package.json b/.cursor/templates/package-separation/package.json index ba72926007..a99b83dce5 100644 --- a/.cursor/templates/package-separation/package.json +++ b/.cursor/templates/package-separation/package.json @@ -48,7 +48,7 @@ "react": "^16.13.0", "react-dom": "^16.13.0", "react-test-renderer": "16", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/CLAUDE.md b/CLAUDE.md index 95eca3c8ae..3e8a2be997 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -132,7 +132,7 @@ The repository includes an MCP (Model Context Protocol) server (`@vibe/mcp`) tha - **Rollup** for bundling with TypeScript support - **Independent versioning** via Lerna - **Metadata generation** for component documentation -- **CSS token handling** via `monday-ui-style` package +- **CSS token handling** via `@vibe/style` package ## Important Files and Conventions diff --git a/README.md b/README.md index e560bdf2a9..9338a41aa7 100644 --- a/README.md +++ b/README.md @@ -58,16 +58,16 @@ To get started, follow the installation instructions in the [@vibe/mcp](https:// - [@vibe/icons](https://github.com/mondaycom/vibe/blob/master/packages/icons/README.md): Icons library - [@vibe/testkit](https://github.com/mondaycom/vibe/blob/master/packages/testkit/README.md): Testing utilities for Playwright - [@vibe/codemod](https://github.com/mondaycom/vibe/blob/master/packages/codemod/README.md): Codemods and CLI tools -- [monday-ui-style](https://github.com/mondaycom/vibe/blob/master/packages/style/README.md): Styling foundations (included in @vibe/core) +- [@vibe/style](https://github.com/mondaycom/vibe/blob/master/packages/style/README.md): Styling foundations (included in @vibe/core) - [vibe-storybook-components](https://github.com/mondaycom/vibe/blob/master/packages/storybook-blocks/README.md): Vibe Storybook Blocks - [storybook-addon-playground](https://github.com/mondaycom/storybook-addon-playground/): A Component Playground Addon for Storybook - [@vibe/mcp](https://github.com/mondaycom/vibe/blob/master/packages/mcp/README.md): MCP server for Vibe Design System ## Older Versions -Vibe 2 ([`monday-ui-react-core`](https://www.npmjs.com/package/monday-ui-react-core)) will no longer receive new features or enhancements but will continue to receive critical bug fixes as needed. We highly recommend following the [migration guide](http://vibe.monday.com/?path=/docs/migration-guide--docs) to upgrade to the actively maintained Vibe 3, which includes the latest improvements, new components, and ongoing support. +Vibe 3 ([`@vibe/core` v3](https://www.npmjs.com/package/@vibe/core)) will no longer receive new features or enhancements but will continue to receive critical bug fixes as needed. We highly recommend following the [migration guide](http://vibe.monday.com/?path=/docs/migration-guide--docs) to upgrade to the actively maintained Vibe 4, which includes the latest improvements, new components, and ongoing support.For version 3 documentation, see [vibe.monday.com/v3](https://vibe.monday.com/v3). -For version 2 documentation, see [vibe.monday.com/v2](https://vibe.monday.com/v2). +Vibe 2 ([`monday-ui-react-core`](https://www.npmjs.com/package/monday-ui-react-core)) is no longer maintained. For version 2 documentation, see [vibe.monday.com/v2](https://vibe.monday.com/v2). ## Contributing diff --git a/package.json b/package.json index 6bee866497..361ce58d78 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "bytes": "^3.1.2", "lerna": "^8.1.2", "size-limit": "^11.2.0", - "typescript": "^4.7.3" + "typescript": "^5.9.3" }, "workspaces": [ "packages/*", diff --git a/packages/base/package.json b/packages/base/package.json index 81043e10ed..83040c5a56 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/base", - "version": "3.0.5", + "version": "4.0.0-rc.0", "description": "Vibe sub-package for base components", "repository": { "type": "git", @@ -38,17 +38,18 @@ "lint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@vibe/shared": "3.0.8", + "@vibe/shared": "^4.0.0-rc.0", + "@vibe/style": "^4.0.0-rc.0", "classnames": "^2.5.1" }, "devDependencies": { "@testing-library/react": "^12.1.2", "@testing-library/user-event": "^13.5.0", - "@vibe/config": "3.0.5", + "@vibe/config": "^4.0.0-rc.0", "react": "^16.13.0", "react-dom": "^16.13.0", "react-test-renderer": "16", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/base/src/BaseInput/BaseInput.module.scss b/packages/base/src/BaseInput/BaseInput.module.scss index acebc52744..c9f500d601 100644 --- a/packages/base/src/BaseInput/BaseInput.module.scss +++ b/packages/base/src/BaseInput/BaseInput.module.scss @@ -1,13 +1,13 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; .wrapper { width: 100%; position: relative; display: flex; align-items: center; - gap: var(--spacing-small); - padding-block: var(--spacing-xs); - padding-inline: var(--spacing-medium) var(--spacing-xs); + gap: var(--space-8); + padding-block: var(--space-8); + padding-inline: var(--space-12) var(--space-4); @include vibe-text(text1, normal); @include smoothing-text; @@ -22,6 +22,8 @@ &.small { min-height: 32px; height: 32px; + padding-inline-start: var(--space-8); + gap: var(--space-4); @include vibe-text(text2, normal); } @@ -33,11 +35,11 @@ &.large { min-height: 48px; height: 48px; - padding-block: var(--spacing-small); + padding-block: var(--space-8); } &.rightThinnerPadding { - padding-inline-end: var(--spacing-medium); + padding-inline-end: var(--space-16); } &:hover { @@ -160,7 +162,7 @@ outline: none; &::placeholder { - color: var(--secondary-text-color); + color: var(--placeholder-color); font-weight: 400; } diff --git a/packages/codemod/README.md b/packages/codemod/README.md index 6d9666f278..76673b567a 100644 --- a/packages/codemod/README.md +++ b/packages/codemod/README.md @@ -16,13 +16,13 @@ npx @vibe/codemod [options] ### `--migration` (alias: `-m`) -- **Description**: Specifies which migration type to run (e.g., `v3`). -- **Choices**: `v3` +- **Description**: Specifies which migration type to run (e.g., `v3`, `v4`). +- **Choices**: `v3`, `v4` - **Required**: Yes - **Example**: ```bash - npx @vibe/codemod --migration v3 + npx @vibe/codemod --migration v4 ``` ### `--target` (alias: `-t`) @@ -77,6 +77,11 @@ The following migrations are included in this CLI: - **Migration Type**: `v3` (`--migration v3`) - **Description**: This migration transforms components and files to comply with version 3 of @vibe/code. +### `v4` Migration + +- **Migration Type**: `v4` (`--migration v4`) +- **Description**: This migration transforms components and files to comply with version 4 of @vibe/core. Handles breaking changes including component API updates, type changes, and package structure modifications. + ### `enums` Migration - **Migration Type**: `enums` (`--migration enums`) diff --git a/packages/codemod/bin/vibe-codemod.ts b/packages/codemod/bin/vibe-codemod.ts index 784e7e5024..6641a4cc01 100755 --- a/packages/codemod/bin/vibe-codemod.ts +++ b/packages/codemod/bin/vibe-codemod.ts @@ -13,7 +13,8 @@ import { hideBin } from "yargs/helpers"; const mapMigrationType: { [key: string]: string } = { v3: "v2-to-v3", - enums: "v2-to-v3/enums" + enums: "v2-to-v3/enums", + v4: "v3-to-v4" }; const migrations = Object.keys(mapMigrationType); @@ -22,7 +23,7 @@ const argv = yargs(hideBin(process.argv)) .option("migration", { alias: "m", type: "string", - description: "Migration type to run (e.g., v3, enums)", + description: "Migration type to run (e.g., v3, v4, enums)", choices: migrations }) .option("target", { @@ -123,6 +124,9 @@ async function main() { if (migrationType === "v3" && !isVibeCoreInstalled) { console.log(chalk.yellow("Warning: You need to install @vibe/core package to fully apply the v3 migration.")); } + if (migrationType === "v4" && !isVibeCoreInstalled) { + console.log(chalk.yellow("Warning: You need to install @vibe/core package to fully apply the v4 migration.")); + } if (migrationType === "enums" && !isVibeCoreInstalled) { console.log(chalk.red("Error: Please install @vibe/core to run the enum migration.")); process.exit(1); @@ -259,7 +263,12 @@ async function main() { resolve(transformationsDir, "type-imports-migration.js"), resolve(transformationsDir, "packages-rename-migration.js") ] - : []; + : migrationType === "v4" + ? [ + resolve(transformationsDir, "next-imports-migration.js"), + resolve(transformationsDir, "packages-rename-migration.js") + ] + : []; const orderedTransformationFiles = [ ...transformationFiles.filter(file => !filesToProcessLast.includes(file)), ...filesToProcessLast diff --git a/packages/codemod/package.json b/packages/codemod/package.json index 89c7ac5340..6bfa4e8909 100644 --- a/packages/codemod/package.json +++ b/packages/codemod/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/codemod", - "version": "1.1.0", + "version": "4.0.0-rc.0", "description": "Vibe's component library migration tool", "repository": { "type": "git", diff --git a/packages/codemod/transformations/core/v3-to-v4/Chips-component-migration.ts b/packages/codemod/transformations/core/v3-to-v4/Chips-component-migration.ts new file mode 100644 index 0000000000..ad4524dfe6 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/Chips-component-migration.ts @@ -0,0 +1,28 @@ +import { + wrap, + getImports, + getComponentNameOrAliasFromImports, + findComponentElements, + removeProp +} from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; + +/** + * Chips migration for v3 to v4: + * Remove the deprecated `disableClickableBehavior` prop + */ +function transform({ j, root }: TransformationContext) { + const imports = getImports(root, NEW_CORE_IMPORT_PATH); + const componentName = getComponentNameOrAliasFromImports(j, imports, "Chips"); + if (!componentName) return; + + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + elements.forEach(elementPath => { + removeProp(j, elementPath, "disableClickableBehavior"); + }); +} + +export default wrap(transform); diff --git a/packages/codemod/transformations/core/v3-to-v4/Dialog-component-migration.ts b/packages/codemod/transformations/core/v3-to-v4/Dialog-component-migration.ts new file mode 100644 index 0000000000..59d1ba0c53 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/Dialog-component-migration.ts @@ -0,0 +1,29 @@ +import { + wrap, + getImports, + getComponentNameOrAliasFromImports, + findComponentElements, + removeProp +} from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; + +/** + * Dialog migration for v3 to v4: + * Remove the deprecated `enableNestedDialogLayer` prop. + * Dialog now always wraps content with LayerProvider. + */ +function transform({ j, root }: TransformationContext) { + const imports = getImports(root, NEW_CORE_IMPORT_PATH); + const componentName = getComponentNameOrAliasFromImports(j, imports, "Dialog"); + if (!componentName) return; + + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + elements.forEach(elementPath => { + removeProp(j, elementPath, "enableNestedDialogLayer"); + }); +} + +export default wrap(transform); diff --git a/packages/codemod/transformations/core/v3-to-v4/Flex-component-migration.ts b/packages/codemod/transformations/core/v3-to-v4/Flex-component-migration.ts new file mode 100644 index 0000000000..8d83dbb425 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/Flex-component-migration.ts @@ -0,0 +1,38 @@ +import { + wrap, + getCoreImportsForFile, + getComponentNameOrAliasFromImports, + findComponentElements, + isPropExists, + findProps, + getPropValue, + removeProp +} from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; + +/** + * Flex component migration for v3 to v4: + * 1. Remove `justify="stretch"` โ€” "stretch" is not a valid value for justify-content in flexbox + * and had no CSS implementation. The prop is removed entirely. + * 2. Remove `justify={FlexJustify.STRETCH}` โ€” same reason, enum value removed. + */ +function transform({ j, root }: TransformationContext) { + const imports = getCoreImportsForFile(root, NEW_CORE_IMPORT_PATH); + const componentName = getComponentNameOrAliasFromImports(j, imports, "Flex"); + if (!componentName) return; + + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + elements.forEach(elementPath => { + if (!isPropExists(j, elementPath, "justify")) return; + const prop = findProps(j, elementPath, "justify").get(0); + const justifyValue = getPropValue(j, prop.node); + if (justifyValue === "stretch" || justifyValue === "FlexJustify.STRETCH") { + removeProp(j, elementPath, "justify"); + } + }); +} + +export default wrap(transform); diff --git a/packages/codemod/transformations/core/v3-to-v4/Icon-component-migration.ts b/packages/codemod/transformations/core/v3-to-v4/Icon-component-migration.ts new file mode 100644 index 0000000000..3b043fc98e --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/Icon-component-migration.ts @@ -0,0 +1,34 @@ +import { + wrap, + getImports, + getComponentNameOrAliasFromImports, + findComponentElements, + migratePropsNames +} from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; + +/** + * Icon component migration for v3 to v4: + * 1. Rename iconLabel prop to label + * 2. Rename iconType prop to type + * 3. Rename iconSize prop to size + */ +function transform({ j, root, filePath }: TransformationContext) { + const imports = getImports(root, NEW_CORE_IMPORT_PATH); + const componentName = getComponentNameOrAliasFromImports(j, imports, "Icon"); + if (!componentName) return; + + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + elements.forEach(elementPath => { + migratePropsNames(j, elementPath, filePath, componentName, { + iconLabel: "label", + iconType: "type", + iconSize: "size" + }); + }); +} + +export default wrap(transform); diff --git a/packages/codemod/transformations/core/v3-to-v4/LinearProgressBar-component-migration.ts b/packages/codemod/transformations/core/v3-to-v4/LinearProgressBar-component-migration.ts new file mode 100644 index 0000000000..010dcb882f --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/LinearProgressBar-component-migration.ts @@ -0,0 +1,53 @@ +import { wrap, getImports } from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; + +/** + * LinearProgressBar migration for v3 to v4: + * 1. Rename LinearProgressBar import to ProgressBar + * 2. Rename LinearProgressBarProps type import to ProgressBarProps + * 3. Rename JSX usage from to + */ +function transform({ j, root }: TransformationContext) { + const imports = getImports(root, NEW_CORE_IMPORT_PATH); + + // Rename the component import specifier + imports.find(j.ImportSpecifier).forEach(specifier => { + const imported = specifier.node.imported; + if (imported.type === "Identifier") { + if (imported.name === "LinearProgressBar") { + // If no local alias, rename both imported and local + if (!specifier.node.local || specifier.node.local.name === "LinearProgressBar") { + imported.name = "ProgressBar"; + specifier.node.local = j.identifier("ProgressBar"); + } else { + // Has an alias โ€” only rename the imported name + imported.name = "ProgressBar"; + } + } + if (imported.name === "LinearProgressBarProps") { + if (!specifier.node.local || specifier.node.local.name === "LinearProgressBarProps") { + imported.name = "ProgressBarProps"; + specifier.node.local = j.identifier("ProgressBarProps"); + } else { + imported.name = "ProgressBarProps"; + } + } + } + }); + + // Find all JSX elements named LinearProgressBar and rename them + root.find(j.JSXIdentifier, { name: "LinearProgressBar" }).forEach(path => { + path.node.name = "ProgressBar"; + }); + + // Rename any remaining references (e.g. in type annotations) + root.find(j.Identifier, { name: "LinearProgressBarProps" }).forEach(path => { + // Don't rename import specifiers (already handled above) + if (path.parent.node.type !== "ImportSpecifier") { + path.node.name = "ProgressBarProps"; + } + }); +} + +export default wrap(transform); diff --git a/packages/codemod/transformations/core/v3-to-v4/README.md b/packages/codemod/transformations/core/v3-to-v4/README.md new file mode 100644 index 0000000000..5d53ff84a5 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/README.md @@ -0,0 +1,139 @@ +# Vibe v3 to v4 Migration Transformations + +This directory contains all transformation files for migrating from Vibe v3 to v4. + +## Structure + +``` +v3-to-v4/ +โ”œโ”€โ”€ README.md # This file +โ”œโ”€โ”€ {Component}-component-migration.ts # Component-specific migrations +โ”œโ”€โ”€ enums/ # Enum-related migrations +โ”‚ โ”œโ”€โ”€ Enums-migration.ts +โ”‚ โ”œโ”€โ”€ enumMappings.json +โ”‚ โ””โ”€โ”€ __tests__/ +โ””โ”€โ”€ __tests__/ # Test files for migrations + โ””โ”€โ”€ {Component}-component-migration.test.ts +``` + +## Adding New Transformations + +### 1. Component Migration + +For component-specific breaking changes, create a file named `{ComponentName}-component-migration.ts`: + +```typescript +import { + wrap, + getCoreImportsForFile, + getComponentNameOrAliasFromImports, + findComponentElements, + migratePropsNames +} from "../../../src/utils"; +import { TransformationContext } from "../../../types"; + +/** + * Migration description: + * 1. Change description + * 2. Another change + */ +function transform({ j, root, filePath }: TransformationContext) { + const imports = getCoreImportsForFile(root); + const componentName = getComponentNameOrAliasFromImports(j, imports, "ComponentName"); + if (!componentName) return; + + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + elements.forEach(elementPath => { + // Apply transformations here + migratePropsNames(j, elementPath, filePath, componentName, { + oldPropName: "newPropName" + }); + }); +} + +export default wrap(transform); +``` + +### 2. Add Tests + +Create a corresponding test file in `__tests__/{ComponentName}-component-migration.test.ts`: + +```typescript +import { defineInlineTest } from "jscodeshift/dist/testUtils"; +import transform from "../{ComponentName}-component-migration"; + +describe("{ComponentName} Migration", () => { + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { ComponentName } from "@vibe/core"; + + `, + ` +import { ComponentName } from "@vibe/core"; + + `, + "should migrate oldPropName to newPropName" + ); +}); +``` + +### 3. Update CLI Support + +The CLI will automatically pick up new transformation files. No additional registration needed. + +## Migration Types + +### Component Props + +- Renaming props +- Changing prop types +- Removing deprecated props +- Adding required props with defaults + +### Component Names + +- Renaming components +- Moving components between packages + +### Imports + +- Changing import paths +- Updating package names + +### Enums + +- Converting enums to string literals +- Renaming enum values + +## Testing + +Run tests for all v3-to-v4 transformations: + +```bash +yarn workspace @vibe/codemod test v3-to-v4 +``` + +Run a specific transformation test: + +```bash +yarn workspace @vibe/codemod test ComponentName-component-migration +``` + +## Running Transformations + +Once implemented, users can run the v4 migration: + +```bash +npx @vibe/codemod --migration v4 +``` + +## Notes + +- All transformations should be idempotent (safe to run multiple times) +- Consider edge cases and provide helpful error messages +- Document the reasoning for each breaking change +- Update the main VIBE4_CHANGELOG.md when adding new transformations diff --git a/packages/codemod/transformations/core/v3-to-v4/TextField-component-migration.ts b/packages/codemod/transformations/core/v3-to-v4/TextField-component-migration.ts new file mode 100644 index 0000000000..3f7e0a87da --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/TextField-component-migration.ts @@ -0,0 +1,86 @@ +import { + wrap, + getImports, + getComponentNameOrAliasFromImports, + findComponentElements, + isPropExists, + findProps, + removeProp, + migratePropsNames +} from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; + +/** + * TextField migration for v3 to v4: + * - Replace object prop `iconsNames={{ primary, secondary }}` with flat props `iconLabel` and `secondaryIconLabel` + * - Rename `iconName` prop to `icon` + */ +function transform({ j, root, filePath }: TransformationContext) { + const imports = getImports(root, NEW_CORE_IMPORT_PATH); + const componentName = getComponentNameOrAliasFromImports(j, imports, "TextField"); + if (!componentName) return; + + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + elements.forEach(elementPath => { + // Rename `iconName` prop to `icon` + migratePropsNames(j, elementPath, filePath, componentName, { iconName: "icon" }); + + if (!isPropExists(j, elementPath, "iconsNames")) return; + + const iconsNamesProp = findProps(j, elementPath, "iconsNames"); + if (!iconsNamesProp.length) return; + + const propNode = iconsNamesProp.at(0).get().node; + const value = propNode.value; + + // Handle object expression: iconsNames={{ primary: "X", secondary: "Y" }} + if (value?.type === "JSXExpressionContainer" && value.expression.type === "ObjectExpression") { + const properties = value.expression.properties; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + properties.forEach((prop: any) => { + if (prop.type !== "ObjectProperty" && prop.type !== "Property") return; + + const key = prop.key; + const keyName = key.type === "Identifier" ? key.name : key.type === "StringLiteral" ? key.value : null; + + if (!keyName) return; + + let newPropName: string | null = null; + if (keyName === "primary") { + newPropName = "iconLabel"; + } else if (keyName === "secondary") { + newPropName = "secondaryIconLabel"; + } + + if (!newPropName) return; + + // Skip if the new prop already exists + if (isPropExists(j, elementPath, newPropName)) return; + + const propValue = prop.value; + + let newAttr; + if (propValue.type === "StringLiteral") { + // Static string: primary: "Search" -> iconLabel="Search" + newAttr = j.jsxAttribute(j.jsxIdentifier(newPropName), j.literal(propValue.value)); + } else { + // Dynamic expression: primary: dynamicLabel -> iconLabel={dynamicLabel} + newAttr = j.jsxAttribute(j.jsxIdentifier(newPropName), j.jsxExpressionContainer(propValue)); + } + + elementPath.node.openingElement.attributes?.push(newAttr); + }); + + // Remove the old iconsNames prop + removeProp(j, elementPath, "iconsNames"); + } + // For non-object expressions (e.g. iconsNames={someVariable}), we can't safely transform + // Leave as-is and let the developer handle manually + }); +} + +export default wrap(transform); diff --git a/packages/codemod/transformations/core/v3-to-v4/TextWithHighlight-component-migration.ts b/packages/codemod/transformations/core/v3-to-v4/TextWithHighlight-component-migration.ts new file mode 100644 index 0000000000..f198bf3367 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/TextWithHighlight-component-migration.ts @@ -0,0 +1,84 @@ +import { + wrap, + getCoreImportsForFile, + getComponentNameOrAliasFromImports, + findComponentElements, + removeProp, + isPropExists, + findProps, + getPropValue +} from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; + +/** + * TextWithHighlight migration for v3 to v4: + * 1. Migrate tooltipPosition prop to tooltipProps={{ position: "..." }} + */ +function transform({ j, root, filePath }: TransformationContext) { + const imports = getCoreImportsForFile(root, NEW_CORE_IMPORT_PATH); + const componentName = getComponentNameOrAliasFromImports(j, imports, "TextWithHighlight"); + if (!componentName) return; + + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + elements.forEach(elementPath => { + if (isPropExists(j, elementPath, "tooltipPosition")) { + // Get the tooltipPosition value + const tooltipPositionProps = findProps(j, elementPath, "tooltipPosition"); + if (!tooltipPositionProps.length) return; + + const tooltipPositionValue = getPropValue(j, tooltipPositionProps.get().node); + + // Check if tooltipProps already exists + const hasTooltipProps = isPropExists(j, elementPath, "tooltipProps"); + + // Handle different value types: string literals, expressions, etc. + const tooltipPositionAttr = tooltipPositionProps.get().node; + + let positionValue; + if (tooltipPositionAttr.value?.type === "JSXExpressionContainer") { + // Use the expression directly (e.g., {pos} becomes pos) + positionValue = tooltipPositionAttr.value.expression; + } else { + // Use literal value (e.g., "top" stays "top") + positionValue = j.literal(String(tooltipPositionValue)); + } + + if (hasTooltipProps) { + // Merge position into existing tooltipProps + const existingTooltipProps = findProps(j, elementPath, "tooltipProps"); + const existingTooltipPropsAttr = existingTooltipProps.get().node; + + if ( + existingTooltipPropsAttr.value?.type === "JSXExpressionContainer" && + existingTooltipPropsAttr.value.expression?.type === "ObjectExpression" + ) { + // Add position property to existing object + const existingObject = existingTooltipPropsAttr.value.expression; + const positionProperty = j.property("init", j.identifier("position"), positionValue); + existingObject.properties.push(positionProperty); + } else { + console.warn( + `[MANUAL] ${filePath}: ${componentName} has both tooltipPosition and tooltipProps with complex structure. ` + + `Please manually merge tooltipPosition="${tooltipPositionValue}" into existing tooltipProps.` + ); + } + } else { + // Create new tooltipProps={{ position: value }} + const tooltipPropsValue = j.jsxExpressionContainer( + j.objectExpression([j.property("init", j.identifier("position"), positionValue)]) + ); + + const tooltipPropAttr = j.jsxAttribute(j.jsxIdentifier("tooltipProps"), tooltipPropsValue); + elementPath.node.openingElement.attributes?.push(tooltipPropAttr); + } + + // Remove the old tooltipPosition prop + removeProp(j, elementPath, "tooltipPosition"); + } + }); +} + +export default wrap(transform); diff --git a/packages/codemod/transformations/core/v3-to-v4/TipseenImage-component-migration.ts b/packages/codemod/transformations/core/v3-to-v4/TipseenImage-component-migration.ts new file mode 100644 index 0000000000..826e13096a --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/TipseenImage-component-migration.ts @@ -0,0 +1,108 @@ +import { + wrap, + getImports, + getComponentNameOrAliasFromImports, + findComponentElements, + findProps, + removeSpecifierFromImport, + findImportsThatIncludesSpecifier +} from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; +import { JSXAttribute } from "jscodeshift"; + +/** + * TipseenImage migration for v3 to v4: + * 1. Replace + * with {..} + * 2. Update imports: remove TipseenImage, add TipseenMedia if not already imported + */ +function transform({ j, root }: TransformationContext) { + const imports = getImports(root, NEW_CORE_IMPORT_PATH); + const componentName = getComponentNameOrAliasFromImports(j, imports, "TipseenImage"); + if (!componentName) return; + + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + elements.forEach(elementPath => { + // Extract prop values from TipseenImage + const srcProps = findProps(j, elementPath, "src"); + const altProps = findProps(j, elementPath, "alt"); + const classNameProps = findProps(j, elementPath, "className"); + const tipseenMediaClassNameProps = findProps(j, elementPath, "tipseenMediaClassName"); + + // Build img attributes + const imgAttrs: JSXAttribute[] = []; + + if (srcProps.length) { + const srcAttr = srcProps.get().node; + imgAttrs.push(j.jsxAttribute(j.jsxIdentifier("src"), srcAttr.value)); + } + + if (altProps.length) { + const altAttr = altProps.get().node; + imgAttrs.push(j.jsxAttribute(j.jsxIdentifier("alt"), altAttr.value)); + } + + if (classNameProps.length) { + const classNameAttr = classNameProps.get().node; + imgAttrs.push(j.jsxAttribute(j.jsxIdentifier("className"), classNameAttr.value)); + } + + // Add style={{ objectFit: "cover", width: "100%" }} + imgAttrs.push( + j.jsxAttribute( + j.jsxIdentifier("style"), + j.jsxExpressionContainer( + j.objectExpression([ + j.property("init", j.identifier("objectFit"), j.literal("cover")), + j.property("init", j.identifier("width"), j.literal("100%")) + ]) + ) + ) + ); + + // Create element + const imgElement = j.jsxElement(j.jsxOpeningElement(j.jsxIdentifier("img"), imgAttrs, true), null, []); + + // Build TipseenMedia attributes + const mediaAttrs: JSXAttribute[] = []; + + if (tipseenMediaClassNameProps.length) { + const mediaClassNameAttr = tipseenMediaClassNameProps.get().node; + mediaAttrs.push(j.jsxAttribute(j.jsxIdentifier("className"), mediaClassNameAttr.value)); + } + + // Determine the TipseenMedia component name to use + const tipseenMediaName = getComponentNameOrAliasFromImports(j, imports, "TipseenMedia") || "TipseenMedia"; + + // Create {img} + const mediaElement = j.jsxElement( + j.jsxOpeningElement(j.jsxIdentifier(tipseenMediaName), mediaAttrs, false), + j.jsxClosingElement(j.jsxIdentifier(tipseenMediaName)), + [j.jsxText("\n "), imgElement, j.jsxText("\n")] + ); + + // Replace TipseenImage with TipseenMedia wrapping img + j(elementPath).replaceWith(mediaElement); + }); + + // Update imports: remove TipseenImage, add TipseenMedia if not present + const tipseenImageImports = findImportsThatIncludesSpecifier(j, imports, "TipseenImage"); + if (tipseenImageImports.length) { + const hasTipseenMedia = getComponentNameOrAliasFromImports(j, imports, "TipseenMedia") !== null; + + tipseenImageImports.forEach(importPath => { + if (!hasTipseenMedia) { + // Add TipseenMedia specifier before removing TipseenImage + const newSpecifier = j.importSpecifier(j.identifier("TipseenMedia"), j.identifier("TipseenMedia")); + importPath.node.specifiers?.push(newSpecifier); + } + + removeSpecifierFromImport(j, importPath, "TipseenImage"); + }); + } +} + +export default wrap(transform); diff --git a/packages/codemod/transformations/core/v3-to-v4/Toggle-component-migration.ts b/packages/codemod/transformations/core/v3-to-v4/Toggle-component-migration.ts new file mode 100644 index 0000000000..3bfa19e373 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/Toggle-component-migration.ts @@ -0,0 +1,29 @@ +import { + wrap, + getImports, + getComponentNameOrAliasFromImports, + findComponentElements, + removeProp +} from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; + +/** + * Toggle migration for v3 to v4: + * Remove the deprecated `noSpacing` prop โ€” the toggle now automatically + * removes its margin when `areLabelsHidden` is true. + */ +function transform({ j, root }: TransformationContext) { + const imports = getImports(root, NEW_CORE_IMPORT_PATH); + const componentName = getComponentNameOrAliasFromImports(j, imports, "Toggle"); + if (!componentName) return; + + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + elements.forEach(elementPath => { + removeProp(j, elementPath, "noSpacing"); + }); +} + +export default wrap(transform); diff --git a/packages/codemod/transformations/core/v3-to-v4/VirtualizedList-component-migration.ts b/packages/codemod/transformations/core/v3-to-v4/VirtualizedList-component-migration.ts new file mode 100644 index 0000000000..a7249f603b --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/VirtualizedList-component-migration.ts @@ -0,0 +1,32 @@ +import { + wrap, + getImports, + getComponentNameOrAliasFromImports, + findComponentElements, + migratePropsNames +} from "../../../src/utils"; +import { NEW_CORE_IMPORT_PATH } from "../../../src/consts"; +import { TransformationContext } from "../../../types"; + +/** + * VirtualizedList component migration for v3 to v4: + * 1. Rename getItemHeight prop to getItemSize + * 2. Rename onVerticalScrollbarVisiblityChange prop to onLayoutDirectionScrollbarVisibilityChange + */ +function transform({ j, root, filePath }: TransformationContext) { + const imports = getImports(root, NEW_CORE_IMPORT_PATH); + const componentName = getComponentNameOrAliasFromImports(j, imports, "VirtualizedList"); + if (!componentName) return; + + const elements = findComponentElements(root, componentName); + if (!elements.length) return; + + elements.forEach(elementPath => { + migratePropsNames(j, elementPath, filePath, componentName, { + getItemHeight: "getItemSize", + onVerticalScrollbarVisiblityChange: "onLayoutDirectionScrollbarVisibilityChange" + }); + }); +} + +export default wrap(transform); diff --git a/packages/codemod/transformations/core/v3-to-v4/__tests__/Chips-component-migration.test.ts b/packages/codemod/transformations/core/v3-to-v4/__tests__/Chips-component-migration.test.ts new file mode 100644 index 0000000000..8c08f705d1 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/__tests__/Chips-component-migration.test.ts @@ -0,0 +1,40 @@ +import transform from "../Chips-component-migration"; +import { defineInlineTest } from "jscodeshift/src/testUtils"; + +function prependImport(source: string): string { + return `import { Chips } from "@vibe/core";\n${source}`; +} + +describe("Chips component migration", () => { + defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should remove disableClickableBehavior boolean prop" + ); + + defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should remove disableClickableBehavior with explicit true value" + ); + + defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should remove disableClickableBehavior with false value" + ); + + defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should not modify Chips without disableClickableBehavior prop" + ); +}); diff --git a/packages/codemod/transformations/core/v3-to-v4/__tests__/Dialog-component-migration.test.ts b/packages/codemod/transformations/core/v3-to-v4/__tests__/Dialog-component-migration.test.ts new file mode 100644 index 0000000000..221dcbeb35 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/__tests__/Dialog-component-migration.test.ts @@ -0,0 +1,44 @@ +import transform from "../Dialog-component-migration"; +import { defineInlineTest } from "jscodeshift/src/testUtils"; + +function prependImport(source: string): string { + return `import { Dialog } from "@vibe/core";\n${source}`; +} + +describe("Dialog component migration", () => { + defineInlineTest( + transform, + {}, + prependImport("const element = }>;"), + prependImport("const element = }>;"), + "should remove enableNestedDialogLayer boolean prop" + ); + + defineInlineTest( + transform, + {}, + prependImport( + "const element = }>;" + ), + prependImport("const element = }>;"), + "should remove enableNestedDialogLayer with explicit true value" + ); + + defineInlineTest( + transform, + {}, + prependImport( + "const element = }>;" + ), + prependImport("const element = }>;"), + "should remove enableNestedDialogLayer with false value" + ); + + defineInlineTest( + transform, + {}, + prependImport("const element = }>;"), + prependImport("const element = }>;"), + "should not modify Dialog without enableNestedDialogLayer prop" + ); +}); diff --git a/packages/codemod/transformations/core/v3-to-v4/__tests__/Flex-component-migration.test.ts b/packages/codemod/transformations/core/v3-to-v4/__tests__/Flex-component-migration.test.ts new file mode 100644 index 0000000000..a84018e8d1 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/__tests__/Flex-component-migration.test.ts @@ -0,0 +1,74 @@ +import transform from "../Flex-component-migration"; +import { defineInlineTest } from "jscodeshift/src/testUtils"; + +describe("Flex component migration", () => { + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { Flex } from "@vibe/core"; + + `, + ` +import { Flex } from "@vibe/core"; + + `, + "should remove justify prop when value is stretch" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { Flex, FlexJustify } from "@vibe/core"; + + `, + ` +import { Flex, FlexJustify } from "@vibe/core"; + + `, + "should remove justify prop when value is FlexJustify.STRETCH" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { Flex } from "@vibe/core"; + + `, + ` +import { Flex } from "@vibe/core"; + + `, + "should not change justify prop for valid values" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { Flex } from "@vibe/core"; + + `, + ` +import { Flex } from "@vibe/core"; + + `, + "should remove only the justify prop when value is stretch, leaving other props intact" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { Flex } from "another-library"; + + `, + ` +import { Flex } from "another-library"; + + `, + "should not change Flex not imported from @vibe/core" + ); +}); diff --git a/packages/codemod/transformations/core/v3-to-v4/__tests__/Icon-component-migration.test.ts b/packages/codemod/transformations/core/v3-to-v4/__tests__/Icon-component-migration.test.ts new file mode 100644 index 0000000000..3f6a887472 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/__tests__/Icon-component-migration.test.ts @@ -0,0 +1,112 @@ +import transform from "../Icon-component-migration"; +import { defineInlineTest } from "jscodeshift/src/testUtils"; + +function prependImport(source: string): string { + return `import { Icon } from "@vibe/core";\n${source}`; +} + +describe("Icon component migration", () => { + defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should rename iconLabel, iconType, and iconSize props" + ); + + defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should rename only iconLabel prop when other props are not present" + ); + + defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should rename iconType and iconSize props when iconLabel is not present" + ); + + defineInlineTest( + transform, + {}, + prependImport("const element = ;"), + prependImport("const element = ;"), + "should not change Icon without deprecated props" + ); + + defineInlineTest( + transform, + {}, + prependImport(` + const element = ( + + ); + `), + prependImport(` + const element = ( + + ); + `), + "should handle complex prop expressions" + ); + + defineInlineTest( + transform, + {}, + prependImport(` + function MyComponent() { + return ( +
+ + +
+ ); + } + `), + prependImport(` + function MyComponent() { + return ( + (
+ + +
) + ); + } + `), + "should handle multiple Icon components" + ); + + defineInlineTest( + transform, + {}, + `import { Icon as VibeIcon } from "@vibe/core"; + const element = ;`, + `import { Icon as VibeIcon } from "@vibe/core"; + const element = ;`, + "should work with aliased imports" + ); + + defineInlineTest( + transform, + {}, + 'const element = ;', + 'const element = ;', + "should not transform Icon without import from @vibe/core" + ); +}); diff --git a/packages/codemod/transformations/core/v3-to-v4/__tests__/LinearProgressBar-component-migration.test.ts b/packages/codemod/transformations/core/v3-to-v4/__tests__/LinearProgressBar-component-migration.test.ts new file mode 100644 index 0000000000..da2ed26429 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/__tests__/LinearProgressBar-component-migration.test.ts @@ -0,0 +1,52 @@ +import transform from "../LinearProgressBar-component-migration"; +import { defineInlineTest } from "jscodeshift/src/testUtils"; + +function prependImport(source: string, importName = "LinearProgressBar"): string { + return `import { ${importName} } from "@vibe/core";\n${source}`; +} + +describe("LinearProgressBar component migration", () => { + defineInlineTest( + transform, + {}, + prependImport(`const element = ;`), + `import { ProgressBar } from "@vibe/core";\nconst element = ;`, + "should rename LinearProgressBar to ProgressBar in import and JSX" + ); + + defineInlineTest( + transform, + {}, + prependImport(`const element = ;`), + `import { ProgressBar } from "@vibe/core";\nconst element = ;`, + "should rename LinearProgressBar with multiple props" + ); + + defineInlineTest( + transform, + {}, + `import { LinearProgressBar, LinearProgressBarProps } from "@vibe/core"; +const props: LinearProgressBarProps = { value: 50 }; +const element = ;`, + `import { ProgressBar, ProgressBarProps } from "@vibe/core"; +const props: ProgressBarProps = { value: 50 }; +const element = ;`, + "should rename both LinearProgressBar and LinearProgressBarProps" + ); + + defineInlineTest( + transform, + {}, + `import { Button } from "@vibe/core";\nconst element = + `, + ` +import { Button } from "@vibe/core"; + + `, + "should not affect non-TextWithHighlight components" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { TextWithHighlight } from "@vibe/core"; + + `, + ` +import { TextWithHighlight } from "@vibe/core"; + + `, + "should handle different position values" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { TextWithHighlight } from "@vibe/core"; +const pos = "right"; + + `, + ` +import { TextWithHighlight } from "@vibe/core"; +const pos = "right"; + + `, + "should handle expression values" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { TextWithHighlight } from "@vibe/core"; + + `, + ` +import { TextWithHighlight } from "@vibe/core"; + + `, + "should merge tooltipPosition into existing tooltipProps" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { TextWithHighlight } from "@vibe/core"; + + `, + ` +import { TextWithHighlight } from "@vibe/core"; + + `, + "should merge tooltipPosition into existing tooltipProps with multiple properties" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { TextWithHighlight } from "@vibe/core"; +const pos = "left"; + + `, + ` +import { TextWithHighlight } from "@vibe/core"; +const pos = "left"; + + `, + "should merge expression tooltipPosition into existing tooltipProps" + ); +}); diff --git a/packages/codemod/transformations/core/v3-to-v4/__tests__/TipseenImage-component-migration.test.ts b/packages/codemod/transformations/core/v3-to-v4/__tests__/TipseenImage-component-migration.test.ts new file mode 100644 index 0000000000..d65721c738 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/__tests__/TipseenImage-component-migration.test.ts @@ -0,0 +1,152 @@ +import { defineInlineTest } from "jscodeshift/dist/testUtils"; +import transform from "../TipseenImage-component-migration"; + +describe("TipseenImage component migration", () => { + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { TipseenImage } from "@vibe/core"; + + `, + ` +import { TipseenMedia } from "@vibe/core"; + + description + + `, + "should migrate basic TipseenImage to TipseenMedia with img" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { TipseenImage } from "@vibe/core"; + + `, + ` +import { TipseenMedia } from "@vibe/core"; + + description + + `, + "should handle all props including tipseenMediaClassName" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { TipseenImage, TipseenMedia } from "@vibe/core"; + + `, + ` +import { TipseenMedia } from "@vibe/core"; + + + + `, + "should not duplicate TipseenMedia import when already imported" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { TipseenImage as MyImage } from "@vibe/core"; + + `, + ` +import { TipseenMedia } from "@vibe/core"; + + test + + `, + "should handle aliased imports" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { Button } from "@vibe/core"; + + `, + ` +import { Button } from "@vibe/core"; + + `, + "should not affect files without TipseenImage" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { TipseenImage } from "@vibe/core"; + + `, + ` +import { TipseenMedia } from "@vibe/core"; + + + + `, + "should handle expression values for src" + ); + + defineInlineTest( + { default: transform, parser: "tsx" }, + {}, + ` +import { Tipseen, TipseenImage, TipseenContent } from "@vibe/core"; +Text}> +
+ + `, + ` +import { Tipseen, TipseenContent, TipseenMedia } from "@vibe/core"; + + +Text}> +
+ + `, + "should work within Tipseen content prop and update imports" + ); +}); diff --git a/packages/codemod/transformations/core/v3-to-v4/__tests__/Toggle-component-migration.test.ts b/packages/codemod/transformations/core/v3-to-v4/__tests__/Toggle-component-migration.test.ts new file mode 100644 index 0000000000..5aa44e08c1 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/__tests__/Toggle-component-migration.test.ts @@ -0,0 +1,40 @@ +import transform from "../Toggle-component-migration"; +import { defineInlineTest } from "jscodeshift/src/testUtils"; + +function prependImport(source: string): string { + return `import { Toggle } from "@vibe/core";\n${source}`; +} + +describe("Toggle component migration", () => { + defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should remove noSpacing boolean prop" + ); + + defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should remove noSpacing with explicit true value" + ); + + defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should remove noSpacing with false value" + ); + + defineInlineTest( + transform, + {}, + prependImport('const element = ;'), + prependImport('const element = ;'), + "should not modify Toggle without noSpacing prop" + ); +}); diff --git a/packages/codemod/transformations/core/v3-to-v4/__tests__/VirtualizedList-component-migration.test.ts b/packages/codemod/transformations/core/v3-to-v4/__tests__/VirtualizedList-component-migration.test.ts new file mode 100644 index 0000000000..5870eca7b3 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/__tests__/VirtualizedList-component-migration.test.ts @@ -0,0 +1,66 @@ +import transform from "../VirtualizedList-component-migration"; +import { defineInlineTest } from "jscodeshift/src/testUtils"; + +function prependImport(source: string): string { + return `import { VirtualizedList } from "@vibe/core";\n${source}`; +} + +describe("VirtualizedList component migration", () => { + defineInlineTest( + transform, + {}, + prependImport(" item.height} itemRenderer={renderer} />"), + prependImport(" item.height} itemRenderer={renderer} />"), + "should rename getItemHeight prop to getItemSize" + ); + + defineInlineTest( + transform, + {}, + prependImport( + " item.height} onVerticalScrollbarVisiblityChange={cb} itemRenderer={renderer} />" + ), + prependImport( + " item.height} onLayoutDirectionScrollbarVisibilityChange={cb} itemRenderer={renderer} />" + ), + "should rename onVerticalScrollbarVisiblityChange prop to onLayoutDirectionScrollbarVisibilityChange" + ); + + defineInlineTest( + transform, + {}, + prependImport( + " item.height} onVerticalScrollbarVisiblityChange={cb} itemRenderer={renderer} />" + ), + prependImport( + " item.height} onLayoutDirectionScrollbarVisibilityChange={cb} itemRenderer={renderer} />" + ), + "should rename both deprecated props at once" + ); + + defineInlineTest( + transform, + {}, + prependImport(" item.height} itemRenderer={renderer} />"), + prependImport(" item.height} itemRenderer={renderer} />"), + "should not change VirtualizedList without deprecated props" + ); + + defineInlineTest( + transform, + {}, + `import { VirtualizedList as VList } from "@vibe/core"; + item.height} itemRenderer={renderer} />`, + `import { VirtualizedList as VList } from "@vibe/core"; + item.height} itemRenderer={renderer} />`, + "should work with aliased imports" + ); + + defineInlineTest( + transform, + {}, + " item.height} itemRenderer={renderer} />", + " item.height} itemRenderer={renderer} />", + "should not transform VirtualizedList without import from @vibe/core" + ); +}); diff --git a/packages/codemod/transformations/core/v3-to-v4/__tests__/aria-props-migration.test.ts b/packages/codemod/transformations/core/v3-to-v4/__tests__/aria-props-migration.test.ts new file mode 100644 index 0000000000..6617c6e2ec --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/__tests__/aria-props-migration.test.ts @@ -0,0 +1,251 @@ +import transform from "../aria-props-migration"; +import { defineInlineTest } from "jscodeshift/src/testUtils"; + +function prependImport(source: string): string { + return ` + import { Button } from "@vibe/core"; + ${source} + `; +} + +function prependLinkImport(source: string): string { + return ` + import { Link } from "@vibe/core"; + ${source} + `; +} + +function prependMultiImport(source: string): string { + return ` + import { Button, IconButton, Search } from "@vibe/core"; + ${source} + `; +} + +describe("Aria props migration", () => { + describe("ariaLabel", () => { + defineInlineTest( + transform, + {}, + prependImport(``), + prependImport(``), + "should update 'ariaLabel' to 'aria-label'" + ); + + defineInlineTest( + transform, + {}, + prependImport(``), + prependImport(``), + "should update 'ariaLabel' to 'aria-label' with expression value" + ); + }); + + describe("ariaHidden", () => { + defineInlineTest( + transform, + {}, + ` + import { Icon } from "@vibe/core"; + + `, + ` + import { Icon } from "@vibe/core"; + + `, + "should update 'ariaHidden' to 'aria-hidden'" + ); + }); + + describe("ariaExpanded", () => { + defineInlineTest( + transform, + {}, + prependImport(``), + prependImport(``), + "should update 'ariaExpanded' to 'aria-expanded'" + ); + }); + + describe("ariaControls", () => { + defineInlineTest( + transform, + {}, + prependImport(``), + prependImport(``), + "should update 'ariaControls' to 'aria-controls'" + ); + }); + + describe("ariaHasPopup", () => { + defineInlineTest( + transform, + {}, + prependImport(``), + prependImport(``), + "should update 'ariaHasPopup' to 'aria-haspopup'" + ); + }); + + describe("ariaLabeledBy (misspelled)", () => { + defineInlineTest( + transform, + {}, + prependImport(``), + prependImport(``), + "should update 'ariaLabeledBy' to 'aria-labelledby' (fixing spelling)" + ); + }); + + describe("ariaLabelledBy", () => { + defineInlineTest( + transform, + {}, + ` + import { Checkbox } from "@vibe/core"; + + `, + ` + import { Checkbox } from "@vibe/core"; + + `, + "should update 'ariaLabelledBy' to 'aria-labelledby'" + ); + }); + + describe("ariaDescribedBy", () => { + defineInlineTest( + transform, + {}, + ` + import { List } from "@vibe/core"; + + `, + ` + import { List } from "@vibe/core"; + + `, + "should update 'ariaDescribedBy' to 'aria-describedby'" + ); + }); + + describe("Link ariaLabelDescription", () => { + defineInlineTest( + transform, + {}, + prependLinkImport(``), + prependLinkImport(``), + "should update Link 'ariaLabelDescription' to 'aria-label'" + ); + }); + + describe("multiple props on same element", () => { + defineInlineTest( + transform, + {}, + prependImport(``), + prependImport(``), + "should update multiple aria props on the same element" + ); + }); + + describe("multiple components", () => { + defineInlineTest( + transform, + {}, + prependMultiImport(` +
+ + + +
+ `), + prependMultiImport(` +
+ + + +
+ `), + "should update aria props across multiple different components" + ); + }); + + describe("no import - no change", () => { + defineInlineTest( + transform, + {}, + ``, + ``, + "should not change when no vibe import exists" + ); + }); + + describe("other library - no change", () => { + defineInlineTest( + transform, + {}, + ` + import { Button } from "other-library"; + + `, + ` + import { Button } from "other-library"; + + `, + "should not change when component is imported from another library" + ); + }); + + describe("aliased import", () => { + defineInlineTest( + transform, + {}, + ` + import { Button as MyButton } from "@vibe/core"; + test + `, + ` + import { Button as MyButton } from "@vibe/core"; + test + `, + "should handle aliased imports" + ); + }); + + describe("legacy import path", () => { + defineInlineTest( + transform, + {}, + ` + import { Button } from "monday-ui-react-core"; + + `, + ` + import { Button } from "monday-ui-react-core"; + + `, + "should handle legacy import path" + ); + }); + + describe("both old and new props with same value", () => { + defineInlineTest( + transform, + {}, + prependImport(``), + prependImport(``), + "should remove old prop when both exist with same value" + ); + }); + + describe("both old and new props with different values", () => { + defineInlineTest( + transform, + {}, + prependImport(``), + prependImport(``), + "should not change when both old and new props exist with different values" + ); + }); +}); diff --git a/packages/codemod/transformations/core/v3-to-v4/__tests__/enums-migration.test.ts b/packages/codemod/transformations/core/v3-to-v4/__tests__/enums-migration.test.ts new file mode 100644 index 0000000000..3bed5c79f5 --- /dev/null +++ b/packages/codemod/transformations/core/v3-to-v4/__tests__/enums-migration.test.ts @@ -0,0 +1,63 @@ +import transform from "../enums-migration"; +import { defineInlineTest } from "jscodeshift/src/testUtils"; + +function prependImport(source: string): string { + return ` + import { Button } from "@vibe/core"; + ${source} + `; +} + +describe("Enums migration", () => { + defineInlineTest( + transform, + {}, + prependImport(`).toJSON(); + const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); }); - Object.values(Button.colors).forEach(color => { + BUTTON_COLORS.forEach(color => { it(`renders Button color- ${color}`, () => { const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); }); - Object.values(Button.kinds).forEach(kind => { + BUTTON_KINDS.forEach(kind => { it(`renders Button kind- ${kind}`, () => { const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); @@ -82,7 +92,13 @@ describe("Button renders correctly", () => { it("renders correctly with a11y props (false)", () => { const tree = renderer .create( - ) @@ -93,7 +109,7 @@ describe("Button renders correctly", () => { it("renders correctly with a11y props (true)", () => { const tree = renderer .create( - ) diff --git a/packages/components/button/src/Button/__tests__/Button.test.jsx b/packages/components/button/src/Button/__tests__/Button.test.tsx similarity index 99% rename from packages/components/button/src/Button/__tests__/Button.test.jsx rename to packages/components/button/src/Button/__tests__/Button.test.tsx index 109befc51d..b9e39cd772 100644 --- a/packages/components/button/src/Button/__tests__/Button.test.jsx +++ b/packages/components/button/src/Button/__tests__/Button.test.tsx @@ -199,7 +199,7 @@ describe(" ); diff --git a/packages/components/clickable/package.json b/packages/components/clickable/package.json index 0d80ae991e..8f2946caea 100644 --- a/packages/components/clickable/package.json +++ b/packages/components/clickable/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/clickable", - "version": "3.0.3", + "version": "4.0.0-rc.0", "description": "Vibe sub-package for Clickable component", "repository": { "type": "git", @@ -38,18 +38,19 @@ "lint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@vibe/shared": "3.0.8", + "@vibe/shared": "^4.0.0-rc.0", + "@vibe/style": "^4.0.0-rc.0", "classnames": "^2.5.1", "es-toolkit": "^1.39.10" }, "devDependencies": { "@testing-library/react": "^12.1.2", "@testing-library/user-event": "^13.5.0", - "@vibe/config": "3.0.5", + "@vibe/config": "^4.0.0-rc.0", "react": "^16.13.0", "react-dom": "^16.13.0", "react-test-renderer": "16", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/components/clickable/src/Clickable/Clickable.module.scss b/packages/components/clickable/src/Clickable/Clickable.module.scss index 3b2d74f801..cbb6c6536a 100644 --- a/packages/components/clickable/src/Clickable/Clickable.module.scss +++ b/packages/components/clickable/src/Clickable/Clickable.module.scss @@ -1,5 +1,5 @@ -@import "~monday-ui-style/dist/mixins/typography"; -@import "~monday-ui-style/dist/mixins/states"; +@import "~@vibe/style/dist/mixins/typography"; +@import "~@vibe/style/dist/mixins/states"; .clickable { @include clickable; diff --git a/packages/components/clickable/src/Clickable/Clickable.tsx b/packages/components/clickable/src/Clickable/Clickable.tsx index 25a07c09ef..0beb053b6f 100644 --- a/packages/components/clickable/src/Clickable/Clickable.tsx +++ b/packages/components/clickable/src/Clickable/Clickable.tsx @@ -41,25 +41,23 @@ export interface ClickableProps extends VibeComponentProps { /** * The label of the element for accessibility. */ - ariaLabel?: string; + "aria-label"?: string; /** * If true, hides the element from assistive technologies. */ - ariaHidden?: boolean; - // TODO: [breaking] remove string type + "aria-hidden"?: boolean; /** * Indicates the presence of a popup associated with the element. */ - ariaHasPopup?: boolean | string; + "aria-haspopup"?: boolean; /** * If true, indicates that the associated popup is open. */ - ariaExpanded?: boolean; - // TODO: [breaking] remove string type + "aria-expanded"?: boolean; /** * The tab order of the element. */ - tabIndex?: string | number; + tabIndex?: number; /** * If true, the element is disabled. */ @@ -83,11 +81,11 @@ const Clickable = forwardRef( onMouseDown = NOOP, onMouseEnter = NOOP, onMouseLeave = NOOP, - ariaLabel, - ariaHidden, - ariaHasPopup, - ariaExpanded, - tabIndex = "0", + "aria-label": ariaLabel, + "aria-hidden": ariaHidden, + "aria-haspopup": ariaHasPopup, + "aria-expanded": ariaExpanded, + tabIndex = 0, disabled = false, style, "data-testid": dataTestId @@ -105,10 +103,10 @@ const Clickable = forwardRef( "data-testid": dataTestId, role, tabIndex, - ariaLabel, - ariaHidden, - ariaHasPopup, - ariaExpanded + "aria-label": ariaLabel, + "aria-hidden": ariaHidden, + "aria-haspopup": ariaHasPopup, + "aria-expanded": ariaExpanded }, ref ); diff --git a/packages/components/clickable/src/Clickable/__tests__/Clickable.test.tsx b/packages/components/clickable/src/Clickable/__tests__/Clickable.test.tsx index b38e779b39..73dbc533dd 100644 --- a/packages/components/clickable/src/Clickable/__tests__/Clickable.test.tsx +++ b/packages/components/clickable/src/Clickable/__tests__/Clickable.test.tsx @@ -44,7 +44,7 @@ describe("Clickable tests", () => { describe("a11y", () => { it("should add the label", () => { const ariaLabel = "Lable Name"; - const { getByLabelText } = renderComponent({ ariaLabel }); + const { getByLabelText } = renderComponent({ "aria-label": ariaLabel }); const element = getByLabelText(ariaLabel); expect(element).toBeTruthy(); }); diff --git a/packages/components/clickable/src/useClickableProps/useClickableProps.ts b/packages/components/clickable/src/useClickableProps/useClickableProps.ts index b13cc77c9e..1124fb5fef 100644 --- a/packages/components/clickable/src/useClickableProps/useClickableProps.ts +++ b/packages/components/clickable/src/useClickableProps/useClickableProps.ts @@ -17,19 +17,16 @@ export default function useClickableProps( "data-testid": dataTestId, role = "button", tabIndex = 0, - ariaLabel, - ariaHidden, - ariaHasPopup, - ariaExpanded + "aria-label": ariaLabel, + "aria-hidden": ariaHidden, + "aria-haspopup": ariaHasPopup, + "aria-expanded": ariaExpanded }: ClickableProps, ref: React.ForwardedRef ) { const onKeyDown = useKeyboardButtonPressedFunc(onClick); const componentRef = useRef(null); const mergedRef = useMergeRef(ref, componentRef); - // Remove when ariaHasPopup is no longer a string - const overrideAriaHasPopup = ariaHasPopup === undefined ? undefined : !!ariaHasPopup; - return { ref: mergedRef, id, @@ -39,11 +36,11 @@ export default function useClickableProps( onMouseDown, onMouseEnter, onMouseLeave, - tabIndex: disabled ? -1 : Number(tabIndex), + tabIndex: disabled ? -1 : tabIndex, role, "aria-label": ariaLabel, "aria-hidden": ariaHidden, - "aria-haspopup": overrideAriaHasPopup, + "aria-haspopup": ariaHasPopup, "aria-expanded": ariaExpanded }; } diff --git a/packages/components/dialog/package.json b/packages/components/dialog/package.json index 4267c8db5b..a017e01d47 100644 --- a/packages/components/dialog/package.json +++ b/packages/components/dialog/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/dialog", - "version": "3.0.12", + "version": "4.0.0-rc.0", "description": "Vibe sub-package for dialog components", "repository": { "type": "git", @@ -38,13 +38,13 @@ "lint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@popperjs/core": "2.11.6", - "@vibe/hooks": "3.0.3", - "@vibe/layer": "3.0.10", - "@vibe/shared": "3.0.8", + "@floating-ui/react-dom": "^2.1.2", + "@vibe/hooks": "^4.0.0-rc.0", + "@vibe/layer": "^4.0.0-rc.0", + "@vibe/shared": "^4.0.0-rc.0", + "@vibe/style": "^4.0.0-rc.0", "classnames": "^2.5.1", "es-toolkit": "^1.39.10", - "react-popper": "^2.3.0", "react-transition-group": "^4.4.5" }, "devDependencies": { @@ -52,11 +52,11 @@ "@testing-library/react-hooks": "^7.0.2", "@testing-library/user-event": "^13.5.0", "@types/react-transition-group": "^4.4.5", - "@vibe/config": "3.0.5", + "@vibe/config": "^4.0.0-rc.0", "react": "^16.13.0", "react-dom": "^16.13.0", "react-test-renderer": "16", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/components/dialog/src/Dialog/Dialog.tsx b/packages/components/dialog/src/Dialog/Dialog.tsx index cc42d6ad2f..f3bda28a99 100644 --- a/packages/components/dialog/src/Dialog/Dialog.tsx +++ b/packages/components/dialog/src/Dialog/Dialog.tsx @@ -1,7 +1,17 @@ import cx from "classnames"; -import React, { PureComponent, type ReactElement } from "react"; +import React, { useState, useEffect, useRef, useContext, useCallback, useMemo } from "react"; import { createPortal } from "react-dom"; -import { Manager, type Modifier, Popper, Reference } from "react-popper"; +import { + useFloating, + offset, + flip, + shift, + hide, + arrow as arrowMiddleware, + autoUpdate, + type Placement, + type Middleware +} from "@floating-ui/react-dom"; import { isFunction } from "es-toolkit"; import { chainFunctions, @@ -9,715 +19,530 @@ import { convertToArray, NOOP, isInsideClass, - type VibeComponentProps, ComponentDefaultTestId, getTestId, isClient } from "@vibe/shared"; -import DialogContent from "./DialogContent/DialogContent"; -import { Refable } from "../Refable/Refable"; -import { - AnimationType as AnimationTypeEnum, - HideShowEvent as DialogTriggerEventEnum, - DialogPosition as DialogPositionEnum -} from "./DialogConstants"; -import type * as PopperJS from "@popperjs/core"; +import DialogContent from "./components/DialogContent/DialogContent"; +import { Refable } from "./components/Refable/Refable"; import styles from "./Dialog.module.scss"; -import { - type DialogAnimationType, - type DialogPosition, - type DialogTriggerEvent, - type DialogEvent -} from "./Dialog.types"; +import { type DialogTriggerEvent, type DialogEvent, type DialogProps } from "./Dialog.types"; import { LayerContext, LayerProvider } from "@vibe/layer"; -import { createObserveContentResizeModifier } from "./modifiers/observeContentResizeModifier"; - -export interface DialogProps extends VibeComponentProps { - /** - * Class name applied to the reference wrapper element. - */ - referenceWrapperClassName?: string; - /** - * The wrapper element type to use for React components. Defaults to "span". - */ - referenceWrapperElement?: "span" | "div"; - /** - * The placement of the dialog relative to the reference element. - */ - position?: DialogPosition; - /** - * Custom Popper.js modifiers. - * https://popper.js.org/docs/v2/modifiers/ - */ - modifiers?: Modifier[]; - /** - * The starting edge of the dialog. - */ - startingEdge?: string; - /** - * Offset values for positioning adjustments. - * `main` - horizontal offset - * `secondary` - vertical offset - */ - moveBy?: { main?: number; secondary?: number }; - /** - * Delay in milliseconds before showing the dialog. - */ - showDelay?: number; - /** - * Delay in milliseconds before hiding the dialog. - */ - hideDelay?: number; - /** - * Events that trigger showing the dialog. - */ - showTrigger?: DialogTriggerEvent | DialogTriggerEvent[]; - /** - * Events that trigger hiding the dialog. - */ - hideTrigger?: DialogTriggerEvent | DialogTriggerEvent[]; - /** - * If true, prevents closing the dialog when the mouse enters it. - */ - showOnDialogEnter?: boolean; - /** - * If true, shows the dialog when the component mounts. - */ - shouldShowOnMount?: boolean; - /** - * If true, disables opening the dialog. - */ - disable?: boolean; - /** - * Controls the open state of the dialog. - */ - open?: boolean; - /** - * Derived state control for managing dialog visibility. - */ - isOpen?: boolean; - /** - * Classes that prevent showing the dialog when present. - */ - showTriggerIgnoreClass?: string | Array; - /** - * Classes that prevent hiding the dialog when present. - */ - hideTriggerIgnoreClass?: string | Array; - /** - * The animation type used for the dialog. - */ - animationType?: DialogAnimationType; - /** - * Class name applied to the dialog content container. - */ - wrapperClassName?: string; - /** - * If true, prevents animation when mounting. - */ - preventAnimationOnMount?: boolean; - /** - * The CSS selector of the container where the dialog is rendered. - */ - containerSelector?: string; - /** - * If true, positions the tooltip element. - */ - tooltip?: boolean; - /** - * Class name applied to the tooltip element. - */ - tooltipClassName?: string; - /** - * Callback fired when the dialog is shown. - */ - onDialogDidShow?: (event?: DialogEvent, eventName?: DialogTriggerEvent | string) => void; - /** - * Callback fired when the dialog is hidden. - */ - onDialogDidHide?: (event: DialogEvent, eventName: DialogTriggerEvent | string) => void; - /** - * Callback fired when clicking outside the dialog. - */ - onClickOutside?: (event: React.MouseEvent) => void; - /** - * Callback fired when clicking inside the dialog content. - */ - onContentClick?: (event: React.MouseEvent) => void; - /** - * The z-index applied to the dialog. - */ - zIndex?: number; - /** - * If true, uses derived state from props. - */ - useDerivedStateFromProps?: boolean; - /** - * If true, makes the dialog disappear when the reference element is hidden. - */ - hideWhenReferenceHidden?: boolean; - /** - * If true, triggers the callback when the dialog mounts. - */ - shouldCallbackOnMount?: boolean; - /** - * If true, instantly shows and hides the dialog without delay. - */ - instantShowAndHide?: boolean; - /** - * Callback to dynamically adjust show delay and animation behavior. - */ - getDynamicShowDelay?: () => { showDelay: number; preventAnimation: boolean }; - /** - * The content displayed inside the dialog. - */ - content?: (() => JSX.Element) | JSX.Element; - /** - * The element to position the dialog beside. - */ - children?: ReactElement | ReactElement[] | string; - /** - * If true, keyboard focus/blur events behave like mouse enter/leave. - */ - addKeyboardHideShowTriggersByDefault?: boolean; - /** - * If true, disables scrolling for the container element. - */ - disableContainerScroll?: boolean | string; - /** - * Enables the observation of content resize for the popper element. - * When set to `true`, a ResizeObserver is attached to the popper content, - * automatically triggering repositioning when the size of the content changes. - * - * This is useful for dialogs, tooltips, or popovers with dynamic content - * that may grow or shrink without a re-render being triggered. - */ - observeContentResize?: boolean; - /** - * If true, provides a LayerProvider context for nested dialogs to render correctly. - * This is useful when you have components that use Dialog internally (like Dropdown) - * inside another Dialog, ensuring proper z-index stacking and click-outside behavior. - */ - enableNestedDialogLayer?: boolean; -} -export interface DialogState { - /** - * If true, the dialog is open. - */ - isOpen?: boolean; - /** - * If true, the dialog state is derived from props. - */ - shouldUseDerivedStateFromProps?: boolean; - /** - * If true, prevents animation when opening or closing the dialog. - */ - preventAnimation?: boolean; -} - -export default class Dialog extends PureComponent { - static hideShowTriggers = DialogTriggerEventEnum; - static positions = DialogPositionEnum; - static animationTypes = AnimationTypeEnum; - static defaultProps = { - position: "top", - modifiers: [] as Modifier[], - moveBy: { main: 0, secondary: 0 }, - showDelay: 100, - hideDelay: 100, - showTrigger: Dialog.hideShowTriggers.MOUSE_ENTER, - hideTrigger: Dialog.hideShowTriggers.MOUSE_LEAVE, - showOnDialogEnter: false, - shouldShowOnMount: false, - disable: false, - open: false, - animationType: Dialog.animationTypes.EXPAND, - preventAnimationOnMount: false, - tooltip: false, - onDialogDidShow: NOOP, - onDialogDidHide: NOOP, - onClickOutside: NOOP, - onContentClick: NOOP, - useDerivedStateFromProps: false, - hideWhenReferenceHidden: false, - shouldCallbackOnMount: false, - instantShowAndHide: false, - addKeyboardHideShowTriggersByDefault: false, - observeContentResize: false, - enableNestedDialogLayer: false - }; - private showTimeout: NodeJS.Timeout; - private hideTimeout: NodeJS.Timeout; - private containerRef: React.RefObject; - context!: React.ContextType; - - constructor(props: DialogProps) { - super(props); - this.state = { - shouldUseDerivedStateFromProps: props.useDerivedStateFromProps, - isOpen: props.shouldShowOnMount - }; - - this.containerRef = React.createRef(); - - // Binding section. - this.onMouseEnter = this.onMouseEnter.bind(this); - this.onMouseLeave = this.onMouseLeave.bind(this); - this.onMouseDown = this.onMouseDown.bind(this); - this.onClick = this.onClick.bind(this); - this.onFocus = this.onFocus.bind(this); - this.onBlur = this.onBlur.bind(this); - this.isShown = this.isShown.bind(this); - this.onEsc = this.onEsc.bind(this); - this.onClickOutside = this.onClickOutside.bind(this); - this.onDialogEnter = this.onDialogEnter.bind(this); - this.onDialogLeave = this.onDialogLeave.bind(this); - this.getContainer = this.getContainer.bind(this); - this.onContentClick = this.onContentClick.bind(this); - this.onKeyDown = this.onKeyDown.bind(this); - this.closeDialogOnEscape = this.closeDialogOnEscape.bind(this); - this.onContextMenu = this.onContextMenu.bind(this); - this.getDefaultContainer = this.getDefaultContainer.bind(this); - - // Timeouts - this.hideTimeout = null; - this.showTimeout = null; - } - - closeDialogOnEscape(event: KeyboardEvent) { - const { isOpen } = this.state; - if (!isOpen) { - return; - } - switch (event.key) { - case "Escape": - this.hideDialogIfNeeded(event, DialogTriggerEventEnum.ESCAPE_KEY); - break; - case "Tab": - this.handleEvent(DialogTriggerEventEnum.TAB_KEY, event.target, event); - break; - case "Enter": - this.handleEvent(DialogTriggerEventEnum.ENTER, event.target, event); - break; - default: - break; - } - } - - componentDidMount() { - const { shouldCallbackOnMount, onDialogDidShow } = this.props; - const { isOpen } = this.state; - if (isClient()) { - document.addEventListener("keyup", this.closeDialogOnEscape); - } - if (shouldCallbackOnMount && isOpen) { - onDialogDidShow && onDialogDidShow(); - } - } - - componentWillUnmount() { - if (isClient()) { - document.removeEventListener("keyup", this.closeDialogOnEscape); - } - if (this.showTimeout) { - clearTimeout(this.showTimeout); - this.showTimeout = null; - } - if (this.hideTimeout) { - clearTimeout(this.hideTimeout); - this.hideTimeout = null; - } - } - - static getDerivedStateFromProps(nextProps: DialogProps, state: DialogState): DialogState { - if (state.shouldUseDerivedStateFromProps) { - return { isOpen: nextProps.isOpen }; - } - return null; - } - - getDefaultContainer() { - const { layerRef } = this.context; - if (layerRef?.current) { - return layerRef.current; - } - return document.body; - } - - getContainer() { - const { containerSelector } = this.props; - if (!containerSelector) { - return this.getDefaultContainer(); - } - - const containerElement = document.querySelector(containerSelector); - if (!containerElement || !(containerElement instanceof Element)) { - return this.getDefaultContainer(); - } - return containerElement; - } - - showDialog(event: DialogEvent, eventName: DialogTriggerEvent | string, options: { preventAnimation?: boolean } = {}) { - const { showDelay, instantShowAndHide, getDynamicShowDelay } = this.props; - let finalShowDelay = showDelay; - let preventAnimation = options.preventAnimation; - if (getDynamicShowDelay) { - const dynamicDelayObj = getDynamicShowDelay(); - finalShowDelay = dynamicDelayObj.showDelay || 0; - preventAnimation = preventAnimation || dynamicDelayObj.preventAnimation; - } - - if (instantShowAndHide) { - this.onShowDialog(event, eventName); - this.setState({ isOpen: true, preventAnimation }); - this.showTimeout = null; - } else { - this.showTimeout = setTimeout(() => { - this.onShowDialog(event, eventName); - this.showTimeout = null; - this.setState({ isOpen: true, preventAnimation }); - }, finalShowDelay); - } - } - - onShowDialog(event: DialogEvent, eventName: DialogTriggerEvent | string) { - if (this.isShown()) return; - const { onDialogDidShow } = this.props; - onDialogDidShow(event, eventName); - } +function Dialog({ + // Core props + id, + "data-testid": dataTestId, + children, + content, + position = "top", + moveBy = { main: 0, secondary: 0 }, + middleware: middlewareProp = [], + startingEdge, + showDelay = 100, + hideDelay = 100, + instantShowAndHide = false, + getDynamicShowDelay, + showTrigger = "mouseenter", + hideTrigger = "mouseleave", + showOnDialogEnter = false, + showTriggerIgnoreClass, + hideTriggerIgnoreClass, + addKeyboardHideShowTriggersByDefault = true, + shouldShowOnMount = false, + disable = false, + open = false, + isOpen: isOpenProp, + useDerivedStateFromProps = false, + animationType = "expand", + preventAnimationOnMount = false, + tooltip = false, + tooltipClassName, + containerSelector, + disableContainerScroll, + zIndex, + wrapperClassName, + referenceWrapperClassName, + referenceWrapperElement, + onBlur: onBlurProp, + onKeyDown: onKeyDownProp, + onClick: onClickProp, + onFocus: onFocusProp, + onMouseDown: onMouseDownProp, + onMouseEnter: onMouseEnterProp, + onMouseLeave: onMouseLeaveProp, + onContextMenu: onContextMenuProp, + onDialogDidShow = NOOP, + onDialogDidHide = NOOP, + onClickOutside: onClickOutsideProp = NOOP, + onContentClick: onContentClickProp = NOOP, + hideWhenReferenceHidden = false, + shouldCallbackOnMount = false, + observeContentResize = false, + enableNestedDialogLayer = true +}: DialogProps) { + const [isOpenState, setIsOpenState] = useState(shouldShowOnMount); + const [preventAnimation, setPreventAnimation] = useState(false); + const [referenceElement, setReferenceElement] = useState(null); + + const showTimeoutRef = useRef | null>(null); + const hideTimeoutRef = useRef | null>(null); + const containerRef = useRef(null); + const arrowRef = useRef(null); + + // Check if children are valid React elements (Refable returns null for non-elements) + const hasValidChildren = React.Children.toArray(children).some(child => React.isValidElement(child)); + + const { layerRef } = useContext(LayerContext); + + // Derived state + const isOpenInternal = useDerivedStateFromProps ? isOpenProp : isOpenState; + const isShown = isOpenInternal || open; + + // Build middleware array for Floating UI + const floatingMiddleware = useMemo(() => { + const middlewareList: Middleware[] = []; + + // Get user-provided middleware (filter out invalid ones) + const validMiddleware = middlewareProp.filter( + (m): m is Middleware => m != null && typeof m === "object" && typeof m.fn === "function" + ); - showDialogIfNeeded(event: DialogEvent, eventName: DialogTriggerEvent | string, options = {}) { - const { disable } = this.props; - if (disable) { - return; - } + // Check if user provided their own middleware to override defaults + const hasCustomOffset = validMiddleware.some(m => m.name === "offset"); + const hasCustomFlip = validMiddleware.some(m => m.name === "flip"); + const hasCustomShift = validMiddleware.some(m => m.name === "shift"); - if (this.hideTimeout) { - clearTimeout(this.hideTimeout); - this.hideTimeout = null; + // Offset middleware - skip if user provided their own + if (!hasCustomOffset && (moveBy.main !== 0 || moveBy.secondary !== 0)) { + middlewareList.push(offset({ mainAxis: moveBy.main || 0, crossAxis: moveBy.secondary || 0 })); } - if (!this.showTimeout) { - this.showDialog(event, eventName, options); + // Core positioning middleware - skip if user provided their own + if (!hasCustomFlip) { + middlewareList.push(flip()); } - } - - hideDialog(event: DialogEvent, eventName: DialogTriggerEvent | string) { - const { hideDelay, instantShowAndHide } = this.props; - if (instantShowAndHide) { - this.onHideDialog(event, eventName); - this.setState({ isOpen: false }); - this.hideTimeout = null; - } else { - this.hideTimeout = setTimeout(() => { - this.onHideDialog(event, eventName); - this.setState({ isOpen: false }); - this.hideTimeout = null; - }, hideDelay); + if (!hasCustomShift) { + middlewareList.push(shift()); } - } - onHideDialog(event: DialogEvent, eventName: DialogTriggerEvent | string) { - const { onDialogDidHide } = this.props; - if (onDialogDidHide) onDialogDidHide(event, eventName); - } - - hideDialogIfNeeded(event: DialogEvent, eventName: DialogTriggerEvent | string) { - if (this.showTimeout) { - clearTimeout(this.showTimeout); - this.showTimeout = null; - } + // Add user-provided middleware + middlewareList.push(...validMiddleware); - if (this.hideTimeout) { - return; + // Arrow middleware - pass ref directly, Floating UI handles null refs + if (tooltip) { + middlewareList.push(arrowMiddleware({ element: arrowRef })); } - this.hideDialog(event, eventName); - } - handleEvent(eventName: DialogTriggerEvent, target: EventTarget, event: DialogEvent) { - const { showTriggerIgnoreClass, hideTriggerIgnoreClass } = this.props; - if ( - this.isShowTrigger(eventName) && - !this.isShown() && - !isInsideClass(target as HTMLElement, showTriggerIgnoreClass) - ) { - return this.showDialogIfNeeded(event, eventName); + // Hide middleware for detecting when reference is hidden + if (hideWhenReferenceHidden) { + middlewareList.push(hide()); } - if (this.isHideTrigger(eventName) && !isInsideClass(target as HTMLElement, hideTriggerIgnoreClass)) { - return this.hideDialogIfNeeded(event, eventName); + return middlewareList; + }, [moveBy.main, moveBy.secondary, tooltip, hideWhenReferenceHidden, middlewareProp]); + + // Configure autoUpdate for position tracking + const whileElementsMounted = useCallback( + (reference: HTMLElement, floating: HTMLElement, update: () => void) => { + return autoUpdate(reference, floating, update, { + elementResize: observeContentResize, + layoutShift: false + }); + }, + [observeContentResize] + ); + + // Use Floating UI hook + const { refs, floatingStyles, placement, middlewareData } = useFloating({ + placement: position as Placement, + middleware: floatingMiddleware, + whileElementsMounted, + elements: { + reference: referenceElement } - } - - isShown() { - const { isOpen } = this.state; - const { open } = this.props; - - return isOpen || open; - } + }); - isShowTrigger(eventName: DialogTriggerEvent) { - const { showTrigger, addKeyboardHideShowTriggersByDefault } = this.props; - const showTriggersArray = convertToArray(showTrigger); + // Check if reference is hidden (from hide middleware) + const isReferenceHidden = middlewareData.hide?.referenceHidden ?? false; - if (addKeyboardHideShowTriggersByDefault) { - if (eventName === "focus" && showTriggersArray.indexOf("mouseenter") > -1) { + const isShowTrigger = useCallback( + (eventName: DialogTriggerEvent) => { + const showTriggersArray = convertToArray(showTrigger); + if (addKeyboardHideShowTriggersByDefault && eventName === "focus" && showTriggersArray.includes("mouseenter")) { return true; } - } - - return showTriggersArray.indexOf(eventName) > -1; - } - - isHideTrigger(eventName: DialogTriggerEvent) { - const { hideTrigger, addKeyboardHideShowTriggersByDefault } = this.props; - const hideTriggersArray = convertToArray(hideTrigger); - - if (addKeyboardHideShowTriggersByDefault) { - if (eventName === "blur" && hideTriggersArray.indexOf("mouseleave") > -1) { + return showTriggersArray.includes(eventName); + }, + [showTrigger, addKeyboardHideShowTriggersByDefault] + ); + + const isHideTrigger = useCallback( + (eventName: DialogTriggerEvent) => { + const hideTriggersArray = convertToArray(hideTrigger); + if (addKeyboardHideShowTriggersByDefault && eventName === "blur" && hideTriggersArray.includes("mouseleave")) { return true; } - } + return hideTriggersArray.includes(eventName); + }, + [hideTrigger, addKeyboardHideShowTriggersByDefault] + ); + + const showDialog = useCallback( + (event: DialogEvent, eventName: DialogTriggerEvent | string, options: { preventAnimation?: boolean } = {}) => { + let finalShowDelay = showDelay; + let preventAnimationValue = options.preventAnimation; + if (getDynamicShowDelay) { + const dynamicDelayObj = getDynamicShowDelay(); + finalShowDelay = dynamicDelayObj.showDelay || 0; + preventAnimationValue = preventAnimationValue ?? dynamicDelayObj.preventAnimation; + } - return hideTriggersArray.indexOf(eventName) > -1; - } + if (instantShowAndHide) { + onDialogDidShow(event, eventName); + setIsOpenState(true); + setPreventAnimation(!!preventAnimationValue); + showTimeoutRef.current = null; + } else { + showTimeoutRef.current = setTimeout(() => { + onDialogDidShow(event, eventName); + showTimeoutRef.current = null; + setIsOpenState(true); + setPreventAnimation(!!preventAnimationValue); + }, finalShowDelay); + } + }, + [showDelay, getDynamicShowDelay, instantShowAndHide, onDialogDidShow] + ); + + const hideDialog = useCallback( + (event: DialogEvent, eventName: DialogTriggerEvent | string) => { + if (instantShowAndHide) { + onDialogDidHide(event, eventName); + setIsOpenState(false); + hideTimeoutRef.current = null; + } else { + hideTimeoutRef.current = setTimeout(() => { + onDialogDidHide(event, eventName); + setIsOpenState(false); + hideTimeoutRef.current = null; + }, hideDelay); + } + }, + [hideDelay, instantShowAndHide, onDialogDidHide] + ); + + const showDialogIfNeeded = useCallback( + (event: DialogEvent, eventName: DialogTriggerEvent | string, options = {}) => { + if (disable) { + return; + } - onMouseEnter(e: React.MouseEvent) { - this.handleEvent("mouseenter", e.target, e); - } + if (hideTimeoutRef.current) { + clearTimeout(hideTimeoutRef.current); + hideTimeoutRef.current = null; + } - onMouseLeave(e: React.MouseEvent) { - this.handleEvent("mouseleave", e.target, e); - } + if (!showTimeoutRef.current) { + showDialog(event, eventName, options); + } + }, + [disable, showDialog] + ); + + const hideDialogIfNeeded = useCallback( + (event: DialogEvent, eventName: DialogTriggerEvent | string) => { + if (showTimeoutRef.current) { + clearTimeout(showTimeoutRef.current); + showTimeoutRef.current = null; + } - onClick(e: React.MouseEvent) { - if (e.button) return; - this.handleEvent("click", e.target, e); - } + if (!hideTimeoutRef.current) { + hideDialog(event, eventName); + } + }, + [hideDialog] + ); + + // Event handling + const handleEvent = useCallback( + (eventName: DialogTriggerEvent, target: EventTarget | null, event: DialogEvent) => { + if (!target) return; // Guard against null targets (e.g., when focus leaves the document) + if (isShowTrigger(eventName) && !isShown && !isInsideClass(target as HTMLElement, showTriggerIgnoreClass)) { + return showDialogIfNeeded(event, eventName); + } - onKeyDown(event: React.KeyboardEvent) { - if (event.key === "Enter") { - this.handleEvent("enter", event.target, event); + if (isHideTrigger(eventName) && !isInsideClass(target as HTMLElement, hideTriggerIgnoreClass)) { + return hideDialogIfNeeded(event, eventName); + } + }, + [ + isShowTrigger, + isHideTrigger, + isShown, + showTriggerIgnoreClass, + hideTriggerIgnoreClass, + showDialogIfNeeded, + hideDialogIfNeeded + ] + ); + + const getContainer = useCallback(() => { + if (containerSelector) { + const containerElement = document.querySelector(containerSelector); + if (containerElement instanceof Element) { + return containerElement; + } } + return layerRef?.current || document.body; + }, [containerSelector, layerRef]); + + // Memoized event handlers to prevent unnecessary re-renders + const onMouseEnter = useCallback((e: React.MouseEvent) => handleEvent("mouseenter", e.target, e), [handleEvent]); + const onMouseLeave = useCallback((e: React.MouseEvent) => handleEvent("mouseleave", e.target, e), [handleEvent]); + const onClick = useCallback( + (e: React.MouseEvent) => { + if (e.button) return; + handleEvent("click", e.target, e); + }, + [handleEvent] + ); + const onKeyDown = useCallback( + (event: React.KeyboardEvent) => { + // Handle element-level keyboard events for triggers + if (event.key === "Enter") handleEvent("enter", event.target, event); + if (event.key === "Tab") handleEvent("tab", event.target, event); + }, + [handleEvent] + ); + + const onMouseDown = useCallback( + (e: React.MouseEvent) => { + if (e.button) return; + handleEvent("mousedown", e.target, e); + }, + [handleEvent] + ); + const onFocus = useCallback((e: React.FocusEvent) => handleEvent("focus", e.target, e), [handleEvent]); + const onBlur = useCallback( + (e: React.FocusEvent) => { + const target = e.relatedTarget || e.target; + handleEvent("blur", target, e); + }, + [handleEvent] + ); + const onEsc = useCallback((e: React.KeyboardEvent) => handleEvent("esckey", e.target, e), [handleEvent]); + const onContextMenu = useCallback( + (e: React.MouseEvent) => { + if ((isShowTrigger("contextmenu") && !isShown) || (isHideTrigger("contextmenu") && isShown)) { + e.preventDefault(); + } + handleEvent("contextmenu", e.target, e); + }, + [isShown, isShowTrigger, isHideTrigger, handleEvent] + ); + + const onClickOutside = useCallback( + (event: React.MouseEvent) => { + handleEvent("clickoutside", event.target, event); + onClickOutsideProp(event); + }, + [handleEvent, onClickOutsideProp] + ); + + const onDialogEnter = useCallback( + (event: React.MouseEvent) => { + if (showOnDialogEnter) showDialogIfNeeded(event, "DialogEnter"); + }, + [showOnDialogEnter, showDialogIfNeeded] + ); + + const onDialogLeave = useCallback( + (event: React.MouseEvent) => { + if (showOnDialogEnter) hideDialogIfNeeded(event, "DialogLeave"); + }, + [showOnDialogEnter, hideDialogIfNeeded] + ); + + const onContentClick = useCallback( + (e: React.MouseEvent) => { + handleEvent("onContentClick", e.target, e); + onContentClickProp(e); + }, + [handleEvent, onContentClickProp] + ); + + // Memoized chained event handlers to prevent Refable children re-renders + const chainedOnBlur = useMemo(() => chainFunctions([onBlurProp, onBlur], true), [onBlurProp, onBlur]); + const chainedOnKeyDown = useMemo(() => chainFunctions([onKeyDownProp, onKeyDown], true), [onKeyDownProp, onKeyDown]); + const chainedOnClick = useMemo(() => chainFunctions([onClickProp, onClick], true), [onClickProp, onClick]); + const chainedOnFocus = useMemo(() => chainFunctions([onFocusProp, onFocus], true), [onFocusProp, onFocus]); + const chainedOnMouseDown = useMemo( + () => chainFunctions([onMouseDownProp, onMouseDown], true), + [onMouseDownProp, onMouseDown] + ); + const chainedOnMouseEnter = useMemo( + () => chainFunctions([onMouseEnterProp, onMouseEnter], true), + [onMouseEnterProp, onMouseEnter] + ); + const chainedOnMouseLeave = useMemo( + () => chainFunctions([onMouseLeaveProp, onMouseLeave], true), + [onMouseLeaveProp, onMouseLeave] + ); + const chainedOnContextMenu = useMemo( + () => chainFunctions([onContextMenuProp, onContextMenu], true), + [onContextMenuProp, onContextMenu] + ); + + // Document-level keyboard handler using stable ref pattern + // Must handle Escape, Tab, and Enter at document level to match old behavior + const closeDialogOnEscapeRef = useRef<(event: KeyboardEvent) => void>(); + closeDialogOnEscapeRef.current = (event: KeyboardEvent) => { + if (!isShown) return; - if (event.key === "Tab") { - this.handleEvent("tab", event.target, event); + switch (event.key) { + case "Escape": + hideDialogIfNeeded(event, "esckey"); + break; + case "Tab": + handleEvent("tab", event.target, event); + break; + case "Enter": + handleEvent("enter", event.target, event); + break; } - } - - onMouseDown(e: React.MouseEvent) { - if (e.button) return; - this.handleEvent("mousedown", e.target, e); - } - - onFocus(e: React.FocusEvent) { - this.handleEvent("focus", e.target, e); - } - - onBlur(e: React.FocusEvent) { - this.handleEvent("blur", e.relatedTarget, e); - } + }; - onEsc(e: React.KeyboardEvent) { - this.handleEvent("esckey", e.target, e); - } + // Effects - onContextMenu(e: React.MouseEvent) { - const isShown = this.isShown(); - if ((this.isShowTrigger("contextmenu") && !isShown) || (this.isHideTrigger("contextmenu") && isShown)) { - e.preventDefault(); + // Callback on mount + useEffect(() => { + if (shouldCallbackOnMount && shouldShowOnMount) { + onDialogDidShow(); } - this.handleEvent("contextmenu", e.target, e); - } - - onClickOutside(event: React.MouseEvent) { - const { onClickOutside } = this.props; - this.handleEvent("clickoutside", event.target, event); - onClickOutside(event); - } - - onDialogEnter(event: React.MouseEvent) { - const { showOnDialogEnter } = this.props; - if (showOnDialogEnter) this.showDialogIfNeeded(event, "DialogEnter"); - } - - onDialogLeave(event: React.MouseEvent) { - const { showOnDialogEnter } = this.props; - if (showOnDialogEnter) this.hideDialogIfNeeded(event, "DialogLeave"); - } - - onContentClick(e: React.MouseEvent) { - const { onContentClick } = this.props; - this.handleEvent("onContentClick", e.target, e); - onContentClick(e); - } - - render() { - const { - wrapperClassName, - content, - startingEdge, - children, - preventAnimationOnMount, - animationType, - position, - showDelay, - moveBy, - modifiers, - tooltip, - tooltipClassName, - referenceWrapperClassName, - referenceWrapperElement, - zIndex, - hideWhenReferenceHidden, - disableContainerScroll, - containerSelector, - observeContentResize, - enableNestedDialogLayer, - id, - "data-testid": dataTestId - } = this.props; - const { preventAnimation } = this.state; - const overrideDataTestId = dataTestId || getTestId(ComponentDefaultTestId.DIALOG, id); - - const animationTypeCalculated = preventAnimationOnMount || preventAnimation ? undefined : animationType; - const contentRendered = isFunction(content) ? content() : content; - - if (!contentRendered) { - return children; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // Cleanup timeouts on unmount + useEffect(() => { + return () => { + if (showTimeoutRef.current) clearTimeout(showTimeoutRef.current); + if (hideTimeoutRef.current) clearTimeout(hideTimeoutRef.current); + }; + }, []); + + // Document keyboard listener (stable reference via ref) + useEffect(() => { + if (!isClient()) return; + + const handler = (event: KeyboardEvent) => closeDialogOnEscapeRef.current?.(event); + document.addEventListener("keyup", handler); + return () => document.removeEventListener("keyup", handler); + }, []); + + // Handle reference hidden state + useEffect(() => { + if (hideWhenReferenceHidden && isReferenceHidden && isShown) { + const event = new CustomEvent("onReferenceHidden"); + hideDialog(event, "onReferenceHidden"); } - return ( - - - {({ ref }) => { - return ( - - {children} - - ); - }} - - {isClient() && - createPortal( - - {({ placement, style, ref, arrowProps, isReferenceHidden }) => { - if (!this.isShown() && placement) { - return null; - } - - if (hideWhenReferenceHidden && isReferenceHidden) { - const event = new CustomEvent("onReferenceHidden"); - this.hideDialog(event, "onReferenceHidden"); - } - - const mergedRef = chainRefFunctions([ref, this.containerRef]); - - const dialogContent = ( - - {contentRendered} - {tooltip && ( -
- )} - - ); - - return enableNestedDialogLayer ? ( - {dialogContent} - ) : ( - dialogContent - ); - }} - , - this.getContainer() - )} - - ); + }, [hideWhenReferenceHidden, isReferenceHidden, isShown, hideDialog]); + + // Computed values + const overrideDataTestId = dataTestId || getTestId(ComponentDefaultTestId.DIALOG, id); + + const arrowStyles = useMemo(() => { + if (!middlewareData.arrow) return {}; + const { x, y } = middlewareData.arrow; + return { + left: x != null ? `${x}px` : "", + top: y != null ? `${y}px` : "", + transform: "rotate(45deg)" + }; + }, [middlewareData.arrow]); + + // Skip Floating UI positioning when no children (e.g. floating Tipseen) โ€” let CSS handle it + const finalFloatingStyles = useMemo( + () => + hasValidChildren + ? { ...floatingStyles, ...(zIndex !== undefined && { zIndex }) } + : { ...(zIndex !== undefined && { zIndex }) }, + [floatingStyles, zIndex, hasValidChildren] + ); + + const animationTypeCalculated = preventAnimationOnMount || preventAnimation ? undefined : animationType; + const contentRendered = isFunction(content) ? content() : content; + + // Early return if no content - wrap in fragment for type safety + if (!contentRendered) { + return <>{children}; } -} -function chainOnPropsAndInstance(name: string, instance: Dialog, props: DialogProps) { - // @ts-ignore - return chainFunctions([props[name], instance[name]], true); + const mergedFloatingRef = chainRefFunctions([refs.setFloating, containerRef]); + + const dialogContent = ( + + {contentRendered} + {tooltip && ( +
+ )} + + ); + + return ( + <> + + {children} + + {isClient() && + isShown && + createPortal( + enableNestedDialogLayer ? ( + {dialogContent} + ) : ( + dialogContent + ), + getContainer() + )} + + ); } -Dialog.contextType = LayerContext; +export default Dialog; diff --git a/packages/components/dialog/src/Dialog/Dialog.types.ts b/packages/components/dialog/src/Dialog/Dialog.types.ts index af8a11c42c..9cc02e448c 100644 --- a/packages/components/dialog/src/Dialog/Dialog.types.ts +++ b/packages/components/dialog/src/Dialog/Dialog.types.ts @@ -1,3 +1,8 @@ +import type React from "react"; +import type { ReactElement } from "react"; +import type { VibeComponentProps } from "@vibe/shared"; +import type { Middleware, Placement } from "@floating-ui/react-dom"; + export type DialogType = "modal" | "popover"; export type DialogSize = "none" | "small" | "medium" | "large"; @@ -32,9 +37,213 @@ export type DialogTriggerEvent = export type DialogAnimationType = "opacity-and-slide" | "expand"; +export type DialogStartingEdge = "top" | "bottom"; + export type DialogOffset = { main?: number; secondary?: number; }; export type DialogEvent = React.MouseEvent | React.KeyboardEvent | KeyboardEvent | React.FocusEvent | CustomEvent; + +export type DialogMiddleware = Middleware; + +export type DialogPlacement = Placement; + +export interface DialogProps extends VibeComponentProps { + /** + * Event handler for blur events on the reference element. + */ + onBlur?: (e: React.FocusEvent) => void; + /** + * Event handler for keydown events on the reference element. + */ + onKeyDown?: (e: React.KeyboardEvent) => void; + /** + * Event handler for click events on the reference element. + */ + onClick?: (e: React.MouseEvent) => void; + /** + * Event handler for focus events on the reference element. + */ + onFocus?: (e: React.FocusEvent) => void; + /** + * Event handler for mousedown events on the reference element. + */ + onMouseDown?: (e: React.MouseEvent) => void; + /** + * Event handler for mouseenter events on the reference element. + */ + onMouseEnter?: (e: React.MouseEvent) => void; + /** + * Event handler for mouseleave events on the reference element. + */ + onMouseLeave?: (e: React.MouseEvent) => void; + /** + * Event handler for contextmenu events on the reference element. + */ + onContextMenu?: (e: React.MouseEvent) => void; + /** + * Class name applied to the reference wrapper element. + */ + referenceWrapperClassName?: string; + /** + * The wrapper element type to use for React components. Defaults to "span". + */ + referenceWrapperElement?: "span" | "div"; + /** + * The placement of the dialog relative to the reference element. + */ + position?: DialogPosition; + /** + * Custom Floating UI middleware for positioning logic. + * If you provide `offset`, `flip`, or `shift` middleware, the defaults will be skipped. + * Other middleware (like `size`, `inline`, `autoPlacement`) are added to the chain. + * @see https://floating-ui.com/docs/middleware + */ + middleware?: DialogMiddleware[]; + /** + * The starting edge of the dialog animation. + */ + startingEdge?: DialogStartingEdge; + /** + * Offset values for positioning adjustments. + * `main` - distance from reference element + * `secondary` - cross-axis offset (skidding) + */ + moveBy?: { main?: number; secondary?: number }; + /** + * Delay in milliseconds before showing the dialog. + */ + showDelay?: number; + /** + * Delay in milliseconds before hiding the dialog. + */ + hideDelay?: number; + /** + * Events that trigger showing the dialog. + */ + showTrigger?: DialogTriggerEvent | DialogTriggerEvent[]; + /** + * Events that trigger hiding the dialog. + */ + hideTrigger?: DialogTriggerEvent | DialogTriggerEvent[]; + /** + * If true, keeps the dialog open when mouse enters it. + */ + showOnDialogEnter?: boolean; + /** + * If true, shows the dialog when the component mounts. + */ + shouldShowOnMount?: boolean; + /** + * If true, disables opening the dialog. + */ + disable?: boolean; + /** + * Controls the open state of the dialog (controlled mode). + */ + open?: boolean; + /** + * Controlled open state for derived state pattern. + */ + isOpen?: boolean; + /** + * CSS class names that, when present on target, prevent showing the dialog. + */ + showTriggerIgnoreClass?: string | string[]; + /** + * CSS class names that, when present on target, prevent hiding the dialog. + */ + hideTriggerIgnoreClass?: string | string[]; + /** + * The animation type used for the dialog. + */ + animationType?: DialogAnimationType; + /** + * Class name applied to the dialog content wrapper. + */ + wrapperClassName?: string; + /** + * If true, prevents animation when mounting. + */ + preventAnimationOnMount?: boolean; + /** + * CSS selector of the container where the dialog is portaled. + */ + containerSelector?: string; + /** + * If true, renders with tooltip arrow styling. + */ + tooltip?: boolean; + /** + * Class name applied to the tooltip arrow element. + */ + tooltipClassName?: string; + /** + * Callback fired when the dialog is shown. + */ + onDialogDidShow?: (event?: DialogEvent, eventName?: DialogTriggerEvent | string) => void; + /** + * Callback fired when the dialog is hidden. + */ + onDialogDidHide?: (event: DialogEvent, eventName: DialogTriggerEvent | string) => void; + /** + * Callback fired when clicking outside the dialog. + */ + onClickOutside?: (event: React.MouseEvent) => void; + /** + * Callback fired when clicking inside the dialog content. + */ + onContentClick?: (event: React.MouseEvent) => void; + /** + * The z-index applied to the dialog. + */ + zIndex?: number; + /** + * If true, uses derived state from props pattern. + */ + useDerivedStateFromProps?: boolean; + /** + * If true, hides the dialog when the reference element scrolls out of view. + */ + hideWhenReferenceHidden?: boolean; + /** + * If true, fires onDialogDidShow callback on mount. + */ + shouldCallbackOnMount?: boolean; + /** + * If true, shows and hides the dialog without delay. + */ + instantShowAndHide?: boolean; + /** + * Callback to dynamically adjust show delay and animation behavior. + */ + getDynamicShowDelay?: () => { showDelay: number; preventAnimation: boolean }; + /** + * The content displayed inside the dialog. Can be a render function. + */ + content?: (() => JSX.Element) | JSX.Element; + /** + * The reference element(s) that the dialog is positioned relative to. + */ + children?: ReactElement | ReactElement[] | string; + /** + * If true, keyboard focus/blur events behave like mouse enter/leave. + */ + addKeyboardHideShowTriggersByDefault?: boolean; + /** + * If true or a selector string, disables scrolling in the container when open. + */ + disableContainerScroll?: boolean | string; + /** + * If true, automatically updates position when content resizes. + */ + observeContentResize?: boolean; + /** + * If true, provides a LayerProvider context for nested dialogs to render correctly. + * This is useful when you have components that use Dialog internally (like Dropdown) + * inside another Dialog, ensuring proper z-index stacking and click-outside behavior. + */ + enableNestedDialogLayer?: boolean; +} diff --git a/packages/components/dialog/src/Dialog/DialogConstants.ts b/packages/components/dialog/src/Dialog/DialogConstants.ts index 3ec1fce9bc..355311b60b 100644 --- a/packages/components/dialog/src/Dialog/DialogConstants.ts +++ b/packages/components/dialog/src/Dialog/DialogConstants.ts @@ -1,65 +1,3 @@ -/** - * @deprecated - */ -export enum HideShowEvent { - CLICK = "click", - CLICK_OUTSIDE = "clickoutside", - ESCAPE_KEY = "esckey", - TAB_KEY = "tab", - MOUSE_ENTER = "mouseenter", - MOUSE_LEAVE = "mouseleave", - ENTER = "enter", - MOUSE_DOWN = "mousedown", - FOCUS = "focus", - BLUR = "blur", - CONTENT_CLICK = "onContentClick", - CONTEXT_MENU = "contextmenu" -} - -/** - * @deprecated - */ -export enum AnimationType { - OPACITY_AND_SLIDE = "opacity-and-slide", - EXPAND = "expand" -} - -/** - * @deprecated - */ -export enum DialogType { - MODAL = "modal", - POPOVER = "popover" -} - -/** - * @deprecated - */ -export enum DialogSize { - NONE = "none", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large" -} - -/** - * @deprecated - */ -export enum DialogPosition { - LEFT = "left", - LEFT_START = "left-start", - LEFT_END = "left-end", - RIGHT = "right", - RIGHT_START = "right-start", - RIGHT_END = "right-end", - TOP = "top", - TOP_START = "top-start", - TOP_END = "top-end", - BOTTOM = "bottom", - BOTTOM_START = "bottom-start", - BOTTOM_END = "bottom-end" -} - export enum DialogPlacement { RIGHT = "right", RIGHT_START = "right-start", diff --git a/packages/components/dialog/src/Dialog/DialogContent/DialogContent.module.scss b/packages/components/dialog/src/Dialog/DialogContent/DialogContent.module.scss deleted file mode 100644 index 2d7be3db69..0000000000 --- a/packages/components/dialog/src/Dialog/DialogContent/DialogContent.module.scss +++ /dev/null @@ -1,173 +0,0 @@ -@import "~monday-ui-style/dist/mixins"; - -.contentWrapper { - outline: 0; - - &.top, - &.right, - &.left, - &.bottom { - padding: var(--spacing-xs); - } - &.bottomStart, - &.topStart, - &.bottomEnd, - &.topEnd { - padding-block: var(--spacing-xs); - } - &.bottomStart, - &.topStart { - padding-inline-end: var(--spacing-xs); - } - &.bottomEnd, - &.topEnd { - padding-inline-start: var(--spacing-xs); - } - &.leftStart, - &.rightStart, - &.leftEnd, - &.rightEnd { - padding-inline: var(--spacing-xs); - } - &.leftStart, - &.rightStart { - padding-block-end: var(--spacing-xs); - } - &.leftEnd, - &.rightEnd { - padding-block-start: var(--spacing-xs); - } -} - -.contentWrapper[data-popper-reference-hidden="true"] { - visibility: hidden; - pointer-events: none; -} - -.contentComponent:focus { - outline: none; -} - -.contentComponent.hasTooltip { - padding: 6px; -} - -// Animations - -$translate-minus-px: calc(var(--spacing-medium) * -1); - -.opacitySlideAppear { - opacity: 0; - - &.top { - transform: translateY(var(--spacing-medium)); - } - - &.right { - transform: translateX($translate-minus-px); - } - - &.bottom { - transform: translateY($translate-minus-px); - } - - &.left { - transform: translateX(var(--spacing-medium)); - } -} - -.opacitySlideAppearActive { - transition: opacity 0.2s ease, transform 0.2s ease-out; - opacity: 1; - pointer-events: none; - - &.top, - &.bottom { - transform: translateY(0); - } - - &.right, - &.left { - transform: translateX(0); - } -} - -.expandAppear, -.expandExit { - transition: transform 0.1s $expand-animation-timing; - &.top, - &.topStart, - &.topEnd { - transform-origin: bottom center; - transform: scale(0.8); - &.edgeBottom { - transform-origin: bottom left; - } - &.edgeTop { - transform-origin: bottom right; - } - } - - &.right, - &.rightStart, - &.rightEnd { - transform-origin: left; - transform: scale(0.8); - &.edgeBottom { - transform-origin: top left; - } - &.edgeTop { - transform-origin: bottom left; - } - } - - &.bottom, - &.bottomStart, - &.bottomEnd { - transform-origin: top; - transform: scale(0.8); - &.edgeBottom { - transform-origin: top left; - } - &.edgeTop { - transform-origin: top right; - } - } - - &.left, - &.leftStart, - &.leftEnd { - transform-origin: right; - transform: scale(0.8); - &.edgeBottom { - transform-origin: top right; - } - &.edgeTop { - transform-origin: bottom right; - } - } -} - -.expandExit { - transition: transform 0.1s $expand-animation-timing; -} - -.expandAppearActive { - transition: transform 0.1s $expand-animation-timing; - pointer-events: none; - - &.top, - &.topStart, - &.topEnd, - &.bottom, - &.bottomStart, - &.bottomEnd, - &.right, - &.rightStart, - &.rightEnd, - &.left, - &.leftStart, - &.leftEnd { - transform: scale(1); - } -} diff --git a/packages/components/dialog/src/Dialog/DialogContent/DialogContent.tsx b/packages/components/dialog/src/Dialog/DialogContent/DialogContent.tsx deleted file mode 100644 index 6d03518c5c..0000000000 --- a/packages/components/dialog/src/Dialog/DialogContent/DialogContent.tsx +++ /dev/null @@ -1,225 +0,0 @@ -import React, { - cloneElement, - type CSSProperties, - forwardRef, - type ReactElement, - useCallback, - useEffect, - useRef -} from "react"; -import cx from "classnames"; -import { camelCase } from "es-toolkit"; -import { CSSTransition } from "react-transition-group"; -import { type CSSTransitionProps } from "react-transition-group/CSSTransition"; -import { useClickOutside } from "@vibe/hooks"; -import { - chainFunctions, - NOOP, - useKeyEvent, - type VibeComponentProps, - keyCodes, - getStyle, - useMergeRef -} from "@vibe/shared"; -import { type DialogAnimationType, type DialogTriggerEvent } from "../Dialog.types"; -import type * as PopperJS from "@popperjs/core"; -import styles from "./DialogContent.module.scss"; -import useDisableScroll from "./useDisableScroll"; - -const EMPTY_OBJECT = {}; -const ESCAPE_KEYS = [keyCodes.ESCAPE]; - -export interface DialogContentProps extends VibeComponentProps { - /** - * The content inside the dialog. - */ - children?: ReactElement | ReactElement[]; - /** - * The placement of the dialog relative to the reference element. - */ - position?: PopperJS.Placement; - /** - * Class name applied to the dialog wrapper. - */ - wrapperClassName?: string; - /** - * If true, the dialog is open. - */ - isOpen?: boolean; - // TODO: [breaking] use type - /** - * The starting edge of the dialog. - */ - startingEdge?: any; - /** - * The animation type used for showing/hiding the dialog. - */ - animationType?: DialogAnimationType; - /** - * Callback fired when the Escape key is pressed. - */ - onEsc?: (event: React.KeyboardEvent) => void; - /** - * Callback fired when the mouse enters the dialog. - */ - onMouseEnter?: (event: React.MouseEvent) => void; - /** - * Callback fired when the mouse leaves the dialog. - */ - onMouseLeave?: (event: React.MouseEvent) => void; - /** - * Callback fired when clicking outside the dialog. - */ - onClickOutside?: (event: React.MouseEvent, hideShowEvent: DialogTriggerEvent) => void; - /** - * Callback fired when clicking inside the dialog. - */ - onClick?: (event: React.MouseEvent) => void; - /** - * Delay before showing the dialog. - */ - showDelay?: number; - /** - * Inline styles applied to the dialog. - */ - styleObject?: CSSProperties; - /** - * If true, hides the reference element when the dialog is shown. - */ - isReferenceHidden?: boolean; - /** - * If true, applies tooltip arrow to the dialog. - */ - hasTooltip?: boolean; - /** - * The CSS selector of the container where the dialog should be rendered. - */ - containerSelector?: string; - /** - * If true, disables scrolling in the container when the dialog is open. - */ - disableContainerScroll?: boolean | string; - /** - * Callback fired when the context menu (right-click) event occurs outside the dialog. - */ - onContextMenu?: (e: React.MouseEvent) => void; -} - -const DialogContent = forwardRef( - ( - { - onEsc = NOOP, - children, - position, - wrapperClassName, - isOpen = false, - startingEdge, - animationType = "expand", - onMouseEnter = NOOP, - onMouseLeave = NOOP, - onClickOutside = NOOP, - onClick = NOOP, - onContextMenu = NOOP, - showDelay, - styleObject = EMPTY_OBJECT, - isReferenceHidden, - hasTooltip = false, - containerSelector, - disableContainerScroll = false, - "data-testid": dataTestId - }: DialogContentProps, - forwardRef: React.ForwardedRef - ) => { - const contentRef = useRef(null); - - const wrapperRef = useRef(null); - const mergedWrapperRef = useMergeRef(forwardRef, wrapperRef); - - const onOutSideClick = useCallback( - (event: React.MouseEvent) => { - if (isOpen) { - return onClickOutside(event, "clickoutside"); - } - }, - [isOpen, onClickOutside] - ); - const overrideOnContextMenu = useCallback( - (event: React.MouseEvent) => { - if (isOpen) { - onContextMenu(event); - } - }, - [isOpen, onContextMenu] - ); - useKeyEvent({ keys: ESCAPE_KEYS, callback: onEsc }); - // Watch the wrapper ref to include padding area, tooltip arrows, and nested Dialogs - useClickOutside({ callback: onOutSideClick, ref: wrapperRef }); - useClickOutside({ eventName: "contextmenu", callback: overrideOnContextMenu, ref: wrapperRef }); - const selectorToDisable = typeof disableContainerScroll === "string" ? disableContainerScroll : containerSelector; - const { disableScroll, enableScroll } = useDisableScroll(selectorToDisable); - - useEffect(() => { - if (disableContainerScroll) { - if (isOpen) { - disableScroll(); - } else { - enableScroll(); - } - } - }, [disableContainerScroll, disableScroll, enableScroll, isOpen]); - - const transitionOptions: Partial = { classNames: undefined }; - - switch (animationType) { - case "expand": - transitionOptions.classNames = { - appear: styles.expandAppear, - appearActive: styles.expandAppearActive, - exit: styles.expandExit - }; - break; - case "opacity-and-slide": - transitionOptions.classNames = { - appear: styles.opacitySlideAppear, - appearActive: styles.opacitySlideAppearActive - }; - break; - } - return ( - - -
- {React.Children.toArray(children).map((child: ReactElement) => { - return cloneElement(child, { - onMouseEnter: chainFunctions([child.props.onMouseEnter, onMouseEnter]), - onMouseLeave: chainFunctions([child.props.onMouseLeave, onMouseLeave]) - }); - })} -
-
-
- ); - } -); - -export default DialogContent; diff --git a/packages/components/dialog/src/Dialog/DialogContent/useDisableScroll.ts b/packages/components/dialog/src/Dialog/DialogContent/useDisableScroll.ts deleted file mode 100644 index 572c892385..0000000000 --- a/packages/components/dialog/src/Dialog/DialogContent/useDisableScroll.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { useCallback, useEffect } from "react"; - -const useDisableScroll = (scrollableQuerySelector: string) => { - const _disableScroll = useCallback((e: Event) => { - e.preventDefault(); - e.stopPropagation(); - - return false; - }, []); - - const disableScroll = useCallback(() => { - if (scrollableQuerySelector?.length > 0) { - document.querySelectorAll(scrollableQuerySelector).forEach((item: Element) => { - item.addEventListener("wheel", _disableScroll); - }); - } - }, [_disableScroll, scrollableQuerySelector]); - - const enableScroll = useCallback(() => { - if (scrollableQuerySelector?.length > 0) { - document.querySelectorAll(scrollableQuerySelector).forEach((item: Element) => { - item.removeEventListener("wheel", _disableScroll); - }); - } - }, [_disableScroll, scrollableQuerySelector]); - - useEffect(() => { - return enableScroll; - }, [enableScroll]); - - return { - disableScroll, - enableScroll - }; -}; - -export default useDisableScroll; diff --git a/packages/components/dialog/src/Dialog/__tests__/Dialog.test.tsx b/packages/components/dialog/src/Dialog/__tests__/Dialog.test.tsx index e8e38bb917..37e682c104 100644 --- a/packages/components/dialog/src/Dialog/__tests__/Dialog.test.tsx +++ b/packages/components/dialog/src/Dialog/__tests__/Dialog.test.tsx @@ -1,18 +1,27 @@ -import { vi, describe, it, expect } from "vitest"; +import { vi, describe, it, expect, beforeEach, afterEach } from "vitest"; import React from "react"; import userEvent from "@testing-library/user-event"; -import { render, screen } from "@testing-library/react"; +import { render, screen, waitFor, within } from "@testing-library/react"; import Dialog, { type DialogProps } from "../Dialog"; function renderVisibleDialogOnMount(dialogProps: DialogProps) { - renderDialogOnMount({ ...dialogProps, shouldShowOnMount: true }); + return renderDialogOnMount({ ...dialogProps, shouldShowOnMount: true }); } function renderDialogOnMount(dialogProps: DialogProps) { - render(); + return render(); } describe("Dialog tests", () => { + beforeEach(() => { + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + vi.useRealTimers(); + }); + describe("Callbacks", () => { it("should call onClickOutside callback when click outside", async () => { const onClickOutsideMock = vi.fn(); @@ -20,6 +29,7 @@ describe("Dialog tests", () => { userEvent.click(document.body); expect(onClickOutsideMock).toBeCalled(); }); + it("should not call onClickOutside callback when clicking inside the dialog", async () => { const onClickOutsideMock = vi.fn(); renderVisibleDialogOnMount({ onClickOutside: onClickOutsideMock, content:
Dialog
}); @@ -27,5 +37,436 @@ describe("Dialog tests", () => { userEvent.click(dialog); expect(onClickOutsideMock).not.toBeCalled(); }); + + it("should call onDialogDidShow callback when dialog is shown", async () => { + const onDialogDidShowMock = vi.fn(); + renderVisibleDialogOnMount({ + onDialogDidShow: onDialogDidShowMock, + content:
Dialog
, + shouldCallbackOnMount: true + }); + vi.runAllTimers(); + expect(onDialogDidShowMock).toBeCalled(); + }); + + it("should call onDialogDidHide callback when dialog is hidden", async () => { + const onDialogDidHideMock = vi.fn(); + const { container } = render( + Dialog
} + showTrigger={["click"]} + hideTrigger={["click"]} + > + + + ); + vi.runAllTimers(); + + const button = within(container).getByText("Toggle"); + await userEvent.click(button); + vi.runAllTimers(); + await waitFor(() => expect(onDialogDidHideMock).toBeCalled()); + }); + + it("should call onContentClick callback when clicking inside dialog", async () => { + const onContentClickMock = vi.fn(); + renderVisibleDialogOnMount({ onContentClick: onContentClickMock, content:
Dialog Content
}); + const dialog = await screen.findByText("Dialog Content"); + await userEvent.click(dialog); + expect(onContentClickMock).toBeCalled(); + }); + }); + + describe("Show/Hide Triggers", () => { + it("should show dialog on click when showTrigger is click", async () => { + const { container } = render( + Dialog Content
}> + + + ); + + expect(screen.queryByText("Dialog Content")).not.toBeInTheDocument(); + + const button = within(container).getByText("Click Me"); + await userEvent.click(button); + vi.runAllTimers(); + + await waitFor(() => expect(screen.getByText("Dialog Content")).toBeInTheDocument()); + }); + + it("should show dialog on mouseenter when showTrigger is mouseenter", async () => { + const { container } = render( + Hover Content
}> +
Hover Me
+ + ); + + expect(screen.queryByText("Hover Content")).not.toBeInTheDocument(); + + const trigger = within(container).getByText("Hover Me"); + await userEvent.hover(trigger); + vi.runAllTimers(); + + await waitFor(() => expect(screen.getByText("Hover Content")).toBeInTheDocument()); + }); + + it("should show dialog on focus when showTrigger is focus", async () => { + const { container } = render( + Focus Content
}> + + + ); + + expect(screen.queryByText("Focus Content")).not.toBeInTheDocument(); + + const input = within(container).getByPlaceholderText("Focus Me"); + input.focus(); + vi.runAllTimers(); + + await waitFor(() => expect(screen.getByText("Focus Content")).toBeInTheDocument()); + }); + + it("should hide dialog on mouseleave when hideTrigger is mouseleave", async () => { + const { container } = render( + Hover Content
} + > +
Hover Me
+ + ); + vi.runAllTimers(); + + await waitFor(() => expect(screen.getByText("Hover Content")).toBeInTheDocument()); + + const trigger = within(container).getByText("Hover Me"); + await userEvent.unhover(trigger); + vi.runAllTimers(); + + await waitFor(() => expect(screen.queryByText("Hover Content")).not.toBeInTheDocument()); + }); + + it("should hide dialog on blur when hideTrigger is blur", async () => { + const onDialogDidHideMock = vi.fn(); + const { container } = render( + Focus Content
} + > + + + ); + + await waitFor(() => expect(screen.getByText("Focus Content")).toBeInTheDocument()); + + const input = within(container).getByPlaceholderText("Focus Me"); + input.focus(); + + // Trigger blur by focusing something else + await userEvent.tab(); + + await waitFor(() => expect(onDialogDidHideMock).toBeCalled()); + }); + }); + + describe("Keyboard Interactions", () => { + it("should hide dialog on Escape key when hideTrigger includes esckey", async () => { + render( + Press Escape
}> + + + ); + vi.runAllTimers(); + + await waitFor(() => expect(screen.getByText("Press Escape")).toBeInTheDocument()); + + await userEvent.keyboard("{Escape}"); + vi.runAllTimers(); + + await waitFor(() => expect(screen.queryByText("Press Escape")).not.toBeInTheDocument()); + }); + + it("should trigger enter event on Enter key", async () => { + const onDialogDidHideMock = vi.fn(); + const { container } = render( + Press Enter
} + > + + + ); + vi.runAllTimers(); + + await waitFor(() => expect(screen.getByText("Press Enter")).toBeInTheDocument()); + + const button = within(container).getByText("Toggle"); + button.focus(); + await userEvent.keyboard("{Enter}"); + vi.runAllTimers(); + + await waitFor(() => expect(onDialogDidHideMock).toBeCalled()); + }); + }); + + describe("Delay Behavior", () => { + it("should respect showDelay when showing dialog", async () => { + const onDialogDidShowMock = vi.fn(); + const { container } = render( + Delayed} + > + + + ); + + const button = within(container).getByText("Click"); + await userEvent.click(button); + + // Should not show immediately + expect(onDialogDidShowMock).not.toBeCalled(); + + // Advance timers by 500ms + vi.advanceTimersByTime(500); + + await waitFor(() => expect(onDialogDidShowMock).toBeCalled()); + }); + + it("should respect hideDelay when hiding dialog", async () => { + const onDialogDidHideMock = vi.fn(); + const { container } = render( + Delayed Hide} + > + + + ); + vi.runAllTimers(); + + const button = within(container).getByText("Click"); + await userEvent.click(button); + + // Should not hide immediately + expect(onDialogDidHideMock).not.toBeCalled(); + + // Advance timers by 300ms + vi.advanceTimersByTime(300); + + await waitFor(() => expect(onDialogDidHideMock).toBeCalled()); + }); + + it("should cancel show timeout when hiding is triggered", async () => { + const onDialogDidShowMock = vi.fn(); + const { container } = render( + Hover} + > +
Hover Me
+
+ ); + + const trigger = within(container).getByText("Hover Me"); + + // Start hovering + await userEvent.hover(trigger); + vi.advanceTimersByTime(200); + + // Stop hovering before showDelay completes + await userEvent.unhover(trigger); + vi.runAllTimers(); + + // Should not have shown + expect(onDialogDidShowMock).not.toBeCalled(); + }); + }); + + describe("Controlled Mode", () => { + it("should show dialog when open prop is true", async () => { + render( + Controlled Open}> + + + ); + + await waitFor(() => expect(screen.getByText("Controlled Open")).toBeInTheDocument()); + }); + + it("should hide dialog when open prop is false", async () => { + const { rerender } = render( + Controlled}> + + + ); + + await waitFor(() => expect(screen.getByText("Controlled")).toBeInTheDocument()); + + rerender( + Controlled}> + + + ); + + expect(screen.queryByText("Controlled")).not.toBeInTheDocument(); + }); + }); + + describe("Positioning", () => { + it("should render dialog with top position", async () => { + renderVisibleDialogOnMount({ + position: "top", + content:
Top Dialog
+ }); + + const dialogContent = await screen.findByTestId("dialog-content"); + expect(dialogContent).toBeInTheDocument(); + }); + + it("should render dialog with bottom position", async () => { + renderVisibleDialogOnMount({ position: "bottom", content:
Bottom Dialog
}); + + await waitFor(() => expect(screen.getByText("Bottom Dialog")).toBeInTheDocument()); + }); + + it("should render dialog with left position", async () => { + renderVisibleDialogOnMount({ position: "left", content:
Left Dialog
}); + + await waitFor(() => expect(screen.getByText("Left Dialog")).toBeInTheDocument()); + }); + + it("should render dialog with right position", async () => { + renderVisibleDialogOnMount({ position: "right", content:
Right Dialog
}); + + await waitFor(() => expect(screen.getByText("Right Dialog")).toBeInTheDocument()); + }); + }); + + describe("Disable Prop", () => { + it("should not show dialog when disable is true", async () => { + const { container } = render( + Disabled Dialog}> + + + ); + + const button = within(container).getByText("Click"); + await userEvent.click(button); + vi.runAllTimers(); + + expect(screen.queryByText("Disabled Dialog")).not.toBeInTheDocument(); + }); + }); + + describe("Animation", () => { + it("should apply expand animation by default", async () => { + renderVisibleDialogOnMount({ content:
Animated
}); + vi.runAllTimers(); + + await waitFor(() => expect(screen.getByText("Animated")).toBeInTheDocument()); + }); + + it("should apply opacity-and-slide animation when specified", async () => { + renderVisibleDialogOnMount({ animationType: "opacity-and-slide", content:
Slide Animated
}); + vi.runAllTimers(); + + await waitFor(() => expect(screen.getByText("Slide Animated")).toBeInTheDocument()); + }); + + it("should prevent animation on mount when preventAnimationOnMount is true", async () => { + renderVisibleDialogOnMount({ preventAnimationOnMount: true, content:
No Animation
}); + vi.runAllTimers(); + + await waitFor(() => expect(screen.getByText("No Animation")).toBeInTheDocument()); + }); + }); + + describe("Event Handler Chaining", () => { + it("should chain onClick handlers", async () => { + const dialogOnClickMock = vi.fn(); + const childOnClickMock = vi.fn(); + + const { container } = render( + Content}> + + + ); + + const button = within(container).getByText("Click Both"); + await userEvent.click(button); + + expect(dialogOnClickMock).toBeCalled(); + expect(childOnClickMock).toBeCalled(); + }); + + it("should chain onMouseEnter handlers", async () => { + const dialogOnMouseEnterMock = vi.fn(); + const childOnMouseEnterMock = vi.fn(); + + const { container } = render( + Content}> +
Hover
+
+ ); + + const trigger = within(container).getByText("Hover"); + await userEvent.hover(trigger); + + expect(dialogOnMouseEnterMock).toBeCalled(); + expect(childOnMouseEnterMock).toBeCalled(); + }); + }); + + describe("Instant Show and Hide", () => { + it("should show and hide instantly when instantShowAndHide is true", async () => { + const onDialogDidShowMock = vi.fn(); + const onDialogDidHideMock = vi.fn(); + + const { container } = render( + Instant} + > + + + ); + + const button = within(container).getByText("Toggle"); + + // Show + await userEvent.click(button); + expect(onDialogDidShowMock).toBeCalled(); + + // Hide + await userEvent.click(button); + expect(onDialogDidHideMock).toBeCalled(); + }); }); }); diff --git a/packages/core/src/components/next/Dialog/__tests__/useDisableScroll.test.ts b/packages/components/dialog/src/Dialog/__tests__/useDisableScroll.test.ts similarity index 100% rename from packages/core/src/components/next/Dialog/__tests__/useDisableScroll.test.ts rename to packages/components/dialog/src/Dialog/__tests__/useDisableScroll.test.ts diff --git a/packages/core/src/components/next/Dialog/components/DialogContent/DialogContent.module.scss b/packages/components/dialog/src/Dialog/components/DialogContent/DialogContent.module.scss similarity index 98% rename from packages/core/src/components/next/Dialog/components/DialogContent/DialogContent.module.scss rename to packages/components/dialog/src/Dialog/components/DialogContent/DialogContent.module.scss index 6cdaebfa23..9638037113 100644 --- a/packages/core/src/components/next/Dialog/components/DialogContent/DialogContent.module.scss +++ b/packages/components/dialog/src/Dialog/components/DialogContent/DialogContent.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; .contentWrapper { outline: 0; diff --git a/packages/core/src/components/next/Dialog/components/DialogContent/DialogContent.tsx b/packages/components/dialog/src/Dialog/components/DialogContent/DialogContent.tsx similarity index 100% rename from packages/core/src/components/next/Dialog/components/DialogContent/DialogContent.tsx rename to packages/components/dialog/src/Dialog/components/DialogContent/DialogContent.tsx diff --git a/packages/core/src/components/next/Dialog/components/Refable/Refable.tsx b/packages/components/dialog/src/Dialog/components/Refable/Refable.tsx similarity index 100% rename from packages/core/src/components/next/Dialog/components/Refable/Refable.tsx rename to packages/components/dialog/src/Dialog/components/Refable/Refable.tsx diff --git a/packages/components/dialog/src/Refable/__tests__/Refable.test.tsx b/packages/components/dialog/src/Dialog/components/Refable/__tests__/Refable.test.tsx similarity index 100% rename from packages/components/dialog/src/Refable/__tests__/Refable.test.tsx rename to packages/components/dialog/src/Dialog/components/Refable/__tests__/Refable.test.tsx diff --git a/packages/core/src/components/next/Dialog/hooks/__tests__/useDisableScroll.test.ts b/packages/components/dialog/src/Dialog/hooks/__tests__/useDisableScroll.test.ts similarity index 100% rename from packages/core/src/components/next/Dialog/hooks/__tests__/useDisableScroll.test.ts rename to packages/components/dialog/src/Dialog/hooks/__tests__/useDisableScroll.test.ts diff --git a/packages/core/src/components/next/Dialog/hooks/useDisableScroll.ts b/packages/components/dialog/src/Dialog/hooks/useDisableScroll.ts similarity index 100% rename from packages/core/src/components/next/Dialog/hooks/useDisableScroll.ts rename to packages/components/dialog/src/Dialog/hooks/useDisableScroll.ts diff --git a/packages/components/dialog/src/Dialog/index.ts b/packages/components/dialog/src/Dialog/index.ts index 1f273dc78c..c725fbef1d 100644 --- a/packages/components/dialog/src/Dialog/index.ts +++ b/packages/components/dialog/src/Dialog/index.ts @@ -1,10 +1,3 @@ -export { default as Dialog, type DialogProps } from "./Dialog"; -export { - HideShowEvent as DialogTriggerEventEnum, - AnimationType as DialogAnimationTypeEnum, - DialogPosition as DialogPositionEnum, - DialogSize as DialogSizeEnum, - DialogPlacement as DialogPlacementEnum -} from "./DialogConstants"; -export { default as usePopover } from "./usePopover"; +export { default as Dialog } from "./Dialog"; +export { DialogPlacement as DialogPlacementEnum } from "./DialogConstants"; export * from "./Dialog.types"; diff --git a/packages/components/dialog/src/Dialog/modifiers/observeContentResizeModifier.ts b/packages/components/dialog/src/Dialog/modifiers/observeContentResizeModifier.ts deleted file mode 100644 index 8684ededdd..0000000000 --- a/packages/components/dialog/src/Dialog/modifiers/observeContentResizeModifier.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { type Modifier } from "react-popper"; - -export const createObserveContentResizeModifier = (isEnabled = false): Modifier<"observeContentResize"> => ({ - name: "observeContentResize", - enabled: isEnabled, - phase: "beforeWrite", - fn() {}, - effect({ state, instance }) { - const observer = new ResizeObserver(() => { - instance.update(); - }); - observer.observe(state.elements.popper); - - return () => { - observer.disconnect(); - }; - } -}); diff --git a/packages/components/dialog/src/Dialog/modifiers/observeReferenceResizeModifier.ts b/packages/components/dialog/src/Dialog/modifiers/observeReferenceResizeModifier.ts deleted file mode 100644 index 06f271047e..0000000000 --- a/packages/components/dialog/src/Dialog/modifiers/observeReferenceResizeModifier.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { type Modifier } from "react-popper"; - -export const createObserveReferenceResizeModifier = (isEnabled = false): Modifier<"observeReferenceResize"> => ({ - name: "observeReferenceResize", - enabled: isEnabled, - phase: "beforeWrite", - fn() {}, - effect({ state, instance }) { - if (!state.elements.reference) return; - - const observer = new ResizeObserver(() => { - instance.update(); - }); - - const reference = state.elements.reference as Element; - observer.observe(reference); - - return () => { - observer.disconnect(); - }; - } -}); diff --git a/packages/components/dialog/src/Dialog/useForceUpdate.ts b/packages/components/dialog/src/Dialog/useForceUpdate.ts deleted file mode 100644 index 0719c4ed94..0000000000 --- a/packages/components/dialog/src/Dialog/useForceUpdate.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { useReducer } from "react"; - -// the second argument of useReducer is the dispatch function -// after calling dispatch we will set a new object in the state -// if the state will change by reference it will force rerender of the component - -const reducerAlwaysReturningNewObj = () => ({}); - -export default function useForceUpdate() { - return useReducer(reducerAlwaysReturningNewObj, {})[1]; -} diff --git a/packages/components/dialog/src/Dialog/usePopover.ts b/packages/components/dialog/src/Dialog/usePopover.ts deleted file mode 100644 index 8cb6cb92cd..0000000000 --- a/packages/components/dialog/src/Dialog/usePopover.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { useMemo } from "react"; -import { usePopper } from "react-popper"; -import { DialogPlacement as Placement } from "./DialogConstants"; -import { useIsomorphicLayoutEffect } from "@vibe/shared"; -import useForceUpdate from "./useForceUpdate"; -import type { Options, State } from "@popperjs/core"; -import { createObserveContentResizeModifier } from "./modifiers/observeContentResizeModifier"; -import { createObserveReferenceResizeModifier } from "./modifiers/observeReferenceResizeModifier"; -const { RIGHT_START, RIGHT_END, LEFT_START, LEFT_END } = Placement; - -const DEFAULT_FALLBACK_PLACEMENTS = [RIGHT_END, LEFT_START, LEFT_END]; - -export default function usePopover( - referenceElement: HTMLElement, - popperElement: HTMLElement, - { - isOpen, - placement = RIGHT_START, - observeContentResize, - observeReferenceResize, - offset, - fallbackPlacements = DEFAULT_FALLBACK_PLACEMENTS - }: { - isOpen?: boolean; - placement?: Placement; - observeContentResize?: boolean; - observeReferenceResize?: boolean; - offset?: [number, number]; - fallbackPlacements?: Placement[]; - } -) { - const forceUpdate = useForceUpdate(); - - // we have to use forceUpdate because - // usePopper need to run again after any refs changes, even after the first render. - useIsomorphicLayoutEffect(() => { - forceUpdate(); - }, [referenceElement, popperElement, forceUpdate]); - - const popperOptions: Pick = useMemo(() => { - return { - placement, - modifiers: [ - { - name: "flip", - options: { - fallbackPlacements - } - }, - { - name: "displayNone", - enabled: true, - phase: "write", - fn: ({ state }: { state: State }) => { - state.styles.popper.visibility = isOpen ? "visible" : "hidden"; - return state; - } - }, - createObserveContentResizeModifier(observeContentResize), - createObserveReferenceResizeModifier(observeReferenceResize), - ...(offset !== undefined - ? [ - { - name: "offset" as const, - options: { offset } - } - ] - : []) - ] - }; - }, [placement, observeContentResize, offset, isOpen]); - - const { styles, attributes } = usePopper(referenceElement, popperElement, popperOptions); - - return { styles, attributes }; -} diff --git a/packages/components/dialog/src/DialogContentContainer/DialogContentContainer.module.scss b/packages/components/dialog/src/DialogContentContainer/DialogContentContainer.module.scss index 81c391bc51..9a926dc690 100644 --- a/packages/components/dialog/src/DialogContentContainer/DialogContentContainer.module.scss +++ b/packages/components/dialog/src/DialogContentContainer/DialogContentContainer.module.scss @@ -3,15 +3,15 @@ } .sizeSmall { - padding: var(--spacing-small); + padding: var(--space-8); } .sizeMedium { - padding: var(--spacing-medium); + padding: var(--space-16); } .sizeLarge { - padding: var(--spacing-large); + padding: var(--space-24); } .typePopover { diff --git a/packages/components/dialog/src/DialogContentContainer/DialogContentContainer.tsx b/packages/components/dialog/src/DialogContentContainer/DialogContentContainer.tsx index 05fc2fa270..66c680110b 100644 --- a/packages/components/dialog/src/DialogContentContainer/DialogContentContainer.tsx +++ b/packages/components/dialog/src/DialogContentContainer/DialogContentContainer.tsx @@ -1,15 +1,7 @@ import { camelCase } from "es-toolkit"; import cx from "classnames"; import React, { useRef, forwardRef } from "react"; -import { - useMergeRef, - type VibeComponentProps, - withStaticProps, - ComponentDefaultTestId, - getTestId, - getStyle -} from "@vibe/shared"; -import { DialogSize as DialogSizeEnum, DialogType as DialogTypeEnum } from "../Dialog/DialogConstants"; +import { useMergeRef, type VibeComponentProps, ComponentDefaultTestId, getTestId, getStyle } from "@vibe/shared"; import { type DialogSize, type DialogType } from "../Dialog"; import styles from "./DialogContentContainer.module.scss"; @@ -21,11 +13,11 @@ export interface DialogContentContainerProps extends VibeComponentProps { /** * The ID of the element that labels this dialog. */ - ariaLabelledby?: string; + "aria-labelledby"?: string; /** * The ID of the element that describes this dialog. */ - ariaDescribedby?: string; + "aria-describedby"?: string; /** * The type of dialog. */ @@ -50,8 +42,8 @@ const DialogContentContainer = forwardRef( { id, className = "", - ariaLabelledby = "", - ariaDescribedby = "", + "aria-labelledby": ariaLabelledby = "", + "aria-describedby": ariaDescribedby = "", type = "popover", size = "small", children, @@ -90,12 +82,4 @@ const DialogContentContainer = forwardRef( } ); -interface DialogContentContainerStaticProps { - types: typeof DialogTypeEnum; - sizes: typeof DialogSizeEnum; -} - -export default withStaticProps(DialogContentContainer, { - types: DialogTypeEnum, - sizes: DialogSizeEnum -}); +export default DialogContentContainer; diff --git a/packages/components/dialog/src/Refable/Refable.tsx b/packages/components/dialog/src/Refable/Refable.tsx deleted file mode 100644 index 3023aedb41..0000000000 --- a/packages/components/dialog/src/Refable/Refable.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React, { type HTMLProps, type MutableRefObject, type ReactElement } from "react"; -import { chainFunctions, chainRefFunctions, type VibeComponentProps } from "@vibe/shared"; - -export const Refable = React.forwardRef< - ReactElement, - React.PropsWithChildren & VibeComponentProps> & { - children: ReactElement | ReactElement[] | string; - /** - * The wrapper element type to use for React components. Defaults to "span". - */ - wrapperElement?: "span" | "div"; - } - // @ts-expect-error React forwardRef type def doesn't seem to support multiple refs ->(({ children, wrapperElement = "span", ...rest }, ref) => { - return React.Children.map(children, child => { - if (!React.isValidElement(child)) return null; - - if (typeof child.type !== "string") { - const WrapperElement = wrapperElement; - return ( - } {...rest}> - {React.cloneElement(child, { ...child.props })} - - ); - } - - return React.cloneElement(child, { - ...rest, - ...child.props, - onClick: getChainedFunction("onClick", child.props, rest), - onBlur: getChainedFunction("onBlur", child.props, rest), - onMouseEnter: getChainedFunction("onMouseEnter", child.props, rest), - onMouseLeave: getChainedFunction("onMouseLeave", child.props, rest), - onMouseDown: getChainedFunction("onMouseDown", child.props, rest), - onFocus: getChainedFunction("onFocus", child.props, rest), - // @ts-expect-error - ref: chainRefFunctions([child.ref, ref as MutableRefObject]) - }); - }); -}); - -function getChainedFunction( - name: keyof React.HTMLProps, - childProps: React.PropsWithChildren>, - wrapperProps: React.HTMLProps -) { - return chainFunctions([childProps[name], wrapperProps[name]], true); -} diff --git a/packages/components/dialog/src/Refable/index.ts b/packages/components/dialog/src/Refable/index.ts deleted file mode 100644 index dc9552f090..0000000000 --- a/packages/components/dialog/src/Refable/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Refable } from "./Refable"; diff --git a/packages/components/icon-button/package.json b/packages/components/icon-button/package.json index 29d2166abb..fb3e7edd0e 100644 --- a/packages/components/icon-button/package.json +++ b/packages/components/icon-button/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/icon-button", - "version": "3.0.5", + "version": "4.0.0-rc.0", "description": "Vibe sub-package for icon button component", "repository": { "type": "git", @@ -38,21 +38,21 @@ "lint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@vibe/button": "3.0.16", - "@vibe/icon": "3.0.11", - "@vibe/icons": "1.16.0", - "@vibe/shared": "3.0.8", - "@vibe/tooltip": "3.0.5", + "@vibe/button": "^4.0.0-rc.0", + "@vibe/icon": "^4.0.0-rc.0", + "@vibe/icons": "^4.0.0-rc.0", + "@vibe/shared": "^4.0.0-rc.0", + "@vibe/tooltip": "^4.0.0-rc.0", "classnames": "^2.5.1", "es-toolkit": "^1.39.10" }, "devDependencies": { "@testing-library/react": "^12.1.2", - "@vibe/config": "3.0.5", + "@vibe/config": "^4.0.0-rc.0", "react": "^16.13.0", "react-dom": "^16.13.0", "react-test-renderer": "16", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/components/icon-button/src/IconButton/IconButton.tsx b/packages/components/icon-button/src/IconButton/IconButton.tsx index 79a7c7d4e6..9df267698d 100644 --- a/packages/components/icon-button/src/IconButton/IconButton.tsx +++ b/packages/components/icon-button/src/IconButton/IconButton.tsx @@ -4,7 +4,6 @@ import { noop as NOOP } from "es-toolkit"; import { useMergeRef, type VibeComponentProps, - withStaticProps, getTestId, ComponentDefaultTestId, ComponentVibeId, @@ -37,23 +36,23 @@ export interface IconButtonProps extends VibeComponentProps { /** * The ID of the element that labels this button. */ - ariaLabeledBy?: string; + "aria-labelledby"?: string; /** * The ARIA label for accessibility. */ - ariaLabel?: string; + "aria-label"?: string; /** * If true, indicates that the button controls a popup. */ - ariaHasPopup?: React.HTMLProps["aria-haspopup"]; + "aria-haspopup"?: React.HTMLProps["aria-haspopup"]; /** * If true, indicates that the associated popup is open. */ - ariaExpanded?: boolean; + "aria-expanded"?: boolean; /** * The ID of the region controlled by the button. */ - ariaControls?: string; + "aria-controls"?: string; /** * ID of the element describing the button. */ @@ -127,11 +126,11 @@ const IconButton = forwardRef( size = "medium", tooltipProps = {} as TooltipProps, tooltipContent, - ariaLabeledBy, - ariaLabel, - ariaHasPopup, - ariaExpanded, - ariaControls, + "aria-labelledby": ariaLabelledBy, + "aria-label": ariaLabel, + "aria-haspopup": ariaHasPopup, + "aria-expanded": ariaExpanded, + "aria-controls": ariaControls, "aria-describedby": ariaDescribedBy, "aria-hidden": ariaHidden, "aria-pressed": ariaPressed, @@ -214,11 +213,11 @@ const IconButton = forwardRef( disabled={disabled} color={color} kind={kind} - ariaLabeledBy={ariaLabeledBy} - ariaLabel={buttonAriaLabel} - ariaHasPopup={ariaHasPopup} - ariaExpanded={ariaExpanded} - ariaControls={ariaControls} + aria-labelledby={ariaLabelledBy} + aria-label={buttonAriaLabel} + aria-haspopup={ariaHasPopup} + aria-expanded={ariaExpanded} + aria-controls={ariaControls} aria-describedby={ariaDescribedBy} aria-hidden={ariaHidden} aria-pressed={ariaPressed} @@ -235,7 +234,7 @@ const IconButton = forwardRef( loading={loading} loaderClassName={cx(styles.loader, getStyle(styles, size))} > - + @@ -243,14 +242,4 @@ const IconButton = forwardRef( } ); -interface IconButtonStaticProps { - sizes: typeof Button.sizes; - kinds: typeof Button.kinds; - colors: typeof Button.colors; -} - -export default withStaticProps(IconButton, { - sizes: Button.sizes, - kinds: Button.kinds, - colors: Button.colors -}); +export default IconButton; diff --git a/packages/components/icon-button/src/IconButton/__tests__/IconButton.snapshot.test.tsx b/packages/components/icon-button/src/IconButton/__tests__/IconButton.snapshot.test.tsx index 9cb8c7fe5f..9c25643aae 100644 --- a/packages/components/icon-button/src/IconButton/__tests__/IconButton.snapshot.test.tsx +++ b/packages/components/icon-button/src/IconButton/__tests__/IconButton.snapshot.test.tsx @@ -43,7 +43,7 @@ describe("IconButton renders correctly", () => { }); it("with aria label", () => { - const tree = renderer.create().toJSON(); + const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); diff --git a/packages/components/icon-button/src/IconButton/__tests__/IconButton.test.tsx b/packages/components/icon-button/src/IconButton/__tests__/IconButton.test.tsx index 4332fad8c5..d2bafd5839 100644 --- a/packages/components/icon-button/src/IconButton/__tests__/IconButton.test.tsx +++ b/packages/components/icon-button/src/IconButton/__tests__/IconButton.test.tsx @@ -8,7 +8,7 @@ vi.useFakeTimers(); const ariaLabel = "Button Icon"; const renderComponent = (props: IconButtonProps = {}) => { - return render(); + return render(); }; describe("IconButton tests", () => { diff --git a/packages/components/icon/package.json b/packages/components/icon/package.json index 949be34081..8af8145316 100644 --- a/packages/components/icon/package.json +++ b/packages/components/icon/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/icon", - "version": "3.0.11", + "version": "4.0.0-rc.0", "description": "Vibe sub-package for icon components", "repository": { "type": "git", @@ -38,16 +38,16 @@ "lint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@vibe/shared": "3.0.8", + "@vibe/shared": "^4.0.0-rc.0", "classnames": "^2.5.1", "es-toolkit": "^1.39.10", "react-inlinesvg": "^4.1.3" }, "devDependencies": { - "@vibe/config": "3.0.5", + "@vibe/config": "^4.0.0-rc.0", "react": "^16.13.0", "react-dom": "^16.13.0", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/components/icon/src/Icon/CustomSvgIcon/CustomSvgIcon.tsx b/packages/components/icon/src/Icon/CustomSvgIcon/CustomSvgIcon.tsx index cfc78286b1..49f45d36c0 100644 --- a/packages/components/icon/src/Icon/CustomSvgIcon/CustomSvgIcon.tsx +++ b/packages/components/icon/src/Icon/CustomSvgIcon/CustomSvgIcon.tsx @@ -14,10 +14,6 @@ function modifySvgCode(svg: string, color = "currentColor") { } export interface CustomSvgIconProps extends VibeComponentProps { - /** - * Callback fired when the icon is clicked. - */ - onClick?: (event: React.MouseEvent) => void; /** * The source URL or object of the SVG icon. */ @@ -25,7 +21,7 @@ export interface CustomSvgIconProps extends VibeComponentProps { /** * The accessible label for the icon. */ - ariaLabel?: string; + "aria-label"?: string; /** * The tab index of the icon for keyboard navigation. */ @@ -37,11 +33,7 @@ export interface CustomSvgIconProps extends VibeComponentProps { /** * If true, hides the icon from screen readers. */ - ariaHidden?: boolean; - /** - * If true, makes the icon clickable. - */ - clickable?: boolean; + "aria-hidden"?: boolean; /** * If true, replaces the `fill` attribute in the SVG with `currentColor`. */ @@ -50,6 +42,10 @@ export interface CustomSvgIconProps extends VibeComponentProps { * Overrides the default color of the icon. */ customColor?: string; + /** + * The size (width and height) of the icon. + */ + size?: number | string; /** * Reference to the SVG element. */ @@ -60,17 +56,16 @@ const CustomSvgIcon: FunctionComponent = ({ className, ref, src, - onClick, - clickable, - ariaLabel, - ariaHidden, + "aria-label": ariaLabel, + "aria-hidden": ariaHidden, replaceToCurrentColor = false, customColor, + size, id, "data-testid": dataTestId }) => { const screenReaderAccessProps = useIconScreenReaderAccessProps({ - isClickable: clickable, + isClickable: false, label: ariaLabel, isDecorationOnly: ariaHidden }); @@ -101,11 +96,12 @@ const CustomSvgIcon: FunctionComponent = ({ { const overrideExternalTabIndex = externalTabIndex && +externalTabIndex; const { screenReaderAccessProps, onClickCallback, computedClassName, iconRef } = useIconProps({ - iconLabel, + label, className, isDecorationOnly: ariaHidden, ignoreFocusStyle, @@ -108,27 +100,27 @@ const Icon = forwardRef( const isFunctionType = typeof icon === "function"; const overrideDataTestId = dataTestId || getTestId(ComponentDefaultTestId.ICON, id); - if (iconType === "svg" || isFunctionType || typeof icon === "object") { + if (type === "svg" || isFunctionType || typeof icon === "object") { return renderIcon(icon as React.FC, { id, ...screenReaderAccessProps, ref: isFunctionType ? undefined : mergedRef, - size: iconSize.toString(), + size: size.toString(), className: computedClassName, style, "data-testid": overrideDataTestId }); } - if (iconType === "src") { + if (type === "src") { return ( ); @@ -147,10 +139,4 @@ const Icon = forwardRef( } ); -interface IconStaticProps { - type: typeof IconTypeEnum; -} - -export default withStaticProps(Icon, { - type: IconTypeEnum -}); +export default Icon; diff --git a/packages/components/icon/src/Icon/constants.ts b/packages/components/icon/src/Icon/constants.ts deleted file mode 100644 index c2339df67c..0000000000 --- a/packages/components/icon/src/Icon/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @deprecated - */ -export enum IconTypeEnum { - SVG = "svg", - ICON_FONT = "font", - SRC = "src" -} diff --git a/packages/components/icon/src/Icon/hooks/useIconProps.tsx b/packages/components/icon/src/Icon/hooks/useIconProps.tsx index 04706e2aa8..b40b8406ca 100644 --- a/packages/components/icon/src/Icon/hooks/useIconProps.tsx +++ b/packages/components/icon/src/Icon/hooks/useIconProps.tsx @@ -13,7 +13,7 @@ export default function useIconProps({ clickable, ignoreFocusStyle, isDecorationOnly, - iconLabel, + label, externalTabIndex }: { onClick?: (event: UIEvent) => void; @@ -21,7 +21,7 @@ export default function useIconProps({ clickable?: boolean; ignoreFocusStyle?: boolean; isDecorationOnly?: boolean; - iconLabel?: string; + label?: string; externalTabIndex?: number | undefined; }) { const iconRef = useRef(null); @@ -73,7 +73,7 @@ export default function useIconProps({ const screenReaderAccessProps = useIconScreenReaderAccessProps({ isClickable: clickable, - label: iconLabel, + label, isDecorationOnly }); diff --git a/packages/components/layer/package.json b/packages/components/layer/package.json index c3bbe54b0c..c9eb5f1397 100644 --- a/packages/components/layer/package.json +++ b/packages/components/layer/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/layer", - "version": "3.0.10", + "version": "4.0.0-rc.0", "description": "Vibe sub-package for layering components", "repository": { "type": "git", @@ -38,20 +38,20 @@ "lint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@vibe/icon": "3.0.11", - "@vibe/loader": "3.0.11", - "@vibe/shared": "3.0.8", + "@vibe/icon": "^4.0.0-rc.0", + "@vibe/loader": "^4.0.0-rc.0", + "@vibe/shared": "^4.0.0-rc.0", "classnames": "^2.5.1", "es-toolkit": "^1.39.10" }, "devDependencies": { "@testing-library/react": "^12.1.2", - "@vibe/config": "3.0.5", - "@vibe/icons": "1.16.0", + "@vibe/config": "^4.0.0-rc.0", + "@vibe/icons": "^4.0.0-rc.0", "react": "^16.13.0", "react-dom": "^16.13.0", "react-test-renderer": "16", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/components/layout/package.json b/packages/components/layout/package.json index f6277a28fc..81d28ae01e 100644 --- a/packages/components/layout/package.json +++ b/packages/components/layout/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/layout", - "version": "3.0.3", + "version": "4.0.0-rc.0", "description": "Vibe sub-package for layout components", "repository": { "type": "git", @@ -38,18 +38,19 @@ "lint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@vibe/clickable": "3.0.3", - "@vibe/shared": "3.0.8", + "@vibe/clickable": "^4.0.0-rc.0", + "@vibe/shared": "^4.0.0-rc.0", + "@vibe/style": "^4.0.0-rc.0", "classnames": "^2.5.1", "es-toolkit": "^1.39.10" }, "devDependencies": { "@testing-library/react": "^12.1.2", - "@vibe/config": "3.0.5", + "@vibe/config": "^4.0.0-rc.0", "react": "^16.13.0", "react-dom": "^16.13.0", "react-test-renderer": "16", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/components/layout/src/Box/Box.module.scss b/packages/components/layout/src/Box/Box.module.scss index 5cfd84f5a9..cfdb6f6ec9 100644 --- a/packages/components/layout/src/Box/Box.module.scss +++ b/packages/components/layout/src/Box/Box.module.scss @@ -1,5 +1,5 @@ // Monday ui styles dependencies -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; @import "./utilities"; .box { diff --git a/packages/components/layout/src/Box/Box.tsx b/packages/components/layout/src/Box/Box.tsx index 066d1bef97..3154e96c39 100644 --- a/packages/components/layout/src/Box/Box.tsx +++ b/packages/components/layout/src/Box/Box.tsx @@ -1,28 +1,7 @@ import React, { forwardRef, useRef } from "react"; import { camelCase } from "es-toolkit"; import cx from "classnames"; -import { - BackgroundColor as BackgroundColorEnum, - BorderColor as BorderColorEnum, - BoxTextColor as BoxTextColorEnum, - Margin as MarginEnum, - MarginBottom as MarginBottomEnum, - MarginEnd as MarginEndEnum, - MarginStart as MarginStartEnum, - MarginTop as MarginTopEnum, - MarginX as MarginXEnum, - MarginY as MarginYEnum, - Padding as PaddingEnum, - PaddingBottom as PaddingBottomEnum, - PaddingEnd as PaddingEndEnum, - PaddingStart as PaddingStartEnum, - PaddingTop as PaddingTopEnum, - PaddingX as PaddingXEnum, - PaddingY as PaddingYEnum, - Rounded as RoundedEnum, - Shadow as ShadowEnum, - SizePrefixMapping -} from "./BoxConstants"; +import { SizePrefixMapping } from "./BoxConstants"; import { type BackgroundColor, type BorderColor, @@ -34,7 +13,6 @@ import { } from "./Box.types"; import { type VibeComponentProps, - withStaticProps, type ElementContent, getTestId, ComponentDefaultTestId, @@ -194,46 +172,4 @@ const Box = forwardRef( } ); -interface BoxStaticProps { - borderColors: typeof BorderColorEnum; - roundeds: typeof RoundedEnum; - shadows: typeof ShadowEnum; - margins: typeof MarginEnum; - marginXs: typeof MarginXEnum; - marginYs: typeof MarginYEnum; - marginTops: typeof MarginTopEnum; - marginEnds: typeof MarginEndEnum; - marginBottoms: typeof MarginBottomEnum; - marginStarts: typeof MarginStartEnum; - paddings: typeof PaddingEnum; - paddingXs: typeof PaddingXEnum; - paddingYs: typeof PaddingYEnum; - paddingTops: typeof PaddingTopEnum; - paddingEnds: typeof PaddingEndEnum; - paddingBottoms: typeof PaddingBottomEnum; - paddingStarts: typeof PaddingStartEnum; - backgroundColors: typeof BackgroundColorEnum; - textColors: typeof BoxTextColorEnum; -} - -export default withStaticProps(Box, { - borderColors: BorderColorEnum, - roundeds: RoundedEnum, - shadows: ShadowEnum, - margins: MarginEnum, - marginXs: MarginXEnum, - marginYs: MarginYEnum, - marginTops: MarginTopEnum, - marginEnds: MarginEndEnum, - marginBottoms: MarginBottomEnum, - marginStarts: MarginStartEnum, - paddings: PaddingEnum, - paddingXs: PaddingXEnum, - paddingYs: PaddingYEnum, - paddingTops: PaddingTopEnum, - paddingEnds: PaddingEndEnum, - paddingBottoms: PaddingBottomEnum, - paddingStarts: PaddingStartEnum, - backgroundColors: BackgroundColorEnum, - textColors: BoxTextColorEnum -}); +export default Box; diff --git a/packages/components/layout/src/Box/BoxConstants.ts b/packages/components/layout/src/Box/BoxConstants.ts index 011af9be84..b405b82fa9 100644 --- a/packages/components/layout/src/Box/BoxConstants.ts +++ b/packages/components/layout/src/Box/BoxConstants.ts @@ -1,253 +1,3 @@ -/** - * @deprecated - */ -export enum Disabled { - DISABLED = "opacityDisabled" -} - -/** - * @deprecated - */ -export enum Border { - DEFAULT = "border" -} - -/** - * @deprecated - */ -export enum BorderColor { - UI_BORDER_COLOR = "uiBorderColor", - LAYOUT_BORDER_COLOR = "layoutBorderColor" -} - -/** - * @deprecated - */ -export enum Rounded { - SMALL = "small", - MEDIUM = "medium", - BIG = "big" -} - -/** - * @deprecated - */ -export enum Shadow { - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large" -} - -/** - * @deprecated - */ -export enum Margin { - AUTO = "auto", - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum MarginX { - AUTO = "auto", - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum MarginY { - AUTO = "auto", - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum MarginTop { - AUTO = "auto", - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum MarginEnd { - AUTO = "auto", - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum MarginBottom { - AUTO = "auto", - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum MarginStart { - AUTO = "auto", - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum Padding { - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum PaddingX { - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum PaddingY { - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum PaddingTop { - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum PaddingEnd { - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum PaddingBottom { - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum PaddingStart { - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large", - XL = "xl", - XXL = "xxl", - XXXL = "xxxl" -} - -/** - * @deprecated - */ -export enum BackgroundColor { - PRIMARY_BACKGROUND_COLOR = "primaryBackgroundColor", - SECONDARY_BACKGROUND_COLOR = "secondaryBackgroundColor", - GREY_BACKGROUND_COLOR = "greyBackgroundColor", - ALL_GREY_BACKGROUND_COLOR = "allgreyBackgroundColor", - INVERTED_COLOR_BACKGROUND = "invertedColorBackground" -} - -/** - * @deprecated - */ -export enum BoxTextColor { - PRIMARY_TEXT_COLOR = "primaryTextColor", - TEXT_COLOR_ON_INVERTED = "textColorOnInverted", - SECONDARY_TEXT_COLOR = "secondaryTextColor" -} - export const SizePrefixMapping = { margin: "m", marginX: "mx", diff --git a/packages/components/layout/src/Box/_utilities.scss b/packages/components/layout/src/Box/_utilities.scss index 2f2c1f65cb..2e3e366520 100644 --- a/packages/components/layout/src/Box/_utilities.scss +++ b/packages/components/layout/src/Box/_utilities.scss @@ -1,18 +1,18 @@ @use "sass:map"; @use "sass:string"; @use "sass:list"; -@import "~monday-ui-style/dist/functions"; +@import "~@vibe/style/dist/functions"; /* stylelint-disable scss/at-if-no-null */ $utility-spacing-vars: ( 0: 0, - xs: var(--spacing-xs), - small: var(--spacing-small), - medium: var(--spacing-medium), - large: var(--spacing-large), - xl: var(--spacing-xl), - xxl: var(--spacing-xxl), - xxxl: var(--spacing-xxxl) + xs: var(--space-4), + small: var(--space-8), + medium: var(--space-16), + large: var(--space-24), + xl: var(--space-32), + xxl: var(--space-48), + xxxl: var(--space-64) ); $utility-border-colors-vars: ( @@ -84,7 +84,7 @@ $utilities-modules: ( ) ), "marginX": ( - property: margin-right margin-left, + property: margin-inline, class: mx, values: map.merge( @@ -155,7 +155,7 @@ $utilities-modules: ( values: $utility-spacing-vars ), "paddingX": ( - property: padding-right padding-left, + property: padding-inline, class: px, values: $utility-spacing-vars ), diff --git a/packages/components/layout/src/Flex/Flex.tsx b/packages/components/layout/src/Flex/Flex.tsx index 69e8b01710..c0867ebe86 100644 --- a/packages/components/layout/src/Flex/Flex.tsx +++ b/packages/components/layout/src/Flex/Flex.tsx @@ -1,14 +1,8 @@ import React, { forwardRef, useMemo, useRef } from "react"; import cx from "classnames"; import { Clickable } from "@vibe/clickable"; -import { - FlexAlign as FlexAlignEnum, - FlexDirection as FlexDirectionEnum, - FlexGap as FlexGapEnum, - FlexJustify as FlexJustifyEnum -} from "./FlexConstants"; import { type FlexDirection, type FlexJustify, type FlexAlign, type FlexGap, type FlexShorthand } from "./Flex.types"; -import { type ElementContent, withStaticProps, type VibeComponentProps, getStyle, useMergeRef } from "@vibe/shared"; +import { type ElementContent, type VibeComponentProps, getStyle, useMergeRef } from "@vibe/shared"; import styles from "./Flex.module.scss"; import { camelCase } from "es-toolkit"; @@ -52,7 +46,7 @@ export interface FlexProps extends VibeComponentProps { /** * The label of the flex container for accessibility. */ - ariaLabel?: string; + "aria-label"?: string; /** * The tab order of the element. */ @@ -68,7 +62,7 @@ export interface FlexProps extends VibeComponentProps { /** * ID of the element describing the flex container. */ - ariaLabelledby?: string; + "aria-labelledby"?: string; } const Flex = forwardRef( @@ -87,8 +81,8 @@ const Flex = forwardRef( onClick, onMouseDown, style, - ariaLabelledby, - ariaLabel, + "aria-labelledby": ariaLabelledby, + "aria-label": ariaLabel, tabIndex, "data-testid": dataTestId }: FlexProps, @@ -104,7 +98,13 @@ const Flex = forwardRef( if (typeof gap === "number") { return { gap: `${gap}px` }; } - return { gap: `var(--spacing-${gap})` }; + const gapTokenMap: Record = { + xs: "var(--space-4)", + small: "var(--space-8)", + medium: "var(--space-16)", + large: "var(--space-24)" + }; + return { gap: gapTokenMap[gap] }; }, [gap]); const flexStyle = useMemo(() => { @@ -127,7 +127,7 @@ const Flex = forwardRef( const overrideStyle = useMemo(() => ({ ...style, ...gapStyle, ...flexStyle }), [style, gapStyle, flexStyle]); const onClickProps = useMemo(() => { - if (onClick) return { elementType, ariaLabelledby }; + if (onClick) return { elementType, "aria-labelledby": ariaLabelledby }; return { "aria-labelledby": ariaLabelledby }; }, [onClick, elementType, ariaLabelledby]); const Element = onClick ? Clickable : elementType; @@ -160,16 +160,4 @@ const Flex = forwardRef( } ); -interface FlexStaticProps { - justify: typeof FlexJustifyEnum; - align: typeof FlexAlignEnum; - gaps: typeof FlexGapEnum; - directions: typeof FlexDirectionEnum; -} - -export default withStaticProps(Flex, { - justify: FlexJustifyEnum, - align: FlexAlignEnum, - gaps: FlexGapEnum, - directions: FlexDirectionEnum -}); +export default Flex; diff --git a/packages/components/layout/src/Flex/Flex.types.ts b/packages/components/layout/src/Flex/Flex.types.ts index 680c51aeec..67bf5b28b3 100644 --- a/packages/components/layout/src/Flex/Flex.types.ts +++ b/packages/components/layout/src/Flex/Flex.types.ts @@ -2,7 +2,7 @@ import type { CSSProperties } from "react"; export type FlexAlign = "start" | "center" | "end" | "stretch" | "baseline" | "initial"; -export type FlexJustify = "start" | "center" | "end" | "stretch" | "space-around" | "space-between" | "initial"; +export type FlexJustify = "start" | "center" | "end" | "space-around" | "space-between" | "initial"; export type FlexGap = "xs" | "small" | "medium" | "large"; diff --git a/packages/components/layout/src/Flex/FlexConstants.ts b/packages/components/layout/src/Flex/FlexConstants.ts deleted file mode 100644 index e040c170af..0000000000 --- a/packages/components/layout/src/Flex/FlexConstants.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @deprecated - */ -export enum FlexAlign { - START = "start", - CENTER = "center", - END = "end", - STRETCH = "stretch", - BASELINE = "baseline", - INITIAL = "initial" -} - -/** - * @deprecated - */ -export enum FlexJustify { - START = "start", - CENTER = "center", - END = "end", - STRETCH = "stretch", - SPACE_AROUND = "space-around", - SPACE_BETWEEN = "space-between", - INITIAL = "initial" -} - -/** - * @deprecated - */ -export enum FlexGap { - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large" -} - -/** - * @deprecated - */ -export enum FlexDirection { - ROW = "row", - COLUMN = "column" -} diff --git a/packages/components/layout/src/Flex/__tests__/__snapshots__/Flex.snapshot.test.tsx.snap b/packages/components/layout/src/Flex/__tests__/__snapshots__/Flex.snapshot.test.tsx.snap index 146f5d2dba..4471280ef5 100644 --- a/packages/components/layout/src/Flex/__tests__/__snapshots__/Flex.snapshot.test.tsx.snap +++ b/packages/components/layout/src/Flex/__tests__/__snapshots__/Flex.snapshot.test.tsx.snap @@ -121,7 +121,7 @@ exports[`Flex renders correctly > Horizontal display > with gap 1`] = ` className="container directionRow justifyStart alignCenter" style={ { - "gap": "var(--spacing-large)", + "gap": "var(--space-24)", } } > diff --git a/packages/components/loader/package.json b/packages/components/loader/package.json index f22dcaf866..c9851fe578 100644 --- a/packages/components/loader/package.json +++ b/packages/components/loader/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/loader", - "version": "3.0.11", + "version": "4.0.0-rc.0", "description": "Vibe sub-package for loader components", "repository": { "type": "git", @@ -38,16 +38,16 @@ "lint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@vibe/shared": "3.0.8", + "@vibe/shared": "^4.0.0-rc.0", "classnames": "^2.5.1" }, "devDependencies": { "@testing-library/react": "^12.1.2", - "@vibe/config": "3.0.5", + "@vibe/config": "^4.0.0-rc.0", "react": "^16.13.0", "react-dom": "^16.13.0", "react-test-renderer": "16", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/components/loader/src/Loader/Loader.tsx b/packages/components/loader/src/Loader/Loader.tsx index 14df44601d..55d37586bc 100644 --- a/packages/components/loader/src/Loader/Loader.tsx +++ b/packages/components/loader/src/Loader/Loader.tsx @@ -1,8 +1,7 @@ import React, { type ForwardedRef, forwardRef, useMemo } from "react"; import cx from "classnames"; -import { LoaderColors as LoaderColorsEnum, LoaderSizes as LoaderSizesEnum } from "./LoaderConstants"; import { type LoaderColors, type LoaderSize, type LoaderSizes } from "./Loader.types"; -import { getTestId, type VibeComponentProps, withStaticProps, ComponentDefaultTestId } from "@vibe/shared"; +import { getTestId, type VibeComponentProps, ComponentDefaultTestId } from "@vibe/shared"; import styles from "./Loader.module.scss"; const mapSizesToLoaderSize: Record = { @@ -84,12 +83,4 @@ const Loader = forwardRef( } ); -interface LoaderStaticProps { - sizes: typeof LoaderSizesEnum; - colors: typeof LoaderColorsEnum; -} - -export default withStaticProps(Loader, { - sizes: LoaderSizesEnum, - colors: LoaderColorsEnum -}); +export default Loader; diff --git a/packages/components/loader/src/Loader/LoaderConstants.ts b/packages/components/loader/src/Loader/LoaderConstants.ts deleted file mode 100644 index 8b31dfcb86..0000000000 --- a/packages/components/loader/src/Loader/LoaderConstants.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @deprecated - */ -export enum LoaderColors { - PRIMARY = "primary", - SECONDARY = "secondary", - ON_PRIMARY = "onPrimary", - DARK = "dark" -} - -/** - * @deprecated - */ -export enum LoaderSizes { - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large" -} diff --git a/packages/components/loader/src/Loader/__tests__/Loader.test.jsx b/packages/components/loader/src/Loader/__tests__/Loader.test.tsx similarity index 100% rename from packages/components/loader/src/Loader/__tests__/Loader.test.jsx rename to packages/components/loader/src/Loader/__tests__/Loader.test.tsx diff --git a/packages/components/tooltip/package.json b/packages/components/tooltip/package.json index c77aa4b943..2b5b8386d1 100644 --- a/packages/components/tooltip/package.json +++ b/packages/components/tooltip/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/tooltip", - "version": "3.0.5", + "version": "4.0.0-rc.0", "description": "Vibe sub-package for tooltip components", "repository": { "type": "git", @@ -38,21 +38,22 @@ "lint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@vibe/dialog": "3.0.12", - "@vibe/icon": "3.0.11", - "@vibe/layout": "3.0.3", - "@vibe/shared": "3.0.8", + "@vibe/dialog": "^4.0.0-rc.0", + "@vibe/icon": "^4.0.0-rc.0", + "@vibe/layout": "^4.0.0-rc.0", + "@vibe/shared": "^4.0.0-rc.0", + "@vibe/style": "^4.0.0-rc.0", "classnames": "^2.5.1", "es-toolkit": "^1.39.10" }, "devDependencies": { "@testing-library/react": "^12.1.2", - "@vibe/config": "3.0.5", - "@vibe/icons": "1.16.0", + "@vibe/config": "^4.0.0-rc.0", + "@vibe/icons": "^4.0.0-rc.0", "react": "^16.13.0", "react-dom": "^16.13.0", "react-test-renderer": "16", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/components/tooltip/src/Tooltip/Tooltip.module.scss b/packages/components/tooltip/src/Tooltip/Tooltip.module.scss index 0be926d050..e933178fc7 100644 --- a/packages/components/tooltip/src/Tooltip/Tooltip.module.scss +++ b/packages/components/tooltip/src/Tooltip/Tooltip.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; .tooltip { position: relative; @@ -16,8 +16,8 @@ .image { display: block; position: relative; - border-top-right-radius: 2px; - border-top-left-radius: 2px; + border-start-end-radius: 2px; + border-start-start-radius: 2px; padding: 2px 2px 0 2px; object-fit: cover; width: 100%; @@ -33,13 +33,13 @@ .content { word-break: break-word; display: inline-block; - padding: var(--tooltip-padding, #{var(--spacing-small) var(--spacing-medium)}); + padding: var(--tooltip-padding, #{var(--space-8) var(--space-16)}); } } .tooltip.paddingSizeMd { border-radius: var(--border-radius-medium); - padding: var(--spacing-medium); + padding: var(--space-16); @include vibe-text(text2, normal); } diff --git a/packages/components/tooltip/src/Tooltip/Tooltip.tsx b/packages/components/tooltip/src/Tooltip/Tooltip.tsx index 922277e27f..0aa20035bd 100644 --- a/packages/components/tooltip/src/Tooltip/Tooltip.tsx +++ b/packages/components/tooltip/src/Tooltip/Tooltip.tsx @@ -3,21 +3,8 @@ import { isFunction } from "es-toolkit"; import { camelCase } from "es-toolkit"; import cx from "classnames"; import React, { type CSSProperties, isValidElement, PureComponent, type ReactElement } from "react"; -import { type Modifier } from "react-popper"; -import { Dialog, type DialogAnimationType, type DialogTriggerEvent } from "@vibe/dialog"; -import { - DialogTriggerEventEnum as HideShowEventEnum, - DialogAnimationTypeEnum as AnimationTypeEnum -} from "@vibe/dialog"; -import { - type VibeComponentProps, - type ElementContent, - type MoveBy, - getStyle, - ComponentDefaultTestId, - getTestId -} from "@vibe/shared"; -import { TooltipTheme as TooltipThemeEnum, TooltipPositions as TooltipPositionsEnum } from "./TooltipConstants"; +import { Dialog, type DialogProps } from "@vibe/dialog"; +import { type ElementContent, getStyle, ComponentDefaultTestId, getTestId } from "@vibe/shared"; import styles from "./Tooltip.module.scss"; import { Icon, type SubIcon } from "@vibe/icon"; import { Flex } from "@vibe/layout"; @@ -47,8 +34,7 @@ interface TooltipWithChildrenProps { children: ReactElement | Array; } -// TODO TS-migration extend DialogProps, once Dialog is migrated to TS -interface TooltipBaseProps extends VibeComponentProps { +interface TooltipBaseProps extends Omit { /** * The content displayed inside the tooltip. */ @@ -61,10 +47,6 @@ interface TooltipBaseProps extends VibeComponentProps { * Class name applied to the tooltip arrow. */ arrowClassName?: string; - /** - * Offset values for positioning adjustments. - */ - moveBy?: MoveBy; /** * The theme of the tooltip. */ @@ -73,30 +55,14 @@ interface TooltipBaseProps extends VibeComponentProps { * Function to get the container where the tooltip should be rendered. */ getContainer?: () => HTMLElement; - /** - * Delay in milliseconds before hiding the tooltip. - */ - hideDelay?: number; - /** - * Delay in milliseconds before showing the tooltip. - */ - showDelay?: number; /** * If true, disables the slide animation of the tooltip. */ disableDialogSlide?: boolean; - /** - * The animation type used for showing/hiding the tooltip. - */ - animationType?: DialogAnimationType; /** * If true, renders the tooltip without a dialog. */ withoutDialog?: boolean; - /** - * The CSS selector of the container where the tooltip should be rendered. - */ - containerSelector?: string; /** * Delay in milliseconds before showing the tooltip immediately. */ @@ -105,14 +71,6 @@ interface TooltipBaseProps extends VibeComponentProps { * If false, hides the arrow of the tooltip. */ tip?: boolean; - /** - * If true, the tooltip is shown when the component mounts. - */ - shouldShowOnMount?: boolean; - /** - * If true, hides the tooltip when the reference element is hidden. - */ - hideWhenReferenceHidden?: boolean; /** * Callback fired when the tooltip is hidden. */ @@ -121,43 +79,10 @@ interface TooltipBaseProps extends VibeComponentProps { * Callback fired when the tooltip is shown. */ onTooltipShow?: () => void; - /** - * Custom Popper.js modifiers. - * https://popper.js.org/docs/v2/modifiers/ - */ - modifiers?: Array>; /** * The placement of the tooltip relative to the reference element. */ position?: TooltipPositions; - /** - * Events that trigger showing the tooltip. - */ - showTrigger?: DialogTriggerEvent | Array; - /** - * Events that trigger hiding the tooltip. - */ - hideTrigger?: DialogTriggerEvent | Array; - /** - * If true, prevents closing the tooltip when the mouse enters it. - */ - showOnDialogEnter?: boolean; - /** - * Class name applied to the reference wrapper element. - */ - referenceWrapperClassName?: string; - /** - * If true, keyboard focus/blur events behave like mouse enter/leave. - */ - addKeyboardHideShowTriggersByDefault?: boolean; - /** - * If true, controls the open state of the tooltip. - */ - open?: boolean; - /** - * The z-index applied to the tooltip. - */ - zIndex?: number; /** * The title of the tooltip. */ @@ -190,10 +115,7 @@ const globalState: { lastTooltipHideTS: number; openTooltipsCount: number } = { export default class Tooltip extends PureComponent { wasShown: boolean; - static positions = TooltipPositionsEnum; - static hideShowTriggers = HideShowEventEnum; - static themes = TooltipThemeEnum; - static animationTypes = AnimationTypeEnum; + /* eslint-disable react/default-props-match-prop-types -- props inherited from DialogProps via Omit<> */ static defaultProps = { moveBy: { main: 4, secondary: 0 }, theme: "dark", @@ -201,18 +123,18 @@ export default class Tooltip extends PureComponent { hideDelay: 100, showDelay: 300, disableDialogSlide: true, - animationType: AnimationTypeEnum.EXPAND, + animationType: "expand", withoutDialog: false, tip: true, hideWhenReferenceHidden: false, - modifiers: new Array>(), - showTrigger: Tooltip.hideShowTriggers.MOUSE_ENTER, - hideTrigger: Tooltip.hideShowTriggers.MOUSE_LEAVE, + showTrigger: "mouseenter", + hideTrigger: "mouseleave", showOnDialogEnter: true, referenceWrapperClassName: "", addKeyboardHideShowTriggersByDefault: true, open: false }; + /* eslint-enable react/default-props-match-prop-types */ constructor(props: TooltipProps) { super(props); this.renderTooltipContent = this.renderTooltipContent.bind(this); @@ -255,7 +177,7 @@ export default class Tooltip extends PureComponent {
{title && ( - {icon && } + {icon && }
{title}
)} diff --git a/packages/components/tooltip/src/Tooltip/TooltipConstants.ts b/packages/components/tooltip/src/Tooltip/TooltipConstants.ts deleted file mode 100644 index 15f7f3bedb..0000000000 --- a/packages/components/tooltip/src/Tooltip/TooltipConstants.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @deprecated - */ -export enum TooltipPositions { - TOP = "top", - RIGHT = "right", - BOTTOM = "bottom", - LEFT = "left" -} - -/** - * @deprecated - */ -export enum TooltipTheme { - Dark = "dark", - Primary = "primary" -} diff --git a/packages/components/tooltip/src/Tooltip/__tests__/__snapshots__/Tooltip.snapshot.test.tsx.snap b/packages/components/tooltip/src/Tooltip/__tests__/__snapshots__/Tooltip.snapshot.test.tsx.snap index a87e9b676a..2eeba803d1 100644 --- a/packages/components/tooltip/src/Tooltip/__tests__/__snapshots__/Tooltip.snapshot.test.tsx.snap +++ b/packages/components/tooltip/src/Tooltip/__tests__/__snapshots__/Tooltip.snapshot.test.tsx.snap @@ -9,9 +9,9 @@ exports[`Tooltip renders correctly > with data-testid 1`] = `
with data-testid 1`] = `
@@ -44,8 +43,9 @@ exports[`Tooltip renders correctly > with hideWhenReferenceHidden 1`] = `
with hideWhenReferenceHidden 1`] = `
@@ -78,9 +77,9 @@ exports[`Tooltip renders correctly > with position 1`] = `
with position 1`] = `
@@ -113,9 +111,9 @@ exports[`Tooltip renders correctly > with style 1`] = `
with style 1`] = `
@@ -149,9 +146,9 @@ exports[`Tooltip renders correctly > with theme 1`] = `
with theme 1`] = `
@@ -196,9 +192,9 @@ exports[`Tooltip renders correctly > without arrow 1`] = `
(Heading, { - types: HeadingTypeEnum, - weights: HeadingWeightEnum, - align: TypographyAlignEnum, - colors: TypographyColorEnum -}); +export default Heading; diff --git a/packages/components/typography/src/Heading/HeadingConstants.ts b/packages/components/typography/src/Heading/HeadingConstants.ts deleted file mode 100644 index 7a9b676854..0000000000 --- a/packages/components/typography/src/Heading/HeadingConstants.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @deprecated - */ -export enum HeadingType { - H1 = "h1", - H2 = "h2", - H3 = "h3" -} - -/** - * @deprecated - */ -export enum HeadingWeight { - BOLD = "bold", - MEDIUM = "medium", - NORMAL = "normal", - LIGHT = "light" -} diff --git a/packages/components/typography/src/Text/Text.module.scss b/packages/components/typography/src/Text/Text.module.scss index db1465f307..f2aca2c9dd 100644 --- a/packages/components/typography/src/Text/Text.module.scss +++ b/packages/components/typography/src/Text/Text.module.scss @@ -7,7 +7,7 @@ p:first-of-type.text { } p.text + p { - margin-block-start: var(--spacing-large); + margin-block-start: var(--space-24); margin-block-end: 0; } diff --git a/packages/components/typography/src/Text/Text.tsx b/packages/components/typography/src/Text/Text.tsx index 965bcc5b9c..077ce6c55a 100644 --- a/packages/components/typography/src/Text/Text.tsx +++ b/packages/components/typography/src/Text/Text.tsx @@ -1,13 +1,8 @@ import React, { forwardRef, type ReactNode } from "react"; import cx from "classnames"; import { camelCase } from "es-toolkit"; -import { getStyle, withStaticProps } from "@vibe/shared"; -import { TextType as TextTypeEnum, TextWeight as TextWeightEnum } from "./TextConstants"; +import { getStyle } from "@vibe/shared"; import Typography, { type TypographyProps } from "../Typography/Typography"; -import { - TypographyAlign as TypographyAlignEnum, - TypographyColor as TypographyColorEnum -} from "../Typography/TypographyConstants"; import { type TextType, type TextWeight } from "./Text.types"; import styles from "./Text.module.scss"; @@ -54,16 +49,4 @@ const Text = forwardRef( } ); -interface TextStaticProps { - types: typeof TextTypeEnum; - weights: typeof TextWeightEnum; - colors: typeof TypographyColorEnum; - align: typeof TypographyAlignEnum; -} - -export default withStaticProps(Text, { - types: TextTypeEnum, - weights: TextWeightEnum, - colors: TypographyColorEnum, - align: TypographyAlignEnum -}); +export default Text; diff --git a/packages/components/typography/src/Text/TextConstants.ts b/packages/components/typography/src/Text/TextConstants.ts deleted file mode 100644 index 39a8c9d10c..0000000000 --- a/packages/components/typography/src/Text/TextConstants.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @deprecated - */ -export enum TextType { - TEXT1 = "text1", - TEXT2 = "text2", - TEXT3 = "text3" -} - -/** - * @deprecated - */ -export enum TextWeight { - BOLD = "bold", - MEDIUM = "medium", - NORMAL = "normal" -} diff --git a/packages/components/typography/src/Typography/Typography.module.scss b/packages/components/typography/src/Typography/Typography.module.scss index f13a4bac31..510658abbd 100644 --- a/packages/components/typography/src/Typography/Typography.module.scss +++ b/packages/components/typography/src/Typography/Typography.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; .typography { // TODO: [breaking] remove anchor styles diff --git a/packages/components/typography/src/Typography/TypographyConstants.ts b/packages/components/typography/src/Typography/TypographyConstants.ts deleted file mode 100644 index fa7b861abb..0000000000 --- a/packages/components/typography/src/Typography/TypographyConstants.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @deprecated - */ -export enum TypographyColor { - PRIMARY = "primary", - SECONDARY = "secondary", - ON_PRIMARY = "onPrimary", - ON_INVERTED = "onInverted", - FIXED_LIGHT = "fixedLight", - FIXED_DARK = "fixedDark", - INHERIT = "inherit", - NEGATIVE = "negative" -} - -/** - * @deprecated - */ -export enum TypographyAlign { - START = "start", - CENTER = "center", - END = "end", - INHERIT = "inherit" -} diff --git a/packages/components/typography/src/index.ts b/packages/components/typography/src/index.ts index 21a1426fc3..f9a4b77a71 100644 --- a/packages/components/typography/src/index.ts +++ b/packages/components/typography/src/index.ts @@ -1,7 +1,4 @@ export { Text, type TextProps, type TextType, type TextWeight } from "./Text"; export { Heading, type HeadingProps, type HeadingType, type HeadingWeight } from "./Heading"; -export { TextType as TextTypeEnum, TextWeight as TextWeightEnum } from "./Text/TextConstants"; -export { HeadingType as HeadingTypeEnum, HeadingWeight as HeadingWeightEnum } from "./Heading/HeadingConstants"; - export type { TypographyColor, TypographyAlign } from "./Typography"; diff --git a/packages/components/typography/src/styles/typography.scss b/packages/components/typography/src/styles/typography.scss index 0752cd3170..8bbac7bbf9 100644 --- a/packages/components/typography/src/styles/typography.scss +++ b/packages/components/typography/src/styles/typography.scss @@ -1,6 +1,6 @@ @use "sass:string"; -@import "~monday-ui-style/dist/mixins"; -@import "~monday-ui-style/dist/functions"; +@import "~@vibe/style/dist/mixins"; +@import "~@vibe/style/dist/functions"; @mixin create-text-classes() { @include create-typography-classes($textClasses, text); diff --git a/packages/config/package.json b/packages/config/package.json index 3d1381861a..b86f758d34 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/config", - "version": "3.0.5", + "version": "4.0.0-rc.0", "private": true, "description": "Shared configurations for Vibe packages", "repository": { @@ -23,7 +23,7 @@ "./vitest.config": "./vitest.config.mjs" }, "devDependencies": { - "typescript": "^4.7.3" + "typescript": "^5.9.3" }, "dependencies": { "@rollup/plugin-babel": "^6.0.2", diff --git a/packages/config/rollup.config.mjs b/packages/config/rollup.config.mjs index e716ceb351..287b9ef12e 100644 --- a/packages/config/rollup.config.mjs +++ b/packages/config/rollup.config.mjs @@ -64,7 +64,7 @@ export default { }, input: path.join(SRC_PATH, "index.ts"), external: [ - /node_modules\/(?!monday-ui-style)(.*)/, + /node_modules\/(?!@vibe\/style)(.*)/, /@vibe\/.*/ // Externalize all @vibe packages ], plugins: [ diff --git a/packages/core/.stylelintrc.json b/packages/core/.stylelintrc.json index bb25e4a2db..d3bf4c00ba 100644 --- a/packages/core/.stylelintrc.json +++ b/packages/core/.stylelintrc.json @@ -1,15 +1,15 @@ { - "extends": ["stylelint-config-recommended-scss", "monday-ui-style/stylelint-config"], + "extends": ["stylelint-config-recommended-scss", "@vibe/style/stylelint-config"], "plugins": ["stylelint-use-logical", "stylelint-no-unsupported-browser-features"], "rules": { "no-descending-specificity": null, - "monday-ui-style/use-defined-css-var-when-available": [ + "@vibe/style/use-defined-css-var-when-available": [ true, { "severity": "warning" } ], - "monday-ui-style/use-new-spacing-tokens": [ + "@vibe/style/use-new-spacing-tokens": [ null, { "severity": "warning" diff --git a/packages/core/README.md b/packages/core/README.md index d97fe65534..67e979a6d0 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -13,6 +13,18 @@ Components are imported from the library's root entry: import { Button } from "@vibe/core"; ``` +### Global CSS Setup + +Vibe components require `box-sizing: border-box` to be applied globally. Add the following to your root stylesheet: + +```css +*, +*::before, +*::after { + box-sizing: border-box; +} +``` + ### Font installation We don't import fonts ourselves, we work best with the following fonts - diff --git a/packages/core/package.json b/packages/core/package.json index e50d2dc6f0..e66a266f4c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/core", - "version": "3.88.0", + "version": "4.0.0-rc.0", "description": "Official monday.com UI resources for application development in React.js", "repository": { "type": "git", @@ -90,42 +90,34 @@ }, "dependencies": { "@floating-ui/react-dom": "^2.1.2", - "@popperjs/core": "2.11.6", - "@vibe/base": "3.0.5", - "@vibe/button": "3.0.16", - "@vibe/clickable": "3.0.3", - "@vibe/dialog": "3.0.12", - "@vibe/hooks": "3.0.3", - "@vibe/icon": "3.0.11", - "@vibe/icon-button": "3.0.5", - "@vibe/icons": "1.16.0", - "@vibe/layer": "3.0.10", - "@vibe/layout": "3.0.3", - "@vibe/loader": "3.0.11", - "@vibe/shared": "3.0.8", - "@vibe/tooltip": "3.0.5", - "@vibe/typography": "3.0.5", - "a11y-dialog": "^7.5.2", - "body-scroll-lock": "^4.0.0-beta.0", + "@vibe/base": "^4.0.0-rc.0", + "@vibe/button": "^4.0.0-rc.0", + "@vibe/clickable": "^4.0.0-rc.0", + "@vibe/dialog": "^4.0.0-rc.0", + "@vibe/hooks": "^4.0.0-rc.0", + "@vibe/icon": "^4.0.0-rc.0", + "@vibe/icon-button": "^4.0.0-rc.0", + "@vibe/icons": "^4.0.0-rc.0", + "@vibe/layer": "^4.0.0-rc.0", + "@vibe/layout": "^4.0.0-rc.0", + "@vibe/loader": "^4.0.0-rc.0", + "@vibe/shared": "^4.0.0-rc.0", + "@vibe/style": "^4.0.0-rc.0", + "@vibe/tooltip": "^4.0.0-rc.0", + "@vibe/typography": "^4.0.0-rc.0", "browserslist-config-monday": "1.0.6", "classnames": "^2.3.2", "date-fns": "^2.30.0", "downshift": "^9.0.8", "es-toolkit": "^1.39.10", - "framer-motion": "^6.5.1", - "monday-ui-style": "0.26.2", - "react-dates": "21.8.0", "react-day-picker": "^8.8.0", "react-focus-lock": "^2.13.2", "react-inlinesvg": "^4.1.3", "react-is": "^16.9.0", - "react-popper": "^2.3.0", "react-remove-scroll": "^2.6.0", - "react-select": "npm:react-select-module@3.4.0", "react-transition-group": "^4.4.5", "react-virtualized-auto-sizer": "^1.0.7", "react-window": "^1.8.7", - "react-windowed-select": "^2.0.4", "style-inject": "^0.3.0" }, "devDependencies": { @@ -177,13 +169,10 @@ "@testing-library/react-hooks": "^7.0.2", "@testing-library/user-event": "^13.5.0", "@types/babel__standalone": "^7.1.7", - "@types/body-scroll-lock": "^3.1.0", "@types/jest-axe": "^3.5.9", "@types/react": "^18.0.25", - "@types/react-dates": "^21.8.3", "@types/react-is": "^16.7.5", "@types/react-resizable": "^3.0.7", - "@types/react-select": "^3.1.2", "@types/react-syntax-highlighter": "^11.0.5", "@types/react-test-renderer": "^16.9.0", "@types/react-transition-group": "^4.4.5", @@ -194,7 +183,7 @@ "@uiw/codemirror-extensions-langs": "^4.21.25", "@uiw/codemirror-theme-github": "^4.21.25", "@uiw/react-codemirror": "^4.21.25", - "@vibe/config": "3.0.5", + "@vibe/config": "^4.0.0-rc.0", "@vitejs/plugin-react": "^4.3.1", "@vitest/coverage-v8": "^1.6.0", "@vitest/ui": "^1.6.0", @@ -265,14 +254,13 @@ "ts-morph": "^25.0.1", "ts-node": "^10.9.2", "tsx": "^4.20.3", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "typescript-plugin-css-modules": "^4.2.1", - "vibe-storybook-components": "1.0.7", + "vibe-storybook-components": "^4.0.0-rc.0", "vite": "^5.3.1", "vitest": "^1.6.0" }, "peerDependencies": { - "moment": "^2.29.1", "react": ">=16.9.0", "react-dom": ">=16.9.0" }, diff --git a/packages/core/rollup.config.js b/packages/core/rollup.config.js index 1894a5e643..b186c6bad3 100644 --- a/packages/core/rollup.config.js +++ b/packages/core/rollup.config.js @@ -100,7 +100,7 @@ export default { testIds: path.join(SRC_PATH, "tests/test-ids-utils.ts"), next: path.join(SRC_PATH, "components/next.ts") }, - external: [/node_modules\/(?!monday-ui-style)(.*)/], + external: [/node_modules\/(?!@vibe\/style)(.*)/], plugins: [ ...(shouldMockModularClassnames ? [ @@ -176,7 +176,7 @@ export default { copy({ targets: [ { - src: "../../node_modules/monday-ui-style/dist/index.min.css", + src: "../../node_modules/@vibe/style/dist/index.min.css", dest: "dist/tokens", rename: () => "tokens.css" }, diff --git a/packages/core/src/__tests__/__snapshots__/exports.test.ts.snap b/packages/core/src/__tests__/__snapshots__/exports.test.ts.snap index 19a9c9df72..b2ee49832e 100644 --- a/packages/core/src/__tests__/__snapshots__/exports.test.ts.snap +++ b/packages/core/src/__tests__/__snapshots__/exports.test.ts.snap @@ -9,7 +9,6 @@ exports[`exports > should export all components 1`] = ` "AlertBannerLink", "AlertBannerText", "AttentionBox", - "AttentionBoxLink", "Avatar", "AvatarGroup", "Badge", @@ -34,9 +33,6 @@ exports[`exports > should export all components 1`] = ` "DialogContentContainer", "Divider", "Dropdown", - "DropdownMenu", - "DropdownOption", - "DropdownSingleValue", "EditableHeading", "EditableText", "EmptyState", @@ -51,7 +47,6 @@ exports[`exports > should export all components 1`] = ` "Info", "Label", "LayerProvider", - "LinearProgressBar", "Link", "List", "ListItem", @@ -67,12 +62,17 @@ exports[`exports > should export all components 1`] = ` "MenuItemButton", "MenuTitle", "Modal", + "ModalBasicLayout", "ModalContent", "ModalFooter", - "ModalFooterButtons", + "ModalFooterWizard", "ModalHeader", + "ModalMedia", + "ModalMediaLayout", + "ModalSideBySideLayout", "MultiStepIndicator", "NumberField", + "ProgressBar", "RadioButton", "Search", "Skeleton", @@ -101,7 +101,6 @@ exports[`exports > should export all components 1`] = ` "ThemeProvider", "Tipseen", "TipseenContent", - "TipseenImage", "TipseenMedia", "TipseenWizard", "Toast", @@ -130,9 +129,7 @@ exports[`exports > should export all hooks 1`] = ` "useIsMouseOver", "useIsOverflowing", "useKeyEvent", - "useListenFocusTriggers", "useMediaQuery", - "useMergeRefs", "usePrevious", "useResizeObserver", "useSetFocus", @@ -145,21 +142,8 @@ exports[`exports > should export all hooks 1`] = ` exports[`exports > should export next all components 1`] = ` [ - "AttentionBox", - "DatePicker", - "Dialog", - "Dropdown", "List", "ListItem", "ListTitle", - "Modal", - "ModalBasicLayout", - "ModalContent", - "ModalFooter", - "ModalFooterWizard", - "ModalHeader", - "ModalMedia", - "ModalMediaLayout", - "ModalSideBySideLayout", ] `; diff --git a/packages/core/src/components/Accordion/Accordion/Accordion.module.scss b/packages/core/src/components/Accordion/Accordion/Accordion.module.scss index 77403e2ef7..1d89f50726 100644 --- a/packages/core/src/components/Accordion/Accordion/Accordion.module.scss +++ b/packages/core/src/components/Accordion/Accordion/Accordion.module.scss @@ -8,8 +8,7 @@ .accordion .accordionItemExpandCollapse { border-radius: 0; border-top: none; - border-left: none; - border-right: none; + border-inline: none; border-color: var(--ui-border-color); } diff --git a/packages/core/src/components/AlertBanner/AlertBanner.module.scss b/packages/core/src/components/AlertBanner/AlertBanner.module.scss index 595c0f409e..c85bf0dac1 100644 --- a/packages/core/src/components/AlertBanner/AlertBanner.module.scss +++ b/packages/core/src/components/AlertBanner/AlertBanner.module.scss @@ -38,7 +38,7 @@ min-width: 0; align-items: center; justify-content: center; - padding-left: var(--spacing-medium); + padding-inline-start: var(--space-16); } .closeButtonWrapper { @@ -50,7 +50,7 @@ .closeBtn { position: absolute; - right: 4px; + inset-inline-end: 4px; top: 4px; } diff --git a/packages/core/src/components/AlertBanner/AlertBanner.tsx b/packages/core/src/components/AlertBanner/AlertBanner.tsx index 2f1d78da06..3accbbdecb 100644 --- a/packages/core/src/components/AlertBanner/AlertBanner.tsx +++ b/packages/core/src/components/AlertBanner/AlertBanner.tsx @@ -3,7 +3,6 @@ import cx from "classnames"; import React, { type ForwardedRef, forwardRef, type ReactElement, useMemo } from "react"; import { IconButton } from "@vibe/icon-button"; import { CloseSmall } from "@vibe/icons"; -import { AlertBannerBackgroundColor as AlertBannerBackgroundColorEnum } from "./AlertBannerConstants"; import { type AlertBannerBackgroundColor } from "./AlertBanner.types"; import { NOOP } from "../../utils/function-utils"; import { type AlertBannerLinkProps } from "./AlertBannerLink/AlertBannerLink"; @@ -11,7 +10,7 @@ import { type AlertBannerButtonProps } from "./AlertBannerButton/AlertBannerButt import { type AlertBannerTextProps } from "./AlertBannerText/AlertBannerText"; import { ComponentDefaultTestId, ComponentVibeId } from "../../tests/constants"; import { getTestId } from "../../tests/test-ids-utils"; -import { type VibeComponentProps, withStaticProps } from "../../types"; +import { type VibeComponentProps } from "../../types"; import styles from "./AlertBanner.module.scss"; import { Text } from "@vibe/typography"; import { AlertBannerContext } from "./AlertBannerContext"; @@ -30,7 +29,7 @@ export interface AlertBannerProps extends VibeComponentProps { /** * The ARIA label of the alert banner for accessibility. */ - ariaLabel?: string; + "aria-label"?: string; /** * The ARIA label of the close button for accessibility. */ @@ -52,7 +51,7 @@ const AlertBanner = forwardRef( className, backgroundColor = "primary", onClose = NOOP, - ariaLabel = "", + "aria-label": ariaLabel = "", closeButtonAriaLabel = "Close", isCloseHidden = false, id, @@ -145,7 +144,7 @@ const AlertBanner = forwardRef( size="small" kind="tertiary" color={isDarkBackground ? "on-inverted-background" : "on-primary-color"} - ariaLabel={closeButtonAriaLabel} + aria-label={closeButtonAriaLabel} /> )}
@@ -154,10 +153,4 @@ const AlertBanner = forwardRef( } ); -interface AlertBannerStaticProps { - backgroundColors: typeof AlertBannerBackgroundColorEnum; -} - -export default withStaticProps(AlertBanner, { - backgroundColors: AlertBannerBackgroundColorEnum -}); +export default AlertBanner; diff --git a/packages/core/src/components/AlertBanner/AlertBannerButton/AlertBannerButton.module.scss b/packages/core/src/components/AlertBanner/AlertBannerButton/AlertBannerButton.module.scss index 074d4ae115..9048472310 100644 --- a/packages/core/src/components/AlertBanner/AlertBannerButton/AlertBannerButton.module.scss +++ b/packages/core/src/components/AlertBanner/AlertBannerButton/AlertBannerButton.module.scss @@ -9,5 +9,5 @@ } .marginLeft { - margin-left: var(--spacing-small); + margin-inline-start: var(--space-8); } diff --git a/packages/core/src/components/AlertBanner/AlertBannerConstants.ts b/packages/core/src/components/AlertBanner/AlertBannerConstants.ts deleted file mode 100644 index 5b717f02d3..0000000000 --- a/packages/core/src/components/AlertBanner/AlertBannerConstants.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @deprecated - */ -export enum AlertBannerBackgroundColor { - PRIMARY = "primary", - POSITIVE = "positive", - NEGATIVE = "negative", - DARK = "dark", - WARNING = "warning" -} diff --git a/packages/core/src/components/AlertBanner/AlertBannerLink/AlertBannerLink.module.scss b/packages/core/src/components/AlertBanner/AlertBannerLink/AlertBannerLink.module.scss index 53596f03f5..b0311f19c4 100644 --- a/packages/core/src/components/AlertBanner/AlertBannerLink/AlertBannerLink.module.scss +++ b/packages/core/src/components/AlertBanner/AlertBannerLink/AlertBannerLink.module.scss @@ -13,5 +13,5 @@ } .marginLeft { - margin-left: var(--spacing-small); + margin-inline-start: var(--space-8); } diff --git a/packages/core/src/components/AlertBanner/AlertBannerText/AlertBannerText.module.scss b/packages/core/src/components/AlertBanner/AlertBannerText/AlertBannerText.module.scss index 6d5b90525f..6689e502f2 100644 --- a/packages/core/src/components/AlertBanner/AlertBannerText/AlertBannerText.module.scss +++ b/packages/core/src/components/AlertBanner/AlertBannerText/AlertBannerText.module.scss @@ -5,5 +5,5 @@ } .marginLeft { - margin-left: var(--spacing-small); + margin-inline-start: var(--space-8); } diff --git a/packages/core/src/components/AlertBanner/__tests__/AlertBanner.snapshot.test.tsx b/packages/core/src/components/AlertBanner/__tests__/AlertBanner.snapshot.test.tsx index 36672e4873..5cbde309b0 100644 --- a/packages/core/src/components/AlertBanner/__tests__/AlertBanner.snapshot.test.tsx +++ b/packages/core/src/components/AlertBanner/__tests__/AlertBanner.snapshot.test.tsx @@ -19,7 +19,7 @@ describe("AlertBanner", () => { diff --git a/packages/core/src/components/AlertBanner/__tests__/AlertBanner.test.tsx b/packages/core/src/components/AlertBanner/__tests__/AlertBanner.test.tsx index 2777b5018f..5b89ece55e 100644 --- a/packages/core/src/components/AlertBanner/__tests__/AlertBanner.test.tsx +++ b/packages/core/src/components/AlertBanner/__tests__/AlertBanner.test.tsx @@ -39,7 +39,7 @@ describe("", () => { describe("a11y", () => { it("should add the label", () => { const ariaLabel = "Lable Name"; - const { getByLabelText } = render(); + const { getByLabelText } = render(); const alertBannerComponentLabel = getByLabelText(ariaLabel); expect(alertBannerComponentLabel).toBeTruthy(); }); diff --git a/packages/core/src/components/AttentionBox/AttentionBox.module.scss b/packages/core/src/components/AttentionBox/AttentionBox.module.scss index 94425c6bad..48de2c4698 100644 --- a/packages/core/src/components/AttentionBox/AttentionBox.module.scss +++ b/packages/core/src/components/AttentionBox/AttentionBox.module.scss @@ -1,100 +1,45 @@ -@keyframes entryAnimation { +@keyframes attentionBoxAnimation { 0% { - transform: translateY(-10px); opacity: 0; + transform: translateY(-10px); } 50% { opacity: 1; } 100% { - transform: translateX(0); opacity: 1; + transform: translateY(0); } } -.attentionBox { - position: relative; - padding: 12px var(--spacing-medium); +.attention { + width: 100%; border-radius: var(--border-radius-medium); - width: fit-content; - max-width: 100%; - height: fit-content; + padding: var(--space-12) var(--space-16); + position: relative; + overflow: hidden; - &.entryAnimation { - animation: entryAnimation 200ms cubic-bezier(0.00, 0.00, 0.40, 1.00) forwards; + &.animate { + animation: attentionBoxAnimation 200ms var(--motion-timing-transition); } - // Overriding on primary icon color should be with bigger specificity than the original color - & .closeIconWrapper .closeIcon { - color: var(--primary-text-color); + &.primary { + background-color: var(--primary-selected-color); } - .icon { - flex-shrink: 0; + &.positive { + background-color: var(--positive-color-selected); } -} - -.title { - margin: 0; - padding-right: var(--spacing-xl); -} - -.text, -.titleContainer { - color: var(--primary-text-color); -} - -.typePrimary { - color: var(--primary-text-color); - background-color: var(--primary-selected-color); -} - -.typeDanger { - background-color: var(--negative-color-selected); -} - -.typeSuccess { - background-color: var(--positive-color-selected); -} - -.typeDark { - background-color: var(--ui-background-color); - color: var(--primary-text-color); -} - -.typeWarning { - background-color: var(--warning-color-selected); - color: var(--primary-text-color); -} - -.titleContainer { - margin-bottom: var(--spacing-xs); -} -.titleContainer:empty { - margin-bottom: 0; -} - -.closeIconWrapper { - position: absolute; - top: 12px; - right: 12px; -} - -.closeIconCompact { - top: calc(50% - 16px); -} - -.text { - &.compact { - flex: 1; + &.negative { + background-color: var(--negative-color-selected); + } - &.dismissible { - padding-right: var(--spacing-xl); - } + &.warning { + background-color: var(--warning-color-selected); } - &.paragraph { - margin: 0 var(--spacing-medium) 0 0; + &.neutral { + background-color: var(--allgrey-background-color); } } diff --git a/packages/core/src/components/AttentionBox/AttentionBox.tsx b/packages/core/src/components/AttentionBox/AttentionBox.tsx index 0cd4d501b2..c6edae9356 100644 --- a/packages/core/src/components/AttentionBox/AttentionBox.tsx +++ b/packages/core/src/components/AttentionBox/AttentionBox.tsx @@ -1,167 +1,74 @@ +import React, { forwardRef } from "react"; import cx from "classnames"; -import React, { useMemo } from "react"; -import { camelCase } from "es-toolkit"; -import { getStyle } from "../../helpers/typesciptCssModulesHelper"; -import { ComponentDefaultTestId, getTestId } from "../../tests/test-ids-utils"; -import { Icon, type SubIcon } from "@vibe/icon"; -import { IconButton } from "@vibe/icon-button"; -import { CloseSmall, Alert as AlertIcon, Info as InfoIcon } from "@vibe/icons"; -import { AttentionBoxType as AttentionBoxTypeEnum, IconTypeEnum } from "./AttentionBoxConstants"; -import { type AttentionBoxType } from "./AttentionBox.types"; -import { type VibeComponentProps, type ElementContent, withStaticPropsWithoutForwardRef } from "../../types"; -import { Text } from "@vibe/typography"; -import { Flex } from "@vibe/layout"; +import type { AttentionBoxProps, AttentionBoxRole } from "./AttentionBox.types"; +import AttentionBoxDefault from "./layouts/AttentionBoxDefault/AttentionBoxDefault"; +import AttentionBoxCompact from "./layouts/AttentionBoxCompact/AttentionBoxCompact"; +import { resolveAttentionBoxIcon } from "./utils/iconUtils"; +import { ComponentDefaultTestId, ComponentVibeId } from "../../tests/constants"; +import { getTestId } from "../../tests/test-ids-utils"; import styles from "./AttentionBox.module.scss"; -import { ComponentVibeId } from "../../tests/constants"; -/** - * @deprecated AttentionBox is deprecated. Please use AttentionBox from "@vibe/core/next" instead. - */ -export interface AttentionBoxProps extends VibeComponentProps { - // TODO: [breaking] remove prop - /** - * If true, displays an icon even when no header is provided. - */ - withIconWithoutHeader?: boolean; - /** - * The type of the AttentionBox. - */ - type?: AttentionBoxType; - /** - * The icon displayed next to the title or text. - */ - icon?: SubIcon; - /** - * The type of the icon. - */ - iconType?: "svg" | "font"; - /** - * The title of the component. - */ - title?: string; - /** - * The text content displayed inside. - */ - text?: string; - /** - * The content of the component. - */ - children?: ElementContent; - // TODO: [breaking] remove prop - /** - * If true, the icon is not displayed. - */ - withoutIcon?: boolean; - /** - * Callback fired when the close button is clicked. - */ - onClose?: (event: React.MouseEvent) => void; - /** - * If true, renders in compact mode. - */ - compact?: boolean; - /** - * The label of the close button. - */ - closeButtonAriaLabel?: string; - /** - * If true, an entry animation is applied when the component appears. - */ - entryAnimation?: boolean; -} +const AttentionBox = forwardRef( + ( + { + compact = false, + title, + animate = true, + icon, + iconType = "svg", + type = "primary", + children, + text, + action, + link, + id, + onClose, + closeButtonAriaLabel, + className, + "data-testid": dataTestId + }: AttentionBoxProps, + ref: React.ForwardedRef + ) => { + const role: AttentionBoxRole = type === "negative" ? "alert" : "status"; + const displayIcon = resolveAttentionBoxIcon(icon, type); + const content = children || text; -/** - * @deprecated AttentionBox is deprecated. Please use AttentionBox from "@vibe/core/next" instead. - */ -const AttentionBox = ({ - className, - withIconWithoutHeader = false, - type = "primary", - icon, - iconType = "svg", - title, - text, - children, - withoutIcon = false, - onClose, - compact = false, - id, - "data-testid": dataTestId, - closeButtonAriaLabel = "Close", - entryAnimation = false -}: AttentionBoxProps) => { - const defaultIcon = useMemo(() => { - return type === "primary" ? InfoIcon : AlertIcon; - }, [type]); + const baseClasses = cx( + styles.attention, + styles[type], + { + [styles.animate]: animate + }, + className + ); - const overrideIcon = icon === undefined ? defaultIcon : icon; + const layoutSharedProps = { + onClose, + closeButtonAriaLabel, + action, + link, + icon: displayIcon, + iconType, + content + }; - return ( - - ); -}; + + ); + } +); -interface AttentionBoxStaticProps { - types: typeof AttentionBoxTypeEnum; - iconTypes: typeof IconTypeEnum; -} - -export default withStaticPropsWithoutForwardRef(AttentionBox, { - types: AttentionBoxTypeEnum, - iconTypes: IconTypeEnum -}); +export default AttentionBox; diff --git a/packages/core/src/components/AttentionBox/AttentionBox.types.ts b/packages/core/src/components/AttentionBox/AttentionBox.types.ts index 0a86f624ac..4da4679602 100644 --- a/packages/core/src/components/AttentionBox/AttentionBox.types.ts +++ b/packages/core/src/components/AttentionBox/AttentionBox.types.ts @@ -1 +1,86 @@ -export type AttentionBoxType = "primary" | "success" | "danger" | "dark" | "warning"; +import type { ReactNode, MouseEvent } from "react"; +import type { VibeComponentProps } from "../../types"; +import type { SubIcon, IconType } from "@vibe/icon"; +import type { AttentionBoxButtonProps } from "./components/AttentionBoxButton/AttentionBoxButton"; +import type { AttentionBoxLinkProps } from "./components/AttentionBoxLink/AttentionBoxLink"; + +export type AttentionBoxType = "primary" | "positive" | "negative" | "warning" | "neutral"; + +// Mutually exclusive content props +export type AttentionBoxContentProps = + | { + /** + * The main text content + */ + text: string; + children?: never; + } + | { + /** + * Custom children to override the default text content + */ + children: ReactNode; + text?: never; + }; + +// Shared props for both compact and default layouts +export interface AttentionBoxLayoutSharedProps + extends Pick { + content: ReactNode; +} + +// Mutually exclusive title props +export type AttentionBoxCompactTitleProps = + | { + compact?: false; + /** + * The title of the attention box + */ + title?: string; + } + | { + /** + * When true, the attention box will be displayed in compact mode of one-liner + */ + compact: true; + title?: never; + }; + +export type AttentionBoxRole = "alert" | "status"; + +export type AttentionBoxProps = VibeComponentProps & + AttentionBoxContentProps & + AttentionBoxCompactTitleProps & { + /** + * The variant type of the attention box + */ + type?: AttentionBoxType; + /** + * The type of the icon + */ + iconType?: IconType; + /** + * The icon to display. Pass `false` to hide the icon entirely, or omit to use the default icon for the type. + */ + icon?: SubIcon | false; + /** + * Callback when the close button is clicked + */ + onClose?: (event: MouseEvent) => void; + /** + * Custom aria label for the close button + */ + closeButtonAriaLabel?: string; + /** + * Whether to animate the entrance + */ + animate?: boolean; + /** + * Action button configuration + */ + action?: AttentionBoxButtonProps; + /** + * Link configuration + */ + link?: Omit; + }; diff --git a/packages/core/src/components/AttentionBox/AttentionBoxConstants.ts b/packages/core/src/components/AttentionBox/AttentionBoxConstants.ts deleted file mode 100644 index 63a94fc8ca..0000000000 --- a/packages/core/src/components/AttentionBox/AttentionBoxConstants.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * @deprecated - */ -export enum AttentionBoxType { - PRIMARY = "primary", - SUCCESS = "success", - DANGER = "danger", - DARK = "dark", - WARNING = "warning" -} - -/** - * @deprecated - */ -export enum IconTypeEnum { - SVG = "svg", - ICON_FONT = "font", - SRC = "src" -} diff --git a/packages/core/src/components/AttentionBox/AttentionBoxLink/AttentionBoxLink.module.scss b/packages/core/src/components/AttentionBox/AttentionBoxLink/AttentionBoxLink.module.scss deleted file mode 100644 index 8334da7fbf..0000000000 --- a/packages/core/src/components/AttentionBox/AttentionBoxLink/AttentionBoxLink.module.scss +++ /dev/null @@ -1,7 +0,0 @@ -// TODO: use just a class (without [href]) after removing anchor style from Typography -// Using [href] to increase specificity and override `Typography a` styles -a[href].attentionBoxLink, -a[href].attentionBoxLink:hover { - color: var(--primary-text-color); - text-decoration: underline; -} diff --git a/packages/core/src/components/AttentionBox/AttentionBoxLink/AttentionBoxLink.tsx b/packages/core/src/components/AttentionBox/AttentionBoxLink/AttentionBoxLink.tsx deleted file mode 100644 index 110fe2260b..0000000000 --- a/packages/core/src/components/AttentionBox/AttentionBoxLink/AttentionBoxLink.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from "react"; -import styles from "./AttentionBoxLink.module.scss"; -import Link, { type LinkProps } from "../../Link/Link"; -import cx from "classnames"; - -/** - * @deprecated AttentionBoxLink is deprecated. Please use AttentionBox from "@vibe/core/next" instead. - */ -export type AttentionBoxLinkProps = LinkProps; - -/** - * @deprecated AttentionBoxLink is deprecated. Please use AttentionBox from "@vibe/core/next" instead. - */ -const AttentionBoxLink = ({ - href, - text, - // TODO: use Link's target default in next major - // For backward compatibility - using _self as default - target = "_self", - className, - ...linkProps -}: AttentionBoxLinkProps) => { - return ( - - ); -}; - -export default AttentionBoxLink; diff --git a/packages/core/src/components/AttentionBox/__tests__/AttentionBox.snapshot.test.tsx b/packages/core/src/components/AttentionBox/__tests__/AttentionBox.snapshot.test.tsx deleted file mode 100644 index efb4ef303a..0000000000 --- a/packages/core/src/components/AttentionBox/__tests__/AttentionBox.snapshot.test.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { describe, it, expect } from "vitest"; -import React from "react"; -import renderer from "react-test-renderer"; -import AttentionBox from "../AttentionBox"; - -describe("AttentionBox renders correctly", () => { - it("renders correctly", () => { - const tree = renderer.create().toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it("renders correctly with empty props", () => { - const tree = renderer.create().toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it("renders correctly with empty title prop", () => { - const tree = renderer.create().toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it("renders correctly with no icon", () => { - const tree = renderer.create().toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it("renders correctly when compact", () => { - const tree = renderer.create().toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it("renders correctly dark type", () => { - const tree = renderer.create().toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it("renders with icon font type", () => { - const tree = renderer.create().toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it("renders correctly className", () => { - const tree = renderer.create().toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it("renders correctly with onClose", () => { - const tree = renderer.create( {}} />).toJSON(); - expect(tree).toMatchSnapshot(); - }); - - it("renders correctly with renderer", () => { - const tree = renderer - .create( - - - renderer with components
and sub components
-
-
- ) - .toJSON(); - expect(tree).toMatchSnapshot(); - }); -}); diff --git a/packages/core/src/components/AttentionBox/__tests__/AttentionBox.test.tsx b/packages/core/src/components/AttentionBox/__tests__/AttentionBox.test.tsx index a302d00354..b7b451bcc7 100644 --- a/packages/core/src/components/AttentionBox/__tests__/AttentionBox.test.tsx +++ b/packages/core/src/components/AttentionBox/__tests__/AttentionBox.test.tsx @@ -1,16 +1,196 @@ -import { vi, describe, it, expect } from "vitest"; import React from "react"; -import { fireEvent, render, screen } from "@testing-library/react"; +import { render, screen, fireEvent } from "@testing-library/react"; +import { vi, describe, it, expect, beforeEach } from "vitest"; +import { Robot } from "@vibe/icons"; import AttentionBox from "../AttentionBox"; +import userEvent from "@testing-library/user-event"; +import type { AttentionBoxType } from "../AttentionBox.types"; -describe("AttentionBox tests", () => { - const title = "title"; - const text = "text"; +describe("AttentionBox", () => { + beforeEach(() => { + vi.clearAllTimers(); + vi.useFakeTimers(); + }); + + afterEach(() => { + vi.runOnlyPendingTimers(); + vi.useRealTimers(); + }); + + describe("Core Functionality", () => { + it("renders in default mode with title and text", () => { + render(); + + expect(screen.getByText("Test Title")).toBeInTheDocument(); + expect(screen.getByText("Test text")).toBeInTheDocument(); + }); + + it("renders all variant types with correct class styling", () => { + const variants: AttentionBoxType[] = ["primary", "positive", "negative", "warning", "neutral"]; + + variants.forEach(variant => { + const { unmount } = render(); + const container = screen.getByRole(variant === "negative" ? "alert" : "status"); + expect(container).toHaveClass(`${variant}`); + unmount(); + }); + }); + + it("renders negative variant with alert role", () => { + render(); + expect(screen.getByRole("alert")).toBeInTheDocument(); + }); + + it("renders non-negative variants with status role", () => { + render(); + expect(screen.getByRole("status")).toBeInTheDocument(); + }); + }); + + describe("Icon Control", () => { + it("renders default icon when no icon is provided", () => { + render(); + expect(screen.getByTestId("icon")).toBeInTheDocument(); + }); + + it("renders custom icon when provided", () => { + render(); + expect(screen.getByTestId("icon")).toBeInTheDocument(); + }); + + it("hides icon when icon is false", () => { + render(); + expect(screen.queryByTestId("icon")).not.toBeInTheDocument(); + }); + }); + + describe("Dismiss Control", () => { + it("renders close button when onClose is provided", () => { + const onClose = vi.fn(); + render(); + + const closeButton = screen.getByRole("button", { name: /close/i }); + expect(closeButton).toBeInTheDocument(); + }); + + it("does not render close button when onClose is not provided", () => { + render(); + expect(screen.queryByRole("button", { name: /close/i })).not.toBeInTheDocument(); + }); + + it("calls onClose when close button is clicked", () => { + const onClose = vi.fn(); + render(); + + const closeButton = screen.getByRole("button", { name: /close/i }); + fireEvent.click(closeButton); + + expect(onClose).toHaveBeenCalledTimes(1); + }); + }); + + describe("Action Button", () => { + it("renders action button when provided", () => { + const onClick = vi.fn(); + render(); + + const actionButton = screen.getByRole("button", { name: "Action" }); + expect(actionButton).toBeInTheDocument(); + }); + + it("calls action onClick when button is clicked", () => { + const onClick = vi.fn(); + render(); + + const actionButton = screen.getByRole("button", { name: "Action" }); + fireEvent.click(actionButton); + + expect(onClick).toHaveBeenCalledTimes(1); + }); + }); + + describe("Link", () => { + it("renders link when provided", () => { + render(); + + const link = screen.getByRole("link", { name: "Test Link" }); + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute("href", "/test"); + }); + + it("renders link as block level element", () => { + render(); + + const link = screen.getByRole("link", { name: "Test Link" }); + expect(link).toBeInTheDocument(); + expect(link).not.toHaveClass("inlineText"); + }); + + it("renders link in action section when action also exists", () => { + const onClick = vi.fn(); + render( + + ); + + const link = screen.getByRole("link", { name: "Test Link" }); + const actionButton = screen.getByRole("button", { name: "Action" }); + expect(link).toBeInTheDocument(); + expect(actionButton).toBeInTheDocument(); + }); + }); + + describe("Animation", () => { + it("applies animation classes when animate is true", () => { + render(); + + vi.advanceTimersByTime(200); + + const container = screen.getByRole("status"); + expect(container).toHaveClass("animate"); + }); + }); + + describe("Children Override", () => { + it("renders children instead of text when provided", () => { + render( + + Custom children + + ); + + expect(screen.getByText("Custom children")).toBeInTheDocument(); + expect(screen.queryByText("Original text")).not.toBeInTheDocument(); + }); + }); + + describe("Accessibility", () => { + it("has correct tab order", () => { + const onClose = vi.fn(); + const actionClick = vi.fn(); + + render( + + ); + + const closeButton = screen.getByRole("button", { name: /close/i }); + const link = screen.getByRole("link"); + const actionButton = screen.getByRole("button", { name: "Action" }); - it("should call onClose callback when close button clicked", () => { - const onCloseMock = vi.fn(); - render(); - fireEvent.click(screen.getByLabelText("Close")); - expect(onCloseMock.mock.calls.length).toBe(1); + userEvent.tab(); + expect(closeButton).toHaveFocus(); + userEvent.tab(); + expect(link).toHaveFocus(); + userEvent.tab(); + expect(actionButton).toHaveFocus(); + }); }); }); diff --git a/packages/core/src/components/AttentionBox/__tests__/__snapshots__/AttentionBox.snapshot.test.tsx.snap b/packages/core/src/components/AttentionBox/__tests__/__snapshots__/AttentionBox.snapshot.test.tsx.snap deleted file mode 100644 index 26cae706df..0000000000 --- a/packages/core/src/components/AttentionBox/__tests__/__snapshots__/AttentionBox.snapshot.test.tsx.snap +++ /dev/null @@ -1,495 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`AttentionBox renders correctly > renders correctly 1`] = ` - -`; - -exports[`AttentionBox renders correctly > renders correctly className 1`] = ` - -`; - -exports[`AttentionBox renders correctly > renders correctly dark type 1`] = ` - -`; - -exports[`AttentionBox renders correctly > renders correctly when compact 1`] = ` - -`; - -exports[`AttentionBox renders correctly > renders correctly with empty props 1`] = ` - -`; - -exports[`AttentionBox renders correctly > renders correctly with empty title prop 1`] = ` - -`; - -exports[`AttentionBox renders correctly > renders correctly with no icon 1`] = ` - -`; - -exports[`AttentionBox renders correctly > renders correctly with onClose 1`] = ` - -`; - -exports[`AttentionBox renders correctly > renders correctly with renderer 1`] = ` - -`; - -exports[`AttentionBox renders correctly > renders with icon font type 1`] = ` - -`; diff --git a/packages/core/src/components/next/AttentionBox/components/AttentionBoxButton/AttentionBoxButton.tsx b/packages/core/src/components/AttentionBox/components/AttentionBoxButton/AttentionBoxButton.tsx similarity index 100% rename from packages/core/src/components/next/AttentionBox/components/AttentionBoxButton/AttentionBoxButton.tsx rename to packages/core/src/components/AttentionBox/components/AttentionBoxButton/AttentionBoxButton.tsx diff --git a/packages/core/src/components/next/AttentionBox/components/AttentionBoxCloseButton/AttentionBoxCloseButton.module.scss b/packages/core/src/components/AttentionBox/components/AttentionBoxCloseButton/AttentionBoxCloseButton.module.scss similarity index 100% rename from packages/core/src/components/next/AttentionBox/components/AttentionBoxCloseButton/AttentionBoxCloseButton.module.scss rename to packages/core/src/components/AttentionBox/components/AttentionBoxCloseButton/AttentionBoxCloseButton.module.scss diff --git a/packages/core/src/components/next/AttentionBox/components/AttentionBoxCloseButton/AttentionBoxCloseButton.tsx b/packages/core/src/components/AttentionBox/components/AttentionBoxCloseButton/AttentionBoxCloseButton.tsx similarity index 94% rename from packages/core/src/components/next/AttentionBox/components/AttentionBoxCloseButton/AttentionBoxCloseButton.tsx rename to packages/core/src/components/AttentionBox/components/AttentionBoxCloseButton/AttentionBoxCloseButton.tsx index 937f912328..62bfd0c293 100644 --- a/packages/core/src/components/next/AttentionBox/components/AttentionBoxCloseButton/AttentionBoxCloseButton.tsx +++ b/packages/core/src/components/AttentionBox/components/AttentionBoxCloseButton/AttentionBoxCloseButton.tsx @@ -21,7 +21,7 @@ const AttentionBoxCloseButton = ({ size="xs" kind="tertiary" onClick={onClose} - ariaLabel={closeButtonAriaLabel} + aria-label={closeButtonAriaLabel} hideTooltip className={cx(styles.closeButton, className)} /> diff --git a/packages/core/src/components/next/AttentionBox/components/AttentionBoxLeadingIcon/AttentionBoxLeadingIcon.module.scss b/packages/core/src/components/AttentionBox/components/AttentionBoxLeadingIcon/AttentionBoxLeadingIcon.module.scss similarity index 100% rename from packages/core/src/components/next/AttentionBox/components/AttentionBoxLeadingIcon/AttentionBoxLeadingIcon.module.scss rename to packages/core/src/components/AttentionBox/components/AttentionBoxLeadingIcon/AttentionBoxLeadingIcon.module.scss diff --git a/packages/core/src/components/next/AttentionBox/components/AttentionBoxLeadingIcon/AttentionBoxLeadingIcon.tsx b/packages/core/src/components/AttentionBox/components/AttentionBoxLeadingIcon/AttentionBoxLeadingIcon.tsx similarity index 81% rename from packages/core/src/components/next/AttentionBox/components/AttentionBoxLeadingIcon/AttentionBoxLeadingIcon.tsx rename to packages/core/src/components/AttentionBox/components/AttentionBoxLeadingIcon/AttentionBoxLeadingIcon.tsx index 3f8a463f83..74a9e0fb72 100644 --- a/packages/core/src/components/next/AttentionBox/components/AttentionBoxLeadingIcon/AttentionBoxLeadingIcon.tsx +++ b/packages/core/src/components/AttentionBox/components/AttentionBoxLeadingIcon/AttentionBoxLeadingIcon.tsx @@ -10,7 +10,7 @@ export interface AttentionBoxLeadingIconProps { } const AttentionBoxLeadingIcon = ({ icon, iconType = "svg", className }: AttentionBoxLeadingIconProps) => { - return ; + return ; }; export default AttentionBoxLeadingIcon; diff --git a/packages/core/src/components/next/AttentionBox/components/AttentionBoxLink/AttentionBoxLink.module.scss b/packages/core/src/components/AttentionBox/components/AttentionBoxLink/AttentionBoxLink.module.scss similarity index 100% rename from packages/core/src/components/next/AttentionBox/components/AttentionBoxLink/AttentionBoxLink.module.scss rename to packages/core/src/components/AttentionBox/components/AttentionBoxLink/AttentionBoxLink.module.scss diff --git a/packages/core/src/components/next/AttentionBox/components/AttentionBoxLink/AttentionBoxLink.tsx b/packages/core/src/components/AttentionBox/components/AttentionBoxLink/AttentionBoxLink.tsx similarity index 83% rename from packages/core/src/components/next/AttentionBox/components/AttentionBoxLink/AttentionBoxLink.tsx rename to packages/core/src/components/AttentionBox/components/AttentionBoxLink/AttentionBoxLink.tsx index af1c1a6650..078f4bd160 100644 --- a/packages/core/src/components/next/AttentionBox/components/AttentionBoxLink/AttentionBoxLink.tsx +++ b/packages/core/src/components/AttentionBox/components/AttentionBoxLink/AttentionBoxLink.tsx @@ -1,7 +1,7 @@ import React, { forwardRef } from "react"; import cx from "classnames"; -import { Link } from "../../../../Link"; -import type { LinkProps } from "../../../../Link"; +import { Link } from "../../../Link"; +import type { LinkProps } from "../../../Link"; import styles from "./AttentionBoxLink.module.scss"; export type AttentionBoxLinkProps = LinkProps; diff --git a/packages/core/src/components/next/AttentionBox/consts/icons.ts b/packages/core/src/components/AttentionBox/consts/icons.ts similarity index 100% rename from packages/core/src/components/next/AttentionBox/consts/icons.ts rename to packages/core/src/components/AttentionBox/consts/icons.ts diff --git a/packages/core/src/components/AttentionBox/index.ts b/packages/core/src/components/AttentionBox/index.ts index 4847d2fac9..8e3617834c 100644 --- a/packages/core/src/components/AttentionBox/index.ts +++ b/packages/core/src/components/AttentionBox/index.ts @@ -1,4 +1,2 @@ -export { default as AttentionBox, type AttentionBoxProps } from "./AttentionBox"; -export { default as AttentionBoxLink, type AttentionBoxLinkProps } from "./AttentionBoxLink/AttentionBoxLink"; - -export * from "./AttentionBox.types"; +export { default as AttentionBox } from "./AttentionBox"; +export type { AttentionBoxProps, AttentionBoxType } from "./AttentionBox.types"; diff --git a/packages/core/src/components/next/AttentionBox/layouts/AttentionBoxCompact/AttentionBoxCompact.module.scss b/packages/core/src/components/AttentionBox/layouts/AttentionBoxCompact/AttentionBoxCompact.module.scss similarity index 100% rename from packages/core/src/components/next/AttentionBox/layouts/AttentionBoxCompact/AttentionBoxCompact.module.scss rename to packages/core/src/components/AttentionBox/layouts/AttentionBoxCompact/AttentionBoxCompact.module.scss diff --git a/packages/core/src/components/next/AttentionBox/layouts/AttentionBoxCompact/AttentionBoxCompact.tsx b/packages/core/src/components/AttentionBox/layouts/AttentionBoxCompact/AttentionBoxCompact.tsx similarity index 100% rename from packages/core/src/components/next/AttentionBox/layouts/AttentionBoxCompact/AttentionBoxCompact.tsx rename to packages/core/src/components/AttentionBox/layouts/AttentionBoxCompact/AttentionBoxCompact.tsx diff --git a/packages/core/src/components/next/AttentionBox/layouts/AttentionBoxCompact/__tests__/AttentionBoxCompact.test.tsx b/packages/core/src/components/AttentionBox/layouts/AttentionBoxCompact/__tests__/AttentionBoxCompact.test.tsx similarity index 100% rename from packages/core/src/components/next/AttentionBox/layouts/AttentionBoxCompact/__tests__/AttentionBoxCompact.test.tsx rename to packages/core/src/components/AttentionBox/layouts/AttentionBoxCompact/__tests__/AttentionBoxCompact.test.tsx diff --git a/packages/core/src/components/next/AttentionBox/layouts/AttentionBoxDefault/AttentionBoxDefault.module.scss b/packages/core/src/components/AttentionBox/layouts/AttentionBoxDefault/AttentionBoxDefault.module.scss similarity index 100% rename from packages/core/src/components/next/AttentionBox/layouts/AttentionBoxDefault/AttentionBoxDefault.module.scss rename to packages/core/src/components/AttentionBox/layouts/AttentionBoxDefault/AttentionBoxDefault.module.scss diff --git a/packages/core/src/components/next/AttentionBox/layouts/AttentionBoxDefault/AttentionBoxDefault.tsx b/packages/core/src/components/AttentionBox/layouts/AttentionBoxDefault/AttentionBoxDefault.tsx similarity index 100% rename from packages/core/src/components/next/AttentionBox/layouts/AttentionBoxDefault/AttentionBoxDefault.tsx rename to packages/core/src/components/AttentionBox/layouts/AttentionBoxDefault/AttentionBoxDefault.tsx diff --git a/packages/core/src/components/next/AttentionBox/layouts/AttentionBoxDefault/__tests__/AttentionBoxDefault.test.tsx b/packages/core/src/components/AttentionBox/layouts/AttentionBoxDefault/__tests__/AttentionBoxDefault.test.tsx similarity index 100% rename from packages/core/src/components/next/AttentionBox/layouts/AttentionBoxDefault/__tests__/AttentionBoxDefault.test.tsx rename to packages/core/src/components/AttentionBox/layouts/AttentionBoxDefault/__tests__/AttentionBoxDefault.test.tsx diff --git a/packages/core/src/components/next/AttentionBox/utils/__tests__/iconUtils.test.ts b/packages/core/src/components/AttentionBox/utils/__tests__/iconUtils.test.ts similarity index 100% rename from packages/core/src/components/next/AttentionBox/utils/__tests__/iconUtils.test.ts rename to packages/core/src/components/AttentionBox/utils/__tests__/iconUtils.test.ts diff --git a/packages/core/src/components/next/AttentionBox/utils/iconUtils.ts b/packages/core/src/components/AttentionBox/utils/iconUtils.ts similarity index 100% rename from packages/core/src/components/next/AttentionBox/utils/iconUtils.ts rename to packages/core/src/components/AttentionBox/utils/iconUtils.ts diff --git a/packages/core/src/components/Avatar/Avatar.module.scss b/packages/core/src/components/Avatar/Avatar.module.scss index d990390776..9b4dc9f9da 100644 --- a/packages/core/src/components/Avatar/Avatar.module.scss +++ b/packages/core/src/components/Avatar/Avatar.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; @import "./AvatarConstants"; .avatar { @@ -86,8 +86,7 @@ transform: translate(-50%, -50%); display: grid; grid-template-columns: 50% 50%; - margin-left: auto; - margin-right: auto; + margin-inline: auto; height: 110%; width: 110%; } diff --git a/packages/core/src/components/Avatar/Avatar.tsx b/packages/core/src/components/Avatar/Avatar.tsx index 2df10fb4e4..081716a32a 100644 --- a/packages/core/src/components/Avatar/Avatar.tsx +++ b/packages/core/src/components/Avatar/Avatar.tsx @@ -4,15 +4,13 @@ import { ComponentDefaultTestId, getTestId } from "../../tests/test-ids-utils"; import cx from "classnames"; import React, { type AriaRole, useCallback, useMemo } from "react"; import { isNil } from "es-toolkit"; -import { ElementAllowedColor as ElementAllowedColorEnum } from "../../utils/colors-vars-map"; import { type ElementAllowedColor, getElementColor } from "../../types/Colors"; -import { AvatarSize as AvatarSizeEnum, AvatarType as AvatarTypeEnum } from "./AvatarConstants"; import { type AvatarSize, type AvatarType } from "./Avatar.types"; import AvatarBadge, { type AvatarBadgeProps } from "./AvatarBadge"; import AvatarContent from "./AvatarContent"; import { Tooltip, type TooltipProps } from "@vibe/tooltip"; import { ClickableWrapper } from "@vibe/clickable"; -import { type VibeComponentProps, withStaticPropsWithoutForwardRef } from "../../types"; +import { type VibeComponentProps } from "../../types"; import { type SubIcon } from "@vibe/icon"; import styles from "./Avatar.module.scss"; import { ComponentVibeId } from "../../tests/constants"; @@ -33,7 +31,7 @@ export interface AvatarProps extends VibeComponentProps { /** * The ARIA label of the avatar. */ - ariaLabel?: string; + "aria-label"?: string; /** * If true, the tooltip is disabled. */ @@ -81,7 +79,7 @@ export interface AvatarProps extends VibeComponentProps { /** * If true, the avatar is hidden from assistive technologies. */ - ariaHidden?: boolean; + "aria-hidden"?: boolean; /** * If true, the avatar is disabled. */ @@ -127,14 +125,14 @@ const Avatar = ({ icon, text, tooltipProps, - ariaLabel, + "aria-label": ariaLabel, withoutTooltip = false, role, backgroundColor = "chili-blue", square, disabled, tabIndex, - ariaHidden = false, + "aria-hidden": ariaHidden = false, topLeftBadgeProps, topRightBadgeProps, bottomLeftBadgeProps, @@ -228,7 +226,7 @@ const Avatar = ({ isClickable={!!onClick} clickableProps={{ onClick: clickHandler, - tabIndex: "-1", + tabIndex: -1, className: styles.clickableWrapper }} > @@ -254,7 +252,7 @@ const Avatar = ({ src={src} icon={icon} text={text} - ariaLabel={ariaLabel} + aria-label={ariaLabel} role={role} textClassName={textClassName} /> @@ -266,16 +264,4 @@ const Avatar = ({ ); }; -interface AvatarStaticProps { - types: typeof AvatarTypeEnum; - sizes: typeof AvatarSizeEnum; - colors: typeof ElementAllowedColorEnum; - backgroundColors: typeof ElementAllowedColorEnum; -} - -export default withStaticPropsWithoutForwardRef(Avatar, { - types: AvatarTypeEnum, - sizes: AvatarSizeEnum, - colors: ElementAllowedColorEnum, - backgroundColors: ElementAllowedColorEnum -}); +export default Avatar; diff --git a/packages/core/src/components/Avatar/AvatarBadge.tsx b/packages/core/src/components/Avatar/AvatarBadge.tsx index a985d401a1..f666f033de 100644 --- a/packages/core/src/components/Avatar/AvatarBadge.tsx +++ b/packages/core/src/components/Avatar/AvatarBadge.tsx @@ -4,11 +4,11 @@ import { getStyle } from "../../helpers/typesciptCssModulesHelper"; import { ComponentDefaultTestId, getTestId } from "../../tests/test-ids-utils"; import cx from "classnames"; import { Icon, CustomSvgIcon } from "@vibe/icon"; -import { AvatarSize as AvatarSizeEnum } from "./AvatarConstants"; import { type AvatarSize } from "./Avatar.types"; import styles from "./AvatarBadge.module.scss"; -import { type VibeComponentProps, withStaticPropsWithoutForwardRef } from "../../types"; +import { type VibeComponentProps } from "../../types"; import { type SubIcon } from "@vibe/icon"; +import { ClickableWrapper } from "@vibe/clickable"; export interface AvatarBadgeProps extends VibeComponentProps { /** @@ -22,11 +22,15 @@ export interface AvatarBadgeProps extends VibeComponentProps { /** * The tab index of the badge. */ - tabIndex?: string | number; + tabIndex?: number; /** * The size of the badge. */ size?: AvatarSize; + /** + * Callback fired when the badge is clicked. + */ + onClick?: (event: React.MouseEvent | React.KeyboardEvent) => void; } const AvatarBadge = ({ @@ -36,25 +40,35 @@ const AvatarBadge = ({ className, size = "large", id, + onClick, "data-testid": dataTestId, ...otherProps }: AvatarBadgeProps) => { const classNames = cx(getStyle(styles, camelCase("badge--" + size)), className); const testId = dataTestId || getTestId(ComponentDefaultTestId.AVATAR_BADGE, id); + const isClickable = tabIndex === -1 && !!onClick; if (icon) { - return ; + return isClickable ? ( + + + + ) : ( + + ); } + const svgIcon = ; + return src ? ( - + isClickable ? ( + + {svgIcon} + + ) : ( + svgIcon + ) ) : null; }; -interface AvatarBadgeStaticProps { - sizes: typeof AvatarSizeEnum; -} - -export default withStaticPropsWithoutForwardRef(AvatarBadge, { - sizes: AvatarSizeEnum -}); +export default AvatarBadge; diff --git a/packages/core/src/components/Avatar/AvatarConstants.tsx b/packages/core/src/components/Avatar/AvatarConstants.tsx deleted file mode 100644 index fd8941cd7d..0000000000 --- a/packages/core/src/components/Avatar/AvatarConstants.tsx +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @deprecated - */ -export enum AvatarType { - IMG = "img", - ICON = "icon", - TEXT = "text" -} - -/** - * @deprecated - */ -export enum AvatarSize { - XS = "xs", - SMALL = "small", - MEDIUM = "medium", - LARGE = "large" -} diff --git a/packages/core/src/components/Avatar/AvatarContent.tsx b/packages/core/src/components/Avatar/AvatarContent.tsx index 5b618680c6..a80eb8e0c5 100644 --- a/packages/core/src/components/Avatar/AvatarContent.tsx +++ b/packages/core/src/components/Avatar/AvatarContent.tsx @@ -3,10 +3,9 @@ import { getStyle } from "../../helpers/typesciptCssModulesHelper"; import { ComponentDefaultTestId, getTestId } from "../../tests/test-ids-utils"; import cx from "classnames"; import React from "react"; -import { AvatarSize as AvatarSizeEnum, AvatarType as AvatarTypeEnum } from "./AvatarConstants"; import { type AvatarSize, type AvatarType } from "./Avatar.types"; import { Icon, type SubIcon } from "@vibe/icon"; -import { type VibeComponentProps, withStaticPropsWithoutForwardRef } from "../../types"; +import { type VibeComponentProps } from "../../types"; import styles from "./AvatarContent.module.scss"; export interface AvatarContentProps extends VibeComponentProps { @@ -29,7 +28,7 @@ export interface AvatarContentProps extends VibeComponentProps { /** * The label of the content for accessibility. */ - ariaLabel?: string; + "aria-label"?: string; /** * The icon displayed when the type is set to `icon`. */ @@ -49,7 +48,7 @@ const AvatarContent = ({ src, icon, text, - ariaLabel, + "aria-label": ariaLabel, role, size = "large", textClassName = "", @@ -79,7 +78,7 @@ const AvatarContent = ({ aria-label={ariaLabel} // role={role} className={className} - ariaHidden={false} + aria-hidden={false} id={id} data-testid={dataTestId || getTestId(ComponentDefaultTestId.AVATAR_CONTENT, id)} /> @@ -101,12 +100,4 @@ const AvatarContent = ({ } }; -interface AvatarContentStaticProps { - sizes: typeof AvatarSizeEnum; - types: typeof AvatarTypeEnum; -} - -export default withStaticPropsWithoutForwardRef(AvatarContent, { - sizes: AvatarSizeEnum, - types: AvatarTypeEnum -}); +export default AvatarContent; diff --git a/packages/core/src/components/Avatar/__tests__/Avatar.snapshot.test.tsx b/packages/core/src/components/Avatar/__tests__/Avatar.snapshot.test.tsx index 50fc51451d..b722ea6e87 100644 --- a/packages/core/src/components/Avatar/__tests__/Avatar.snapshot.test.tsx +++ b/packages/core/src/components/Avatar/__tests__/Avatar.snapshot.test.tsx @@ -78,7 +78,7 @@ describe("Avatar renders correctly", () => { }); it("renders correctly accessibility props", () => { - const tree = renderer.create().toJSON(); + const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); }); diff --git a/packages/core/src/components/AvatarGroup/AvatarGroup.module.scss b/packages/core/src/components/AvatarGroup/AvatarGroup.module.scss index fbc9c226fb..ceb28f1d91 100644 --- a/packages/core/src/components/AvatarGroup/AvatarGroup.module.scss +++ b/packages/core/src/components/AvatarGroup/AvatarGroup.module.scss @@ -8,5 +8,5 @@ } .avatarContainer { - margin-left: calc(-1 * var(--spacing-small)); + margin-inline-start: calc(-1 * var(--space-8)); } diff --git a/packages/core/src/components/AvatarGroup/AvatarGroupCounter.module.scss b/packages/core/src/components/AvatarGroup/AvatarGroupCounter.module.scss index 393cca766c..b4fb673e6b 100644 --- a/packages/core/src/components/AvatarGroup/AvatarGroupCounter.module.scss +++ b/packages/core/src/components/AvatarGroup/AvatarGroupCounter.module.scss @@ -1,5 +1,5 @@ @use "sass:math"; -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; @import "../../components/Avatar/AvatarConstants"; .counterContainer.counterContainer { @@ -7,7 +7,7 @@ display: flex; justify-content: center; align-items: center; - margin-left: calc(-1 * var(--spacing-small)); + margin-inline-start: calc(-1 * var(--space-8)); z-index: 1; border: 1px solid var(--primary-background-color); @@ -36,7 +36,7 @@ } &.large { @include counterSize($avatar-size-large); - padding: 0 var(--spacing-xs); + padding: 0 var(--space-4); } &.primary { diff --git a/packages/core/src/components/AvatarGroup/AvatarGroupCounter.tsx b/packages/core/src/components/AvatarGroup/AvatarGroupCounter.tsx index c24c9c5fa3..a503648a29 100644 --- a/packages/core/src/components/AvatarGroup/AvatarGroupCounter.tsx +++ b/packages/core/src/components/AvatarGroup/AvatarGroupCounter.tsx @@ -84,7 +84,7 @@ const AvatarGroupCounter: React.FC = ({ count={counterValue} prefix={counterPrefix} maxDigits={counterMaxDigits} - ariaLabel={counterAriaLabel ? counterAriaLabel : `Tab for more ${counterAriaLabelItemsName}`} + aria-label={counterAriaLabel ? counterAriaLabel : `Tab for more ${counterAriaLabelItemsName}`} noAnimation={noAnimation} counterClassName={cx({ [styles.disabled]: disabled })} size={size === "xs" ? "xs" : undefined} @@ -123,23 +123,23 @@ const AvatarGroupCounter: React.FC = ({ component={counterComponent} zIndex={1} className={cx(styles.counterContainer, counterSizeStyle, counterColorStyle)} - ariaLabel={counterAriaLabel ? counterAriaLabel : `${counterValue} additional ${counterAriaLabelItemsName}`} + aria-label={counterAriaLabel ? counterAriaLabel : `${counterValue} additional ${counterAriaLabelItemsName}`} dialogContainerSelector={dialogContainerSelector} > - + {counterTooltipAvatars.map((avatar, index) => { return ( // eslint-disable-next-line react/jsx-key avatarOnClick(event, avatar.props) }} avatarProps={{ ...avatar.props, customSize: AVATAR_GROUP_COUNTER_AVATAR_SIZE, - ariaLabel: "", + "aria-label": "", tabIndex: -1 }} /> diff --git a/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContent.module.scss b/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContent.module.scss index e099daa6b7..354d446ba1 100644 --- a/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContent.module.scss +++ b/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContent.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; .tooltipAvatarItemTitle { white-space: nowrap; @@ -10,11 +10,11 @@ .tooltipContainer { max-height: 300px; - padding-right: var(--spacing-medium); + padding-inline-end: var(--space-16); align-items: flex-start !important; overflow-x: visible; overflow-y: auto; - margin-top: var(--spacing-small); + margin-top: var(--space-8); &:focus, &:focus-visible, &.focus-visible { /* stylelint-disable-line selector-class-pattern */ @@ -24,7 +24,7 @@ .tooltipGridContainer { max-width: 160px; - padding-right: 0; + padding-inline-end: 0; } .scrollableContainer { @include scroller(); diff --git a/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContent.tsx b/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContent.tsx index 263286168b..e670c27e87 100644 --- a/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContent.tsx +++ b/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContent.tsx @@ -1,6 +1,6 @@ import React, { type ReactElement, type Ref, useCallback, useMemo } from "react"; import cx from "classnames"; -import { Flex } from "@vibe/layout"; +import { Flex, type FlexProps } from "@vibe/layout"; import { type AvatarProps } from "../Avatar/Avatar"; import AvatarGroupCounterTooltipContentVirtualizedList from "./AvatarGroupCounterTooltipContentVirtualizedList"; import { avatarRenderer } from "./AvatarGroupCounterTooltipHelper"; @@ -35,7 +35,7 @@ const AvatarGroupCounterTooltipContent: React.FC { const getTooltipContent = useCallback((avatarProps: AvatarProps) => { - return avatarProps?.tooltipProps?.content || avatarProps?.ariaLabel; + return avatarProps?.tooltipProps?.content || avatarProps?.["aria-label"]; }, []); const { avatarItems, displayAsGrid, tooltipContainerAriaLabel } = useMemo(() => { @@ -70,16 +70,16 @@ const AvatarGroupCounterTooltipContent: React.FC{renderedItems}; + return {renderedItems}; }; export default AvatarGroupCounterTooltipContent; diff --git a/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContentVirtualizedList.module.scss b/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContentVirtualizedList.module.scss index bf21442f61..6f6e138576 100644 --- a/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContentVirtualizedList.module.scss +++ b/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipContentVirtualizedList.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; .scrollableContainer { @include scroller(); diff --git a/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipHelper.tsx b/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipHelper.tsx index 87e31e6045..cb1ffb6987 100644 --- a/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipHelper.tsx +++ b/packages/core/src/components/AvatarGroup/AvatarGroupCounterTooltipHelper.tsx @@ -129,18 +129,18 @@ export const avatarRenderer = ( avatarProps.onClick(event, avatarProps.id), tabIndex: "-1" }} + clickableProps={{ onClick: event => avatarProps.onClick(event, avatarProps.id), tabIndex: -1 }} >
- + {!displayAsGrid && ( diff --git a/packages/core/src/components/AvatarGroup/__tests__/AvatarGroup.snapshot.test.jsx b/packages/core/src/components/AvatarGroup/__tests__/AvatarGroup.snapshot.test.tsx similarity index 94% rename from packages/core/src/components/AvatarGroup/__tests__/AvatarGroup.snapshot.test.jsx rename to packages/core/src/components/AvatarGroup/__tests__/AvatarGroup.snapshot.test.tsx index b1602dbf2f..bbbbce85d7 100644 --- a/packages/core/src/components/AvatarGroup/__tests__/AvatarGroup.snapshot.test.jsx +++ b/packages/core/src/components/AvatarGroup/__tests__/AvatarGroup.snapshot.test.tsx @@ -81,12 +81,12 @@ describe("AvatarGroup renders correctly", () => { expect(tree).toMatchSnapshot(); }); - it("renders correctly with counter ariaLabel default-tooltip", () => { + it("renders correctly with counter aria-label default-tooltip", () => { const tree = renderer .create( - - + + ) .toJSON(); diff --git a/packages/core/src/components/AvatarGroup/__tests__/__snapshots__/AvatarGroup.snapshot.test.jsx.snap b/packages/core/src/components/AvatarGroup/__tests__/__snapshots__/AvatarGroup.snapshot.test.jsx.snap index b59293bfdf..4c20e77413 100644 --- a/packages/core/src/components/AvatarGroup/__tests__/__snapshots__/AvatarGroup.snapshot.test.jsx.snap +++ b/packages/core/src/components/AvatarGroup/__tests__/__snapshots__/AvatarGroup.snapshot.test.jsx.snap @@ -90,7 +90,7 @@ exports[`AvatarGroup renders correctly > renders correctly with counter 1`] = `
`; -exports[`AvatarGroup renders correctly > renders correctly with counter ariaLabel default-tooltip 1`] = ` +exports[`AvatarGroup renders correctly > renders correctly with counter aria-label default-tooltip 1`] = `
renders correctly with counter 1`] = ` +
+
+
+
+ + P1 + +
+
+
+
+
+ +
+ + +1 + +
+
+
+
+
+`; + +exports[`AvatarGroup renders correctly > renders correctly with counter aria-label default-tooltip 1`] = ` +
+
+
+
+ + P1 + +
+
+
+
+
+ +
+ + +1 + +
+
+
+
+
+`; + +exports[`AvatarGroup renders correctly > renders correctly with counter default-tooltip 1`] = ` +
+
+
+
+ + P1 + +
+
+
+
+
+ +
+ + +1 + +
+
+
+
+
+`; + +exports[`AvatarGroup renders correctly > renders correctly with custom counter tooltip 1`] = ` +
+
+
+
+ + P1 + +
+
+
+
+
+ +
+ + +1 + +
+
+
+
+
+`; + +exports[`AvatarGroup renders correctly > renders correctly with custom counter value 1`] = ` +
+
+
+
+ + P1 + +
+
+
+
+
+
+ + P2 + +
+
+
+
+`; + +exports[`AvatarGroup renders correctly > renders correctly with dummy class name 1`] = ` +
+
+
+
+ + P1 + +
+
+
+
+`; + +exports[`AvatarGroup renders correctly > renders correctly with large size 1`] = ` +
+
+
+
+ + P1 + +
+
+
+
+
+ +
+ + +1 + +
+
+
+
+
+`; + +exports[`AvatarGroup renders correctly > renders correctly with xs size 1`] = ` +
+
+
+
+ + P1 + +
+
+
+
+
+ +
+ + +1 + +
+
+
+
+
+`; + +exports[`AvatarGroup renders correctly > renders correctly without counter 1`] = ` +
+
+
+
+ + P1 + +
+
+
+
+
+
+ + P2 + +
+
+
+
+`; + +exports[`AvatarGroup renders correctly > with empty props 1`] = `null`; diff --git a/packages/core/src/components/Badge/Badge.module.scss b/packages/core/src/components/Badge/Badge.module.scss index 5688afa716..1b868837c4 100644 --- a/packages/core/src/components/Badge/Badge.module.scss +++ b/packages/core/src/components/Badge/Badge.module.scss @@ -16,19 +16,19 @@ $embeddedPosition: 15%; &.topEnd { &.rectangular { top: 0; - right: 0; + inset-inline-end: 0; translate: 50% -50%; } &.outside { top: 0; - right: -2px; + inset-inline-end: -2px; translate: 100% 0; } &.circular { top: $embeddedPosition; - right: $embeddedPosition; + inset-inline-end: $embeddedPosition; translate: 50% -50%; } } @@ -36,19 +36,19 @@ $embeddedPosition: 15%; &.topStart { &.rectangular { top: 0; - left: 0; + inset-inline-start: 0; translate: -50% -50%; } &.outside { top: 0; - left: -2px; + inset-inline-start: -2px; translate: -100% 0; } &.circular { top: $embeddedPosition; - left: $embeddedPosition; + inset-inline-start: $embeddedPosition; translate: -50% -50%; } } @@ -56,19 +56,19 @@ $embeddedPosition: 15%; &.bottomEnd { &.rectangular { bottom: 0; - right: 0; + inset-inline-end: 0; translate: 50% 50%; } &.outside { bottom: 0; - right: -2px; + inset-inline-end: -2px; translate: 100% 0; } &.circular { bottom: $embeddedPosition; - right: $embeddedPosition; + inset-inline-end: $embeddedPosition; translate: 50% 50%; } } @@ -76,19 +76,19 @@ $embeddedPosition: 15%; &.bottomStart { &.rectangular { bottom: 0; - left: 0; + inset-inline-start: 0; translate: -50% 50%; } &.outside { bottom: 0; - left: -2px; + inset-inline-start: -2px; translate: -100% 0; } &.circular { bottom: $embeddedPosition; - left: $embeddedPosition; + inset-inline-start: $embeddedPosition; translate: -50% 50%; } } diff --git a/packages/core/src/components/Badge/Badge.tsx b/packages/core/src/components/Badge/Badge.tsx index 08df64b3be..931c2370c3 100644 --- a/packages/core/src/components/Badge/Badge.tsx +++ b/packages/core/src/components/Badge/Badge.tsx @@ -4,19 +4,14 @@ import { camelCase } from "es-toolkit"; import useMergeRef from "../../hooks/useMergeRef"; import { getTestId } from "../../tests/test-ids-utils"; import { ComponentDefaultTestId, ComponentVibeId } from "../../tests/constants"; -import { - BadgeAlignments as BadgeAlignmentsEnum, - BadgeAnchor as BadgeAnchorEnum, - BadgeType as BadgeTypeEnum -} from "./BadgeConstants"; import { type BadgeAlignments, type BadgeAnchor, type BadgeType } from "./Badge.types"; import Indicator, { type IndicatorProps } from "./Indicator/Indicator"; import Counter, { type CounterProps } from "../Counter/Counter"; import { getStyle } from "../../helpers/typesciptCssModulesHelper"; -import { type IndicatorColor } from "./Indicator/IndicatorConstants"; -import { type CounterColor } from "../Counter/CounterConstants"; +import { type IndicatorColor } from "./Indicator/Indicator.types"; +import { type CounterColor } from "../Counter/Counter.types"; import styles from "./Badge.module.scss"; -import { type VibeComponentProps, withStaticProps } from "../../types"; +import { type VibeComponentProps } from "../../types"; export interface BadgeBaseProps extends VibeComponentProps { /** @@ -88,14 +83,4 @@ const Badge = forwardRef( } ); -interface BadgeStaticProps { - types: typeof BadgeTypeEnum; - alignments: typeof BadgeAlignmentsEnum; - anchors: typeof BadgeAnchorEnum; -} - -export default withStaticProps(Badge, { - types: BadgeTypeEnum, - alignments: BadgeAlignmentsEnum, - anchors: BadgeAnchorEnum -}); +export default Badge; diff --git a/packages/core/src/components/Badge/BadgeConstants.ts b/packages/core/src/components/Badge/BadgeConstants.ts deleted file mode 100644 index ce3fdd79c5..0000000000 --- a/packages/core/src/components/Badge/BadgeConstants.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { DialogPositionEnum as DialogPosition } from "@vibe/dialog"; - -/** - * @deprecated - */ -export enum BadgeType { - INDICATOR = "indicator", - COUNTER = "counter" -} - -/** - * @deprecated - */ -export enum BadgeAnchor { - TOP_START = DialogPosition.TOP_START, - TOP_END = DialogPosition.TOP_END, - BOTTOM_START = DialogPosition.BOTTOM_START, - BOTTOM_END = DialogPosition.BOTTOM_END -} - -/** - * @deprecated - */ -export enum BadgeAlignments { - RECTANGULAR = "rectangular", - OUTSIDE = "outside", - CIRCULAR = "circular" -} diff --git a/packages/core/src/components/Badge/Indicator/Indicator.tsx b/packages/core/src/components/Badge/Indicator/Indicator.tsx index 35cacf71ae..9d9874d3c0 100644 --- a/packages/core/src/components/Badge/Indicator/Indicator.tsx +++ b/packages/core/src/components/Badge/Indicator/Indicator.tsx @@ -1,10 +1,9 @@ import React from "react"; import cx from "classnames"; import styles from "./Indicator.module.scss"; -import { IndicatorColor as IndicatorColorEnum } from "./IndicatorConstants"; import { type IndicatorColor } from "./Indicator.types"; import { ComponentDefaultTestId } from "../../../tests/constants"; -import { type VibeComponentProps, withStaticPropsWithoutForwardRef } from "../../../types"; +import { type VibeComponentProps } from "../../../types"; import { getTestId } from "../../../tests/test-ids-utils"; export interface IndicatorProps extends VibeComponentProps { @@ -23,10 +22,4 @@ const Indicator = ({ color = "notification", className, id, "data-testid": dataT ); }; -interface IndicatorStaticProps { - colors: typeof IndicatorColorEnum; -} - -export default withStaticPropsWithoutForwardRef(Indicator, { - colors: IndicatorColorEnum -}); +export default Indicator; diff --git a/packages/core/src/components/Badge/Indicator/IndicatorConstants.ts b/packages/core/src/components/Badge/Indicator/IndicatorConstants.ts deleted file mode 100644 index da42c85de7..0000000000 --- a/packages/core/src/components/Badge/Indicator/IndicatorConstants.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * @deprecated - */ -export enum IndicatorColor { - PRIMARY = "primary", - NOTIFICATION = "notification" -} diff --git a/packages/core/src/components/Badge/Indicator/__tests__/Indicator.snapshot.test.tsx b/packages/core/src/components/Badge/Indicator/__tests__/Indicator.snapshot.test.tsx index bcd1af8e57..cd5c85c5d4 100644 --- a/packages/core/src/components/Badge/Indicator/__tests__/Indicator.snapshot.test.tsx +++ b/packages/core/src/components/Badge/Indicator/__tests__/Indicator.snapshot.test.tsx @@ -2,7 +2,6 @@ import { describe, it, expect } from "vitest"; import React from "react"; import renderer from "react-test-renderer"; import Indicator from "../Indicator"; -import { IndicatorColor } from "../IndicatorConstants"; describe("Indicator", () => { it("renders correctly with empty props", () => { @@ -11,7 +10,7 @@ describe("Indicator", () => { }); it("renders correctly with a custom color", () => { - const tree = renderer.create().toJSON(); + const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); }); diff --git a/packages/core/src/components/BaseItem/BaseItem.module.scss b/packages/core/src/components/BaseItem/BaseItem.module.scss index a9e6459b4b..024a56bddd 100644 --- a/packages/core/src/components/BaseItem/BaseItem.module.scss +++ b/packages/core/src/components/BaseItem/BaseItem.module.scss @@ -3,7 +3,7 @@ .wrapper { display: flex; align-items: center; - gap: var(--spacing-xs); + gap: var(--space-4); border-radius: var(--border-radius-small); color: var(--primary-text-color); cursor: pointer; @@ -33,7 +33,7 @@ } &.small { - padding: 6px var(--spacing-small); + padding: 6px var(--space-8); } &.medium { @@ -54,7 +54,7 @@ } .endElement { - padding-inline-start: var(--spacing-small); + padding-inline-start: var(--space-8); margin-inline-start: auto; display: flex; } diff --git a/packages/core/src/components/BaseItem/utils.tsx b/packages/core/src/components/BaseItem/utils.tsx index cf18ee06d1..d8de792372 100644 --- a/packages/core/src/components/BaseItem/utils.tsx +++ b/packages/core/src/components/BaseItem/utils.tsx @@ -25,7 +25,7 @@ export function renderSideElement( ); case "icon": - return ; + return ; case "indent": return
; diff --git a/packages/core/src/components/BaseList/BaseList.module.scss b/packages/core/src/components/BaseList/BaseList.module.scss index 8f3c216f5c..156c8fbed6 100644 --- a/packages/core/src/components/BaseList/BaseList.module.scss +++ b/packages/core/src/components/BaseList/BaseList.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; @import "../../styles/states"; @import "../../styles/typography"; diff --git a/packages/core/src/components/BaseList/BaseList.tsx b/packages/core/src/components/BaseList/BaseList.tsx index eef685f2ec..95f2c04e95 100644 --- a/packages/core/src/components/BaseList/BaseList.tsx +++ b/packages/core/src/components/BaseList/BaseList.tsx @@ -21,8 +21,8 @@ const BaseList = forwardRef( id, as = "ul", children, - ariaLabel, - ariaDescribedBy, + "aria-label": ariaLabel, + "aria-describedby": ariaDescribedBy, "aria-controls": ariaControls, role = "listbox", size = "medium", diff --git a/packages/core/src/components/BaseList/BaseList.types.ts b/packages/core/src/components/BaseList/BaseList.types.ts index a12b30fea1..76433450fb 100644 --- a/packages/core/src/components/BaseList/BaseList.types.ts +++ b/packages/core/src/components/BaseList/BaseList.types.ts @@ -21,13 +21,13 @@ export interface BaseListProps extends Omit, " */ children?: ReactElement | ReactElement[]; /** - * The ARIA label describing the list. Required for accessibility when ariaDescribedBy is not provided. + * The ARIA label describing the list. Required for accessibility when aria-describedby is not provided. */ - ariaLabel?: string; + "aria-label"?: string; /** * The ID of an element that describes the list. */ - ariaDescribedBy?: string; + "aria-describedby"?: string; /** * The ID of an element controlled by the list. */ diff --git a/packages/core/src/components/BaseList/__tests__/BaseList.test.tsx b/packages/core/src/components/BaseList/__tests__/BaseList.test.tsx index d6d3c5c50f..5592c89c56 100644 --- a/packages/core/src/components/BaseList/__tests__/BaseList.test.tsx +++ b/packages/core/src/components/BaseList/__tests__/BaseList.test.tsx @@ -8,7 +8,7 @@ import { type BaseListProps } from "../BaseList.types"; function renderBaseList(props?: Partial> & { id?: string }) { return render( - + @@ -37,7 +37,7 @@ describe("BaseList", () => { it("should render with custom element type", () => { render( - + ); @@ -48,7 +48,7 @@ describe("BaseList", () => { describe("accessibility", () => { it("should have correct aria-label", () => { - renderBaseList({ ariaLabel: "Custom Label" }); + renderBaseList({ "aria-label": "Custom Label" }); expect(screen.getByLabelText("Custom Label")).toBeInTheDocument(); }); @@ -61,7 +61,7 @@ describe("BaseList", () => { render( <> List description - + @@ -195,7 +195,7 @@ describe("BaseList", () => { describe("with selected items", () => { it("should focus on selected item initially", () => { render( - + @@ -211,7 +211,7 @@ describe("BaseList", () => { describe("with disabled items", () => { it("should support disabled items", () => { render( - + @@ -232,7 +232,7 @@ describe("BaseList", () => { it("should assign role='menuitem' to children when list role is 'menu'", () => { render( - + @@ -243,7 +243,7 @@ describe("BaseList", () => { it("should allow explicit role override on individual items", () => { render( - + diff --git a/packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/BreadcrumbContent/BreadcrumbContent.module.scss b/packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/BreadcrumbContent/BreadcrumbContent.module.scss index 39e179671f..de056f1a16 100644 --- a/packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/BreadcrumbContent/BreadcrumbContent.module.scss +++ b/packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/BreadcrumbContent/BreadcrumbContent.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; @import "../../../../styles/states"; .breadcrumbContent { diff --git a/packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/__tests__/BreadcrumbItem.snapshot.test.jsx b/packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/__tests__/BreadcrumbItem.snapshot.test.tsx similarity index 100% rename from packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/__tests__/BreadcrumbItem.snapshot.test.jsx rename to packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/__tests__/BreadcrumbItem.snapshot.test.tsx diff --git a/packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/__tests__/BreadcrumbItem.test.jsx b/packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/__tests__/BreadcrumbItem.test.tsx similarity index 100% rename from packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/__tests__/BreadcrumbItem.test.jsx rename to packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/__tests__/BreadcrumbItem.test.tsx diff --git a/packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/__tests__/__snapshots__/BreadcrumbItem.snapshot.test.tsx.snap b/packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/__tests__/__snapshots__/BreadcrumbItem.snapshot.test.tsx.snap new file mode 100644 index 0000000000..d977871ed4 --- /dev/null +++ b/packages/core/src/components/BreadcrumbsBar/BreadcrumbItem/__tests__/__snapshots__/BreadcrumbItem.snapshot.test.tsx.snap @@ -0,0 +1,185 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`BreadcrumbsItem renders correctly > when disabled item 1`] = ` + +`; + +exports[`BreadcrumbsItem renders correctly > with className 1`] = ` + +`; + +exports[`BreadcrumbsItem renders correctly > with current item 1`] = ` + +`; + +exports[`BreadcrumbsItem renders correctly > with empty props 1`] = ` + +`; + +exports[`BreadcrumbsItem renders correctly > with icon 1`] = ` + +`; + +exports[`BreadcrumbsItem renders correctly > with text 1`] = ` + +`; diff --git a/packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/BreadcrumbMenu.tsx b/packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/BreadcrumbMenu.tsx index b89f7cc297..4a8eeafd9b 100644 --- a/packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/BreadcrumbMenu.tsx +++ b/packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/BreadcrumbMenu.tsx @@ -23,12 +23,12 @@ const BreadcrumbMenu = forwardRef( id={menuButtonId} size={"xxs"} closeMenuOnItemClick - ariaLabel={"Hidden breadcrumbs menu"} + aria-label={"Hidden breadcrumbs menu"} data-testid={dataTestId || getTestId(ComponentDefaultTestId.BREADCRUMB_MENU, id)} - ariaControls={menuId} + aria-controls={menuId} ref={ref} > - + {children} diff --git a/packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/__tests__/BreadcrumbMenu.snapshot.test.jsx b/packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/__tests__/BreadcrumbMenu.snapshot.test.tsx similarity index 100% rename from packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/__tests__/BreadcrumbMenu.snapshot.test.jsx rename to packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/__tests__/BreadcrumbMenu.snapshot.test.tsx diff --git a/packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/__tests__/BreadcrumbMenu.test.jsx b/packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/__tests__/BreadcrumbMenu.test.tsx similarity index 100% rename from packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/__tests__/BreadcrumbMenu.test.jsx rename to packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/__tests__/BreadcrumbMenu.test.tsx diff --git a/packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/__tests__/__snapshots__/BreadcrumbMenu.snapshot.test.tsx.snap b/packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/__tests__/__snapshots__/BreadcrumbMenu.snapshot.test.tsx.snap new file mode 100644 index 0000000000..f4c681120c --- /dev/null +++ b/packages/core/src/components/BreadcrumbsBar/BreadcrumbMenu/__tests__/__snapshots__/BreadcrumbMenu.snapshot.test.tsx.snap @@ -0,0 +1,127 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`BreadcrumbMenu > renders correctly with custom className 1`] = ` +
  • + +
  • +`; + +exports[`BreadcrumbMenu > renders correctly with empty props 1`] = ` +
  • + +
  • +`; + +exports[`BreadcrumbMenu > renders correctly with various MenuItem props 1`] = ` +
  • + +
  • +`; diff --git a/packages/core/src/components/BreadcrumbsBar/BreadcrumbsBar.tsx b/packages/core/src/components/BreadcrumbsBar/BreadcrumbsBar.tsx index 4e76982a95..56e9010b0c 100644 --- a/packages/core/src/components/BreadcrumbsBar/BreadcrumbsBar.tsx +++ b/packages/core/src/components/BreadcrumbsBar/BreadcrumbsBar.tsx @@ -2,10 +2,9 @@ import React, { type ReactElement } from "react"; import cx from "classnames"; import { ComponentDefaultTestId, getTestId } from "../../tests/test-ids-utils"; import { NavigationChevronRight } from "@vibe/icons"; -import { BreadcrumbsBarType as BreadcrumbsBarTypeEnum } from "./BreadcrumbsConstants"; import { type BreadcrumbsBarType } from "./Breadcrumbs.types"; import { type BreadcrumbItemProps } from "./BreadcrumbItem/BreadcrumbItem"; -import { type VibeComponentProps, withStaticPropsWithoutForwardRef } from "../../types"; +import { type VibeComponentProps } from "../../types"; import styles from "./BreadcrumbsBar.module.scss"; import { type BreadcrumbMenuProps } from "./BreadcrumbMenu/BreadcrumbMenu"; import { ComponentVibeId } from "../../tests/constants"; @@ -54,10 +53,4 @@ const BreadcrumbsBar = ({ ); -interface BreadcrumbsBarStaticProps { - types: typeof BreadcrumbsBarTypeEnum; -} - -export default withStaticPropsWithoutForwardRef(BreadcrumbsBar, { - types: BreadcrumbsBarTypeEnum -}); +export default BreadcrumbsBar; diff --git a/packages/core/src/components/BreadcrumbsBar/BreadcrumbsConstants.ts b/packages/core/src/components/BreadcrumbsBar/BreadcrumbsConstants.ts deleted file mode 100644 index 52fa25791a..0000000000 --- a/packages/core/src/components/BreadcrumbsBar/BreadcrumbsConstants.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum BreadcrumbsBarType { - NAVIGATION = "navigation", - INDICATION = "indication" -} diff --git a/packages/core/src/components/BreadcrumbsBar/__tests__/BreadcrumbsBar.snapshot.test.jsx b/packages/core/src/components/BreadcrumbsBar/__tests__/BreadcrumbsBar.snapshot.test.tsx similarity index 95% rename from packages/core/src/components/BreadcrumbsBar/__tests__/BreadcrumbsBar.snapshot.test.jsx rename to packages/core/src/components/BreadcrumbsBar/__tests__/BreadcrumbsBar.snapshot.test.tsx index 6a42c8adf1..8dba17ceb0 100644 --- a/packages/core/src/components/BreadcrumbsBar/__tests__/BreadcrumbsBar.snapshot.test.jsx +++ b/packages/core/src/components/BreadcrumbsBar/__tests__/BreadcrumbsBar.snapshot.test.tsx @@ -13,7 +13,7 @@ describe("BreadcrumbsBar renders correctly", () => { }); it("with bar type", () => { - Object.values(BreadcrumbsBar.types).forEach(type => { + Object.values(["default", "divider", "hidden"]).forEach(type => { const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); diff --git a/packages/core/src/components/BreadcrumbsBar/__tests__/__snapshots__/BreadcrumbsBar.snapshot.test.jsx.snap b/packages/core/src/components/BreadcrumbsBar/__tests__/__snapshots__/BreadcrumbsBar.snapshot.test.jsx.snap index bd2741b5f9..eaf7e53057 100644 --- a/packages/core/src/components/BreadcrumbsBar/__tests__/__snapshots__/BreadcrumbsBar.snapshot.test.jsx.snap +++ b/packages/core/src/components/BreadcrumbsBar/__tests__/__snapshots__/BreadcrumbsBar.snapshot.test.jsx.snap @@ -22,6 +22,17 @@ exports[`BreadcrumbsBar renders correctly > with bar type 2`] = ` `; +exports[`BreadcrumbsBar renders correctly > with bar type 3`] = ` + +`; + exports[`BreadcrumbsBar renders correctly > with breadcrumb menu 1`] = `
    {label === false ? null : ( diff --git a/packages/core/src/components/Checkbox/__tests__/Checkbox.snapshot.test.tsx b/packages/core/src/components/Checkbox/__tests__/Checkbox.snapshot.test.tsx index 3f3a7151f5..25a55e185a 100644 --- a/packages/core/src/components/Checkbox/__tests__/Checkbox.snapshot.test.tsx +++ b/packages/core/src/components/Checkbox/__tests__/Checkbox.snapshot.test.tsx @@ -51,8 +51,8 @@ describe("Checkbox renders correctly", () => { expect(tree).toMatchSnapshot(); }); - it("with ariaLabelledBy", () => { - const tree = renderer.create().toJSON(); + it("with aria-labelledby", () => { + const tree = renderer.create().toJSON(); expect(tree).toMatchSnapshot(); }); diff --git a/packages/core/src/components/Checkbox/__tests__/__snapshots__/Checkbox.snapshot.test.tsx.snap b/packages/core/src/components/Checkbox/__tests__/__snapshots__/Checkbox.snapshot.test.tsx.snap index 32bb24b283..1638518bec 100644 --- a/packages/core/src/components/Checkbox/__tests__/__snapshots__/Checkbox.snapshot.test.tsx.snap +++ b/packages/core/src/components/Checkbox/__tests__/__snapshots__/Checkbox.snapshot.test.tsx.snap @@ -250,7 +250,7 @@ exports[`Checkbox renders correctly > when interminate 1`] = ` `; -exports[`Checkbox renders correctly > with ariaLabelledBy 1`] = ` +exports[`Checkbox renders correctly > with aria-labelledby 1`] = `
    } > diff --git a/packages/docs/src/pages/components/Steps/Steps.stories.tsx b/packages/docs/src/pages/components/Steps/Steps.stories.tsx index e4a0559045..7a5b1b743f 100644 --- a/packages/docs/src/pages/components/Steps/Steps.stories.tsx +++ b/packages/docs/src/pages/components/Steps/Steps.stories.tsx @@ -48,7 +48,6 @@ const NavigableStepsTemplate = (args: StepsProps) => { }} {...args} onChangeActiveStep={onChangeActiveStep} - onFinish={() => {}} /> ); }; @@ -132,7 +131,6 @@ export const NavigableSteps = { nextButtonProps={{ onClick: stepNext }} - onFinish={() => {}} /> ); @@ -185,7 +183,6 @@ export const StepsInsideATipseen = { size: "small", onClick: stepNext }} - onFinish={() => {}} /> } > diff --git a/packages/docs/src/pages/components/Table/Table.stories.helpers.tsx b/packages/docs/src/pages/components/Table/Table.stories.helpers.tsx index b58f9129eb..f51f1b2316 100644 --- a/packages/docs/src/pages/components/Table/Table.stories.helpers.tsx +++ b/packages/docs/src/pages/components/Table/Table.stories.helpers.tsx @@ -479,7 +479,7 @@ export const TableAvatar = ({ text }: { text: string }) => ( .join("")} customSize={24} size="small" - ariaLabel={text} + aria-label={text} backgroundColor="dark_purple" /> ); diff --git a/packages/docs/src/pages/components/Tabs/Tabs.stories.helpers.tsx b/packages/docs/src/pages/components/Tabs/Tabs.stories.helpers.tsx index 185207c7ab..5a08f1eab6 100644 --- a/packages/docs/src/pages/components/Tabs/Tabs.stories.helpers.tsx +++ b/packages/docs/src/pages/components/Tabs/Tabs.stories.helpers.tsx @@ -4,7 +4,7 @@ import { StorybookLink, Tip } from "vibe-storybook-components"; export const TipButtonGroup = () => ( Use tabs to navigate between different contents, if switching between states within the same content use{" "} - + Button group {" "} instead diff --git a/packages/docs/src/pages/components/Text/Text.stories.helpers.tsx b/packages/docs/src/pages/components/Text/Text.stories.helpers.tsx index 4a387b8bb9..d0699f61cc 100644 --- a/packages/docs/src/pages/components/Text/Text.stories.helpers.tsx +++ b/packages/docs/src/pages/components/Text/Text.stories.helpers.tsx @@ -4,7 +4,7 @@ import { StorybookLink, Tip } from "vibe-storybook-components"; export const TipHeading = () => ( Check out our{" "} - + Heading {" "} component for text headlines. @@ -14,7 +14,7 @@ export const TipHeading = () => ( export const TipLink = () => ( If you need to place a link outside of the textual flow, please use our{" "} - + Link {" "} component. diff --git a/packages/docs/src/pages/components/Text/Text.stories.tsx b/packages/docs/src/pages/components/Text/Text.stories.tsx index 2a95c245e1..a93edc66e2 100644 --- a/packages/docs/src/pages/components/Text/Text.stories.tsx +++ b/packages/docs/src/pages/components/Text/Text.stories.tsx @@ -173,7 +173,7 @@ export const LinksInsideRunningText = { This is the story of a{" "} - + link {" "} inside running text. @@ -181,7 +181,7 @@ export const LinksInsideRunningText = { This is the story of a{" "} - + link {" "} inside running text on a primary color @@ -190,7 +190,7 @@ export const LinksInsideRunningText = { This is the story of a{" "} - + link {" "} inside running text on an inverted color @@ -199,7 +199,7 @@ export const LinksInsideRunningText = { This is the story of a{" "} - + link {" "} inside running text with fixed light color @@ -208,7 +208,7 @@ export const LinksInsideRunningText = { This is the story of a{" "} - + link {" "} inside running text with fixed dark color diff --git a/packages/docs/src/pages/components/TextField/TextField.mdx b/packages/docs/src/pages/components/TextField/TextField.mdx index d013266923..570fb7f3ae 100644 --- a/packages/docs/src/pages/components/TextField/TextField.mdx +++ b/packages/docs/src/pages/components/TextField/TextField.mdx @@ -55,8 +55,8 @@ import { TextField } from "@vibe/core"; and correct validation issues. , <> - When using icons, provide meaningful labels through iconsNames.primary and{" "} - iconsNames.secondary props for screen reader users. + When using icons, provide meaningful labels through iconLabel and{" "} + secondaryIconLabel props for screen reader users. , <> For search inputs, use type='search' and provide searchResultsContainerId to properly diff --git a/packages/docs/src/pages/components/TextField/TextField.stories.tsx b/packages/docs/src/pages/components/TextField/TextField.stories.tsx index 9f89c26745..f561e9aa25 100644 --- a/packages/docs/src/pages/components/TextField/TextField.stories.tsx +++ b/packages/docs/src/pages/components/TextField/TextField.stories.tsx @@ -8,7 +8,7 @@ type Story = StoryObj; const metaSettings = createStoryMetaSettingsDecorator({ component: TextField, - iconPropNamesArray: ["secondaryIconName", "iconName", "labelIconName"] + iconPropNamesArray: ["secondaryIconName", "icon", "labelIconName"] }); export default { @@ -27,7 +27,7 @@ export const Overview: Story = { args: { id: "overview-textfield", title: "Name", - iconName: Show, + icon: Show, validation: { text: "Helper text" @@ -70,7 +70,7 @@ export const States: Story = { id="states-with-icon" inputAriaLabel="Text field with icon" placeholder="With icon" - iconName={Email} + icon={Email} size="medium" /> {}} size="medium" /> @@ -92,7 +92,7 @@ export const States: Story = { validation={{ status: "success" }} - iconName={Check} + icon={Check} size="medium" /> diff --git a/packages/docs/src/pages/components/ThemeProvider/ThemeProvider.mdx b/packages/docs/src/pages/components/ThemeProvider/ThemeProvider.mdx index ee929ae4ec..0510447c3e 100644 --- a/packages/docs/src/pages/components/ThemeProvider/ThemeProvider.mdx +++ b/packages/docs/src/pages/components/ThemeProvider/ThemeProvider.mdx @@ -13,10 +13,6 @@ import { Frame } from "vibe-storybook-components"; # ThemeProvider -

    - -

    - This component helps to customize the colors of library's components by overriding a specific css variables with a values provided within themeConfig object. There are 2 levels of theming: **system theme** and **product theme**. System theme is a one of a 3 predefined themes: light(.light-app-theme), dark(.dark-app-theme) and black(.black-app-theme). diff --git a/packages/docs/src/pages/components/ThemeProvider/ThemeProvider.stories.tsx b/packages/docs/src/pages/components/ThemeProvider/ThemeProvider.stories.tsx index 685ac7dbd0..b1194c89b9 100644 --- a/packages/docs/src/pages/components/ThemeProvider/ThemeProvider.stories.tsx +++ b/packages/docs/src/pages/components/ThemeProvider/ThemeProvider.stories.tsx @@ -18,7 +18,7 @@ const metaSettings = createStoryMetaSettingsDecorator({ }); export default { - title: "Theming/ThemeProvider [alpha]", + title: "Theming/ThemeProvider", component: ThemeProvider, argTypes: metaSettings.argTypes, decorators: metaSettings.decorators diff --git a/packages/docs/src/pages/components/Tipseen/Tipseen.mdx b/packages/docs/src/pages/components/Tipseen/Tipseen.mdx index 4845c74384..061d78b6b0 100644 --- a/packages/docs/src/pages/components/Tipseen/Tipseen.mdx +++ b/packages/docs/src/pages/components/Tipseen/Tipseen.mdx @@ -3,7 +3,7 @@ import { Link } from "vibe-storybook-components"; import { Tipseen, TipseenContent, TipseenWizard } from "@vibe/core"; import { Icon } from "@vibe/icon"; import { Board, BoardPrivate } from "@vibe/icons"; -import { modifiers, TipCheckYourself, wizardContent } from "./Tipseen.stories.helpers"; +import { TipCheckYourself, wizardContent } from "./Tipseen.stories.helpers"; import * as TipseenStories from "./Tipseen.stories"; import do1 from "./assets/do1.png"; import do2 from "./assets/do2.png"; @@ -23,7 +23,7 @@ Tipseen is a virtual unboxing experience that helps users get started with the s ### Import ```js -import { Tipseen, TipseenMedia, TipseenImage, TipseenContent, TipseenWizard } from "@vibe/core"; +import { Tipseen, TipseenMedia, TipseenContent, TipseenWizard } from "@vibe/core"; ``` ## Props diff --git a/packages/docs/src/pages/components/Tipseen/Tipseen.stories.helpers.tsx b/packages/docs/src/pages/components/Tipseen/Tipseen.stories.helpers.tsx index aa71f67e6b..8fa1e4acf2 100644 --- a/packages/docs/src/pages/components/Tipseen/Tipseen.stories.helpers.tsx +++ b/packages/docs/src/pages/components/Tipseen/Tipseen.stories.helpers.tsx @@ -1,15 +1,6 @@ import React from "react"; import { StorybookLink, Tip } from "vibe-storybook-components"; -export const modifiers = [ - { - name: "preventOverflow", - options: { - mainAxis: false - } - } -]; - export const wizardContent = [
    Message for the user will appear here, to give more information about the feature.
    ,
    Message for the user will appear here, to give more information about the feature.
    , @@ -21,11 +12,11 @@ export const wizardContent = [ export const TipCheckYourself = () => ( If you need to provide additional information about a component, use the{" "} - + Tooltip {" "} or{" "} - + Attention box. diff --git a/packages/docs/src/pages/components/Tipseen/Tipseen.stories.tsx b/packages/docs/src/pages/components/Tipseen/Tipseen.stories.tsx index dad76e34be..50d1a8c0fb 100644 --- a/packages/docs/src/pages/components/Tipseen/Tipseen.stories.tsx +++ b/packages/docs/src/pages/components/Tipseen/Tipseen.stories.tsx @@ -6,14 +6,18 @@ import { TipseenContent, type TipseenContentProps, TipseenWizard, - TipseenImage, TipseenMedia, Flex } from "@vibe/core"; +import { shift, flip } from "@floating-ui/react-dom"; import picture from "./assets/picture.svg"; import video from "./assets/video.mp4"; import { createStoryMetaSettingsDecorator } from "../../../utils/createStoryMetaSettingsDecorator"; +// Middleware to prevent the tipseen from being displaced when the user scrolls the story. +// Not needed in real implementations. +const storyMiddleware = [shift({ mainAxis: false }), flip({ fallbackPlacements: [] })]; + const metaSettings = createStoryMetaSettingsDecorator({ component: Tipseen }); @@ -23,7 +27,6 @@ export default { component: Tipseen, subcomponents: { TipseenMedia, - TipseenImage, TipseenContent, TipseenWizard }, @@ -34,16 +37,7 @@ export default { const tipseenTemplate = ({ title, children, position, ...otherArgs }: TipseenProps & TipseenContentProps) => { return ( {children}} {...otherArgs} @@ -77,16 +71,7 @@ export const Colors: StoryObj = { @@ -98,16 +83,7 @@ export const Colors: StoryObj = { = { return ( = { return ( - + + + } @@ -221,17 +181,8 @@ export const TipseenWithCustomMedia: StoryObj = { render: () => { return ( @@ -275,7 +226,9 @@ export const FloatingTipseen: StoryObj = { floating content={ <> - + + + Message for the user will appear here, to give more information about the feature. diff --git a/packages/docs/src/pages/components/Toast/Toast.stories.helpers.tsx b/packages/docs/src/pages/components/Toast/Toast.stories.helpers.tsx index 62a1337e88..e66f6d6c97 100644 --- a/packages/docs/src/pages/components/Toast/Toast.stories.helpers.tsx +++ b/packages/docs/src/pages/components/Toast/Toast.stories.helpers.tsx @@ -4,7 +4,7 @@ import { StorybookLink, Tip } from "vibe-storybook-components"; export const TipAlertBanner = () => ( Need to inform the user about a systemโ€™s action? Use an{" "} - + AlertBanner {" "} instead. diff --git a/packages/docs/src/pages/components/Toast/Toast.stories.tsx b/packages/docs/src/pages/components/Toast/Toast.stories.tsx index fab28ecedc..8560497f63 100644 --- a/packages/docs/src/pages/components/Toast/Toast.stories.tsx +++ b/packages/docs/src/pages/components/Toast/Toast.stories.tsx @@ -179,17 +179,17 @@ export const Animation = { diff --git a/packages/docs/src/pages/components/Toggle/Toggle.stories.helpers.tsx b/packages/docs/src/pages/components/Toggle/Toggle.stories.helpers.tsx index deb9c35edd..311e178dad 100644 --- a/packages/docs/src/pages/components/Toggle/Toggle.stories.helpers.tsx +++ b/packages/docs/src/pages/components/Toggle/Toggle.stories.helpers.tsx @@ -4,11 +4,11 @@ import { StorybookLink, Tip } from "vibe-storybook-components"; export const TipOtherComponents = () => ( If the user needs to choose an item from a set of options, use{" "} - + Radio button {" "} or{" "} - + Checkboxes {" "} instead. diff --git a/packages/docs/src/pages/components/Toggle/Toggle.stories.tsx b/packages/docs/src/pages/components/Toggle/Toggle.stories.tsx index c2acf09d31..a162c126e0 100644 --- a/packages/docs/src/pages/components/Toggle/Toggle.stories.tsx +++ b/packages/docs/src/pages/components/Toggle/Toggle.stories.tsx @@ -24,7 +24,7 @@ export const Overview: Story = { render: toggleTemplate.bind({}), args: { id: "overview-toggle", - ariaLabel: "Toggle option" + "aria-label": "Toggle option" }, parameters: { docs: { @@ -38,8 +38,8 @@ export const Overview: Story = { export const States: Story = { render: () => ( - - + + ), parameters: { @@ -54,8 +54,8 @@ export const States: Story = { export const Size: Story = { render: () => ( - - + + ), parameters: { @@ -70,8 +70,8 @@ export const Size: Story = { export const Disabled: Story = { render: () => ( - - + + ), parameters: { @@ -87,7 +87,7 @@ export const TurnOnOffAnAutomation: Story = { render: () => ( Board automations - + ), name: "Turn on/ off an automation" diff --git a/packages/docs/src/pages/components/Tooltip/Tooltip.mdx b/packages/docs/src/pages/components/Tooltip/Tooltip.mdx index e31c97137b..402f6384d4 100644 --- a/packages/docs/src/pages/components/Tooltip/Tooltip.mdx +++ b/packages/docs/src/pages/components/Tooltip/Tooltip.mdx @@ -2,7 +2,7 @@ import { Tooltip, Icon, Button, TabList, Tab, Link, IconButton } from "@vibe/cor import { Canvas, Meta } from "@storybook/blocks"; import { Bolt } from "@vibe/icons"; import * as TooltipStories from "./Tooltip.stories"; -import { TipOtherComponents, modifiers } from "./Tooltip.stories.helpers"; +import { TipOtherComponents } from "./Tooltip.stories.helpers"; import image from "./assets/tooltip-image.png"; @@ -89,7 +89,7 @@ Tooltip's arrow can appear from top, bottom, left or right. { positive: { component: ( - + ), @@ -97,7 +97,7 @@ Tooltip's arrow can appear from top, bottom, left or right. }, negative: { component: ( - + ), @@ -117,7 +117,7 @@ Tooltip's arrow can appear from top, bottom, left or right. negative: { component: ( - + Boards Items @@ -132,7 +132,6 @@ Tooltip's arrow can appear from top, bottom, left or right. @@ -150,7 +149,6 @@ Tooltip's arrow can appear from top, bottom, left or right. @@ -172,7 +170,6 @@ Tooltip's arrow can appear from top, bottom, left or right. shouldShowOnMount image={image} position="right" - modifiers={modifiers} open >
    @@ -187,7 +184,6 @@ Tooltip's arrow can appear from top, bottom, left or right. shouldShowOnMount image={image} position="right" - modifiers={modifiers} open >
    diff --git a/packages/docs/src/pages/components/Tooltip/Tooltip.stories.helpers.tsx b/packages/docs/src/pages/components/Tooltip/Tooltip.stories.helpers.tsx index 4639c02e4f..6b7d93540f 100644 --- a/packages/docs/src/pages/components/Tooltip/Tooltip.stories.helpers.tsx +++ b/packages/docs/src/pages/components/Tooltip/Tooltip.stories.helpers.tsx @@ -1,31 +1,15 @@ import React from "react"; import { StorybookLink, Tip } from "vibe-storybook-components"; -export const modifiers = [ - { - name: "preventOverflow", - options: { - mainAxis: false - } - }, - { - name: "flip", - options: { - // @ts-ignore - fallbackPlacements: [] - } - } -]; - export const TipOtherComponents = () => ( As tooltips only surface from a hover, never include links or buttons in the copy. If your tooltip requires either of these, considers putting your content in a{" "} - + Attention box {" "} or{" "} - + Dialog. diff --git a/packages/docs/src/pages/components/Tooltip/Tooltip.stories.tsx b/packages/docs/src/pages/components/Tooltip/Tooltip.stories.tsx index f0ca296bb4..cf6aff5917 100644 --- a/packages/docs/src/pages/components/Tooltip/Tooltip.stories.tsx +++ b/packages/docs/src/pages/components/Tooltip/Tooltip.stories.tsx @@ -1,10 +1,14 @@ import React from "react"; import { Tooltip, type TooltipProps, Button, Flex, IconButton } from "@vibe/core"; import { Hide, Menu, Subitems } from "@vibe/icons"; -import { modifiers } from "./Tooltip.stories.helpers"; +import { shift, flip } from "@floating-ui/react-dom"; import image from "./assets/tooltip-image.png"; import { createStoryMetaSettingsDecorator } from "../../../utils/createStoryMetaSettingsDecorator"; +// Middleware to prevent the tooltip from being displaced when the user scrolls the story. +// Not needed in real implementations. +const storyMiddleware = [shift({ mainAxis: false }), flip({ fallbackPlacements: [] })]; + const metaSettings = createStoryMetaSettingsDecorator({ component: Tooltip, iconPropNamesArray: ["icon"] @@ -24,24 +28,8 @@ export const Overview = { id="overview-tooltip" {...args} open + middleware={storyMiddleware} hideWhenReferenceHidden - // The modifier's purpose is to prevent the tooltip from being displayed when the user scrolls the story upwards / downwards. - // Therefore, there is no need to move this prop in your implementations. - modifiers={[ - { - name: "preventOverflow", - options: { - mainAxis: false - } - }, - { - name: "flip", - options: { - // @ts-ignore - fallbackPlacements: [] - } - } - ]} >
    @@ -67,24 +55,8 @@ export const TooltipWithTitle = () => ( title="Tooltip title" shouldShowOnMount position="right" + middleware={storyMiddleware} hideWhenReferenceHidden - // The modifier's purpose is to prevent the tooltip from being displayed when the user scrolls the story upwards / downwards. - // Therefore, there is no need to move this prop in your implementations. - modifiers={[ - { - name: "preventOverflow", - options: { - mainAxis: false - } - }, - { - name: "flip", - options: { - // @ts-ignore - fallbackPlacements: [] - } - } - ]} open >
    @@ -101,8 +73,8 @@ export const TooltipWithImage = () => ( shouldShowOnMount image={image} position="right" + middleware={storyMiddleware} hideWhenReferenceHidden - modifiers={modifiers} style={{ minHeight: "135px" }} open > @@ -113,33 +85,13 @@ export const TooltipWithImage = () => ( export const Positions = { render: () => { - // The modifier's purpose is to prevent the tooltip from being displayed when the user scrolls the story upwards / downwards. - // Therefore, there is no need to move this prop in your implementations. - const modifiers = [ - { - name: "preventOverflow", - options: { - mainAxis: false - } - }, - { - name: "flip", - options: { - // @ts-ignore - fallbackPlacements: [] - } - } - ]; - return (
    - {/* The modifier's purpose is to prevent the tipseen from being displayed when the user scrolls the story upwards / downwards. - Therefore, there is no need to move this prop in your implementations. */} ( - + ); export const DefinitionTooltip = () => ( - @@ -214,13 +166,13 @@ export const DefinitionTooltip = () => ( export const ImmediateTooltips = () => ( - + - + - + ); diff --git a/packages/docs/src/pages/components/VirtualizedGrid/VirtualizedGrid.stories.helpers.tsx b/packages/docs/src/pages/components/VirtualizedGrid/VirtualizedGrid.stories.helpers.tsx index 4f21f4fdd8..8f153085b7 100644 --- a/packages/docs/src/pages/components/VirtualizedGrid/VirtualizedGrid.stories.helpers.tsx +++ b/packages/docs/src/pages/components/VirtualizedGrid/VirtualizedGrid.stories.helpers.tsx @@ -15,7 +15,7 @@ export const itemRenderer = (item: VirtualizedGridItemType, index: number, style return (
    ( - + Download our accessibility checklist file. @@ -15,7 +15,7 @@ export const TipColorContrast = () => (
    To check color contrast in design use - + contrast figma plugin and inspect elements accessibility tab for live code verification diff --git a/packages/docs/src/pages/foundations/accessibility/principles/principlesAccesability.scss b/packages/docs/src/pages/foundations/accessibility/principles/principlesAccesability.scss index bc0f0d3e89..a30b9a7b27 100644 --- a/packages/docs/src/pages/foundations/accessibility/principles/principlesAccesability.scss +++ b/packages/docs/src/pages/foundations/accessibility/principles/principlesAccesability.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; $principles-cell-min-width: $grid-auto-fit-cell-width-medium; $principles-column-gap: var(--sb-spacing-large); diff --git a/packages/docs/src/pages/foundations/motion/animation-overviews/animation-overviews.module.scss b/packages/docs/src/pages/foundations/motion/animation-overviews/animation-overviews.module.scss index db769bccdb..ac02153f59 100644 --- a/packages/docs/src/pages/foundations/motion/animation-overviews/animation-overviews.module.scss +++ b/packages/docs/src/pages/foundations/motion/animation-overviews/animation-overviews.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; $animation-overview-grid-gap: 64px; $animation-overview-cell-min-width: $grid-auto-fit-cell-width-large; diff --git a/packages/docs/src/pages/foundations/motion/animation-types/animation-types.module.scss b/packages/docs/src/pages/foundations/motion/animation-types/animation-types.module.scss index 2e75c138b0..bd179f8e50 100644 --- a/packages/docs/src/pages/foundations/motion/animation-types/animation-types.module.scss +++ b/packages/docs/src/pages/foundations/motion/animation-types/animation-types.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; $animation-types-grid-gap: 64px; $animation-types-cell-min-width: $grid-auto-fit-cell-width-medium; diff --git a/packages/docs/src/pages/foundations/motion/duration-expressive-tokens/duaration-expressive-tokens.module.scss b/packages/docs/src/pages/foundations/motion/duration-expressive-tokens/duaration-expressive-tokens.module.scss index 4bd7b73267..39bcd7c216 100644 --- a/packages/docs/src/pages/foundations/motion/duration-expressive-tokens/duaration-expressive-tokens.module.scss +++ b/packages/docs/src/pages/foundations/motion/duration-expressive-tokens/duaration-expressive-tokens.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; $duration-grid-column-gap: var(--sb-spacing-large); $duration-cell-min-width: $grid-auto-fit-cell-width-medium; diff --git a/packages/docs/src/pages/foundations/motion/duration-productive-tokens/duaration-productive-tokens.module.scss b/packages/docs/src/pages/foundations/motion/duration-productive-tokens/duaration-productive-tokens.module.scss index 4bd7b73267..39bcd7c216 100644 --- a/packages/docs/src/pages/foundations/motion/duration-productive-tokens/duaration-productive-tokens.module.scss +++ b/packages/docs/src/pages/foundations/motion/duration-productive-tokens/duaration-productive-tokens.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; $duration-grid-column-gap: var(--sb-spacing-large); $duration-cell-min-width: $grid-auto-fit-cell-width-medium; diff --git a/packages/docs/src/pages/foundations/round-corners/corners-settings/corner-settings.scss b/packages/docs/src/pages/foundations/round-corners/corners-settings/corner-settings.scss index 14d1c60531..9f6c0ad239 100644 --- a/packages/docs/src/pages/foundations/round-corners/corners-settings/corner-settings.scss +++ b/packages/docs/src/pages/foundations/round-corners/corners-settings/corner-settings.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; .global-settings-general-component { margin-top: var(--sb-spacing-between-section-items); diff --git a/packages/docs/src/pages/foundations/round-corners/rounded-corner/rounded-corner.module.scss b/packages/docs/src/pages/foundations/round-corners/rounded-corner/rounded-corner.module.scss index 6dcd5c5bb0..f95fcad50a 100644 --- a/packages/docs/src/pages/foundations/round-corners/rounded-corner/rounded-corner.module.scss +++ b/packages/docs/src/pages/foundations/round-corners/rounded-corner/rounded-corner.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; .rounded-corner-component { @include vibe-text(text1, bold); diff --git a/packages/docs/src/pages/foundations/shadow/shadow-levels/shadow-levels.scss b/packages/docs/src/pages/foundations/shadow/shadow-levels/shadow-levels.scss index 16cfa99483..12840a85d3 100644 --- a/packages/docs/src/pages/foundations/shadow/shadow-levels/shadow-levels.scss +++ b/packages/docs/src/pages/foundations/shadow/shadow-levels/shadow-levels.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; $shadow-level-grid-column-gap: var(--sb-spacing-large); $shadow-level-grid-row-gap: var(--sb-spacing-xl); diff --git a/packages/docs/src/pages/foundations/typography/examples-table/example-table.module.scss b/packages/docs/src/pages/foundations/typography/examples-table/example-table.module.scss index a281008c61..a8f89636c0 100644 --- a/packages/docs/src/pages/foundations/typography/examples-table/example-table.module.scss +++ b/packages/docs/src/pages/foundations/typography/examples-table/example-table.module.scss @@ -1,7 +1,7 @@ .table { $border: 1px solid var(--sb-layout-border-color); width: 100%; - margin-block: var(--spacing-medium) var(--spacing-xl); + margin-block: var(--space-16) var(--space-32); border-inline: $border; border-block-start: $border; border-spacing: 0; @@ -9,7 +9,7 @@ th, td { text-align: start !important; - padding: var(--spacing-small) var(--spacing-medium); + padding: var(--space-8) var(--space-16); height: 40px; border-bottom: $border; &:not(:last-child) { diff --git a/packages/docs/src/pages/foundations/typography/typograpghy.stories.helpers.tsx b/packages/docs/src/pages/foundations/typography/typograpghy.stories.helpers.tsx index 26c053e2f5..c2e786e3f7 100644 --- a/packages/docs/src/pages/foundations/typography/typograpghy.stories.helpers.tsx +++ b/packages/docs/src/pages/foundations/typography/typograpghy.stories.helpers.tsx @@ -4,11 +4,11 @@ import { Link, Tip } from "vibe-storybook-components"; export const TipHowToUseFonts = () => ( Figtree and Poppins are both Google Fonts. Click - + here to download Figtree, and click - + here to download Poppins. diff --git a/packages/docs/src/pages/hooks/useActiveDescendantListFocus/useActiveDescendantListFocus.module.scss b/packages/docs/src/pages/hooks/useActiveDescendantListFocus/useActiveDescendantListFocus.module.scss index 192ce31e9c..da90cc37cf 100644 --- a/packages/docs/src/pages/hooks/useActiveDescendantListFocus/useActiveDescendantListFocus.module.scss +++ b/packages/docs/src/pages/hooks/useActiveDescendantListFocus/useActiveDescendantListFocus.module.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; .visualFocus { @include focus-style-css; diff --git a/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.mdx b/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.mdx index 78db1c7eed..51eddd8b5c 100644 --- a/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.mdx +++ b/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.mdx @@ -49,7 +49,7 @@ import { useClickableProps } from "@vibe/core"; description={ <> Provide semantic meaning to content. - + More details. @@ -62,7 +62,7 @@ import { useClickableProps } from "@vibe/core"; <> Specifies the tab order of the element. Default value is 0. More details. @@ -71,13 +71,13 @@ import { useClickableProps } from "@vibe/core"; } /> Defines a string value that labels an interactive element for assistive technologies. More details. @@ -86,13 +86,13 @@ import { useClickableProps } from "@vibe/core"; } /> HTML attribute for hiding content from screen readers and other assistive technologies. More details. @@ -101,14 +101,14 @@ import { useClickableProps } from "@vibe/core"; } /> Indicates the availability and type of interactive popup element that can be triggered by the element on which the attribute is set. More details. @@ -117,13 +117,13 @@ import { useClickableProps } from "@vibe/core"; } /> Indicate if a control is expanded or collapsed, and whether or not its child elements are displayed or hidden. More details. @@ -142,7 +142,7 @@ import { useClickableProps } from "@vibe/core"; description={ <> A React - + ref object for the clickable element. @@ -168,7 +168,7 @@ import { useClickableProps } from "@vibe/core"; description={ <> Provide semantic meaning to content. - + More details. @@ -181,7 +181,7 @@ import { useClickableProps } from "@vibe/core"; <> Specifies the tab order of an element. More details. @@ -196,7 +196,7 @@ import { useClickableProps } from "@vibe/core"; <> Defines a string value that labels an interactive element. More details. @@ -211,7 +211,7 @@ import { useClickableProps } from "@vibe/core"; <> Used to hide non-interactive content from the accessibility API. More details. @@ -227,7 +227,7 @@ import { useClickableProps } from "@vibe/core"; Indicates the availability and type of interactive popup element that can be triggered by the element on which the attribute is set. More details. @@ -242,7 +242,7 @@ import { useClickableProps } from "@vibe/core"; <> Indicate if a control is expanded or collapsed, and whether or not its child elements are displayed or hidden. More details. diff --git a/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.helpers.tsx b/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.helpers.tsx index 64811aa529..24de6f6ec0 100644 --- a/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.helpers.tsx +++ b/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.helpers.tsx @@ -4,7 +4,7 @@ import { StorybookLink, Tip } from "vibe-storybook-components"; export const TipClickable = () => ( For more simple use cases, you also can use our{" "} - + Clickable {" "} component wrapper. diff --git a/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.scss b/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.scss index d891765418..49e752587d 100644 --- a/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.scss +++ b/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.scss @@ -1,4 +1,4 @@ -@import "~monday-ui-style/dist/mixins"; +@import "~@vibe/style/dist/mixins"; .monday-storybook-use-clickable-props { @include focus-style; diff --git a/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.tsx b/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.tsx index 856a4a06d7..c07f109c84 100644 --- a/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.tsx +++ b/packages/docs/src/pages/hooks/useClickableProps/useClickableProps.stories.tsx @@ -15,9 +15,9 @@ export const Overview = { { onClick: onClick, id: "clickable-id", - ariaHidden: false, - ariaHasPopup: false, - ariaExpanded: false + "aria-hidden": false, + "aria-haspopup": false, + "aria-expanded": false }, ref ); diff --git a/packages/docs/src/pages/hooks/useHover/useHover.mdx b/packages/docs/src/pages/hooks/useHover/useHover.mdx index 6620a1de77..56175f624a 100644 --- a/packages/docs/src/pages/hooks/useHover/useHover.mdx +++ b/packages/docs/src/pages/hooks/useHover/useHover.mdx @@ -34,7 +34,7 @@ import { useHover } from "@vibe/core"; description={ <> A React - + ref object to assign to the element which hover state needs to be tracked. diff --git a/packages/docs/src/pages/hooks/useIsOverflowing/useIsOverflowing.mdx b/packages/docs/src/pages/hooks/useIsOverflowing/useIsOverflowing.mdx index f9a8686362..4aabdabb28 100644 --- a/packages/docs/src/pages/hooks/useIsOverflowing/useIsOverflowing.mdx +++ b/packages/docs/src/pages/hooks/useIsOverflowing/useIsOverflowing.mdx @@ -32,7 +32,7 @@ import { useIsOverflowing } from "@vibe/core"; description={ <> A React - + ref object for the container of likely to overflow content. diff --git a/packages/docs/src/pages/hooks/useIsOverflowing/useIsOverflowing.stories.helpers.tsx b/packages/docs/src/pages/hooks/useIsOverflowing/useIsOverflowing.stories.helpers.tsx index d9d5276f07..2e1e856c76 100644 --- a/packages/docs/src/pages/hooks/useIsOverflowing/useIsOverflowing.stories.helpers.tsx +++ b/packages/docs/src/pages/hooks/useIsOverflowing/useIsOverflowing.stories.helpers.tsx @@ -4,7 +4,7 @@ import { StorybookLink, Tip } from "vibe-storybook-components"; export const TipTooltip = () => ( You might want to use{" "} - + Tooltip {" "} to display all the content. diff --git a/packages/docs/src/pages/hooks/useKeyEvent/useKeyEvent.stories.tsx b/packages/docs/src/pages/hooks/useKeyEvent/useKeyEvent.stories.tsx index 5ee6ace652..9aed5a7ead 100644 --- a/packages/docs/src/pages/hooks/useKeyEvent/useKeyEvent.stories.tsx +++ b/packages/docs/src/pages/hooks/useKeyEvent/useKeyEvent.stories.tsx @@ -12,7 +12,6 @@ export const Overview = { useKeyEvent({ keys: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], - // @ts-ignore callback: e => setKeyName(e.key) }); diff --git a/packages/docs/src/pages/hooks/useListenFocusTriggers/useListenFocusTriggers.mdx b/packages/docs/src/pages/hooks/useListenFocusTriggers/useListenFocusTriggers.mdx deleted file mode 100644 index f8e5a27dd9..0000000000 --- a/packages/docs/src/pages/hooks/useListenFocusTriggers/useListenFocusTriggers.mdx +++ /dev/null @@ -1,51 +0,0 @@ -import { Canvas, Meta } from "@storybook/blocks"; -import { FunctionArgument, FunctionArguments, Link, UsageGuidelines } from "vibe-storybook-components"; -import * as UseListenFocusTriggersStories from "./useListenFocusTriggers.stories"; - - - -# useListenFocusTriggers - -Fire provided callbacks, when focus by mouse or by keyboard happens. - - - -### Import - -```js -import { useListenFocusTriggers } from "@vibe/core"; -``` - -## Arguments - - - - - A React - - ref - - object. - - } - /> - - - - - -## Usage - - diff --git a/packages/docs/src/pages/hooks/useListenFocusTriggers/useListenFocusTriggers.stories.tsx b/packages/docs/src/pages/hooks/useListenFocusTriggers/useListenFocusTriggers.stories.tsx deleted file mode 100644 index e0c3866554..0000000000 --- a/packages/docs/src/pages/hooks/useListenFocusTriggers/useListenFocusTriggers.stories.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import React, { useCallback, useRef, useState } from "react"; -import { useListenFocusTriggers, Button, Flex } from "@vibe/core"; - -export default { - title: "Hooks/useListenFocusTriggers" -}; - -export const Overview = { - render: () => { - const [text, setText] = useState("-"); - const ref = useRef(null); - - const onFocusByMouse = useCallback((_event: FocusEvent) => { - setText("mouse"); - }, []); - - const onFocusByKeyboard = useCallback((_event: FocusEvent) => { - setText("keyboard"); - }, []); - - useListenFocusTriggers({ - ref: ref, - onFocusByMouse: onFocusByMouse, - onFocusByKeyboard: onFocusByKeyboard - }); - - return ( - - -
    Received focus by: {text}
    -
    - ); - }, - - name: "Overview" -}; diff --git a/packages/docs/src/pages/hooks/useWizard/useWizard.stories.tsx b/packages/docs/src/pages/hooks/useWizard/useWizard.stories.tsx index 8c0fcadc2b..6ad8c9e6e6 100644 --- a/packages/docs/src/pages/hooks/useWizard/useWizard.stories.tsx +++ b/packages/docs/src/pages/hooks/useWizard/useWizard.stories.tsx @@ -4,7 +4,7 @@ import { type Decorator } from "@storybook/react"; import styles from "./useWizard.stories.module.scss"; const withUseWizardDecorator: Decorator = Story => ( - + ); @@ -23,11 +23,11 @@ export const Overview = { return ( <> - + Active Step: {activeStep} - - @@ -46,11 +46,11 @@ export const WithInitialStep = { return ( <> - + Active Step: {activeStep} - - @@ -84,8 +84,8 @@ export const WithStepsComponent = { activeStepIndex={activeStep} onChangeActiveStep={(_e, stepIndex) => goToStep(stepIndex)} /> - - diff --git a/packages/docs/src/pages/mcp/mcp.mdx b/packages/docs/src/pages/mcp/mcp.mdx index 6c2f92a856..7fddd81e2d 100644 --- a/packages/docs/src/pages/mcp/mcp.mdx +++ b/packages/docs/src/pages/mcp/mcp.mdx @@ -3,7 +3,7 @@ import { Link, Tip, Frame, UsageGuidelines } from "vibe-storybook-components"; import { Text, Heading } from "@vibe/core"; import vibeXMcp from "./assets/vibe-x-mcp.png"; - + # MCP Server @@ -106,7 +106,7 @@ This tool analyzes your entire project and provides: #### `dropdown-migration` -Specialized migration assistant for upgrading from the old Dropdown component to the new Dropdown available in `@vibe/core/next`. +Specialized migration assistant for upgrading from the old Dropdown component to the new Dropdown available in `@vibe/core`. **Key capabilities:** diff --git a/packages/docs/src/pages/migration-guide/migration-guide.mdx b/packages/docs/src/pages/migration-guide/migration-guide.mdx deleted file mode 100644 index 612cfe52f9..0000000000 --- a/packages/docs/src/pages/migration-guide/migration-guide.mdx +++ /dev/null @@ -1,454 +0,0 @@ -import { Meta, Markdown, Source } from "@storybook/blocks"; -import { Link, Tip } from "vibe-storybook-components"; -import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; -import { dark } from "react-syntax-highlighter/dist/esm/styles/prism"; -import { DiffCodeBlock } from "./DiffCodeBlock"; -import propsGif from "./assets/props.gif"; -import packageSplitDiagram from "./assets/package-split.png"; -import layersGif from "./assets/layers.gif"; - - - -# Vibe 3 Migration Guide - -
    - - - **Skip the manual work and let AI handle your entire Vibe 3 migration!** With the [Vibe MCP - server](https://vibe.monday.com/?path=/docs/mcp-new--docs), simply ask your AI assistant: *"Help me migrate this - project from Vibe 2 to Vibe 3"* and watch the magic happen! The MCP will automatically scan your entire codebase, - identify all deprecated components, detect breaking changes, and generate the exact codemod commands you need - all in - seconds. -

    **[Install the MCP โ†’](https://vibe.monday.com/?path=/docs/mcp-new--docs) and turn your migration work into - minutes!** -
    - -
    - -- [Intro](#intro) -- [New Features โœจ](#new-features-) -- [Migration Steps ๐Ÿš€](#migration-steps-) -- [Breaking Changes ๐Ÿšจ](#breaking-changes-) - - [General](#general) - - [Typography](#typography) - - [Components](#components) - - [Colors](#colors) - - [Icons](#icons) -- [FAQ โ“](#faq-) -- [Help ๐Ÿ™](#help-) - -## Intro - -Vibe 3 is a major update to the Vibe design system, introducing several new features, enhancements, and breaking changes. This guide will walk you through the changes to help you transition smoothly from v2 to v3. - - - To migrate from v1 to v2, please refer to the [Vibe 2 migration - guide](https://github.com/mondaycom/vibe/tree/master/packages/docs/src/pages/migration-guide/vibe2-migration-guide.md) - - -## New Features โœจ - -### Package rename - -The `monday-ui-react-core` package has been renamed to `@vibe/core` to better reflect the library's purpose and align with the Vibe branding. In addition, the `/icons` entry point has been moved to a new `@vibe/icons` package. - -packages split diagram - -### Reduced bundle size - -By removing CommonJS support, dividing the library into packages, and optimizing the library exports - the bundle size has been reduced by 43%, resulting in faster load times and improved performance. - -### New Typography System - -New and improved Heading, EditableHeading components. For more info, check the [Typography](#typography) section. - -### Enhanced Typescript Support - -All components' types and interfaces are exported for better type-safe compositions. For example: - -```tsx -import { Button, ButtonProps, ButtonSize } from "@vibe/core"; -``` - -### Improved prop type-checking - -Now you can use type-safe strings instead of static enums for props, providing a better and faster development experience: - -```tsx -// Before - - -// After - -``` - -See IntelliSense in action in your favorite IDE: - -Using props values instead of enums - -You can keep using the static properties, although not recommended, as they are likely to be deprecated in the future. - -### Improved layering of floating components - -When used inside Modals, the floating components (i.e., Tooltip, Tipseen, Dialog) will now be rendered inside the Modal's container by default, ensuring they are always on top. This change prevents any z-index interference and ensures a consistent user experience. - -floating elements on modal - -## Migration Steps ๐Ÿš€ - -Most of the changes required to migrate from Vibe 2 to Vibe 3 are covered by an automatic migration script, but some manual changes may still be required, especially changes that are related to style, UX, or behavior. - -These steps will guide you through the migration process: - -1. Install `@vibe/core` (and `@vibe/icons`, if needed) and remove `monday-ui-react-core`: - - ```bash - yarn add @vibe/core @vibe/icons # or npm install @vibe/core @vibe/icons - yarn remove monday-ui-react-core # or npm uninstall monday-ui-react-core - ``` - - The changes in your `package.json` file should look like this: - -
      - -
    - -2. Run the migration script to update your codebase: - - ```bash - npx @vibe/codemod -m v3 - ``` - - For more information, and for more options, refer to the [@vibe/codemod](https://github.com/mondaycom/vibe/blob/master/packages/codemod/README.md) docs. - -3. Follow the changes in the [Breaking Changes ๐Ÿšจ](#breaking-changes-) section below and apply any manual changes required. - -4. **Review the changes and test your application thoroughly** to ensure everything works as expected. - -5. Run Prettier or any code formatting tool to format the changes made by the migration script [optional]. - -## Breaking Changes ๐Ÿšจ - -Several breaking changes have been made to optimize and streamline the library. These changes include the removal of deprecated components, updates to component APIs, and overall enhancements. - -Please note that the following changes are **complementary to the migration script** and require manual intervention, assuming that the migration script has ran successfully. If you prefer migrating entirely manually (without the `@vibe/codemod` script), please refer to the [Complete Vibe 3 changelog](https://github.com/mondaycom/vibe/blob/master/packages/docs/src/pages/migration-guide/vibe-3-changelog.md). - -### General - -- **CommonJS support has been removed** to modernize the library, reduce complexity, and ensure better compatibility with modern JavaScript tooling and ESM (ECMAScript Modules) standards. -- The `monday-ui-react-core/storybookComponents` entry **was removed**, use the [vibe-storybook-components](https://www.npmjs.com/package/vibe-storybook-components) package instead. -- The `monday-ui-react-core/dist/main.css` **was removed**, use `@vibe/core/tokens` instead to load all relevant CSS tokens: - -
      - -
    - -- The `monday-ui-style/src/Icons` **was removed**, use `@vibe/icons/raw` instead to use svg icons: - -
      - -
    - -### Typography - -The typography system has been updated to provide a more consistent and accessible experience. The new typography system includes a set of predefined text styles and sizes that can be easily applied to components. - -- Use the Text component for paragraphs, labels, and texts up to - 16px. It includes 3 variants: text1, text2 and text3. Each variant has a - fixed size and three different weights. - -- Use the Heading component the for titles and text from 18px and - up. It includes 3 variants: h1, h2 and h3. Each of these variant is mapped - to an h1, h2, h3 element accordingly. - -To replace old typography usages: - -- **H1 Main heading** (`--font-h1`) โ†’ **Heading/H1/normal** -- **H2 Secondary heading** (`--font-h2`) โ†’ **Heading/H2/bold** -- **H3 Tertiary heading** (`--font-h3`) โ†’ **Heading/H2/light** -- **H4 Fourth heading** (`--font-h4`) โ†’ **Heading/H3/bold** -- **H5 Paragraph** (`--font-h5`) โ†’ **Text/text1/bold** -- **H6 UI text** (`--font-general-label`) โ†’ **Text/text2/normal** -- **Medium Text** (`--font-subtext`) โ†’ **Text/text2/normal** -- **P Paragraph text** (`--font-paragraph`) โ†’ **Text/text1/normal** -- **Medium text link** (`--font-general-label`, `--link-color`) โ†’ **Text/text2/normal** - -For more information, refer to the Typography docs. - -### Components - -The following components have been **removed** and are no longer available: - -- `Input` - use TextField instead -- `EditableInput` - use EditableText instead -- `SearchComponent` - use Search instead -- `ResponsiveList` - use useIsOverflowing hook instead, to show/hide content based on overflow - -These components were **rewritten from scratch** with new API and style, offering better accessibility and usability: - -- Heading -- EditableHeading -- Search - -Make sure to import them from `@vibe/core` instead of the `/next` entry: - - - -The following components have breaking changes to their API, behavior, or style, as detailed below: - -**AvatarGroup** - -- The component no longer has padding at the bottom - -**Button** - -- The `children` prop is now required -- The `sm`, `md`, `lg` sizes were removed, use `small`, `medium`, `large` respectively: - -
      - -+
    - -**Chips** - -- The `DARK_INDIGO` and `BLACKISH` colors were removed from the `color` prop -- The `clickable` and `isClickable` props were removed, use `onClick` instead, to get clickable behavior and style: - -
      - -+ {}}> -`} - /> -
    - -**Counter** - -- The `sm`, `md`, `lg` sizes were removed, use `small`, `medium`, `large` respectively: - -
      - -+ -`} - /> -
    - -**DialogContentContainer** - -- The "medium" `size` now has an increased padding, correcting a previous sizing issue where "medium" and "small" had identical paddings. As a result, "small" is now the default size. If no size was specified there's no action required. - -**Flex** - -- The `Flex.gaps.NONE` property has been removed. To specify no gap simply omit the `gap` prop - -**Dropdown** - -- The `xxs`, `xs` sizes were acting as the `small` size and were therefore removed. Use `small` instead -- New readonly style - -**Icon** - -- The `clickable`, `onClick` props were removed, use `` for clickable icons: - -
      - {}}> -+ {}}> -`} - /> -
    - -**Label** - -- The "Spin In" animation was removed - -**MenuButton** - -- The `hideWhenReferenceHidden` prop default value changes to "true", meaning when the MenuButton is hidden hide the dialog and tooltip as well. To disable this behavior set `hideWhenReferenceHidden` to "false" - -**MenuItem** - -- The provided tooltip (when the text is overflowing) now wraps the entire element so non-block layout given to the `title` prop may break - -**Modal** - -- The `hideCloseButton` has been removed since Modals should always have a close button. It has also been removed from ModalHeader component. -- The `unmountOnClose` prop default value changes to "true", meaning the Modal will not render when `show` is "false". To disable this behavior set `unmountOnClose` to "false" -- Tooltips, Tipseens, and Dialogs on Modals will now be rendered inside the Modal's container by default, without any z-index interference - -**Search** - -- The `type` prop was removed. The `Search.types.SQUARE`, `ROUND`, and `UNDERLINE` variants are no longer available -- The `validation` prop was removed -- The `wrapperClassName` prop was removed, use `className` instead -- The `setRef` prop was removed, use the standard `ref` prop instead -- The `iconNames` prop was removed. Use `clearIconLabel` for the clear button accessibility label -- The `iconName` prop was renamed to `searchIconName` -- The `secondaryIconName` prop was renamed to `clearIconName` -- The `activeDescendant` prop was renamed to `currentAriaResultId`: - -
      - -+ -`} - /> -
    - -- The `Search.sizes.SMALL/MEDIUM/LARGE` size values were removed, use `small`, `medium`, `large` respectively: - -
      - -+ -`} - /> -
    - -- The `debounceRate` default changed from `200` to `400` - -The following components have breaking changes to their API, behavior, or style, as detailed below: - -**SplitButton** - -- The `data-testId` prop will no longer be applied to the internal elements, only the root element - -**Steps** - -- The `isOnPrimary` prop was removed, use `color="primary` instead: - -
      - -+ -`} - /> -
    - -**Tabs** - -- The browser's default margin/padding for `ul` and `li` elements were reset - -**TabList** - -- The component no longer gets a padding bottom - -**TabPanels** - -- TabPanels will render only the active tab instead of rendering all the panels - -**TextField** - -- The `iconsNames` prop no longer accepts the `layout` property -- Providing the `required` prop will now show a red asterisk, implying that the field is mandatory, and so the `requiredAsterisk` prop was removed -- New readonly style -- The `sm`, `md`, `lg` sizes were removed, use `small`, `medium`, `large` respectively: - -
      - -+ -`} - /> -
    - -**Tipseen** - -- The `content` prop is now mandatory -- The default `color` has changed from 'primary' to 'inverted'. To keep the previous color, set the `color` prop to 'primary' -- The `showDelay` prop's default value has changed to 100 -- The `justify` prop was removed, and so the `Tipseen.justifyTypes` static property was removed as well - -**TipseenContent** - -- The `submitButtonProps`, `dismissButtonProps` props were removed, use `submitButtonText` and `dismissButtonText` to change the buttons' text - -**Tooltip** - -- The `paddingSize`, `justify`, and `arrowPosition` props were removed. Accordingly the `Tooltip.paddingSizes`, `Tooltip.justifyTypes`, and `Tooltip.arrowPositions` static properties were removed as well -- The `theme` prop can now accept only "dark" or "primary" -- The `position` prop can now accept only "top, right, bottom, left" -- The `showOnDialogEnter` prop's default value has changed to `true`; now the tooltip will remain open by default when hovering over it -- The `hideDelay` prop's default value changed to 100 -- The `addKeyboardHideShowTriggersByDefault` default changed to true, making it accessible with keyboard navigation -- The tooltip's max-width is now set to 240px -- The Tooltip's `content` is now wrapped in another `div`, meaning that non-block layouts inside the tooltip may break -- The `containerSelector` will now fallback to `document.body` instead of `#tooltips-container` if not provided - -### Colors - -- The `--shareable-color` and `--private-color` CSS variables were removed for all themes -- The `color-warning`, `color-warning-hover`, `color-warning-select`, `color-warning-select-hover` colors were removed from the `colors.json` file (in `monday-ui-style` package), use `warning-color-*` respectively - -### Icons - -- The `Upgrade` icon has been removed, and the `Featured` icon has been renamed to `Upgrade`: -- The `Replay` icon has been renamed to `Reply` - -
      - -+ -`} - /> -
    - -## FAQ โ“ - -**What is the best way to migrate to Vibe 3?** - -It is recommended to follow the migration steps outlined in this guide to ensure a smooth transition from Vibe 2 to Vibe 3. - -**Are there any manual changes required after running the migration script?** - -While the migration script covers most changes, some manual changes may still be required where style, UX, or behavior changes are involved. You should walk through the [breaking changes](#breaking-changes-) section to identify any manual changes required. - -**Do I need to stop using static properties (e.g. `Button.kinds.PRIMARY`) in my code?** - -You can still use static properties, but it is recommended to use type-safe strings instead. Static properties are likely to be deprecated in the future. To migrate to type-safe strings, use the following script: `npx @vibe/codemod -m enums`. - -**I have already been using Heading, EditableHeading, Search from `/next`, do I need to change anything?** - -If you have been using these components from `/next`, you will only need to update your imports to use the components from the main entry (`@vibe/core`). - -**Are there any known issues or limitations with Vibe 3?** - -While we have made every effort to ensure Vibe 3 is stable and reliable, there may be some known issues or limitations. If you encounter any problems, please create an [issue](https://github.com/mondaycom/vibe/issues/new/choose). - -**What if I don't want to migrate to Vibe 3?** - -While we recommend migrating to Vibe 3 to take advantage of the new features and improvements, you can continue using Vibe 2. However, please note that Vibe 2 will no longer recieve any new features or updates aside from critical bug fixes, and will reach EOL upon the next major version (Vibe 4) release. - -**The migration script failed to run, what should I do?** - -Make sure to run the migration script in the root directory of your project, and make sure to run it on a clean git branch to avoid any conflicts. If you still encounter issues, please create an [issue](https://github.com/mondaycom/vibe/issues/new/choose) describing the steps, system info and any error messages you received. - -
    - -## Help ๐Ÿ™ - -If you have any questions, feedback, or need help, please don't hesitate to reach out to us. You can provide [feedback](https://github.com/mondaycom/vibe/discussions) or [report issues](https://github.com/mondaycom/vibe/issues/new/choose) in the [Vibe repository](https://github.com/mondaycom/vibe) on GitHub. diff --git a/packages/docs/src/pages/migration-guide/vibe-4-changelog.md b/packages/docs/src/pages/migration-guide/vibe-4-changelog.md new file mode 100644 index 0000000000..a4d5ffaabb --- /dev/null +++ b/packages/docs/src/pages/migration-guide/vibe-4-changelog.md @@ -0,0 +1,232 @@ +# Vibe 4 Changelog + +This is the complete list of changes in the Vibe 4 release. Changes that are marked with a ๐Ÿ”€ are covered by a migration script (codemod). + +For the complete migration guide see the [Vibe 4 Migration Guide](https://vibe.monday.com/?path=/docs/vibe-4-migration-guide--docs). + +## General + +- React 19 support โ€” all deprecated React APIs removed, including dependencies +- `moment` removed as a peer dependency +- `@vibe/core/next` consolidation โ€” AttentionBox, Dropdown, DatePicker, and Modal promoted to `@vibe/core`. Only `List` remains in `@vibe/core/next` ๐Ÿ”€ +- All deprecated enum exports and static properties removed across all components ๐Ÿ”€ (see [Enums](#enums) section) +- ARIA props renamed from camelCase to standard `aria-*` attributes across all components ๐Ÿ”€ (see [ARIA Props](#aria-props) section) +- Internal Package rename โ€” `monday-ui-style` renamed to `@vibe/style` (the package is included in @vibe/core) ๐Ÿ”€ + +## Enums + +> codemod: `enums-migration` + +All deprecated enum exports and static property accessors have been removed. Use string literals instead. ๐Ÿ”€ + +Affected components: AlertBanner, AttentionBox, Avatar, Badge, Box, Button, Clickable, ColorPicker, Counter, Dialog, Divider, Dropdown, Flex, Heading, Icon, IconButton, Label, LinearProgressBar, List, ListItem, ListItemIcon, Loader, MenuButton, MenuTitle, MultiStepIndicator, Skeleton, Slider, Steps, TabPanels, Text, TextField, ThemeProvider, Tipseen, Toast, Tooltip. + +Example: `Button.sizes.LARGE` โ†’ `"large"`, `ButtonType.PRIMARY` โ†’ `"primary"` + +## ARIA Props + +> codemod: `aria-props-migration` + +All custom camelCase ARIA props renamed to standard HTML `aria-*` attributes. ๐Ÿ”€ + +- `ariaLabel` โ†’ `aria-label` ๐Ÿ”€ +- `ariaHidden` โ†’ `aria-hidden` ๐Ÿ”€ +- `ariaExpanded` โ†’ `aria-expanded` ๐Ÿ”€ +- `ariaControls` โ†’ `aria-controls` ๐Ÿ”€ +- `ariaHasPopup` โ†’ `aria-haspopup` ๐Ÿ”€ +- `ariaLabeledBy` / `ariaLabelledBy` โ†’ `aria-labelledby` ๐Ÿ”€ +- `ariaDescribedBy` / `ariaDescribedby` โ†’ `aria-describedby` ๐Ÿ”€ +- `ariaLabelDescription` (Link) โ†’ `aria-label` ๐Ÿ”€ + +Note: Component-specific compound props like `inputAriaLabel`, `menuAriaLabel`, `closeButtonAriaLabel` are not affected. + +## Components + +### AttentionBox + +- Legacy `AttentionBox` removed, new implementation (from `@vibe/core/next`) promoted to `@vibe/core` ๐Ÿ”€ +- `AttentionBoxLink` removed as a public export โ€” use the `link` prop instead +- `AttentionBoxConstants` removed (deprecated enums: `AttentionBoxType`, `IconTypeEnum`) ๐Ÿ”€ +- Type values changed: `"success"` โ†’ `"positive"`, `"danger"` โ†’ `"negative"`, `"dark"` โ†’ `"neutral"` +- `entryAnimation` prop โ†’ `animate` +- `withoutIcon` / `withIconWithoutHeader` props removed โ€” use `icon={false}` + +### Chips + +> codemod: `Chips-component-migration` + +- The `disableClickableBehavior` prop has been removed โ€” Chips now always uses the clickable wrapper when click handlers are provided ๐Ÿ”€ + +### Clickable + +- `ariaHasPopup` now accepts `boolean` only (was `boolean | string`) +- `tabIndex` now accepts `number` only (was `string | number`) + +### DatePicker + +- Legacy DatePicker (`react-dates` + `moment`) replaced with new implementation (`react-day-picker` + `date-fns`) with an updated visual design +- `date` prop type changed from `moment.Moment` to `Date` +- `onPickDate` renamed to `onDateChange` +- `range` boolean prop replaced with `mode: "single" | "range"` +- `moment` no longer required as a peer dependency + +### Dialog + +> codemod: `Dialog-component-migration` + +- Legacy class-based Dialog (Popper.js) replaced with new functional Dialog (Floating UI) +- `modifiers` prop removed โ€” use `middleware` prop instead +- `enableNestedDialogLayer` prop removed โ€” layer handling is always used internally ๐Ÿ”€ +- `addKeyboardHideShowTriggersByDefault` default changed to `true` + +### Dropdown + +- Old `react-select`-based Dropdown removed, in favor of a new improved and customizable Dropdown +- Import changed from `@vibe/core/next` to `@vibe/core` ๐Ÿ”€ +- Options structure changed from `{ id, text }` to `{ value, label }` +- Sub-components removed: `DropdownMenu`, `DropdownOption`, `DropdownSingleValue` +- `padding-inline-start` reduced from `16px` to `8px` on Dropdown Trigger +- Placeholder now uses `var(--placeholder-color)` instead of `var(--secondary-text-color)` +- See the [Dropdown Migration Guide](https://vibe.monday.com/?path=/docs/components-dropdown-migration-guide--docs) + +### Flex + +> codemod: `Flex-component-migration` + +- The `"stretch"` value removed from `justify` prop ๐Ÿ”€ + +### Icon + +> codemod: `Icon-component-migration` + +- `iconLabel` prop renamed to `label` ๐Ÿ”€ +- `iconType` prop renamed to `type` ๐Ÿ”€ +- `iconSize` prop renamed to `size` ๐Ÿ”€ +- `size` prop now applies to `type="src"` icons (previously only affected `type="svg"`) +- `onClick` and `clickable` props removed from internal `CustomSvgIcon` โ€” wrap with a clickable element (e.g. ``) instead + +### LinearProgressBar (ProgressBar) + +> codemod: `LinearProgressBar-component-migration` + +- `LinearProgressBar` renamed to `ProgressBar` ๐Ÿ”€ +- `LinearProgressBarProps` renamed to `ProgressBarProps` ๐Ÿ”€ + +### MenuButton + +- `focusItemIndexOnMount` now defaults to `0` for Menu children (first item focused on open) + +### MenuItem + +- `children` prop now accepts only a single `MenuChild`, not `MenuChild[]` + +### Modal + +- Legacy Modal removed and replaced with a completely new Modal component with a different API (previously available via `@vibe/core/next`) ๐Ÿ”€ +- Removed: `ModalFooterButtons`, `ModalWidth` type, legacy sub-component props +- New: `ModalBasicLayout`, `ModalMediaLayout`, `ModalSideBySideLayout`, `ModalFooterWizard` +- For more info see [Modal docs](https://vibe.monday.com/?path=/docs/components-modal-new--docs) + +### Steps + +- Finish button now renders by default on the last step + +### Tipseen + +- `modifiers` prop removed โ€” use `middleware` instead (same as Dialog/Tooltip). For more info see [Floating UI docs](https://floating-ui.com/docs/middleware). + +### TextField + +> codemod: `TextField-component-migration` + +- `iconName` prop renamed to `icon` ๐Ÿ”€ +- `iconsNames` object prop replaced with flat `iconLabel` and `secondaryIconLabel` props ๐Ÿ”€ +- `padding-inline-start` reduced from `16px` to `8px` +- Placeholder now uses `var(--placeholder-color)` instead of `var(--secondary-text-color)` + +### Search + +- `padding-inline-start` reduced from `16px` to `8px` +- Placeholder now uses `var(--placeholder-color)` instead of `var(--secondary-text-color)` + +### TextArea + +- `padding-inline-start` reduced from `16px` to `8px` +- Placeholder now uses `var(--placeholder-color)` instead of `var(--secondary-text-color)` + +### NumberField + +- `padding-inline-start` reduced from `16px` to `8px` +- Placeholder now uses `var(--placeholder-color)` instead of `var(--secondary-text-color)` + +### Combobox + +- `padding-inline-start` reduced from `16px` to `8px` +- Placeholder now uses `var(--placeholder-color)` instead of `var(--secondary-text-color)` + +### TextWithHighlight + +> codemod: `TextWithHighlight-component-migration` + +- The `tooltipPosition` prop has been removed, use `tooltipProps={{ position: "value" }}` instead ๐Ÿ”€ + +### TipseenImage + +> codemod: `TipseenImage-component-migration` + +- `TipseenImage` component removed โ€” use `TipseenMedia` with an `` child instead ๐Ÿ”€ + +### Toggle + +> codemod: `Toggle-component-migration` + +- The `noSpacing` prop has been removed โ€” margin is auto-removed when `areLabelsHidden` is `true` ๐Ÿ”€ +- Duplicate `data-testid="toggle"` removed from internal visual div โ€” the test ID is now only on the input element + +### Tooltip + +- `modifiers` prop removed โ€” use `middleware` instead (same as Dialog) + +### VirtualizedGrid + +- `itemRenderer` return type corrected to `ReactElement` + +### VirtualizedList + +> codemod: `VirtualizedList-component-migration` + +- `getItemHeight` prop renamed to `getItemSize` ๐Ÿ”€ +- `onVerticalScrollbarVisiblityChange` prop renamed to `onLayoutDirectionScrollbarVisibilityChange` ๐Ÿ”€ + +## Hooks + +### useMergeRefs + +- Removed from `@vibe/core` โ€” use `react-merge-refs` or implement your own + +### useListenFocusTriggers + +- Removed from `@vibe/core` โ€” inline the logic using `useEventListener` + +### useActiveDescendantListFocus + +- `onItemClickCallback` and `createOnItemClickCallback` return values removed โ€” use `onItemClick` directly + +### useKeyEvent + +- `callback` type changed from `GenericEventCallback` to `KeyboardEventCallback` (native `KeyboardEvent`) + +## CSS and Design Tokens + +### Spacing Tokens + +- Deprecated semantic spacing tokens removed from `@vibe/style` ๐Ÿ”€ (stylelint auto-fix via `@vibe/style/use-new-spacing-tokens`) + - `--spacing-xs` โ†’ `--space-4` + - `--spacing-small` โ†’ `--space-8` + - `--spacing-medium` โ†’ `--space-16` + - `--spacing-large` โ†’ `--space-24` + - `--spacing-xl` โ†’ `--space-32` + - `--spacing-xxl` โ†’ `--space-48` + - `--spacing-xxxl` โ†’ `--space-64` + +For more info see the [Spacing page](https://vibe.monday.com/?path=/docs/foundations-spacing--spacing) diff --git a/packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx b/packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx new file mode 100644 index 0000000000..5f393e4225 --- /dev/null +++ b/packages/docs/src/pages/migration-guide/vibe-4-migration-guide.mdx @@ -0,0 +1,246 @@ +import { Meta } from "@storybook/blocks"; +import { Link, Tip } from "vibe-storybook-components"; +import { DiffCodeBlock } from "./DiffCodeBlock"; + + + +# Vibe 4 Migration Guide + +
    + +
    + +- [What's New โœจ](#whats-new-) +- [Migration Steps ๐Ÿš€](#migration-steps-) +- [Breaking Changes ๐Ÿšจ](#breaking-changes-) + - [Components](#components) + - [Hooks](#hooks) + - [CSS and Design Tokens](#css-and-design-tokens) +- [FAQ โ“](#faq-) +- [Help ๐Ÿ™](#help-) + +Vibe 4 is a major update to the Vibe design system, built for **React 19 compatibility**. It also delivers a leaner bundle, modern APIs, and stronger type safety. Legacy components, deprecated enums, and outdated dependencies have been removed in favor of simpler, more consistent alternatives. + + + +- From v2 to v3 see [Vibe 3 migration guide](https://vibe.monday.com/v3/?path=/docs/migration-guide--docs) +- From v1 to v2 see [Vibe 2 migration guide](https://github.com/mondaycom/vibe/tree/master/packages/docs/src/pages/migration-guide/vibe2-migration-guide.md) + + + +## What's New โœจ + +### React 19 support + +Vibe 4 is fully compatible with React 19. + +If your app is upgrading to React 19, you **must** upgrade to Vibe 4 โ€” Vibe 3 is not compatible with React 19. + +### New component implementations + +Several components have been completely rewritten with new APIs: + +- **Dropdown** - New custom implementation replacing the old `react-select`-based Dropdown +- **Modal** - New component with layout-based API (`ModalBasicLayout`, `ModalMediaLayout`, etc.) +- **AttentionBox** - New implementation with simplified props +- **DatePicker** - New implementation using native `Date` objects and `date-fns` instead of `moment` +- **Dialog/Tooltip/Tipseen** - Now powered by Floating UI (replacing Popper.js) + +### Smaller bundle + +Several internal runtime dependencies have been removed resulting in a significantly smaller bundle. + +### No more enums or static properties + +All deprecated enum exports (e.g. `ButtonType`, `DialogPositionEnum`) and static property accessors (e.g. `Button.sizes.LARGE`) have been removed in favor of string literals, which are type-safe, tree-shakeable, and provide the same IntelliSense experience. + +### Standard ARIA props + +Custom camelCase ARIA props (`ariaLabel`, `ariaHidden`, etc.) have been replaced with the standard HTML `aria-*` attributes that React natively supports, eliminating an unnecessary abstraction layer. + +## Migration Steps ๐Ÿš€ + +### With AI (recommended) + + + To migrate faster and easier using AI, make sure you have the [Vibe MCP server](?path=/docs/mcp-new--docs) installed. + + +1. Ask your AI assistant: + + ``` + Help me migrate this project from Vibe 3 to Vibe 4 using the Vibe MCP v4 migration tool + ``` + +2. This will scan your codebase, identify the breaking changes, run the codemods automatically, and will fix all the issues. + +3. **Review the changes and test your application thoroughly.** + +4. Run your code formatter to clean up any formatting changes from the codemod (optional). + +### Manually + +If you prefer to migrate manually, follow these steps: + +1. Update `@vibe/core`: + + ```bash + yarn add @vibe/core@^4.0.0 + ``` + +2. Run the automated migration codemod: + + ```bash + npx @vibe/codemod -m v4 + ``` + + The codemod handles enum-to-string-literal conversions, ARIA prop renames, Icon prop renames, and several component-specific migrations. For more options, see the [@vibe/codemod docs](https://github.com/mondaycom/vibe/blob/master/packages/codemod/README.md). + +3. Follow the [Breaking Changes](#breaking-changes-) section below and apply any remaining manual changes. + +4. **Review the changes and test your application thoroughly.** + +5. Run your code formatter to clean up any formatting changes from the codemod (optional). + +## Breaking Changes ๐Ÿšจ + +The following changes are **complementary to the migration codemod** and may require manual intervention. If you prefer to migrate entirely by hand (without `@vibe/codemod`), refer to the [Complete Vibe 4 changelog](https://github.com/mondaycom/vibe/blob/master/packages/docs/src/pages/migration-guide/v4-changelog.md). + +### Components + +**AttentionBox** + +- The component has been completely rewritten with a new API. Legacy `AttentionBox` removed, new implementation (previously at `@vibe/core/next`) promoted to `@vibe/core` +- `AttentionBoxLink` component removed, use the `link` prop instead +- Type values changed: `"success"` โ†’ `"positive"`, `"danger"` โ†’ `"negative"`, `"dark"` โ†’ `"neutral"` +- `entryAnimation` prop โ†’ `animate` +- `withoutIcon` / `withIconWithoutHeader` props removed โ€” use `icon={false}` + +**Clickable** + +- `ariaHasPopup` now accepts `boolean` only (was `boolean | string`) +- `tabIndex` now accepts `number` only (was `string | number`) + +**DatePicker** + +- The legacy DatePicker (based on `react-dates` and `moment.js`) has been replaced with a completely new component (previously at `@vibe/core/next`) with a different visual design, and promoted to `@vibe/core` +- `date` prop now uses native `Date` objects instead of `moment.Moment` +- `onPickDate` prop renamed to `onDateChange` +- `range` boolean prop replaced with `mode: "single" | "range"` +- `moment` no longer required as a peer dependency + +**Dialog** + +- The legacy class-based Dialog (Popper.js) has been replaced with a new functional Dialog (Floating UI) +- The `modifiers` prop (Popper.js) has been removed โ€” use [middleware](https://floating-ui.com/docs/middleware) prop (Floating UI) instead +- `addKeyboardHideShowTriggersByDefault` now defaults to `true` โ€” set it explicitly to `false` if you relied on the previous default + +**Dropdown** + +- The old `react-select`-based Dropdown has been removed. The new custom Dropdown (previously at `@vibe/core/next`) is now the default with a completely different API. See the [Dropdown Migration Guide](?path=/docs/components-dropdown-migration-guide--docs) for full details + +**Combobox** + +- The `Combobox` component is marked as **deprecated** and will be removed in a future version. Use [Dropdown with box mode](?path=/docs/components-dropdown--docs) instead + +**Icon** + +- `size` prop now applies to `type="src"` icons (previously only affected `type="svg"`) + +**MenuButton** + +- MenuButton now passes `focusItemIndexOnMount={0}` to its Menu children by default, so the first menu item is focused when the menu opens + +**MenuItem** + +- The `children` prop now accepts only a single `MenuChild`, not `MenuChild[]` (passing an array was already a runtime error in v3) + +**Modal** + +- The legacy Modal has been removed and replaced with a completely new Modal component with a different API (previously at `@vibe/core/next`) and promoted to `@vibe/core` + See the [Modal documentation](?path=/docs/components-modal--docs) for the full API + +**Steps** + +- The finish button now renders by default on the last step + +**Tipseen** + +- The `modifiers` prop (Popper.js) has been removed โ€” use [`middleware`](https://floating-ui.com/docs/middleware) instead (same as Dialog) + +**Tooltip** + +- The `modifiers` prop (Popper.js) has been removed โ€” use [`middleware`](https://floating-ui.com/docs/middleware) instead (same as Dialog) + +**Toggle** + +- The duplicate `data-testid="toggle"` has been removed from the internal visual `div` โ€” the test ID is now only on the `` element. Update any test selectors that targeted the visual div. + +**VirtualizedGrid** + +- `itemRenderer` return type corrected to `ReactElement` โ€” if TypeScript reports errors, ensure your renderer explicitly returns `ReactElement` + +### Hooks + +- **`useMergeRefs`** removed from `@vibe/core` โ€” use the [`react-merge-refs`](https://www.npmjs.com/package/react-merge-refs) package instead +- **`useListenFocusTriggers`** removed from `@vibe/core` โ€” inline the logic using `useEventListener` +- **`useActiveDescendantListFocus`** โ€” `onItemClickCallback` and `createOnItemClickCallback` return values removed, use `onItemClick` directly +- **`useKeyEvent`** โ€” `callback` type changed from `GenericEventCallback` to `KeyboardEventCallback` (native `KeyboardEvent`) + +### CSS and Design Tokens + +**Spacing Tokens** + +The deprecated semantic spacing CSS custom properties have been removed. Replace with numeric tokens: + +| Removed Token | Replacement | +| ------------------ | ------------ | +| `--spacing-xs` | `--space-4` | +| `--spacing-small` | `--space-8` | +| `--spacing-medium` | `--space-16` | +| `--spacing-large` | `--space-24` | +| `--spacing-xl` | `--space-32` | +| `--spacing-xxl` | `--space-48` | +| `--spacing-xxxl` | `--space-64` | + +**Auto-fix available**: Run `npx stylelint --fix "**/*.scss"` with the `@vibe/style/use-new-spacing-tokens` rule enabled. + +**Input Padding** + +`padding-inline-start` reduced from `16px` to `8px` across TextField, BaseInput, TextArea, and Dropdown Trigger. + +**Input Placeholder Color** + +Inputs now use `var(--placeholder-color)` instead of `var(--secondary-text-color)`. + +## FAQ โ“ + +**What is the best way to migrate to Vibe 4?** + +Follow the [Migration Steps](#migration-steps-) above. It is highly recommended to use the Vibe MCP to handle all the changes. + +**I was using enums like `Button.sizes.LARGE` โ€” what do I use now?** + +Use string literals: `"large"`. They are fully type-safe and provide the same IntelliSense autocomplete in your IDE. Run `npx @vibe/codemod -m enums` to migrate automatically. + +**I was importing components from `@vibe/core/next` โ€” what changed?** + +Most components from `@vibe/core/next` (AttentionBox, Dropdown, DatePicker, Dialog, Modal) have been promoted to `@vibe/core`. Update your imports to use `@vibe/core` directly and follow the API changes (if any). + +**Is Vibe 4 compatible with React 19?** + +Yes. Vibe 4 is fully compatible with React 19. Note that Vibe 3 is **not** compatible with React 19, so if you are upgrading to React 19, you must upgrade to Vibe 4. + +**What if I don't want to migrate to Vibe 4?** + +You can continue using Vibe 3 with React 16โ€“18, but it will only receive critical bug fixes. New features, improvements, and React 19 support are only available in Vibe 4. + +**The migration script failed โ€” what should I do?** + +Run the script in the root of your project on a clean git branch. If issues persist, please [report an issue](https://github.com/mondaycom/vibe/issues/new/choose) with the error message and your environment details. + +
    + +## Help ๐Ÿ™ + +If you have any questions, feedback, or need help, please don't hesitate to reach out. You can provide [feedback](https://github.com/mondaycom/vibe/discussions) or [report issues](https://github.com/mondaycom/vibe/issues/new/choose) in the [Vibe repository](https://github.com/mondaycom/vibe) on GitHub. diff --git a/packages/docs/src/pages/playground/playground-helpers.ts b/packages/docs/src/pages/playground/playground-helpers.ts index 6bfc74bb77..e962257fd2 100644 --- a/packages/docs/src/pages/playground/playground-helpers.ts +++ b/packages/docs/src/pages/playground/playground-helpers.ts @@ -31,7 +31,7 @@ const jsx = `() => { /> - + Playground @@ -63,7 +63,7 @@ const css = `.playground { .vibe-logo { width: 150px; - margin-block-end: var(--spacing-large); + margin-block-end: var(--space-24); transition: transform 0.3s ease, filter 0.3s ease; } @@ -73,7 +73,7 @@ const css = `.playground { } .count-button { - margin-block: var(--spacing-medium); + margin-block: var(--space-16); } `; diff --git a/packages/hooks/package.json b/packages/hooks/package.json index 2cdaddfd52..13379168d6 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/hooks", - "version": "3.0.3", + "version": "4.0.0-rc.0", "description": "Vibe sub-package for React hooks", "repository": { "type": "git", @@ -33,7 +33,7 @@ "lint": "eslint \"./src/**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { - "@vibe/shared": "3.0.8", + "@vibe/shared": "^4.0.0-rc.0", "classnames": "^2.5.1", "es-toolkit": "^1.39.10" }, @@ -41,11 +41,11 @@ "@testing-library/react": "^12.1.2", "@testing-library/react-hooks": "^7.0.2", "@testing-library/user-event": "^13.5.0", - "@vibe/config": "3.0.5", + "@vibe/config": "^4.0.0-rc.0", "react": "^16.13.0", "react-dom": "^16.13.0", "react-test-renderer": "16", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/icons/package.json b/packages/icons/package.json index cab0f10de7..351b30c2a8 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/icons", - "version": "1.16.0", + "version": "4.0.0-rc.0", "description": "Vibe's Icon Library", "repository": { "type": "git", @@ -85,7 +85,7 @@ "rollup-plugin-terser": "^7.0.2", "rollup-plugin-typescript2": "^0.34.1", "svg2react-icon": "^3.1.178", - "typescript": "^4.7.3", + "typescript": "^5.9.3", "vitest": "^1.6.0" }, "peerDependencies": { diff --git a/packages/mcp/README.md b/packages/mcp/README.md index e34c5a19e0..6ccfec24b9 100644 --- a/packages/mcp/README.md +++ b/packages/mcp/README.md @@ -61,7 +61,7 @@ or manually update your VSCode MCP configuration file at `.vscode/mcp.json` (cre #### Token Tool -- `list-vibe-tokens`: Get a list of all available Vibe design tokens from the `monday-ui-style` package. Supports optional filtering by text query, category, or limiting results. Returns token names, values, categories, and files, with optional CSS usage examples. +- `list-vibe-tokens`: Get a list of all available Vibe design tokens from the `@vibe/style` package. Supports optional filtering by text query, category, or limiting results. Returns token names, values, categories, and files, with optional CSS usage examples. #### Migration Tool diff --git a/packages/mcp/package.json b/packages/mcp/package.json index 6ed7166caa..754b51b206 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/mcp", - "version": "0.8.2", + "version": "4.0.0-rc.0", "description": "Vibe's MCP server to interact with the Vibe ecosystem", "repository": { "type": "git", diff --git a/packages/mcp/src/index.ts b/packages/mcp/src/index.ts index 66e5171202..d36d67b29c 100644 --- a/packages/mcp/src/index.ts +++ b/packages/mcp/src/index.ts @@ -10,6 +10,7 @@ import { listVibeIconsTool } from "./server/tools/list-vibe-icons.js"; import { listVibeTokensTool } from "./server/tools/list-vibe-tokens.js"; import { v3MigrationTool } from "./server/tools/v3-migration.js"; import { dropdownMigrationTool } from "./server/tools/dropdown-migration.js"; +import { v4MigrationTool } from "./server/tools/v4-migration.js"; async function main() { const transport = new StdioServerTransport(); @@ -21,6 +22,7 @@ async function main() { addServerTool(listVibeTokensTool); addServerTool(v3MigrationTool); addServerTool(dropdownMigrationTool); + addServerTool(v4MigrationTool); await server.connect(transport); } diff --git a/packages/mcp/src/server/tools/dropdown-migration.ts b/packages/mcp/src/server/tools/dropdown-migration.ts index 090cca06d7..68e107af62 100644 --- a/packages/mcp/src/server/tools/dropdown-migration.ts +++ b/packages/mcp/src/server/tools/dropdown-migration.ts @@ -121,10 +121,10 @@ function getDropdownMigrationInstructions(projectInfo: any) { { step: 2, title: "Update Import Statements", - action: "Change import path from '@vibe/core' to '@vibe/core/next'", - example: "import { Dropdown } from '@vibe/core/next';", - description: "Update all Dropdown imports to use the new Alpha version", - command: "Find and replace: 'from \"@vibe/core\"' โ†’ 'from \"@vibe/core/next\"' (for Dropdown imports only)" + action: "Import Dropdown from '@vibe/core'", + example: "import { Dropdown } from '@vibe/core';", + description: "In v4, the new Dropdown is the default export from @vibe/core. If you were using @vibe/core/next, update to @vibe/core.", + command: "Find and replace: 'from \"@vibe/core/next\"' โ†’ 'from \"@vibe/core\"' (for Dropdown imports)" }, { step: 3, @@ -165,9 +165,9 @@ function getDropdownMigrationInstructions(projectInfo: any) { }, breakingChanges: { importPath: { - old: "import { Dropdown } from '@vibe/core';", - new: "import { Dropdown } from '@vibe/core/next';", - reason: "New Dropdown is available in Alpha under /next path" + old: "import { Dropdown } from '@vibe/core'; // v3 (react-select based)", + new: "import { Dropdown } from '@vibe/core'; // v4 (new implementation)", + reason: "The old react-select Dropdown has been removed in v4. The new Dropdown is now the default export." }, optionStructure: { old: "{ id: 1, text: 'Option 1' }", @@ -480,10 +480,10 @@ function generateRecommendations(analysis: any, projectInfo: any) { recommendations.push({ type: "import-migration", priority: "high", - action: "Update Dropdown imports to use Alpha version", + action: "Update Dropdown imports for v4", details: `Found ${imports.oldDropdownImports.length} files importing Dropdown from '@vibe/core'`, files: imports.oldDropdownImports.slice(0, 10), - fix: "Change 'from \"@vibe/core\"' to 'from \"@vibe/core/next\"' for Dropdown imports", + fix: "The Dropdown API has changed in v4. Update usage to match the new API (see migration guide).", command: "Find and replace in files with Dropdown imports" }); } diff --git a/packages/mcp/src/server/tools/list-vibe-icons.ts b/packages/mcp/src/server/tools/list-vibe-icons.ts index afa0c0ae27..cf50b16f64 100644 --- a/packages/mcp/src/server/tools/list-vibe-icons.ts +++ b/packages/mcp/src/server/tools/list-vibe-icons.ts @@ -62,7 +62,7 @@ export const listVibeIconsTool: MCPTool = "Import Icon Component": `import { Icon } from "@vibe/core";`, "Import Icon": `import { ${icon.name} } from "@vibe/icons";`, "Basic Usage": ``, - "With Props": `` + "With Props": `` } }; } diff --git a/packages/mcp/src/server/tools/token-metadata-service.ts b/packages/mcp/src/server/tools/token-metadata-service.ts index c271a8efd7..c7c31bd3ff 100644 --- a/packages/mcp/src/server/tools/token-metadata-service.ts +++ b/packages/mcp/src/server/tools/token-metadata-service.ts @@ -13,7 +13,7 @@ export interface TokenMetadata { export class TokenMetadataService { private static tokenCache: TokenMetadata[] | null = null; - private static readonly TOKENS_URL = "https://unpkg.com/monday-ui-style@latest/dist/index.css"; + private static readonly TOKENS_URL = "https://unpkg.com/@vibe/style@latest/dist/index.css"; /** * Get all design tokens from the style package diff --git a/packages/mcp/src/server/tools/v4-migration.ts b/packages/mcp/src/server/tools/v4-migration.ts new file mode 100644 index 0000000000..46fc7ad497 --- /dev/null +++ b/packages/mcp/src/server/tools/v4-migration.ts @@ -0,0 +1,1230 @@ +import { getErrorMessage, MCPTool } from "../index.js"; +import { z } from "zod"; +import { readFileSync, existsSync, readdirSync, statSync } from "fs"; +import { join, resolve, extname } from "path"; + +// โ”€โ”€ Promoted Components (old @vibe/core โ†’ new @vibe/core/next in v3, now default in v4) โ”€โ”€ + +const PROMOTED_COMPONENTS = ["Dropdown", "AttentionBox", "Modal", "DatePicker", "Dialog"] as const; + +interface OldComponentDetection { + component: string; + file: string; + line: number; + importSource: "old" | "new"; + importStatement: string; +} + +interface PromotedComponentAnalysis { + oldApiUsage: OldComponentDetection[]; + newApiUsage: OldComponentDetection[]; + summary: Record; +} + +const migrationGuideSchema = z.object({ + projectPath: z.string().describe("Path to the project directory to analyze for migration") +}); + +export const v4MigrationTool: MCPTool = { + name: "v4-migration", + description: + "Analyzes a project for Vibe 3 to Vibe 4 migration requirements. Scans for deprecated APIs, removed props, renamed components, and generates migration commands with file-level before/after replacements.", + inputSchema: migrationGuideSchema.shape, + execute: async input => { + try { + const { projectPath } = input; + const resolvedPath = resolve(projectPath); + + if (!existsSync(resolvedPath)) { + return { + content: [ + { + type: "text", + text: `Error: Project path "${resolvedPath}" does not exist.` + } + ], + isError: true + }; + } + + const projectInfo = { + targetDirectory: resolvedPath + }; + + const migrationData = getMigrationInstructions(projectInfo); + const analysis = await analyzeProject(resolvedPath); + + const result = { + migrationGuide: migrationData, + promotedComponentMigrationGuide: getPromotedComponentMigrationGuide(), + projectInfo, + projectAnalysis: analysis, + recommendations: generateRecommendations(analysis, projectInfo) + }; + + return { + content: [ + { + type: "text", + text: JSON.stringify(result, null, 2) + } + ] + }; + } catch (e) { + const errorMessage = getErrorMessage(e); + return { + content: [ + { + type: "text", + text: `Error in vibe-migration-tool: ${errorMessage}` + } + ], + isError: true + }; + } + } +}; + +// โ”€โ”€ Migration Instructions โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +function getMigrationInstructions(projectInfo: { targetDirectory: string }) { + return { + overview: { + description: "Vibe 4 is a major update focused on React 19 support, ~170KB smaller bundle, and modern API patterns", + keyChanges: [ + "React 19 support โ€” findDOMNode, class components, and framer-motion removed", + "~170KB smaller bundle (min+gzip) from removing 8 heavy dependencies", + "All deprecated enum exports and static properties removed โ€” use string literals", + "ARIA props standardized from camelCase to aria-* attributes", + "Package rename: monday-ui-style โ†’ @vibe/style", + "@vibe/core/next components promoted to @vibe/core (except List)", + "moment removed as peer dependency โ€” DatePicker uses native Date + date-fns" + ] + }, + packageChanges: { + upgrade: ["@vibe/core@^4"], + replace: [{ from: "monday-ui-style", to: "@vibe/style", note: "Uncommon โ€” only a few consumers use this directly" }], + description: "Upgrade @vibe/core. The project analysis below will detect any dependencies that need to be removed or replaced." + }, + importChanges: { + styleImports: { + from: "monday-ui-style", + to: "@vibe/style" + }, + nextComponents: { + from: "@vibe/core/next", + to: "@vibe/core", + components: ["AttentionBox", "Dropdown", "DatePicker", "Dialog", "Modal"], + exception: "List remains in @vibe/core/next" + } + }, + removedComponents: [ + { name: "TipseenImage", replacement: "TipseenMedia with child" }, + { name: "AttentionBoxLink", replacement: "AttentionBox link prop" }, + { name: "DropdownMenu", replacement: "New Dropdown API" }, + { name: "DropdownOption", replacement: "New Dropdown API" }, + { name: "DropdownSingleValue", replacement: "New Dropdown API" }, + { name: "Combobox", replacement: "Dropdown with box mode (from @vibe/core)" } + ], + breakingChanges: { + AttentionBox: { + changes: [ + 'type values: "success" โ†’ "positive", "danger" โ†’ "negative", "dark" โ†’ "neutral"', + "entryAnimation โ†’ animate", + "withoutIcon / withIconWithoutHeader โ†’ icon={false}", + "AttentionBoxLink removed โ€” use link prop" + ] + }, + Chips: { + changes: ["disableClickableBehavior prop removed"] + }, + Clickable: { + changes: [ + "ariaHasPopup now accepts boolean only (was boolean | string)", + "tabIndex now accepts number only (was string | number)" + ] + }, + CustomSvgIcon: { + changes: ["onClick and clickable props removed โ€” wrap with a clickable element"] + }, + DatePicker: { + changes: [ + "date prop: moment.Moment โ†’ Date", + "onPickDate โ†’ onDateChange", + 'range boolean โ†’ mode: "single" | "range"', + "moment no longer required as peer dependency" + ] + }, + Dialog: { + changes: [ + "modifiers prop removed โ€” use middleware (Floating UI)", + "enableNestedDialogLayer removed โ€” LayerProvider always used", + "addKeyboardHideShowTriggersByDefault default changed to true", + "Refable component removed from public exports" + ] + }, + Dropdown: { + changes: [ + "Options: { id, text } โ†’ { value, label }", + "Sub-components removed: DropdownMenu, DropdownOption, DropdownSingleValue" + ] + }, + Flex: { + changes: ['"stretch" value removed from justify prop'] + }, + Icon: { + changes: ["iconLabel โ†’ label", "iconType โ†’ type", "iconSize โ†’ size", 'size prop now applies to type="src" icons'] + }, + LinearProgressBar: { + changes: ["Renamed to ProgressBar", "LinearProgressBarProps โ†’ ProgressBarProps"] + }, + Link: { + changes: ["@supports CSS fallback removed for logical properties"] + }, + MenuButton: { + changes: ["focusItemIndexOnMount now defaults to 0"] + }, + MenuItem: { + changes: ["children accepts only single MenuChild, not array"] + }, + Modal: { + changes: ["Legacy Modal removed โ€” new functional Modal promoted from @vibe/core/next"] + }, + Steps: { + changes: ["Finish button renders by default on last step"] + }, + TextField: { + changes: ["iconName โ†’ icon", "iconsNames โ†’ flat iconLabel and secondaryIconLabel props"] + }, + TextWithHighlight: { + changes: ['tooltipPosition removed โ€” use tooltipProps={{ position }}'] + }, + Tipseen: { + changes: ["modifiers prop removed โ€” use middleware"] + }, + TipseenImage: { + changes: ["Removed โ€” use TipseenMedia with child"] + }, + Toggle: { + changes: ["noSpacing prop removed โ€” auto-removed when areLabelsHidden is true"] + }, + Tooltip: { + changes: ["TooltipProps extends DialogProps", "modifiers removed โ€” use middleware"] + }, + VirtualizedList: { + changes: ["getItemHeight prop removed", "onVerticalScrollbarVisiblityChange prop removed"] + }, + VirtualizedGrid: { + changes: ["itemRenderer return type corrected to ReactElement"] + } + }, + hookChanges: { + useMergeRefs: { status: "removed", replacement: "Use react-merge-refs or implement your own" }, + useListenFocusTriggers: { status: "removed", replacement: "Inline the logic using useEventListener" }, + useActiveDescendantListFocus: { + status: "changed", + changes: ["onItemClickCallback and createOnItemClickCallback removed โ€” use onItemClick directly"] + }, + useKeyEvent: { + status: "changed", + changes: ["callback type: GenericEventCallback โ†’ KeyboardEventCallback (native KeyboardEvent)"] + } + }, + typeChanges: { + VibeComponent: { status: "removed", replacement: "Use forwardRef and let TypeScript infer" }, + withStaticProps: { status: "removed", replacement: "No replacement โ€” static properties removed in v4" }, + withStaticPropsWithoutForwardRef: { status: "removed", replacement: "No replacement" } + }, + cssChanges: { + spacingTokens: { + "--spacing-xs": "--space-4", + "--spacing-small": "--space-8", + "--spacing-medium": "--space-16", + "--spacing-large": "--space-24", + "--spacing-xl": "--space-32", + "--spacing-xxl": "--space-48", + "--spacing-xxxl": "--space-64" + }, + inputPadding: "padding-inline-start reduced from 16px to 8px", + placeholderColor: "Inputs use var(--placeholder-color) instead of var(--secondary-text-color)" + }, + migrationSteps: { + note: "โš ๏ธ IMPORTANT: Follow these steps sequentially. Complete each step fully before proceeding to the next.", + steps: [ + { + step: 1, + title: "Analyze Project", + action: "Run detailed analysis to understand what needs to be migrated", + description: "Use this migration tool to understand the scope of changes needed", + details: [ + "Check package.json dependencies", + "Scan for deprecated APIs, removed props, and enum usage", + "Identify components with breaking changes", + "Check for ARIA prop usage and CSS token changes" + ], + note: "This analysis is already being performed by this tool - review the results carefully" + }, + { + step: 2, + title: "Update Package Dependencies", + action: "Upgrade @vibe/core, replace monday-ui-style, remove unused dependencies", + command: "yarn add @vibe/core@^4 && yarn remove moment react-select", + description: "Update package.json and install new Vibe 4 packages", + important: "Do NOT proceed to step 3 until this completes successfully" + }, + { + step: 3, + title: "Migrate Old Components to New API", + action: "Identify and migrate components that had old/new versions in Vibe 3", + description: + "In Vibe 3, Dropdown, AttentionBox, Modal, DatePicker, and Dialog had OLD versions in @vibe/core and NEW versions in @vibe/core/next. " + + "In Vibe 4, the NEW versions are now the default in @vibe/core. If you were importing these from @vibe/core (old API), " + + "your code now silently uses a completely different component with a different API.", + details: [ + "Check the promotedComponentAnalysis section in the project analysis output", + "Components imported from @vibe/core (old API) need manual migration to the new API", + "Components imported from @vibe/core/next (new API) only need the import path updated (handled by codemod)", + "For Dropdown migration, use the dedicated 'dropdown-migration' tool for detailed guidance", + "Review the promotedComponentMigrationGuide for before/after examples per component" + ], + important: "โš ๏ธ This step MUST be completed BEFORE running codemods. The codemods update import paths but do NOT transform old API usage to new API usage." + }, + { + step: 4, + title: "Run Automated Migration", + action: `Run migration script: npx @vibe/codemod -m v4 --target "${projectInfo.targetDirectory}" --extensions tsx jsx -y`, + command: `npx @vibe/codemod -m v4 --target "${projectInfo.targetDirectory}" --extensions tsx jsx -y`, + description: "Handles enumโ†’string, ARIA props, import renames, and component prop changes automatically.", + important: "โš ๏ธ Complete step 3 (promoted component migration) BEFORE running this. After this script rewrites imports, old vs new API usage becomes indistinguishable.", + codemods: [ + "Enums โ†’ string literals (Button.sizes.LARGE โ†’ \"large\")", + "ARIA camelCase โ†’ aria-* attributes", + "Icon prop renames (iconLabel โ†’ label, iconType โ†’ type, iconSize โ†’ size)", + "TextField prop renames (iconName โ†’ icon, iconsNames โ†’ flat props)", + "LinearProgressBar โ†’ ProgressBar rename", + "@vibe/core/next โ†’ @vibe/core import rewrite (List stays in /next)", + "monday-ui-style โ†’ @vibe/style import rewrite", + "Flex stretch removal, Toggle noSpacing removal, Chips disableClickableBehavior removal", + "Dialog enableNestedDialogLayer removal", + "TipseenImage โ†’ TipseenMedia, TextWithHighlight tooltipPosition โ†’ tooltipProps" + ] + }, + { + step: 5, + title: "Manual Review and Fixes", + action: "Apply changes that codemods cannot handle", + description: "Use the projectAnalysis.manualChanges results to find and fix these issues", + details: [ + "DatePicker: Replace moment objects with native Date, onPickDate โ†’ onDateChange, range โ†’ mode", + "Dropdown: Update options from { id, text } to { value, label }", + "Dialog/Tooltip/Tipseen: Replace modifiers with middleware (@floating-ui/react-dom)", + "CustomSvgIcon: Wrap with // or use ' + }); + } + + // AttentionBox: type value changes + if (content.includes("AttentionBox")) { + const typeMatch = line.match(/type\s*=\s*["'](success|danger|dark)["']/); + if (typeMatch) { + const oldType = typeMatch[1]; + const newType = oldType === "success" ? "positive" : oldType === "danger" ? "negative" : "neutral"; + manualChanges.attentionBoxTypeValues.push({ + file, line: i + 1, match: line.trim(), + before: `type="${oldType}"`, + after: `type="${newType}"` + }); + } + if (line.includes("entryAnimation")) { + manualChanges.attentionBoxProps.push({ + file, line: i + 1, match: line.trim(), before: "entryAnimation", after: "animate" + }); + } + if (line.includes("withoutIcon") || line.includes("withIconWithoutHeader")) { + manualChanges.attentionBoxProps.push({ + file, line: i + 1, match: line.trim(), before: "withoutIcon", after: "icon={false}" + }); + } + } + + // AttentionBoxLink + if (line.includes("AttentionBoxLink")) { + manualChanges.attentionBoxLink.push({ + file, line: i + 1, match: line.trim(), + before: '', + after: '// use AttentionBox link prop: link={{ href: "/docs", text: "Learn more" }}' + }); + } + + // Modal from @vibe/core/next + if (line.includes("Modal") && line.includes("@vibe/core/next")) { + manualChanges.modalFromNext.push({ + file, line: i + 1, match: line.trim(), + before: 'import { Modal } from "@vibe/core/next"', + after: 'import { Modal } from "@vibe/core" // review ModalHeader/ModalContent/ModalFooter API changes' + }); + } + + // useMergeRefs + if (line.includes("useMergeRefs")) { + manualChanges.useMergeRefs.push({ + file, line: i + 1, match: line.trim(), + before: 'import { useMergeRefs } from "@vibe/core";\nconst ref = useMergeRefs({ refs: [ref1, ref2] });', + after: 'import { mergeRefs } from "react-merge-refs";\nconst ref = mergeRefs([ref1, ref2]);' + }); + } + + // useListenFocusTriggers + if (line.includes("useListenFocusTriggers")) { + manualChanges.useListenFocusTriggers.push({ + file, line: i + 1, match: line.trim(), + before: "useListenFocusTriggers({ ref, onFocusByKeyboard, onFocusByMouse })", + after: 'Inline using useEventListener:\nuseEventListener({ eventName: "focusin", callback: ..., ref });\nuseEventListener({ eventName: "mousedown", callback: ..., ref });' + }); + } + + // VibeComponent type + if (/\bVibeComponent\b/.test(line) && !line.includes("VibeComponentProps")) { + manualChanges.vibeComponentType.push({ + file, line: i + 1, match: line.trim(), + before: "const MyComp: VibeComponent = forwardRef(...)", + after: "const MyComp = forwardRef(...) // let TS infer" + }); + } + + // withStaticProps + if (line.includes("withStaticProps")) { + manualChanges.withStaticPropsUsage.push({ + file, line: i + 1, match: line.trim(), + before: "export default withStaticProps(MyComponent, { sizes: MySizes })", + after: "export default MyComponent // static properties removed in v4" + }); + } + + // useActiveDescendantListFocus removed callbacks + if (line.includes("onItemClickCallback") || line.includes("createOnItemClickCallback")) { + manualChanges.useActiveDescendantCallbacks.push({ + file, line: i + 1, match: line.trim(), + before: "const { onItemClickCallback } = useActiveDescendantListFocus(...)", + after: "// use onItemClick directly instead" + }); + } + + // useKeyEvent callback type + if (line.includes("GenericEventCallback") && content.includes("useKeyEvent")) { + manualChanges.useKeyEventCallback.push({ + file, line: i + 1, match: line.trim(), + before: "callback: GenericEventCallback", + after: "callback: KeyboardEventCallback // native KeyboardEvent" + }); + } + } +} + +// โ”€โ”€ Promoted Component Detection โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +/** + * Collapses multi-line imports into single logical lines, preserving the + * start line number of the `import` keyword. + * + * e.g. + * import { <- line 3 + * Dropdown, + * Button + * } from "@vibe/core"; + * + * becomes: { text: 'import { Dropdown, Button } from "@vibe/core";', line: 3 } + */ +function collapseImportLines(lines: string[]): Array<{ text: string; line: number }> { + const result: Array<{ text: string; line: number }> = []; + let i = 0; + + while (i < lines.length) { + const line = lines[i]; + + if (/^\s*import\s+\{/.test(line)) { + const startLine = i + 1; // 1-based + let accumulated = line; + + // If the closing brace and `from` are not on this line, keep accumulating + while (!/\}\s*from\s+["']/.test(accumulated) && i + 1 < lines.length) { + i++; + accumulated = accumulated.trimEnd() + " " + lines[i].trim(); + } + + result.push({ text: accumulated, line: startLine }); + } else { + result.push({ text: line, line: i + 1 }); + } + + i++; + } + + return result; +} + +function detectPromotedComponentImports(files: string[]): PromotedComponentAnalysis { + const oldApiUsage: OldComponentDetection[] = []; + const newApiUsage: OldComponentDetection[] = []; + const summary: PromotedComponentAnalysis["summary"] = {}; + + for (const comp of PROMOTED_COMPONENTS) { + summary[comp] = { oldCount: 0, newCount: 0, oldFiles: [], newFiles: [] }; + } + + // Match import statements from @vibe/core or @vibe/core/next (both quote styles) + const importRegex = /import\s+\{([^}]+)\}\s+from\s+["'](@vibe\/core(?:\/next)?)["']/; + + for (const file of files) { + try { + const content = readFileSync(file, "utf-8"); + const lines = content.split("\n"); + const logicalLines = collapseImportLines(lines); + + for (const { text, line } of logicalLines) { + const match = importRegex.exec(text); + if (!match) continue; + + const importedNames = match[1].split(",").map(s => s.trim().split(/\s+as\s+/)[0].trim()).filter(Boolean); + const source = match[2]; + const isNext = source === "@vibe/core/next"; + + for (const name of importedNames) { + if ((PROMOTED_COMPONENTS as readonly string[]).includes(name)) { + const detection: OldComponentDetection = { + component: name, + file, + line, + importSource: isNext ? "new" : "old", + importStatement: text.trim() + }; + + if (isNext) { + newApiUsage.push(detection); + summary[name].newCount++; + if (!summary[name].newFiles.includes(file)) { + summary[name].newFiles.push(file); + } + } else { + oldApiUsage.push(detection); + summary[name].oldCount++; + if (!summary[name].oldFiles.includes(file)) { + summary[name].oldFiles.push(file); + } + } + } + } + } + } catch { + // Skip unreadable files + } + } + + return { oldApiUsage, newApiUsage, summary }; +} + +function getPromotedComponentMigrationGuide() { + return { + description: + "In Vibe 3, these components had OLD versions in @vibe/core and NEW versions in @vibe/core/next. " + + "In Vibe 4, the NEW versions are now the default in @vibe/core. If you were using the OLD version " + + "from @vibe/core, your import path is now 'correct' but points to a completely different component with a different API.", + components: { + Dropdown: { + severity: "critical" as const, + migrateTool: "dropdown-migration", + note: "Use the 'dropdown-migration' tool for complete Dropdown migration guidance including API changes, before/after examples, and step-by-step instructions." + }, + AttentionBox: { + severity: "high" as const, + apiChanges: [ + 'type values renamed: "success" โ†’ "positive", "danger" โ†’ "negative", "dark" โ†’ "neutral"', + "entryAnimation โ†’ animate", + "withoutIcon / withIconWithoutHeader โ†’ icon={false}", + "AttentionBoxLink removed โ€” use link prop" + ], + before: `import { AttentionBox } from "@vibe/core";\n`, + after: `import { AttentionBox } from "@vibe/core";\n` + }, + Modal: { + severity: "critical" as const, + apiChanges: [ + "id prop is now required", + "show prop controls visibility (replaces external state pattern)", + "width โ†’ size (\"small\" | \"medium\" | \"large\")", + "Layout via ModalHeader, ModalContent, ModalFooter sub-components", + "ModalFooterButtons removed โ€” use ModalFooter with Button children" + ], + before: `import { Modal } from "@vibe/core";\n{content}`, + after: `import { Modal } from "@vibe/core";\n\n \n {content}\n` + }, + DatePicker: { + severity: "critical" as const, + apiChanges: [ + "date prop: moment.Moment โ†’ native Date", + "onPickDate โ†’ onDateChange", + 'range boolean โ†’ mode: "single" | "range"', + "shouldBlockDay โ†’ isDateDisabled", + "moment no longer a peer dependency โ€” uses date-fns internally" + ], + before: `import { DatePicker } from "@vibe/core";\n`, + after: `import { DatePicker } from "@vibe/core";\n` + }, + Dialog: { + severity: "high" as const, + apiChanges: [ + "modifiers โ†’ middleware (uses @floating-ui/react-dom)", + "enableNestedDialogLayer removed โ€” LayerProvider is always used", + "addKeyboardHideShowTriggersByDefault default changed to true" + ], + before: `import { Dialog } from "@vibe/core";\n`, + after: `import { Dialog } from "@vibe/core";\n // import { offset } from "@floating-ui/react-dom"` + } + } + }; +} + +// โ”€โ”€ File Discovery โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +function getAllSourceFiles(dir: string): string[] { + const files: string[] = []; + const validExtensions = [".ts", ".tsx", ".js", ".jsx", ".css", ".scss"]; + + function scanDirectory(currentDir: string, depth = 0) { + if (depth > 5) return; + try { + const entries = readdirSync(currentDir); + for (const entry of entries) { + if (entry.startsWith(".") || entry === "node_modules" || entry === "dist" || entry === "build") continue; + const fullPath = join(currentDir, entry); + try { + const stat = statSync(fullPath); + if (stat.isDirectory()) { + scanDirectory(fullPath, depth + 1); + } else if (stat.isFile() && validExtensions.includes(extname(entry))) { + files.push(fullPath); + } + } catch { /* skip */ } + } + } catch { /* skip */ } + } + + scanDirectory(dir); + return files; +} + +// โ”€โ”€ Recommendations โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + +function generateRecommendations(analysis: any, projectInfo: { targetDirectory: string }) { + const recommendations = []; + + // Package recommendations + if (analysis.packageJsonAnalysis) { + const pkg = analysis.packageJsonAnalysis; + + if (pkg.migrationStatus === "needs-migration") { + recommendations.push({ + type: "package-upgrade", + priority: "high", + action: "Upgrade @vibe/core to v4", + command: "yarn add @vibe/core@^4", + details: "This is the first step in the migration process" + }); + } + + if (pkg.dependencyActions.length > 0) { + recommendations.push({ + type: "dependency-cleanup", + priority: "high", + action: "Remove/replace deprecated dependencies", + details: `${pkg.dependencyActions.length} dependency changes needed`, + commands: pkg.dependencyActions.map((d: any) => d.command) + }); + } + } + + // Import recommendations + if (analysis.importAnalysis) { + const imports = analysis.importAnalysis; + + if (imports.mondayUIStyleImports?.length > 0) { + recommendations.push({ + type: "style-imports", + priority: "medium", + action: "Update monday-ui-style imports to @vibe/style", + details: `Found ${imports.mondayUIStyleImports.length} files. Note: most consumers don't use monday-ui-style directly.`, + files: imports.mondayUIStyleImports.slice(0, 5), + fix: "Handled by codemod: packages-rename-migration" + }); + } + + if (imports.nextImports?.length > 0) { + recommendations.push({ + type: "next-imports", + priority: "high", + action: "Update @vibe/core/next imports to @vibe/core", + details: `Found ${imports.nextImports.length} files`, + files: imports.nextImports.slice(0, 5), + fix: "Handled by codemod: next-imports-migration (List stays in /next)" + }); + } + + if (imports.ariaPropsUsage?.length > 0) { + recommendations.push({ + type: "aria-props", + priority: "high", + action: "Update camelCase ARIA props to standard aria-* attributes", + details: `Found ${imports.ariaPropsUsage.length} files`, + files: imports.ariaPropsUsage.slice(0, 5), + fix: "Handled by codemod: Aria-props-migration" + }); + } + + if (imports.enumUsage?.length > 0) { + recommendations.push({ + type: "enum-usage", + priority: "high", + action: "Replace enum static properties with string literals", + details: `Found ${imports.enumUsage.length} files`, + files: imports.enumUsage.slice(0, 5), + fix: "Handled by codemod: Enums-migration" + }); + } + } + + // Component usage recommendations + if (analysis.componentUsage) { + const usage = analysis.componentUsage; + + Object.entries(usage.renamedComponents || {}).forEach(([component, files]: [string, any]) => { + recommendations.push({ + type: "renamed-component", + priority: "high", + action: `Rename ${component} โ†’ ${getComponentReplacement(component)}`, + details: `Found ${files.length} files`, + files: files.slice(0, 5), + fix: "Handled by codemod" + }); + }); + + Object.entries(usage.removedComponents || {}).forEach(([component, files]: [string, any]) => { + recommendations.push({ + type: "removed-component", + priority: "high", + action: `Replace removed component: ${component}`, + details: `Found ${files.length} files`, + files: files.slice(0, 5), + fix: getComponentReplacementDetails(component) + }); + }); + + Object.entries(usage.breakingChangeComponents || {}).forEach(([component, files]: [string, any]) => { + recommendations.push({ + type: "breaking-change", + priority: "medium", + action: `Review ${component} usage`, + details: `Found ${files.length} files using ${component} - check for breaking changes`, + files: files.slice(0, 5), + fix: getBreakingChangeDetails(component) + }); + }); + } + + // Manual changes recommendations + if (analysis.manualChanges) { + const manual = analysis.manualChanges; + const manualIssueCount = Object.values(manual).reduce((sum: number, arr: any) => sum + arr.length, 0); + + if (manualIssueCount > 0) { + recommendations.push({ + type: "manual-changes", + priority: "high", + action: "Apply manual changes that codemods cannot handle", + details: `${manualIssueCount} issues found across ${Object.entries(manual).filter(([, arr]: [string, any]) => arr.length > 0).length} categories`, + categories: Object.entries(manual) + .filter(([, arr]: [string, any]) => arr.length > 0) + .map(([key, arr]: [string, any]) => `${key}: ${arr.length} issues`), + important: "Review the manualChanges section for file:line locations and before/after replacements" + }); + } + } + + // Promoted component recommendations + if (analysis.promotedComponentAnalysis) { + const promoted = analysis.promotedComponentAnalysis as PromotedComponentAnalysis; + const guide = getPromotedComponentMigrationGuide(); + + for (const [component, counts] of Object.entries(promoted.summary)) { + const componentGuide = guide.components[component as keyof typeof guide.components]; + if (!componentGuide) continue; + + if (counts.oldCount > 0) { + const recommendation: any = { + type: "promoted-component-old-api", + priority: componentGuide.severity, + action: `Migrate ${component} from old API to new API`, + details: + `Found ${counts.oldCount} import(s) of ${component} from @vibe/core (old API) across ${counts.oldFiles.length} file(s). ` + + `After upgrading to Vibe 4, these imports will silently point to the NEW ${component} which has a different API.`, + files: counts.oldFiles.slice(0, 10), + important: "โš ๏ธ Must be resolved BEFORE running codemods (step 3 must be done before step 4)" + }; + + if ("migrateTool" in componentGuide) { + recommendation.migrateTool = componentGuide.migrateTool; + recommendation.note = componentGuide.note; + } else { + recommendation.apiChanges = componentGuide.apiChanges; + recommendation.before = componentGuide.before; + recommendation.after = componentGuide.after; + } + + recommendations.push(recommendation); + } + + if (counts.newCount > 0) { + recommendations.push({ + type: "promoted-component-new-api", + priority: "info", + action: `${component}: already using new API from @vibe/core/next`, + details: + `Found ${counts.newCount} import(s) of ${component} from @vibe/core/next (new API) across ${counts.newFiles.length} file(s). ` + + `The codemod will automatically update the import path from @vibe/core/next to @vibe/core.`, + files: counts.newFiles.slice(0, 10), + fix: "Handled by codemod: next-imports-migration" + }); + } + } + } + + // Migration script + recommendations.push({ + type: "migration-script", + priority: "high", + action: "Run automated migration script", + command: `npx @vibe/codemod -m v4 --target "${projectInfo.targetDirectory}" --extensions tsx jsx -y`, + details: "Handles enums, ARIA props, import renames, and component prop migrations automatically", + warning: "โš ๏ธ ONLY run this AFTER completing promoted component migration (step 3). After codemods rewrite imports, old vs new API usage becomes indistinguishable." + }); + + // CSS tokens + if (analysis.manualChanges?.spacingTokens?.length > 0) { + recommendations.push({ + type: "css-tokens", + priority: "medium", + action: "Update deprecated CSS spacing tokens", + details: `Found ${analysis.manualChanges.spacingTokens.length} uses of deprecated --spacing-* tokens`, + fix: "Replace --spacing-xs/small/medium/large/xl/xxl/xxxl with --space-4/8/16/24/32/48/64" + }); + } + + recommendations.push({ + type: "manual-review", + priority: "medium", + action: "Manual review and fixes", + details: "Review breaking changes and apply manual fixes", + warning: "โš ๏ธ Promoted component migration (step 3) must be done BEFORE codemods. Other manual fixes (step 5) should be done AFTER." + }); + + recommendations.push({ + type: "testing", + priority: "high", + action: "Thorough testing required", + details: [ + "Test DatePicker with native Date objects", + "Verify Dialog/Tooltip/Tipseen positioning (Floating UI)", + "Check Dropdown options with new { value, label } structure", + "Test Modal behavior and sub-component APIs", + "Verify ARIA attributes render correctly", + "Check CSS spacing and layout" + ], + warning: "โš ๏ธ ONLY test AFTER completing manual fixes (step 5)" + }); + + recommendations.push({ + type: "sequential-steps", + priority: "critical", + action: "Follow migration steps in order", + details: "Use the migrationSteps guide to follow the process sequentially", + warning: "๐Ÿšจ DO NOT skip steps or work on multiple steps simultaneously" + }); + + recommendations.push({ + type: "formatting", + priority: "info", + action: "Format code according to your project settings", + details: "Run your project's formatting commands after migration is complete" + }); + + recommendations.push({ + type: "github-star", + priority: "info", + action: "Show your support with a GitHub star โญ", + details: "If this migration tool helped you, consider starring the Vibe repository", + link: "https://github.com/mondaycom/vibe" + }); + + return recommendations; +} + +function getComponentReplacement(component: string): string { + const replacements: Record = { + LinearProgressBar: "ProgressBar" + }; + return replacements[component] || component; +} + +function getComponentReplacementDetails(component: string): string { + const replacements: Record = { + TipseenImage: "Use TipseenMedia with an child", + AttentionBoxLink: "Use the AttentionBox link prop instead", + DropdownMenu: "Removed โ€” use new Dropdown API", + DropdownOption: "Removed โ€” use new Dropdown API", + DropdownSingleValue: "Removed โ€” use new Dropdown API", + Combobox: "Deprecated โ€” use Dropdown with box mode from @vibe/core" + }; + return replacements[component] || "Check migration guide for replacement"; +} + +function getBreakingChangeDetails(component: string): string { + const details: Record = { + AttentionBox: 'Type values changed (successโ†’positive, dangerโ†’negative, darkโ†’neutral). entryAnimationโ†’animate. withoutIconโ†’icon={false}', + Chips: "disableClickableBehavior prop removed", + CustomSvgIcon: "onClick and clickable props removed โ€” wrap with clickable element", + DatePicker: "momentโ†’Date, onPickDateโ†’onDateChange, rangeโ†’mode. Requires manual migration.", + Dialog: "modifiersโ†’middleware (Floating UI). enableNestedDialogLayer removed.", + Dropdown: "Options: { id, text } โ†’ { value, label }. Sub-components removed. Requires manual migration.", + Flex: '"stretch" value removed from justify prop', + Icon: "iconLabelโ†’label, iconTypeโ†’type, iconSizeโ†’size", + LinearProgressBar: "Renamed to ProgressBar", + MenuButton: "focusItemIndexOnMount now defaults to 0", + MenuItem: "children accepts only single MenuChild", + Modal: "Legacy Modal removed โ€” review new API", + Steps: "Finish button renders by default on last step", + TextField: "iconNameโ†’icon, iconsNamesโ†’flat iconLabel/secondaryIconLabel props", + TextWithHighlight: "tooltipPositionโ†’tooltipProps", + Tipseen: "modifiersโ†’middleware", + Toggle: "noSpacing prop removed", + Tooltip: "modifiersโ†’middleware", + VirtualizedList: "getItemHeight and onVerticalScrollbarVisiblityChange props removed", + VirtualizedGrid: "itemRenderer return type corrected to ReactElement" + }; + return details[component] || "Check migration guide for specific changes"; +} diff --git a/packages/shared/package.json b/packages/shared/package.json index 0c9fc9623d..5c4e9365d1 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/shared", - "version": "3.0.8", + "version": "4.0.0-rc.0", "description": "Shared utilities for Vibe packages", "repository": { "type": "git", @@ -34,12 +34,12 @@ "lint:fix": "yarn lint -- --fix" }, "devDependencies": { - "@vibe/config": "3.0.5", + "@vibe/config": "^4.0.0-rc.0", "@vitejs/plugin-react": "^4.3.1", "prettier": "^2.5.1", "react": "^16.14.0", "react-dom": "^16.14.0", - "typescript": "^4.7.3" + "typescript": "^5.9.3" }, "peerDependencies": { "react": ">=16.9.0", diff --git a/packages/shared/src/hooks/useKeyEvent.ts b/packages/shared/src/hooks/useKeyEvent.ts index 90fc6f40d9..7b8dee7843 100644 --- a/packages/shared/src/hooks/useKeyEvent.ts +++ b/packages/shared/src/hooks/useKeyEvent.ts @@ -1,6 +1,6 @@ +import type React from "react"; import { type RefObject, useCallback, useRef } from "react"; import { useEventListener } from "./useEventListener"; -import { type GenericEventCallback } from "../types/events"; import { isClient } from "../utils/ssr-utils"; const CTRL_OR_META = "ctrlOrMetaKey"; @@ -33,11 +33,10 @@ export interface UseKeyEventArgs { * The list of keys that should trigger the event. */ keys: KeyboardEvent["key"][]; - // TODO: [breaking] change to keyboard event /** * Callback fired when a specified key is pressed. */ - callback: GenericEventCallback; + callback: (event: KeyboardEvent | React.KeyboardEvent) => void; /** * Modifier key that must be pressed along with the specified key. */ diff --git a/packages/shared/src/tests/constants.ts b/packages/shared/src/tests/constants.ts index 17e7c23244..0d445aa2c6 100644 --- a/packages/shared/src/tests/constants.ts +++ b/packages/shared/src/tests/constants.ts @@ -53,7 +53,7 @@ export enum ComponentDefaultTestId { BREADCRUMBS_BAR = "breadcrumbs-bar", BREADCRUMB_MENU = "breadcrumb-menu", BREADCRUMB_MENU_ITEM = "breadcrumb-menu-item", - LINEAR_PROGRESS_BAR = "linear-progress-bar", + PROGRESS_BAR = "progress-bar", BAR = "bar", BAR_PRIMARY = "bar-primary", BAR_SECONDARY = "bar-secondary", @@ -103,21 +103,16 @@ export enum ComponentDefaultTestId { ICON = "icon", RESPONSIVE_LIST = "responsive-list", LIST = "list", - MODAL = "monday-dialog-container", - MODAL_OVERLAY = "monday-modal-overlay", - MODAL_CONTENT = "modal-content", + MODAL = "modal", + MODAL_OVERLAY = "modal-overlay", MODAL_HEADER = "modal-header", - MODAL_FOOTER_BUTTONS = "modal-footer-buttons", - MODAL_NEXT = "modal", - MODAL_NEXT_OVERLAY = "modal-overlay", - MODAL_NEXT_HEADER = "modal-header-next", - MODAL_NEXT_CLOSE_BUTTON = "modal-close-button", - MODAL_NEXT_CONTENT = "modal-content-next", - MODAL_NEXT_FOOTER = "modal-footer", - MODAL_NEXT_MEDIA = "modal-media", - MODAL_NEXT_BASIC_LAYOUT = "modal-basic-layout", - MODAL_NEXT_SIDE_BY_SIDE_LAYOUT = "modal-side-by-side-layout", - MODAL_NEXT_MEDIA_LAYOUT = "modal-media-layout", + MODAL_CLOSE_BUTTON = "modal-close-button", + MODAL_CONTENT = "modal-content", + MODAL_FOOTER = "modal-footer", + MODAL_MEDIA = "modal-media", + MODAL_BASIC_LAYOUT = "modal-basic-layout", + MODAL_SIDE_BY_SIDE_LAYOUT = "modal-side-by-side-layout", + MODAL_MEDIA_LAYOUT = "modal-media-layout", FORMATTED_NUMBER = "formatted-number", HIDDEN_TEXT = "hidden-text", DIALOG_CONTENT_CONTAINER = "dialog-content-container", @@ -174,7 +169,7 @@ export enum ComponentVibeId { ICON = "Icon", ICON_BUTTON = "IconButton", LABEL = "Label", - LINEAR_PROGRESS_BAR = "LinearProgressBar", + PROGRESS_BAR = "ProgressBar", LINK = "Link", LIST = "List", LOADER = "Loader", diff --git a/packages/shared/src/types/VibeComponent.ts b/packages/shared/src/types/VibeComponent.ts deleted file mode 100644 index 4b4fdb39d7..0000000000 --- a/packages/shared/src/types/VibeComponent.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { type ForwardRefExoticComponent, type RefAttributes } from "react"; - -type VibeComponent = ForwardRefExoticComponent>; - -export default VibeComponent; diff --git a/packages/shared/src/types/index.ts b/packages/shared/src/types/index.ts index 854f673cbe..ddbc4e97de 100644 --- a/packages/shared/src/types/index.ts +++ b/packages/shared/src/types/index.ts @@ -1,7 +1,5 @@ export * from "./events"; -export type { default as VibeComponent } from "./VibeComponent"; export type { default as VibeComponentProps } from "./VibeComponentProps"; -export * from "./withStaticProps"; export * from "./ArrayLastElement"; export * from "./SplitString"; export * from "./ElementContent"; diff --git a/packages/shared/src/types/withStaticProps.ts b/packages/shared/src/types/withStaticProps.ts deleted file mode 100644 index 318c86489a..0000000000 --- a/packages/shared/src/types/withStaticProps.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { type FC, type ForwardRefExoticComponent, type RefAttributes } from "react"; -type Required = { - [P in keyof T]-?: T[P]; -}; - -export const withStaticProps = ( - component: ForwardRefExoticComponent>, - staticProps: Required -) => Object.assign(component, staticProps); - -export const withStaticPropsWithoutForwardRef = ( - component: FC, - staticProps: Required -) => Object.assign(component, staticProps); diff --git a/packages/shared/src/utils/textManipulations.ts b/packages/shared/src/utils/textManipulations.ts index 9375204ec8..8e52f0c8e5 100644 --- a/packages/shared/src/utils/textManipulations.ts +++ b/packages/shared/src/utils/textManipulations.ts @@ -7,7 +7,7 @@ const DEFAULT_LOCAL = "en-US"; function validateLocalSupported(local: string) { let isLocalSupported; try { - const options = { localeMatcher: "lookup" }; + const options = { localeMatcher: "lookup" as const }; isLocalSupported = !!Intl.NumberFormat.supportedLocalesOf(convertToArray(local), options).length; } catch (err) { isLocalSupported = false; diff --git a/packages/storybook-blocks/package.json b/packages/storybook-blocks/package.json index 5c2df7b4d3..4b8f4af0ac 100644 --- a/packages/storybook-blocks/package.json +++ b/packages/storybook-blocks/package.json @@ -1,6 +1,6 @@ { "name": "vibe-storybook-components", - "version": "1.0.7", + "version": "4.0.0-rc.0", "description": "Collection of Vibe's Storybook components", "repository": { "type": "git", @@ -96,7 +96,7 @@ "rollup-plugin-svg": "^2.0.0", "rollup-plugin-typescript2": "^0.35.0", "ts-jest": "^29.1.2", - "typescript": "^5.1.6", + "typescript": "^5.9.3", "typescript-plugin-css-modules": "^5.0.1" }, "peerDependencies": { diff --git a/packages/storybook-blocks/src/helpers/components/Link/Link.module.scss b/packages/storybook-blocks/src/helpers/components/Link/Link.module.scss index ce7469ea64..3875d2434c 100644 --- a/packages/storybook-blocks/src/helpers/components/Link/Link.module.scss +++ b/packages/storybook-blocks/src/helpers/components/Link/Link.module.scss @@ -27,23 +27,10 @@ .iconStart { line-height: 24px; - margin-right: var(--sb-spacing-small); + margin-inline-end: var(--sb-spacing-xs); } .iconEnd { line-height: 24px; - margin-left: var(--sb-spacing-small); -} - -@supports (margin-inline-start: initial) { - .iconStart { - line-height: 24px; - margin-right: 0; - margin-inline-end: var(--sb-spacing-xs); - } - .iconEnd { - line-height: 24px; - margin-left: 0; - margin-inline-start: var(--sb-spacing-xs); - } + margin-inline-start: var(--sb-spacing-xs); } diff --git a/packages/style/.cursor/rules/color-token-management.mdc b/packages/style/.cursor/rules/color-token-management.mdc index cb9179f3b4..b5a592c97a 100644 --- a/packages/style/.cursor/rules/color-token-management.mdc +++ b/packages/style/.cursor/rules/color-token-management.mdc @@ -1,11 +1,11 @@ --- -description: "Comprehensive guide for adding, modifying, and managing color tokens in the monday-ui-style package (@style). Covers SCSS theme files, build process, naming conventions, and special handling for hacker theme." +description: "Comprehensive guide for adding, modifying, and managing color tokens in the @vibe/style package. Covers SCSS theme files, build process, naming conventions, and special handling for hacker theme." globs: "packages/style/**/*" --- -# Color Token Management for monday-ui-style (@style) +# Color Token Management for @vibe/style -This rule provides a complete guide for managing color tokens in the `@style` package, which publishes as `monday-ui-style`. +This rule provides a complete guide for managing color tokens in the `@style` package, which publishes as `@vibe/style`. ## Package Structure Overview diff --git a/packages/style/README.md b/packages/style/README.md index ae1d83a601..340a4fbe3c 100644 --- a/packages/style/README.md +++ b/packages/style/README.md @@ -5,9 +5,9 @@ ## Installation ```bash -npm install monday-ui-style +npm install @vibe/style # or -yarn add monday-ui-style +yarn add @vibe/style ``` ## Usage @@ -15,32 +15,32 @@ yarn add monday-ui-style **Styles**: Import the css file in order to have the css variables exposed on the root level ```scss -@import "~monday-ui-style/dist/index.min.css"; +@import "~@vibe/style/dist/index.min.css"; ``` or if you want to import it in your JS files ```javascript -import "monday-ui-style/dist/index.min.css"; +import "@vibe/style/dist/index.min.css"; ``` **Mixins and functions**: We export multiple SCSS mixins and function helpers that can be used in your application if you use SASS. All helpers can be imported as demonstrated below. ```scss -@import "~monday-ui-style/dist/mixins"; -@import "~monday-ui-style/dist/functions"; +@import "~@vibe/style/dist/mixins"; +@import "~@vibe/style/dist/functions"; ``` ## Stylelint rules It is recommended to extend our [Stylelint](https://stylelint.io/) config in order to ensure proper usage of this library. -To use the supplied config, add `monday-ui-style/stylelint-config` as a [Stylelint config extension](https://stylelint.io/user-guide/configure/#extends): +To use the supplied config, add `@vibe/style/stylelint-config` as a [Stylelint config extension](https://stylelint.io/user-guide/configure/#extends): ```js // Your .stylelintrc { ... - "extends": "monday-ui-style/stylelint-config", + "extends": "@vibe/style/stylelint-config", ... } ``` diff --git a/packages/style/package.json b/packages/style/package.json index fad81e03da..d564b74c46 100644 --- a/packages/style/package.json +++ b/packages/style/package.json @@ -1,6 +1,6 @@ { - "name": "monday-ui-style", - "version": "0.26.2", + "name": "@vibe/style", + "version": "4.0.0-rc.0", "description": "Monday UI CSS Foundations", "keywords": [ "CSS", diff --git a/packages/style/src/spacing.scss b/packages/style/src/spacing.scss index 61451833c8..a413095e2e 100644 --- a/packages/style/src/spacing.scss +++ b/packages/style/src/spacing.scss @@ -1,11 +1,4 @@ :root { - --spacing-xs: 4px; - --spacing-small: 8px; - --spacing-medium: 16px; - --spacing-large: 24px; - --spacing-xl: 32px; - --spacing-xxl: 48px; - --spacing-xxxl: 64px; --space-2: 2px; --space-4: 4px; --space-8: 8px; diff --git a/packages/style/stylelint-config/index.js b/packages/style/stylelint-config/index.js index 2de93b5446..178b18b741 100644 --- a/packages/style/stylelint-config/index.js +++ b/packages/style/stylelint-config/index.js @@ -2,6 +2,6 @@ module.exports = { customSyntax: require.resolve("postcss-scss"), plugins: ["./rules/use-defined-css-var-when-available/index.js", "./rules/use-new-spacing-tokens/index.js"], rules: { - "monday-ui-style/use-defined-css-var-when-available": true + "@vibe/style/use-defined-css-var-when-available": true } }; diff --git a/packages/style/stylelint-config/rules/use-defined-css-var-when-available/__tests__/index.test.js b/packages/style/stylelint-config/rules/use-defined-css-var-when-available/__tests__/index.test.js index 3ebc282b53..2afbefeb10 100644 --- a/packages/style/stylelint-config/rules/use-defined-css-var-when-available/__tests__/index.test.js +++ b/packages/style/stylelint-config/rules/use-defined-css-var-when-available/__tests__/index.test.js @@ -6,14 +6,14 @@ const config = { plugins: [path.resolve(__dirname, "../index.js")], customSyntax: "postcss-scss", rules: { - "monday-ui-style/use-defined-css-var-when-available": true + "@vibe/style/use-defined-css-var-when-available": true } }; const configWithUseRecommendation = { ...config, rules: { - "monday-ui-style/use-defined-css-var-when-available": [ + "@vibe/style/use-defined-css-var-when-available": [ true, { useRecommendedFixes: true @@ -22,7 +22,7 @@ const configWithUseRecommendation = { } }; -describe("monday-ui-style/use-defined-css-var-when-available", () => { +describe("@vibe/style/use-defined-css-var-when-available", () => { // since we run tests that actually perform code fixes, the fixtures are expected to change const fixturesContentBeforeTests = new Map(); @@ -61,16 +61,13 @@ describe("monday-ui-style/use-defined-css-var-when-available", () => { const [firstWarning, secondWarning] = warnings; expect(firstWarning.text).toBe( - `Expected \"16px\" to be one of vars: ---space-16 ---spacing-medium - (monday-ui-style/use-defined-css-var-when-available)` + `Expected \"16px\" to be \"var(--space-16)\" (@vibe/style/use-defined-css-var-when-available)` ); expect(firstWarning.line).toBe(3); expect(firstWarning.column).toBe(15); expect(secondWarning.text).toBe( - `Expected \"16px\" to be \"var(--border-radius-big)\" (monday-ui-style/use-defined-css-var-when-available)` + `Expected \"16px\" to be \"var(--border-radius-big)\" (@vibe/style/use-defined-css-var-when-available)` ); expect(secondWarning.line).toBe(7); expect(secondWarning.column).toBe(18); @@ -115,7 +112,7 @@ describe("monday-ui-style/use-defined-css-var-when-available", () => { --font-size-20 --font-size-general-label --font-size-subtext - (monday-ui-style/use-defined-css-var-when-available)` + (@vibe/style/use-defined-css-var-when-available)` ); expect(firstWarning.line).toBe(3); expect(firstWarning.column).toBe(14); @@ -162,7 +159,7 @@ describe("monday-ui-style/use-defined-css-var-when-available", () => { config: { ...config, rules: { - "monday-ui-style/use-defined-css-var-when-available": ruleConfigValue + "@vibe/style/use-defined-css-var-when-available": ruleConfigValue } } }); diff --git a/packages/style/stylelint-config/rules/use-defined-css-var-when-available/index.js b/packages/style/stylelint-config/rules/use-defined-css-var-when-available/index.js index 47ea9ce66e..bc2ff26e31 100644 --- a/packages/style/stylelint-config/rules/use-defined-css-var-when-available/index.js +++ b/packages/style/stylelint-config/rules/use-defined-css-var-when-available/index.js @@ -4,7 +4,7 @@ const { getPropsToAllowedCssVars } = require("./parse-monday-css"); const { report, ruleMessages, validateOptions } = stylelint.utils; -const RULE_NAME = "monday-ui-style/use-defined-css-var-when-available"; +const RULE_NAME = "@vibe/style/use-defined-css-var-when-available"; const CONFIGS_THAT_MEAN_IGNORE_FILE = ["disabled", "disable", "off", "0"]; const messages = ruleMessages(RULE_NAME, { diff --git a/packages/style/stylelint-config/rules/use-defined-css-var-when-available/parse-monday-css.js b/packages/style/stylelint-config/rules/use-defined-css-var-when-available/parse-monday-css.js index 688c43d5d2..0e044e2f31 100644 --- a/packages/style/stylelint-config/rules/use-defined-css-var-when-available/parse-monday-css.js +++ b/packages/style/stylelint-config/rules/use-defined-css-var-when-available/parse-monday-css.js @@ -18,7 +18,7 @@ function parseMondayUiCss(css = readCssFromDefaultPath()) { const ast = postcss.parse(css); const varsToCanonicalValue = {}; // css vars that are mapped to canonical values. e.g. {"--color-primary": "#131313"} - const canonicalValuesToVars = {}; // matching css vars for each canonical value. e.g. {"16px": ["--font-size-h3", "--spacing-medium"]} + const canonicalValuesToVars = {}; // matching css vars for each canonical value. e.g. {"16px": ["--font-size-h3", "--space-16"]} const referenceTokens = {}; // css vars that are pointing to other css vars. e.g. {"--color-primary": "--color-red"} const referenceTokensToCanonicalValue = {}; // maps between reference token names to canonical values. e.g. if "--font-size-h5: var(--font-size-30)" and "--font-size-30: 16px", then {"--font-size-h5": "16px"} @@ -70,8 +70,8 @@ function parseMondayUiCss(css = readCssFromDefaultPath()) { { padding: { { - "8px": { allowedVars: [ "--spacing-small" ] }, - "16px": { allowedVars: [ "--spacing-medium" ] }, + "8px": { allowedVars: [ "--space-8" ] }, + "16px": { allowedVars: [ "--space-16" ] }, } }, "font-size":{ diff --git a/packages/style/stylelint-config/rules/use-defined-css-var-when-available/props-to-allowed-vars.js b/packages/style/stylelint-config/rules/use-defined-css-var-when-available/props-to-allowed-vars.js index 51edb91128..675736d604 100644 --- a/packages/style/stylelint-config/rules/use-defined-css-var-when-available/props-to-allowed-vars.js +++ b/packages/style/stylelint-config/rules/use-defined-css-var-when-available/props-to-allowed-vars.js @@ -10,14 +10,7 @@ const SPACINGS = [ "--space-40", "--space-48", "--space-64", - "--space-80", - "--spacing-xs", - "--spacing-small", - "--spacing-medium", - "--spacing-large", - "--spacing-xl", - "--spacing-xxl", - "--spacing-xxxl" + "--space-80" ]; const BORDER_RADIUSES = ["--border-radius-small", "--border-radius-medium", "--border-radius-big"]; @@ -154,15 +147,7 @@ function mapPropsToAllowedVars(propNames, allowedVars, recommended = undefined) // This means that if --border-radius-small or --border-radius-medium can be used while linting a rule with the property "border-radius", we will show an error const PROPS_TO_ALLOWED_VARS = { - ...mapPropsToAllowedVars(SPACING_PROPS, SPACINGS, [ - "--space-4", - "--space-8", - "--space-16", - "--space-24", - "--space-32", - "--space-48", - "--space-64" - ]), + ...mapPropsToAllowedVars(SPACING_PROPS, SPACINGS), ...mapPropsToAllowedVars(BORDER_RADIUSES_PROPS, BORDER_RADIUSES), ...mapPropsToAllowedVars(BORDER_WIDTHS_PROPS, BORDER_WIDTHS), ...mapPropsToAllowedVars(BORDER_STYLES_PROPS, BORDER_STYLES), diff --git a/packages/style/stylelint-config/rules/use-new-spacing-tokens/__tests__/index.test.js b/packages/style/stylelint-config/rules/use-new-spacing-tokens/__tests__/index.test.js index 3d89f2b694..32172136f7 100644 --- a/packages/style/stylelint-config/rules/use-new-spacing-tokens/__tests__/index.test.js +++ b/packages/style/stylelint-config/rules/use-new-spacing-tokens/__tests__/index.test.js @@ -5,18 +5,18 @@ const config = { plugins: [path.resolve(__dirname, "../index.js")], customSyntax: "postcss-scss", rules: { - "monday-ui-style/use-new-spacing-tokens": [true, { severity: "warning" }] + "@vibe/style/use-new-spacing-tokens": [true, { severity: "warning" }] } }; const configWithError = { ...config, rules: { - "monday-ui-style/use-new-spacing-tokens": true // Default severity is error + "@vibe/style/use-new-spacing-tokens": true // Default severity is error } }; -describe("monday-ui-style/use-new-spacing-tokens", () => { +describe("@vibe/style/use-new-spacing-tokens", () => { it("should warn for legacy spacing tokens by default", async () => { const result = await lint({ code: ` diff --git a/packages/style/stylelint-config/rules/use-new-spacing-tokens/index.js b/packages/style/stylelint-config/rules/use-new-spacing-tokens/index.js index 14af93f434..4e94ae64f2 100644 --- a/packages/style/stylelint-config/rules/use-new-spacing-tokens/index.js +++ b/packages/style/stylelint-config/rules/use-new-spacing-tokens/index.js @@ -1,6 +1,6 @@ const stylelint = require("stylelint"); -const ruleName = "monday-ui-style/use-new-spacing-tokens"; +const ruleName = "@vibe/style/use-new-spacing-tokens"; const messages = stylelint.utils.ruleMessages(ruleName, { rejected: (_property, value, replacement) => `Use new spacing tokens instead of legacy ones. Replace "${value}" with "${replacement}".` diff --git a/packages/testkit/__tests__/Button.test.ts b/packages/testkit/__tests__/Button.test.ts index d88d7659cf..f999e63d63 100644 --- a/packages/testkit/__tests__/Button.test.ts +++ b/packages/testkit/__tests__/Button.test.ts @@ -4,7 +4,7 @@ import { buttonStory } from "./utils/url-helper"; let frame: FrameLocator; let button: Button; -const buttonLocator = 'button[data-testid="button"]'; +const buttonLocator = 'button[data-testid^="button"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - Button", () => { diff --git a/packages/testkit/__tests__/ButtonGroup.test.ts b/packages/testkit/__tests__/ButtonGroup.test.ts index c0f0744215..748c9cb87d 100644 --- a/packages/testkit/__tests__/ButtonGroup.test.ts +++ b/packages/testkit/__tests__/ButtonGroup.test.ts @@ -4,7 +4,7 @@ import { buttonGroupStory } from "./utils/url-helper"; let frame: FrameLocator; let buttonGroup: ButtonGroup; -const buttonGroupLocator = 'div[data-testid="button-group"]'; +const buttonGroupLocator = 'div[data-testid^="button-group"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - ButtonGroup", () => { diff --git a/packages/testkit/__tests__/Checkbox.test.ts b/packages/testkit/__tests__/Checkbox.test.ts index 00c4587d4c..e185c3e3f5 100644 --- a/packages/testkit/__tests__/Checkbox.test.ts +++ b/packages/testkit/__tests__/Checkbox.test.ts @@ -1,10 +1,10 @@ import { test, expect, FrameLocator } from "@playwright/test"; -import { BaseElement, Checkbox } from "../components"; +import { Checkbox } from "../components"; import { checkboxStory } from "./utils/url-helper"; let frame: FrameLocator; let checkbox: Checkbox; -const checkboxLocator = 'label[data-testid="checkbox"]'; +const checkboxLocator = 'label[data-testid^="checkbox"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - Checkbox", () => { @@ -17,19 +17,19 @@ test.describe("Testkit - Unit Tests - Checkbox", () => { }); test("Checkbox should be initially checked", async () => { - await expect(checkbox.getLocator()).toBeChecked(); + await expect(checkbox.getLocator().locator("input")).toBeChecked(); }); test("Checkbox should be able to be unchecked", async () => { await checkbox.setUnchecked(); - await expect(checkbox.getLocator()).not.toBeChecked(); + await expect(checkbox.getLocator().locator("input")).not.toBeChecked(); }); test("Checkbox should be able to be checked after being unchecked", async () => { await checkbox.setUnchecked(); - await expect.soft(checkbox.getLocator()).not.toBeChecked(); + await expect.soft(checkbox.getLocator().locator("input")).not.toBeChecked(); await checkbox.setChecked(); - await expect(checkbox.getLocator()).toBeChecked(); + await expect(checkbox.getLocator().locator("input")).toBeChecked(); }); test("Checkbox should return its label text", async () => { @@ -40,13 +40,13 @@ test.describe("Testkit - Unit Tests - Checkbox", () => { }); test("Checkbox should toggle correctly with multiple check/uncheck operations", async () => { - await expect.soft(checkbox.getLocator()).toBeChecked(); + await expect.soft(checkbox.getLocator().locator("input")).toBeChecked(); await checkbox.setUnchecked(); - await expect.soft(checkbox.getLocator()).not.toBeChecked(); + await expect.soft(checkbox.getLocator().locator("input")).not.toBeChecked(); await checkbox.setChecked(); - await expect.soft(checkbox.getLocator()).toBeChecked(); + await expect.soft(checkbox.getLocator().locator("input")).toBeChecked(); await checkbox.setUnchecked(); - await expect(checkbox.getLocator()).not.toBeChecked(); + await expect(checkbox.getLocator().locator("input")).not.toBeChecked(); }); test("Checkbox should be enabled by default", async () => { @@ -63,11 +63,7 @@ test.describe("Testkit - Unit Tests - Checkbox", () => { }); test("should handle attribute retrieval", async () => { - const attributeValue = await new BaseElement( - checkbox.getPage(), - checkbox.getLocator().locator("div"), - "Checkbox" - ).getAttributeValue("data-vibe"); + const attributeValue = await checkbox.getAttributeValue("data-vibe"); expect(attributeValue).toContain("Checkbox"); }); }); diff --git a/packages/testkit/__tests__/Dropdown.test.ts b/packages/testkit/__tests__/Dropdown.test.ts index 716cbd61c8..bad52aa099 100644 --- a/packages/testkit/__tests__/Dropdown.test.ts +++ b/packages/testkit/__tests__/Dropdown.test.ts @@ -1,10 +1,10 @@ import { test, expect, FrameLocator } from "@playwright/test"; -import { BaseElement, Dropdown } from "../components"; +import { Dropdown } from "../components"; import { dropdownStory } from "./utils/url-helper"; let frame: FrameLocator; let dropdown: Dropdown; -const dropdownLocator = ".dropdown-stories-styles_spacing"; +const dropdownLocator = '[data-vibe="Dropdown"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - Dropdown", () => { @@ -37,11 +37,7 @@ test.describe("Testkit - Unit Tests - Dropdown", () => { }); test("should handle attribute retrieval", async () => { - const attributeValue = await new BaseElement( - dropdown.getPage(), - dropdown.getLocator().locator("input"), - "Dropdown" - ).getAttributeValue("aria-label"); - expect(attributeValue).toContain("Dropdown input"); + const attributeValue = await dropdown.getAttributeValue("data-vibe"); + expect(attributeValue).toContain("Dropdown"); }); }); diff --git a/packages/testkit/__tests__/IconButton.test.ts b/packages/testkit/__tests__/IconButton.test.ts index 17b8a19483..be5a594dc9 100644 --- a/packages/testkit/__tests__/IconButton.test.ts +++ b/packages/testkit/__tests__/IconButton.test.ts @@ -4,7 +4,7 @@ import { iconButtonStory } from "./utils/url-helper"; let frame: FrameLocator; let iconButton: IconButton; -const iconButtonLocator = 'button[data-testid="icon-button"]'; +const iconButtonLocator = 'button[data-testid^="icon-button"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - IconButton", () => { diff --git a/packages/testkit/__tests__/Link.test.ts b/packages/testkit/__tests__/Link.test.ts index 9de0c164b0..6ec18abc19 100644 --- a/packages/testkit/__tests__/Link.test.ts +++ b/packages/testkit/__tests__/Link.test.ts @@ -4,7 +4,7 @@ import { linkStory } from "./utils/url-helper"; let frame: FrameLocator; let link: Link; -const linkLocator = 'a[data-testid="link"]'; +const linkLocator = 'a[data-testid^="link"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - Link", () => { diff --git a/packages/testkit/__tests__/Loader.test.ts b/packages/testkit/__tests__/Loader.test.ts index 84566347a5..4f7d73c11c 100644 --- a/packages/testkit/__tests__/Loader.test.ts +++ b/packages/testkit/__tests__/Loader.test.ts @@ -4,7 +4,7 @@ import { loaderStory } from "./utils/url-helper"; let frame: FrameLocator; let loader: Loader; -const loaderLocator = 'div[data-testid="loader"]'; +const loaderLocator = 'div[data-testid^="loader"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - Loader", () => { diff --git a/packages/testkit/__tests__/MenuButton.test.ts b/packages/testkit/__tests__/MenuButton.test.ts index 600812b9cf..1f2257632a 100644 --- a/packages/testkit/__tests__/MenuButton.test.ts +++ b/packages/testkit/__tests__/MenuButton.test.ts @@ -5,7 +5,7 @@ import { Menu } from "../components/Menu"; let frame: FrameLocator; let menuButton: MenuButton; -const menuButtonLocator = 'button[data-testid="menu-button"]'; +const menuButtonLocator = 'button[data-testid^="menu-button"]'; const menuLocator = 'ul[role="menu"]'; const frameLocator = "[id='storybook-preview-iframe']"; diff --git a/packages/testkit/__tests__/RadioButton.test.ts b/packages/testkit/__tests__/RadioButton.test.ts index cf1edcc1e1..00425a3758 100644 --- a/packages/testkit/__tests__/RadioButton.test.ts +++ b/packages/testkit/__tests__/RadioButton.test.ts @@ -4,7 +4,7 @@ import { radioButtonStory } from "./utils/url-helper"; let frame: FrameLocator; let radioButton: RadioButton; -const radioButtonLocator = 'label[data-testid="radio-button"]'; +const radioButtonLocator = 'label[data-testid^="radio-button"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - RadioButton", () => { diff --git a/packages/testkit/__tests__/SplitButton.test.ts b/packages/testkit/__tests__/SplitButton.test.ts index 284d68e638..5caba36e05 100644 --- a/packages/testkit/__tests__/SplitButton.test.ts +++ b/packages/testkit/__tests__/SplitButton.test.ts @@ -5,7 +5,7 @@ import { Menu } from "../components/Menu"; let frame: FrameLocator; let splitButton: SplitButton; -const splitButtonLocator = 'div[data-testid="split-button"]'; +const splitButtonLocator = 'div[data-testid^="split-button"]'; const menuLocator = 'ul[role="menu"]'; const frameLocator = "[id='storybook-preview-iframe']"; diff --git a/packages/testkit/__tests__/Steps.test.ts b/packages/testkit/__tests__/Steps.test.ts index 83d93d8e07..3d2371a008 100644 --- a/packages/testkit/__tests__/Steps.test.ts +++ b/packages/testkit/__tests__/Steps.test.ts @@ -4,7 +4,7 @@ import { stepsStory } from "./utils/url-helper"; let frame: FrameLocator; let steps: Steps; -const stepsLocator = 'div[data-testid="steps"]'; +const stepsLocator = 'div[data-testid^="steps"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - Steps", () => { diff --git a/packages/testkit/__tests__/TextArea.test.ts b/packages/testkit/__tests__/TextArea.test.ts index bae6ce20e7..7308240861 100644 --- a/packages/testkit/__tests__/TextArea.test.ts +++ b/packages/testkit/__tests__/TextArea.test.ts @@ -4,7 +4,7 @@ import { textAreaStory } from "./utils/url-helper"; let frame: FrameLocator; let textArea: TextArea; -const textAreaLocator = 'div[data-testid="text-area"]'; +const textAreaLocator = 'div[data-testid^="text-area"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - TextArea", () => { diff --git a/packages/testkit/__tests__/TextField.test.ts b/packages/testkit/__tests__/TextField.test.ts index aebdd59d9c..f79f208eb1 100644 --- a/packages/testkit/__tests__/TextField.test.ts +++ b/packages/testkit/__tests__/TextField.test.ts @@ -4,7 +4,7 @@ import { textfieldStory } from "./utils/url-helper"; let frame: FrameLocator; let textField: TextField; -const textFieldLocator = "#input"; +const textFieldLocator = 'input[data-testid^="text-field"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - TextField", () => { diff --git a/packages/testkit/__tests__/Toast.test.ts b/packages/testkit/__tests__/Toast.test.ts index d156956525..b4933c9584 100644 --- a/packages/testkit/__tests__/Toast.test.ts +++ b/packages/testkit/__tests__/Toast.test.ts @@ -2,7 +2,7 @@ import { test, expect, FrameLocator } from "@playwright/test"; import { linkToastStory, buttonToastStory, loadingToastStory } from "./utils/url-helper"; import { Toast } from "../components/Toast"; -const toastLocator = 'div[data-testid="toast"]'; +const toastLocator = 'div[data-testid^="toast_"]'; const frameLocator = "[id='storybook-preview-iframe']"; test.describe("Testkit - Unit Tests - Button Toast", () => { diff --git a/packages/testkit/__tests__/utils/url-helper.js b/packages/testkit/__tests__/utils/url-helper.js index dbb23e644b..9f0f34f2ee 100644 --- a/packages/testkit/__tests__/utils/url-helper.js +++ b/packages/testkit/__tests__/utils/url-helper.js @@ -3,8 +3,8 @@ export const buttonGroupStory = "/?path=/story/components-buttongroup--overview" export const buttonToastStory = "/?path=/story/components-toast--default-with-button"; export const checkboxStory = "/?path=/story/components-checkbox--overview"; export const colorPickerStory = "/?path=/story/components-colorpicker--overview"; -export const comboboxStory = "/?path=/story/components-combobox--overview"; -export const dropdownStory = "/?path=/story/components-dropdown-deprecated--overview"; +export const comboboxStory = "/?path=/story/components-combobox-deprecated--overview"; +export const dropdownStory = "/?path=/story/components-dropdown-basic-dropdown--searchable"; export const editableHeadingStory = "/?path=/story/components-editableheading--overview"; export const editableTextStory = "/?path=/story/components-editabletext--overview"; export const expandCollapseStory = "/?path=/story/components-expandcollapse--overview"; @@ -18,7 +18,7 @@ export const loadingToastStory = "/?path=/story/components-toast--toast-with-loa export const menuStory = "/?path=/story/components-menu-menu--overview"; export const menuItemStory = "/?path=/story/components-menu-menuitem--overview"; export const menuButtonStory = "/?path=/story/components-menubutton--overview"; -export const modalStory = "/?path=/story/components-modal-new-basic-modal--overview"; +export const modalStory = "/?path=/story/components-modal-basic-modal--overview"; export const radioButtonStory = "/?path=/story/components-radiobutton--overview"; export const searchStory = "/?path=/story/components-search--overview"; export const splitButtonStory = "/?path=/story/components-splitbutton--overview"; diff --git a/packages/testkit/components/Checkbox.ts b/packages/testkit/components/Checkbox.ts index ca04ce630d..fe43dee8d8 100644 --- a/packages/testkit/components/Checkbox.ts +++ b/packages/testkit/components/Checkbox.ts @@ -19,7 +19,7 @@ export class Checkbox extends BaseElement { */ constructor(page: Page, locator: Locator, elementReportName: string) { super(page, locator, elementReportName); - this.checkbox = new TextField(page, locator.locator("div"), `${elementReportName} - Checkbox`); + this.checkbox = new TextField(page, locator.locator("input"), `${elementReportName} - Checkbox`); this.label = new Text(page, locator.locator("span"), `${elementReportName} - Label`); } @@ -30,7 +30,7 @@ export class Checkbox extends BaseElement { async setChecked(): Promise { await test.step(`Check checkbox for ${this.getElementReportName()}`, async () => { if (!(await this.isChecked())) { - await this.checkbox.getLocator().check(); + await this.getLocator().click(); } }); } @@ -42,7 +42,7 @@ export class Checkbox extends BaseElement { async setUnchecked(): Promise { await test.step(`Uncheck checkbox for ${this.getElementReportName()}`, async () => { if (await this.isChecked()) { - await this.checkbox.getLocator().uncheck(); + await this.getLocator().click(); } }); } diff --git a/packages/testkit/package.json b/packages/testkit/package.json index 4e8a892544..7483be4d7f 100644 --- a/packages/testkit/package.json +++ b/packages/testkit/package.json @@ -1,6 +1,6 @@ { "name": "@vibe/testkit", - "version": "1.16.0", + "version": "4.0.0-rc.0", "description": "Vibe e2e testing toolkit", "keywords": [ "TESTING", diff --git a/yarn.lock b/yarn.lock index fde43ef56d..a5491907c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -50,6 +50,15 @@ js-tokens "^4.0.0" picocolors "^1.1.1" +"@babel/code-frame@^7.28.6", "@babel/code-frame@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.0.tgz#7cd7a59f15b3cc0dcd803038f7792712a7d0b15c" + integrity sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw== + dependencies: + "@babel/helper-validator-identifier" "^7.28.5" + js-tokens "^4.0.0" + picocolors "^1.1.1" + "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.23.5", "@babel/compat-data@^7.24.1": version "7.24.1" resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.1.tgz" @@ -200,6 +209,17 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" +"@babel/generator@^7.29.0": + version "7.29.1" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.29.1.tgz#d09876290111abbb00ef962a7b83a5307fba0d50" + integrity sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw== + dependencies: + "@babel/parser" "^7.29.0" + "@babel/types" "^7.29.0" + "@jridgewell/gen-mapping" "^0.3.12" + "@jridgewell/trace-mapping" "^0.3.28" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@^7.18.6", "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz" @@ -366,13 +386,13 @@ "@babel/traverse" "^7.24.7" "@babel/types" "^7.24.7" -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.24.7": - version "7.24.7" - resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz" - integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== +"@babel/helper-module-imports@^7.0.0": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz#60632cbd6ffb70b22823187201116762a03e2d5c" + integrity sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw== dependencies: - "@babel/traverse" "^7.24.7" - "@babel/types" "^7.24.7" + "@babel/traverse" "^7.28.6" + "@babel/types" "^7.28.6" "@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.22.15", "@babel/helper-module-imports@^7.24.1", "@babel/helper-module-imports@^7.24.3": version "7.24.3" @@ -381,6 +401,14 @@ dependencies: "@babel/types" "^7.24.0" +"@babel/helper-module-imports@^7.24.7": + version "7.24.7" + resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz" + integrity sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA== + dependencies: + "@babel/traverse" "^7.24.7" + "@babel/types" "^7.24.7" + "@babel/helper-module-imports@^7.27.1": version "7.27.1" resolved "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz" @@ -670,6 +698,13 @@ dependencies: "@babel/types" "^7.28.5" +"@babel/parser@^7.28.6", "@babel/parser@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.29.0.tgz#669ef345add7d057e92b7ed15f0bac07611831b6" + integrity sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww== + dependencies: + "@babel/types" "^7.29.0" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.24.1": version "7.24.1" resolved "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.1.tgz" @@ -1590,13 +1625,18 @@ dependencies: regenerator-runtime "^0.14.0" -"@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.7.2": +"@babel/runtime@^7.3.1": version "7.25.0" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.0.tgz" integrity sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw== dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.4.4", "@babel/runtime@^7.7.2": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.6.tgz#d267a43cb1836dc4d182cce93ae75ba954ef6d2b" + integrity sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA== + "@babel/standalone@^7.24.4": version "7.24.4" resolved "https://registry.npmjs.org/@babel/standalone/-/standalone-7.24.4.tgz" @@ -1629,6 +1669,15 @@ "@babel/parser" "^7.24.7" "@babel/types" "^7.24.7" +"@babel/template@^7.28.6": + version "7.28.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.28.6.tgz#0e7e56ecedb78aeef66ce7972b082fce76a23e57" + integrity sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ== + dependencies: + "@babel/code-frame" "^7.28.6" + "@babel/parser" "^7.28.6" + "@babel/types" "^7.28.6" + "@babel/traverse@^7.18.9", "@babel/traverse@^7.24.1", "@babel/traverse@^7.7.2": version "7.24.1" resolved "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.1.tgz" @@ -1687,6 +1736,19 @@ "@babel/types" "^7.28.5" debug "^4.3.1" +"@babel/traverse@^7.28.6": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.29.0.tgz#f323d05001440253eead3c9c858adbe00b90310a" + integrity sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA== + dependencies: + "@babel/code-frame" "^7.29.0" + "@babel/generator" "^7.29.0" + "@babel/helper-globals" "^7.28.0" + "@babel/parser" "^7.29.0" + "@babel/template" "^7.28.6" + "@babel/types" "^7.29.0" + debug "^4.3.1" + "@babel/types@^7.0.0", "@babel/types@^7.18.9", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.19", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.24.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.24.0" resolved "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz" @@ -1721,6 +1783,14 @@ "@babel/helper-string-parser" "^7.27.1" "@babel/helper-validator-identifier" "^7.28.5" +"@babel/types@^7.28.6", "@babel/types@^7.29.0": + version "7.29.0" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.0.tgz#9f5b1e838c446e72cf3cd4b918152b8c605e37c7" + integrity sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A== + dependencies: + "@babel/helper-string-parser" "^7.27.1" + "@babel/helper-validator-identifier" "^7.28.5" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" @@ -2100,7 +2170,7 @@ "@emotion/cache@^10.0.27", "@emotion/cache@^10.0.9": version "10.0.29" - resolved "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz" + resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-10.0.29.tgz#87e7e64f412c060102d589fe7c6dc042e6f9d1e0" integrity sha512-fU2VtSVlHiF27empSbxi1O2JFdNWZO+2NFHfwO0pxgTep6Xa3uGb+3pVKfLww2l/IBGLNEZl5Xf/++A4wAYDYQ== dependencies: "@emotion/sheet" "0.9.4" @@ -2110,7 +2180,7 @@ "@emotion/core@^10.0.9": version "10.3.1" - resolved "https://registry.npmjs.org/@emotion/core/-/core-10.3.1.tgz" + resolved "https://registry.yarnpkg.com/@emotion/core/-/core-10.3.1.tgz#4021b6d8b33b3304d48b0bb478485e7d7421c69d" integrity sha512-447aUEjPIm0MnE6QYIaFz9VQOHSXf4Iu6EWOIqq11EAPqinkSZmfymPTmlOE3QjLv846lH4JVZBUOtwGbuQoww== dependencies: "@babel/runtime" "^7.5.5" @@ -2122,7 +2192,7 @@ "@emotion/css@^10.0.27", "@emotion/css@^10.0.9": version "10.0.27" - resolved "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz" + resolved "https://registry.yarnpkg.com/@emotion/css/-/css-10.0.27.tgz#3a7458198fbbebb53b01b2b87f64e5e21241e14c" integrity sha512-6wZjsvYeBhyZQYNrGoR5yPMYbMBNEnanDrqmsqS1mzDm1cOTu12shvl2j4QHNS36UaTE0USIJawCH9C8oW34Zw== dependencies: "@emotion/serialize" "^0.11.15" @@ -2131,7 +2201,7 @@ "@emotion/hash@0.8.0": version "0.8.0" - resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz" + resolved "https://registry.yarnpkg.com/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413" integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== "@emotion/is-prop-valid@^0.8.2": @@ -2148,7 +2218,7 @@ "@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16": version "0.11.16" - resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz" + resolved "https://registry.yarnpkg.com/@emotion/serialize/-/serialize-0.11.16.tgz#dee05f9e96ad2fb25a5206b6d759b2d1ed3379ad" integrity sha512-G3J4o8by0VRrO+PFeSc3js2myYNOXVJ3Ya+RGVxnshRYgsvErfAOglKAiy1Eo1vhzxqtUvjCyS5gtewzkmvSSg== dependencies: "@emotion/hash" "0.8.0" @@ -2159,17 +2229,17 @@ "@emotion/sheet@0.9.4": version "0.9.4" - resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz" + resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5" integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA== "@emotion/stylis@0.8.5": version "0.8.5" - resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz" + resolved "https://registry.yarnpkg.com/@emotion/stylis/-/stylis-0.8.5.tgz#deacb389bd6ee77d1e7fcaccce9e16c5c7e78e04" integrity sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ== "@emotion/unitless@0.7.5": version "0.7.5" - resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz" + resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== "@emotion/use-insertion-effect-with-fallbacks@^1.0.0": @@ -2179,12 +2249,12 @@ "@emotion/utils@0.11.3": version "0.11.3" - resolved "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz" + resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-0.11.3.tgz#a759863867befa7e583400d322652a3f44820924" integrity sha512-0o4l6pZC+hI88+bzuaX/6BgOvQVhbt2PfmxauVaYOGgbsAw14wdKyvMCZXnsnsHys94iadcF+RG/wZyx6+ZZBw== "@emotion/weak-memoize@0.2.5": version "0.2.5" - resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz" + resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46" integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA== "@esbuild/aix-ppc64@0.21.5": @@ -6013,15 +6083,6 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dates@^21.8.3": - version "21.8.6" - resolved "https://registry.npmjs.org/@types/react-dates/-/react-dates-21.8.6.tgz" - integrity sha512-fDF322SOXAxstapv0/oruiPx9kY4DiiaEHYAVvXdPfQhi/hdaONsA9dFw5JBNPAWz7Niuwk+UUhxPU98h70TjA== - dependencies: - "@types/react" "*" - "@types/react-outside-click-handler" "*" - moment "^2.26.0" - "@types/react-dom@*", "@types/react-dom@>=16.9.0": version "18.2.22" resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.22.tgz" @@ -6043,13 +6104,6 @@ dependencies: "@types/react" "^16" -"@types/react-outside-click-handler@*": - version "1.3.3" - resolved "https://registry.npmjs.org/@types/react-outside-click-handler/-/react-outside-click-handler-1.3.3.tgz" - integrity sha512-fF7x4dHf/IPIne8kkt3rlCGuWFrWkFJmzQm4JkxSBzXJIM9WDLob++VnmGpE3ToVWrW3Xw9D5TxcUWrwqe04Gg== - dependencies: - "@types/react" "*" - "@types/react-resizable@^3.0.7": version "3.0.7" resolved "https://registry.npmjs.org/@types/react-resizable/-/react-resizable-3.0.7.tgz" @@ -6617,6 +6671,34 @@ resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== +"@vibe/base@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vibe/base/-/base-3.0.5.tgz#d15de26ea7399251a047947bd2c5cdc3cd932b32" + integrity sha512-e0JQWygbbRj6q85XlovXAOgkxZtoTR7I9K+OMoMvFc09xnQ1evZr8G2HF6Il8ENT/23s5O8hLJ1Ef/OzdBbp2w== + dependencies: + "@vibe/shared" "3.0.8" + classnames "^2.5.1" + +"@vibe/button@3.0.16": + version "3.0.16" + resolved "https://registry.yarnpkg.com/@vibe/button/-/button-3.0.16.tgz#6612892a8a338b603b77e2fa310df7b342ba3f45" + integrity sha512-fjFb7Khv86P/tk6rO8Mdg2+fwBLen98i+E6YXC2gkWcFJAvNfjkH8kZN4m0HhmaQDBQb+ESmXZdq9vZIllS3Dw== + dependencies: + "@vibe/icon" "3.0.11" + "@vibe/loader" "3.0.11" + "@vibe/shared" "3.0.8" + classnames "^2.5.1" + es-toolkit "^1.39.10" + +"@vibe/clickable@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@vibe/clickable/-/clickable-3.0.3.tgz#e5c0ad7e739f350326eddfa7447acf0458b363e0" + integrity sha512-xsMt0TpUj8Wdv62kADvq//BhXlU3YVBix4WJdcyRPBp5NRPMksIbbOiMc35WIZznO/lA9IwoE1ktFOjwx28hfw== + dependencies: + "@vibe/shared" "3.0.8" + classnames "^2.5.1" + es-toolkit "^1.39.10" + "@vibe/core@3.85.1": version "3.85.1" resolved "https://registry.yarnpkg.com/@vibe/core/-/core-3.85.1.tgz#ba541d5c3798e44810cb76e12720d690d86a04a0" @@ -6660,6 +6742,50 @@ react-windowed-select "^2.0.4" style-inject "^0.3.0" +"@vibe/core@^3.59.0": + version "3.87.0" + resolved "https://registry.yarnpkg.com/@vibe/core/-/core-3.87.0.tgz#ef2e6d2a03bdc59aa792451ea16557f980dbe5b5" + integrity sha512-3IVFH6o0/AbrpyF/hfEzwTbJ1UdubURugTUiam1ElhzXFJAE83tpEkvTSmml34jMojBifEpV3QiIVJZXc+taxA== + dependencies: + "@floating-ui/react-dom" "^2.1.2" + "@popperjs/core" "2.11.6" + "@vibe/base" "3.0.5" + "@vibe/button" "3.0.16" + "@vibe/clickable" "3.0.3" + "@vibe/dialog" "3.0.12" + "@vibe/hooks" "3.0.3" + "@vibe/icon" "3.0.11" + "@vibe/icon-button" "3.0.5" + "@vibe/icons" "1.16.0" + "@vibe/layer" "3.0.10" + "@vibe/layout" "3.0.3" + "@vibe/loader" "3.0.11" + "@vibe/shared" "3.0.8" + "@vibe/tooltip" "3.0.5" + "@vibe/typography" "3.0.5" + a11y-dialog "^7.5.2" + body-scroll-lock "^4.0.0-beta.0" + browserslist-config-monday "1.0.6" + classnames "^2.3.2" + date-fns "^2.30.0" + downshift "^9.0.8" + es-toolkit "^1.39.10" + framer-motion "^6.5.1" + monday-ui-style "0.26.2" + react-dates "21.8.0" + react-day-picker "^8.8.0" + react-focus-lock "^2.13.2" + react-inlinesvg "^4.1.3" + react-is "^16.9.0" + react-popper "^2.3.0" + react-remove-scroll "^2.6.0" + react-select "npm:react-select-module@3.4.0" + react-transition-group "^4.4.5" + react-virtualized-auto-sizer "^1.0.7" + react-window "^1.8.7" + react-windowed-select "^2.0.4" + style-inject "^0.3.0" + "@vibe/dialog@3.0.11": version "3.0.11" resolved "https://registry.yarnpkg.com/@vibe/dialog/-/dialog-3.0.11.tgz#a7be64b673bb7443325b39f38582758b077c6331" @@ -6674,6 +6800,29 @@ react-popper "^2.3.0" react-transition-group "^4.4.5" +"@vibe/dialog@3.0.12": + version "3.0.12" + resolved "https://registry.yarnpkg.com/@vibe/dialog/-/dialog-3.0.12.tgz#f417c21fd15fdbe0c10552e2ccf768125542c1f6" + integrity sha512-XOM7VuSRkDsdOcwtx2Xfx4WfaXeBmW9I1IwkM6nThTZzFv6QlePw9RacIUj/xYwale/IfjlzrpA0Hx5jMq68XA== + dependencies: + "@popperjs/core" "2.11.6" + "@vibe/hooks" "3.0.3" + "@vibe/layer" "3.0.10" + "@vibe/shared" "3.0.8" + classnames "^2.5.1" + es-toolkit "^1.39.10" + react-popper "^2.3.0" + react-transition-group "^4.4.5" + +"@vibe/hooks@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@vibe/hooks/-/hooks-3.0.3.tgz#048284a79c75a9aaddbd730dc5fdb49851a2ecf9" + integrity sha512-74iEe2qLdjm3enpstGtewFkrKxNACvPMtIAxgUiBZ9T28cYu5mjbtH+Lp2Gv/kNMMv3x/V3RWcn7uqjLjyKBbQ== + dependencies: + "@vibe/shared" "3.0.8" + classnames "^2.5.1" + es-toolkit "^1.39.10" + "@vibe/icon-button@3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@vibe/icon-button/-/icon-button-3.0.4.tgz#cd11ca357a16415174bf866ba3124d708ec6ebfc" @@ -6687,6 +6836,70 @@ classnames "^2.5.1" es-toolkit "^1.39.10" +"@vibe/icon-button@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vibe/icon-button/-/icon-button-3.0.5.tgz#3bbb1db95a3c2af1737acfbcef54a38867ba8191" + integrity sha512-GS31OUHgoElhRYUerqALRK8kB3czIXyHVONRhzVtKTGdoKLYaJP3bvIcqpnfzWaYjMMuKjgwp//JlUh2JHUI/g== + dependencies: + "@vibe/button" "3.0.16" + "@vibe/icon" "3.0.11" + "@vibe/icons" "1.16.0" + "@vibe/shared" "3.0.8" + "@vibe/tooltip" "3.0.5" + classnames "^2.5.1" + es-toolkit "^1.39.10" + +"@vibe/icon@3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@vibe/icon/-/icon-3.0.11.tgz#bb3159ec302a389d6f3e20edc36723d068f2efa5" + integrity sha512-4xdj++K+2e7Wy7mSeTS6aLK0rjZpwIuZZoFv6X/lE5PxR7JkA1IRiaHUyCSE9CS9W0TDGEAzN923YuFjU0ghsA== + dependencies: + "@vibe/shared" "3.0.8" + classnames "^2.5.1" + es-toolkit "^1.39.10" + react-inlinesvg "^4.1.3" + +"@vibe/icons@1.16.0", "@vibe/icons@^1.9.0": + version "1.16.0" + resolved "https://registry.yarnpkg.com/@vibe/icons/-/icons-1.16.0.tgz#89f51b2308b5f4c053f6a1e9e6fad2adca2ab1d3" + integrity sha512-/ljaos2zNr4NfZKLoAjOfEilWUlmvBwqh9fFN+Z1BdYW84oGZQGRdj4TJW5jyirZyJ/H6jTcutKUbJaD3Om7+w== + +"@vibe/layer@3.0.10": + version "3.0.10" + resolved "https://registry.yarnpkg.com/@vibe/layer/-/layer-3.0.10.tgz#656e9c33c9511d621199ad12c989bc9c8370ccb0" + integrity sha512-UChefl8dxp4SLadYcItRMc49AS8PEakDv05KBNxzej2vybignt0sqI98+CurC6lr+0dNRlf8x58+Gae9wy5ybQ== + dependencies: + "@vibe/icon" "3.0.11" + "@vibe/loader" "3.0.11" + "@vibe/shared" "3.0.8" + classnames "^2.5.1" + es-toolkit "^1.39.10" + +"@vibe/layout@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@vibe/layout/-/layout-3.0.3.tgz#0cb8f057844fc624696e5e1e13f244088e6ea354" + integrity sha512-9SGVKKl4IJqsqg7Yuy9aQHtqGG/wPs/7ZCK2g3Fp8ot0/yc3EjWgBQh0uP+wOVHyRaRUPfI4CdkCsVmdGJ4YFQ== + dependencies: + "@vibe/clickable" "3.0.3" + "@vibe/shared" "3.0.8" + classnames "^2.5.1" + es-toolkit "^1.39.10" + +"@vibe/loader@3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@vibe/loader/-/loader-3.0.11.tgz#f73390703330369cb5886bc4d42c1df120da8556" + integrity sha512-F1Yn9ti0yaD08VYt0pfubfyE1yRsG8GZA41TR+0erVr/XnVlf6NA8+07pU85N06rQnp89slxlMy8Yf4AevkWDQ== + dependencies: + "@vibe/shared" "3.0.8" + classnames "^2.5.1" + +"@vibe/shared@3.0.8": + version "3.0.8" + resolved "https://registry.yarnpkg.com/@vibe/shared/-/shared-3.0.8.tgz#fddb789695ee5f62a894a1930674466b210819d2" + integrity sha512-qjWaNylbVKbdaDi75jFpp+LmF3y74JYdaLNWf6noiMkhq0fhcPTietVrHBxYO2b0cBvm1sQbrytX5YiBk3ii2w== + dependencies: + es-toolkit "^1.39.10" + "@vibe/tooltip@3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@vibe/tooltip/-/tooltip-3.0.4.tgz#73630b7747dd1721ed1e797e5e452bce06451580" @@ -6699,6 +6912,18 @@ classnames "^2.5.1" es-toolkit "^1.39.10" +"@vibe/tooltip@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vibe/tooltip/-/tooltip-3.0.5.tgz#7e793b7086eec6951e892dfdc52a1375e787248b" + integrity sha512-+PYHdWDw/5ua1qj2RHRC0R1pmzpjd3wnUOpkq9+l7wJvqVmb66SFMhq2O7u4RxpKTvxjj41eS6jcAyHgWiaYvw== + dependencies: + "@vibe/dialog" "3.0.12" + "@vibe/icon" "3.0.11" + "@vibe/layout" "3.0.3" + "@vibe/shared" "3.0.8" + classnames "^2.5.1" + es-toolkit "^1.39.10" + "@vibe/typography@3.0.4": version "3.0.4" resolved "https://registry.yarnpkg.com/@vibe/typography/-/typography-3.0.4.tgz#c19140765431c2f9ae23bfb0e9a69c37dcff7784" @@ -6711,6 +6936,18 @@ es-toolkit "^1.39.10" monday-ui-style "0.26.2" +"@vibe/typography@3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@vibe/typography/-/typography-3.0.5.tgz#d6cc582f24b15ea4bd5181b90f1a3f616f55292b" + integrity sha512-SRLWcFymgsdjagstUXWyUb8nYaQCKiaHi6/488MyjP5ssSXzP0/uJcQkd7c29fmZXxh5lg53xTEebPZrR7bmLA== + dependencies: + "@vibe/hooks" "3.0.3" + "@vibe/shared" "3.0.8" + "@vibe/tooltip" "3.0.5" + classnames "^2.5.1" + es-toolkit "^1.39.10" + monday-ui-style "0.26.2" + "@vitejs/plugin-react@^4.3.1": version "4.5.2" resolved "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.5.2.tgz" @@ -6889,7 +7126,7 @@ JSONStream@^1.3.5: a11y-dialog@^7.5.2: version "7.5.3" - resolved "https://registry.npmjs.org/a11y-dialog/-/a11y-dialog-7.5.3.tgz" + resolved "https://registry.yarnpkg.com/a11y-dialog/-/a11y-dialog-7.5.3.tgz#ceed4b3832d60b0123e29878d13a0ec5c2e7bcab" integrity sha512-ssx2j2FIgxhycE+liCKRam/S7F2TJyDJd8FwaoDS3vq5+yVTSg4dJAQPJr1abJLOlNmsjl5iAGE0VIp0x7gikg== dependencies: focusable-selectors "^0.4.0" @@ -7027,7 +7264,7 @@ aggregate-error@^4.0.0: airbnb-prop-types@^2.14.0, airbnb-prop-types@^2.15.0, airbnb-prop-types@^2.16.0: version "2.16.0" - resolved "https://registry.npmjs.org/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz" + resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz#b96274cefa1abb14f623f804173ee97c13971dc2" integrity sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg== dependencies: array.prototype.find "^2.1.1" @@ -7244,6 +7481,14 @@ array-buffer-byte-length@^1.0.0, array-buffer-byte-length@^1.0.1: call-bind "^1.0.5" is-array-buffer "^3.0.4" +array-buffer-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz#384d12a37295aec3769ab022ad323a18a51ccf8b" + integrity sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw== + dependencies: + call-bound "^1.0.3" + is-array-buffer "^3.0.5" + array-differ@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz" @@ -7283,7 +7528,7 @@ array-union@^2.1.0: array.prototype.find@^2.1.1: version "2.2.3" - resolved "https://registry.npmjs.org/array.prototype.find/-/array.prototype.find-2.2.3.tgz" + resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.2.3.tgz#675a233dbcd9b65ecf1fb3f915741aebc45461e6" integrity sha512-fO/ORdOELvjbbeIfZfzrXFMhYHGofRGqd+am9zm3tZ4GlJINj/pA2eITyfd65Vg6+ZbHd/Cys7stpoRSWtQFdA== dependencies: call-bind "^1.0.7" @@ -7316,7 +7561,17 @@ array.prototype.findlastindex@^1.2.3: es-object-atoms "^1.0.0" es-shim-unscopables "^1.0.2" -array.prototype.flat@^1.2.1, array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: +array.prototype.flat@^1.2.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz#534aaf9e6e8dd79fb6b9a9917f839ef1ec63afe5" + integrity sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-shim-unscopables "^1.0.2" + +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: version "1.3.2" resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz" integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== @@ -7384,6 +7639,19 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" +arraybuffer.prototype.slice@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz#9d760d84dbdd06d0cbf92c8849615a1a7ab3183c" + integrity sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + is-array-buffer "^3.0.4" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" @@ -7624,7 +7892,7 @@ babel-messages@^6.23.0: babel-plugin-emotion@^10.0.27: version "10.2.2" - resolved "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz" + resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz#a1fe3503cff80abfd0bdda14abd2e8e57a79d17d" integrity sha512-SMSkGoqTbTyUTDeuVuPIWifPdUGkTk1Kf9BWRiXIOIcuyMfsdp2EjeiiFvOzX8NOBvEh/ypKYvUh2rkgAJMCLA== dependencies: "@babel/helper-module-imports" "^7.0.0" @@ -7671,7 +7939,7 @@ babel-plugin-jest-hoist@^29.6.3: babel-plugin-macros@^2.0.0: version "2.8.0" - resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz" + resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz#0f958a7cc6556b1e65344465d99111a1e5e10138" integrity sha512-SEP5kJpfGYqYKpBrj5XU3ahw5p5GOHJ0U5ssOSQ/WBVdwkD2Dzlce95exQTs3jOVWPPKLBN2rlEWkCK7dSmLvg== dependencies: "@babel/runtime" "^7.7.2" @@ -7964,7 +8232,7 @@ body-parser@^2.2.0: body-scroll-lock@^4.0.0-beta.0: version "4.0.0-beta.0" - resolved "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-4.0.0-beta.0.tgz" + resolved "https://registry.yarnpkg.com/body-scroll-lock/-/body-scroll-lock-4.0.0-beta.0.tgz#4f78789d10e6388115c0460cd6d7d4dd2bbc4f7e" integrity sha512-a7tP5+0Mw3YlUJcGAKUqIBkYYGlYxk2fnCasq/FUph1hadxlTRjF+gAcZksxANnaMnALjxEddmSi/H3OR8ugcQ== boolbase@^1.0.0, boolbase@~1.0.0: @@ -8010,7 +8278,7 @@ braces@^3.0.3: brcast@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/brcast/-/brcast-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/brcast/-/brcast-2.0.2.tgz#2db16de44140e418dc37fab10beec0369e78dcef" integrity sha512-Tfn5JSE7hrUlFcOoaLzVvkbgIemIorMIyoMr3TgvszWW7jFt2C9PdeMLtysYD9RU0MmU17b69+XJG1eRY2OBRg== browser-assert@^1.2.1: @@ -8163,7 +8431,7 @@ caching-transform@^4.0.0: package-hash "^4.0.0" write-file-atomic "^3.0.0" -call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: +call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== @@ -8182,7 +8450,17 @@ call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bin get-intrinsic "^1.2.4" set-function-length "^1.2.1" -call-bound@^1.0.2: +call-bind@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.8.tgz#0736a9660f537e3388826f440d5ec45f744eaa4c" + integrity sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww== + dependencies: + call-bind-apply-helpers "^1.0.0" + es-define-property "^1.0.0" + get-intrinsic "^1.2.4" + set-function-length "^1.2.2" + +call-bound@^1.0.2, call-bound@^1.0.3, call-bound@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz" integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg== @@ -8801,7 +9079,7 @@ console-control-strings@^1.1.0: "consolidated-events@^1.1.1 || ^2.0.0": version "2.0.2" - resolved "https://registry.npmjs.org/consolidated-events/-/consolidated-events-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/consolidated-events/-/consolidated-events-2.0.2.tgz#da8d8f8c2b232831413d9e190dc11669c79f4a91" integrity sha512-2/uRVMdRypf5z/TW/ncD/66l75P5hH2vM/GR8Jf8HLc2xnfJtmina6F6du8+v4Z2vTrMo7jC+W1tmEEuuELgkQ== constant-case@^3.0.4: @@ -8952,7 +9230,7 @@ cors@^2.8.5: cosmiconfig@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== dependencies: "@types/parse-json" "^4.0.0" @@ -9198,7 +9476,7 @@ cssstyle@^2.3.0: csstype@^2.5.7: version "2.6.21" - resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w== csstype@^3.0.2, csstype@^3.1.0: @@ -9251,6 +9529,15 @@ data-view-buffer@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" +data-view-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.2.tgz#211a03ba95ecaf7798a8c7198d79536211f88570" + integrity sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + data-view-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz" @@ -9260,6 +9547,15 @@ data-view-byte-length@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" +data-view-byte-length@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz#9e80f7ca52453ce3e93d25a35318767ea7704735" + integrity sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-data-view "^1.0.2" + data-view-byte-offset@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz" @@ -9269,6 +9565,15 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" +data-view-byte-offset@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz#068307f9b71ab76dbbe10291389e020856606191" + integrity sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-data-view "^1.0.1" + date-fns@^2.30.0: version "2.30.0" resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" @@ -9404,7 +9709,7 @@ deep-is@^0.1.3: deepmerge@^1.5.2: version "1.5.2" - resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-1.5.2.tgz" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== deepmerge@^4.2.2: @@ -9568,7 +9873,7 @@ dir-glob@^3.0.1: direction@^1.0.4: version "1.0.4" - resolved "https://registry.npmjs.org/direction/-/direction-1.0.4.tgz" + resolved "https://registry.yarnpkg.com/direction/-/direction-1.0.4.tgz#2b86fb686967e987088caf8b89059370d4837442" integrity sha512-GYqKi1aH7PJXxdhTeZBFrg8vUBeKXi+cNprXsC1kpJcbcVnV9wBsrOu1cQEdG0WeQwlfHiy3XvnKfIrJ2R0NzQ== disparity@^2.0.0: @@ -9595,7 +9900,7 @@ doctrine@^3.0.0: document.contains@^1.0.1: version "1.0.2" - resolved "https://registry.npmjs.org/document.contains/-/document.contains-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/document.contains/-/document.contains-1.0.2.tgz#4260abad67a6ae9e135c1be83d68da0db169d5f0" integrity sha512-YcvYFs15mX8m3AO1QNQy3BlIpSMfNRj3Ujk2BEJxsZG+HZf7/hZ6jr7mDpXrF8q+ff95Vef5yjhiZxm8CGJr6Q== dependencies: define-properties "^1.1.3" @@ -9761,7 +10066,7 @@ downshift@^9.0.8: react-is "18.2.0" tslib "^2.6.2" -dunder-proto@^1.0.1: +dunder-proto@^1.0.0, dunder-proto@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== @@ -9921,7 +10226,7 @@ envinfo@7.8.1: enzyme-shallow-equal@^1.0.0: version "1.0.7" - resolved "https://registry.npmjs.org/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.7.tgz#4e3aa678022387a68e6c47aff200587851885b5e" integrity sha512-/um0GFqUXnpM9SvKtje+9Tjoz3f1fpBC3eXRFrNs8kpYn69JljciYP7KZTqM/YQbUY9KUjvKB4jo/q+L6WGGvg== dependencies: hasown "^2.0.0" @@ -9998,6 +10303,66 @@ es-abstract@^1.17.2, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23 unbox-primitive "^1.0.2" which-typed-array "^1.1.15" +es-abstract@^1.23.5, es-abstract@^1.23.9: + version "1.24.1" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.24.1.tgz#f0c131ed5ea1bb2411134a8dd94def09c46c7899" + integrity sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw== + dependencies: + array-buffer-byte-length "^1.0.2" + arraybuffer.prototype.slice "^1.0.4" + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + data-view-buffer "^1.0.2" + data-view-byte-length "^1.0.2" + data-view-byte-offset "^1.0.1" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + es-set-tostringtag "^2.1.0" + es-to-primitive "^1.3.0" + function.prototype.name "^1.1.8" + get-intrinsic "^1.3.0" + get-proto "^1.0.1" + get-symbol-description "^1.1.0" + globalthis "^1.0.4" + gopd "^1.2.0" + has-property-descriptors "^1.0.2" + has-proto "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + internal-slot "^1.1.0" + is-array-buffer "^3.0.5" + is-callable "^1.2.7" + is-data-view "^1.0.2" + is-negative-zero "^2.0.3" + is-regex "^1.2.1" + is-set "^2.0.3" + is-shared-array-buffer "^1.0.4" + is-string "^1.1.1" + is-typed-array "^1.1.15" + is-weakref "^1.1.1" + math-intrinsics "^1.1.0" + object-inspect "^1.13.4" + object-keys "^1.1.1" + object.assign "^4.1.7" + own-keys "^1.0.1" + regexp.prototype.flags "^1.5.4" + safe-array-concat "^1.1.3" + safe-push-apply "^1.0.0" + safe-regex-test "^1.1.0" + set-proto "^1.0.0" + stop-iteration-iterator "^1.1.0" + string.prototype.trim "^1.2.10" + string.prototype.trimend "^1.0.9" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.3" + typed-array-byte-length "^1.0.3" + typed-array-byte-offset "^1.0.4" + typed-array-length "^1.0.7" + unbox-primitive "^1.1.0" + which-typed-array "^1.1.19" + es-array-method-boxes-properly@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz" @@ -10104,6 +10469,15 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" +es-to-primitive@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.3.0.tgz#96c89c82cc49fd8794a24835ba3e1ff87f214e18" + integrity sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g== + dependencies: + is-callable "^1.2.7" + is-date-object "^1.0.5" + is-symbol "^1.0.4" + es-toolkit@^1.22.0, es-toolkit@^1.39.10: version "1.39.10" resolved "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.39.10.tgz" @@ -11062,7 +11436,7 @@ find-process@^1.4.4: find-root@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng== find-up@5.0.0, find-up@^5.0.0: @@ -11154,7 +11528,7 @@ focus-lock@^1.3.5: focusable-selectors@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/focusable-selectors/-/focusable-selectors-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/focusable-selectors/-/focusable-selectors-0.4.0.tgz#c93092bfe65c7cf7ef52aed82fb6f8df31072459" integrity sha512-tc/236hUU3xemsRLu1RKhRQ5UWHjRM9iJTli1zdac43h7b1biRSgG0mILM0qrcsKaGCHcOPJ6NKbk12ouKHLpw== follow-redirects@^1.15.6: @@ -11169,6 +11543,13 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +for-each@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.5.tgz#d650688027826920feeb0af747ee7b9421a41d47" + integrity sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg== + dependencies: + is-callable "^1.2.7" + for-in@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" @@ -11360,7 +11741,19 @@ function-bind@^1.1.2: resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.2, function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: +function.prototype.name@^1.1.2, function.prototype.name@^1.1.8: + version "1.1.8" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.8.tgz#e68e1df7b259a5c949eeef95cdbde53edffabb78" + integrity sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + functions-have-names "^1.2.3" + hasown "^2.0.2" + is-callable "^1.2.7" + +function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: version "1.1.6" resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz" integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== @@ -11427,7 +11820,7 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" -get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.3.0: +get-intrinsic@^1.2.5, get-intrinsic@^1.2.6, get-intrinsic@^1.2.7, get-intrinsic@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== @@ -11500,6 +11893,15 @@ get-symbol-description@^1.0.2: es-errors "^1.3.0" get-intrinsic "^1.2.4" +get-symbol-description@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz#7bdd54e0befe8ffc9f3b4e203220d9f1e881b6ee" + integrity sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + get-tsconfig@^4.5.0: version "4.7.3" resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.7.3.tgz" @@ -11636,7 +12038,7 @@ glob@^9.2.0: global-cache@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/global-cache/-/global-cache-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/global-cache/-/global-cache-1.2.1.tgz#39ca020d3dd7b3f0934c52b75363f8d53312c16d" integrity sha512-EOeUaup5DgWKlCMhA9YFqNRIlZwoxt731jCh47WBV9fQqHgXhr3Fa55hfgIUqilIcPsfdNKN7LHjrNY+Km40KA== dependencies: define-properties "^1.1.2" @@ -11720,6 +12122,14 @@ globalthis@^1.0.3: dependencies: define-properties "^1.1.3" +globalthis@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" + integrity sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ== + dependencies: + define-properties "^1.2.1" + gopd "^1.0.1" + globby@10.0.1: version "10.0.1" resolved "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz" @@ -11840,6 +12250,13 @@ has-proto@^1.0.1, has-proto@^1.0.3: resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz" integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== +has-proto@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.2.0.tgz#5de5a6eabd95fdffd9818b43055e8065e39fe9d5" + integrity sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ== + dependencies: + dunder-proto "^1.0.0" + has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" @@ -11862,11 +12279,6 @@ has-unicode@2.0.1, has-unicode@^2.0.1: resolved "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== -has@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/has/-/has-1.0.4.tgz" - integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ== - hasha@^5.0.0: version "5.2.2" resolved "https://registry.npmjs.org/hasha/-/hasha-5.2.2.tgz#a48477989b3b327aea3c04f53096d816d97522a1" @@ -11923,7 +12335,7 @@ highlight.js@^10.4.1, highlight.js@~10.7.0: hoist-non-react-statics@^3.2.1, hoist-non-react-statics@^3.3.2: version "3.3.2" - resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== dependencies: react-is "^16.7.0" @@ -12164,7 +12576,15 @@ import-cwd@^3.0.0: dependencies: import-from "^3.0.0" -import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: +import-fresh@^3.1.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -12286,6 +12706,15 @@ internal-slot@^1.0.4, internal-slot@^1.0.7: hasown "^2.0.0" side-channel "^1.0.4" +internal-slot@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.1.0.tgz#1eac91762947d2f7056bc838d93e13b2e9604961" + integrity sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.2" + side-channel "^1.1.0" + interpret@^1.0.0: version "1.4.0" resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz" @@ -12353,6 +12782,15 @@ is-array-buffer@^3.0.2, is-array-buffer@^3.0.4: call-bind "^1.0.2" get-intrinsic "^1.2.1" +is-array-buffer@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz#65742e1e687bd2cc666253068fd8707fe4d44280" + integrity sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + get-intrinsic "^1.2.6" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" @@ -12372,6 +12810,13 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" +is-bigint@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.1.0.tgz#dda7a3445df57a42583db4228682eba7c4170672" + integrity sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ== + dependencies: + has-bigints "^1.0.2" + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" @@ -12387,6 +12832,14 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-boolean-object@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.2.2.tgz#7067f47709809a393c71ff5bb3e135d8a9215d9e" + integrity sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + is-builtin-module@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz" @@ -12413,7 +12866,7 @@ is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1, is-core- dependencies: hasown "^2.0.0" -is-core-module@^2.16.0: +is-core-module@^2.16.0, is-core-module@^2.16.1: version "2.16.1" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== @@ -12427,6 +12880,15 @@ is-data-view@^1.0.1: dependencies: is-typed-array "^1.1.13" +is-data-view@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.2.tgz#bae0a41b9688986c2188dda6657e56b8f9e63b8e" + integrity sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw== + dependencies: + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + is-typed-array "^1.1.13" + is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" @@ -12434,6 +12896,14 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: dependencies: has-tostringtag "^1.0.0" +is-date-object@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.1.0.tgz#ad85541996fc7aa8b2729701d27b7319f95d82f7" + integrity sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg== + dependencies: + call-bound "^1.0.2" + has-tostringtag "^1.0.2" + is-decimal@^1.0.0: version "1.0.4" resolved "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz" @@ -12456,6 +12926,13 @@ is-finalizationregistry@^1.0.2: dependencies: call-bind "^1.0.2" +is-finalizationregistry@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz#eefdcdc6c94ddd0674d9c85887bf93f944a97c90" + integrity sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg== + dependencies: + call-bound "^1.0.3" + is-finite@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz" @@ -12535,6 +13012,14 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" +is-number-object@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.1.1.tgz#144b21e95a1bc148205dcc2814a9134ec41b2541" + integrity sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + is-number@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" @@ -12609,7 +13094,17 @@ is-reference@1.2.1: dependencies: "@types/estree" "*" -is-regex@^1.1.0, is-regex@^1.1.4: +is-regex@^1.1.0, is-regex@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== + dependencies: + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + +is-regex@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== @@ -12636,6 +13131,13 @@ is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: dependencies: call-bind "^1.0.7" +is-shared-array-buffer@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz#9b67844bd9b7f246ba0708c3a93e34269c774f6f" + integrity sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A== + dependencies: + call-bound "^1.0.3" + is-ssh@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/is-ssh/-/is-ssh-1.4.0.tgz" @@ -12665,7 +13167,24 @@ is-string@^1.0.5, is-string@^1.0.7: dependencies: has-tostringtag "^1.0.0" -is-symbol@^1.0.1, is-symbol@^1.0.2, is-symbol@^1.0.3: +is-string@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.1.1.tgz#92ea3f3d5c5b6e039ca8677e5ac8d07ea773cbb9" + integrity sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA== + dependencies: + call-bound "^1.0.3" + has-tostringtag "^1.0.2" + +is-symbol@^1.0.1, is-symbol@^1.0.4, is-symbol@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.1.1.tgz#f47761279f532e2b05a7024a7506dbbedacd0634" + integrity sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w== + dependencies: + call-bound "^1.0.2" + has-symbols "^1.1.0" + safe-regex-test "^1.1.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== @@ -12681,7 +13200,7 @@ is-text-path@^1.0.1: is-touch-device@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/is-touch-device/-/is-touch-device-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/is-touch-device/-/is-touch-device-1.0.1.tgz#9a2fd59f689e9a9bf6ae9a86924c4ba805a42eab" integrity sha512-LAYzo9kMT1b2p19L/1ATGt2XcSilnzNlyvq6c0pbPRVisLbAPpLqr53tIJS00kvrTkj0HtR8U7+u8X0yR8lPSw== is-typed-array@^1.1.13, is-typed-array@^1.1.3: @@ -12691,6 +13210,13 @@ is-typed-array@^1.1.13, is-typed-array@^1.1.3: dependencies: which-typed-array "^1.1.14" +is-typed-array@^1.1.14, is-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.15.tgz#4bfb4a45b61cee83a5a46fba778e4e8d59c0ce0b" + integrity sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ== + dependencies: + which-typed-array "^1.1.16" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" @@ -12730,6 +13256,13 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakref@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.1.1.tgz#eea430182be8d64174bd96bffbc46f21bf3f9293" + integrity sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew== + dependencies: + call-bound "^1.0.3" + is-weakset@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz" @@ -14504,7 +15037,12 @@ lodash.uniq@^4.5.0: resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== -lodash@^4.1.1, lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: +lodash@^4.1.1: + version "4.17.23" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.23.tgz#f113b0378386103be4f6893388c73d0bde7f2c5a" + integrity sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w== + +lodash@^4.17.15, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.7.0: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -15532,11 +16070,21 @@ modify-values@^1.0.1: resolved "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment@>=1.6.0, moment@^2.26.0: +moment@>=1.6.0: version "2.30.1" - resolved "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== +monday-ui-style@0.26.2: + version "0.26.2" + resolved "https://registry.yarnpkg.com/monday-ui-style/-/monday-ui-style-0.26.2.tgz#2335dbf63fa988c841e55519eaf7d260c5d822e1" + integrity sha512-LZxjT+eqppL/m/hvxPfGdiFZ5xbv31rFkwItfuVHup8Xw3axTNgsvewDJg/Ahs0G7qcO9T7ucMIk7B440HtUJg== + dependencies: + postcss "8.4.31" + postcss-scss "^4.0.9" + postcss-value-parser "4.2.0" + stylelint "^14.16.1" + mout@1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/mout/-/mout-1.1.0.tgz" @@ -16094,7 +16642,7 @@ object-inspect@^1.13.1: resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== -object-inspect@^1.13.3: +object-inspect@^1.13.3, object-inspect@^1.13.4: version "1.13.4" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz" integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== @@ -16112,7 +16660,19 @@ object-keys@^1.1.1: resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.0, object.assign@^4.1.2, object.assign@^4.1.4, object.assign@^4.1.5: +object.assign@^4.1.0, object.assign@^4.1.7: + version "4.1.7" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.7.tgz#8c14ca1a424c6a561b0bb2a22f66f5049a945d3d" + integrity sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + has-symbols "^1.1.0" + object-keys "^1.1.1" + +object.assign@^4.1.2, object.assign@^4.1.4, object.assign@^4.1.5: version "4.1.5" resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz" integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== @@ -16132,7 +16692,17 @@ object.defaults@^1.1.0: for-own "^1.0.0" isobject "^3.0.0" -object.entries@^1.1.2, object.entries@^1.1.5, object.entries@^1.1.7: +object.entries@^1.1.2: + version "1.1.9" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.9.tgz#e4770a6a1444afb61bd39f984018b5bede25f8b3" + integrity sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.4" + define-properties "^1.2.1" + es-object-atoms "^1.1.1" + +object.entries@^1.1.5, object.entries@^1.1.7: version "1.1.8" resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz" integrity sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ== @@ -16197,7 +16767,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.0, object.values@^1.1.5, object.values@^1.1.6, object.values@^1.1.7: +object.values@^1.1.0, object.values@^1.1.6, object.values@^1.1.7: version "1.2.0" resolved "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz" integrity sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ== @@ -16206,6 +16776,16 @@ object.values@^1.1.0, object.values@^1.1.5, object.values@^1.1.6, object.values@ define-properties "^1.2.1" es-object-atoms "^1.0.0" +object.values@^1.1.5: + version "1.2.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.2.1.tgz#deed520a50809ff7f75a7cfd4bc64c7a038c6216" + integrity sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.3" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + on-finished@^2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" @@ -16309,6 +16889,15 @@ os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== +own-keys@^1.0.0, own-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" + integrity sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg== + dependencies: + get-intrinsic "^1.2.6" + object-keys "^1.1.1" + safe-push-apply "^1.0.0" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" @@ -16694,7 +17283,7 @@ pathval@^2.0.0: performance-now@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== picocolors@^1.0.0: @@ -17348,13 +17937,16 @@ promzard@^1.0.0: read "^2.0.0" prop-types-exact@^1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/prop-types-exact/-/prop-types-exact-1.2.0.tgz" - integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA== + version "1.2.7" + resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.7.tgz#dba4509df22b72f79583bb82e22cce663b05f0e3" + integrity sha512-A4RaV6mg3jocQqBYmqi2ojJ2VnV4AKTEHhl3xHsud08/u87gcVJc8DUOtgnPegoOCQv/shUqEk4eZGYibjnHzQ== dependencies: - has "^1.0.3" - object.assign "^4.1.0" - reflect.ownkeys "^0.2.0" + call-bound "^1.0.3" + es-errors "^1.3.0" + hasown "^2.0.2" + isarray "^2.0.5" + object.assign "^4.1.7" + own-keys "^1.0.0" prop-types@15.x, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" @@ -17461,7 +18053,7 @@ quick-lru@^4.0.1: raf@^3.4.1: version "3.4.1" - resolved "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== dependencies: performance-now "^2.1.0" @@ -17514,7 +18106,7 @@ react-confetti@^6.1.0: react-dates@21.8.0: version "21.8.0" - resolved "https://registry.npmjs.org/react-dates/-/react-dates-21.8.0.tgz" + resolved "https://registry.yarnpkg.com/react-dates/-/react-dates-21.8.0.tgz#355c3c7a243a7c29568fe00aca96231e171a5e94" integrity sha512-PPriGqi30CtzZmoHiGdhlA++YPYPYGCZrhydYmXXQ6RAvAsaONcPtYgXRTLozIOrsQ5mSo40+DiA5eOFHnZ6xw== dependencies: airbnb-prop-types "^2.15.0" @@ -17615,14 +18207,14 @@ react-inlinesvg@^4.1.3: react-input-autosize@^2.2.2: version "2.2.2" - resolved "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.2.tgz" + resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-2.2.2.tgz#fcaa7020568ec206bc04be36f4eb68e647c4d8c2" integrity sha512-jQJgYCA3S0j+cuOwzuCd1OjmBmnZLdqQdiLKRYrsMMzbjUrVDS5RvJUDwJqA7sKuksDuzFtm6hZGKFu7Mjk5aw== dependencies: prop-types "^15.5.8" react-input-autosize@^3.0.0: version "3.0.0" - resolved "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-3.0.0.tgz" + resolved "https://registry.yarnpkg.com/react-input-autosize/-/react-input-autosize-3.0.0.tgz#6b5898c790d4478d69420b55441fcc31d5c50a85" integrity sha512-nL9uS7jEs/zu8sqwFE5MAPx6pPkNAriACQ2rGLlqmKr2sPGtN7TXTyDdQt4lbNXVx7Uzadb40x8qotIuru6Rhg== dependencies: prop-types "^15.5.8" @@ -17662,14 +18254,14 @@ react-live@^4.1.7: react-moment-proptypes@^1.6.0: version "1.8.1" - resolved "https://registry.npmjs.org/react-moment-proptypes/-/react-moment-proptypes-1.8.1.tgz" + resolved "https://registry.yarnpkg.com/react-moment-proptypes/-/react-moment-proptypes-1.8.1.tgz#7ba4076147f6b5998f0d4f51d302d6d8c62049fd" integrity sha512-Er940DxWoObfIqPrZNfwXKugjxMIuk1LAuEzn23gytzV6hKS/sw108wibi9QubfMN4h+nrlje8eUCSbQRJo2fQ== dependencies: moment ">=1.6.0" react-outside-click-handler@^1.2.4: version "1.3.0" - resolved "https://registry.npmjs.org/react-outside-click-handler/-/react-outside-click-handler-1.3.0.tgz" + resolved "https://registry.yarnpkg.com/react-outside-click-handler/-/react-outside-click-handler-1.3.0.tgz#3831d541ac059deecd38ec5423f81e80ad60e115" integrity sha512-Te/7zFU0oHpAnctl//pP3hEAeobfeHMyygHB8MnjP6sX5OR8KHT1G3jmLsV3U9RnIYo+Yn+peJYWu+D5tUS8qQ== dependencies: airbnb-prop-types "^2.15.0" @@ -17687,9 +18279,9 @@ react-popper@^2.3.0: warning "^4.0.2" react-portal@^4.2.0: - version "4.2.2" - resolved "https://registry.npmjs.org/react-portal/-/react-portal-4.2.2.tgz" - integrity sha512-vS18idTmevQxyQpnde0Td6ZcUlv+pD8GTyR42n3CHUQq9OHi1C4jDE4ZWEbEsrbrLRhSECYiao58cvocwMtP7Q== + version "4.3.0" + resolved "https://registry.yarnpkg.com/react-portal/-/react-portal-4.3.0.tgz#92ca3492b1309883134f317a6aa88004534c860f" + integrity sha512-qs/2uKq1ifB3J1+K8ExfgUvCDZqlqCkfOEhqTELEDTfosloKiuzOzc7hl7IQ/7nohiFZD41BUYU0boAsIsGYHw== dependencies: prop-types "^15.5.8" @@ -17738,7 +18330,7 @@ react-resizable@^3.0.4: react-select@3.1.0: version "3.1.0" - resolved "https://registry.npmjs.org/react-select/-/react-select-3.1.0.tgz" + resolved "https://registry.yarnpkg.com/react-select/-/react-select-3.1.0.tgz#ab098720b2e9fe275047c993f0d0caf5ded17c27" integrity sha512-wBFVblBH1iuCBprtpyGtd1dGMadsG36W5/t2Aj8OE6WbByDg5jIFyT7X5gT+l0qmT5TqWhxX+VsKJvCEl2uL9g== dependencies: "@babel/runtime" "^7.4.4" @@ -17752,7 +18344,7 @@ react-select@3.1.0: "react-select@npm:react-select-module@3.4.0": version "3.4.0" - resolved "https://registry.npmjs.org/react-select-module/-/react-select-module-3.4.0.tgz#0a132a29428e841928ce16e7c758683f747ee189" + resolved "https://registry.yarnpkg.com/react-select-module/-/react-select-module-3.4.0.tgz#0a132a29428e841928ce16e7c758683f747ee189" integrity sha512-d9swlWWqqQyrZPfako6aKFK/kdLY9m4TQMIdhuD387VVAqB3+0zEKGb0ANQWsNNobo5ga46SltQzXuklxUvteA== dependencies: "@babel/runtime" "^7.4.4" @@ -17811,7 +18403,7 @@ react-virtualized-auto-sizer@^1.0.7: react-window@1.8.5: version "1.8.5" - resolved "https://registry.npmjs.org/react-window/-/react-window-1.8.5.tgz" + resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.5.tgz#a56b39307e79979721021f5d06a67742ecca52d1" integrity sha512-HeTwlNa37AFa8MDZFZOKcNEkuF2YflA0hpGPiTT9vR7OawEt+GZbfM6wqkBahD3D3pUjIabQYzsnY/BSJbgq6Q== dependencies: "@babel/runtime" "^7.0.0" @@ -17827,7 +18419,7 @@ react-window@^1.8.7: react-windowed-select@^2.0.4: version "2.0.5" - resolved "https://registry.npmjs.org/react-windowed-select/-/react-windowed-select-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/react-windowed-select/-/react-windowed-select-2.0.5.tgz#4fa56bdbe246706cb3d01f8882933807bea2e900" integrity sha512-HIpl3jl7fjxAFAn9RgY7JMw240Gj1Spookx656zyG7cYucYtCK4RLqv1celqdGHcXTCy5R3IFVoDcVasF2YNDg== dependencies: react-select "3.1.0" @@ -17835,7 +18427,7 @@ react-windowed-select@^2.0.4: react-with-direction@^1.3.1: version "1.4.0" - resolved "https://registry.npmjs.org/react-with-direction/-/react-with-direction-1.4.0.tgz" + resolved "https://registry.yarnpkg.com/react-with-direction/-/react-with-direction-1.4.0.tgz#ebdf64d685d0650ce966e872e6431ad5a2485444" integrity sha512-ybHNPiAmaJpoWwugwqry9Hd1Irl2hnNXlo/2SXQBwbLn/jGMauMS2y9jw+ydyX5V9ICryCqObNSthNt5R94xpg== dependencies: airbnb-prop-types "^2.16.0" @@ -17849,7 +18441,7 @@ react-with-direction@^1.3.1: react-with-styles-interface-css@^6.0.0: version "6.0.0" - resolved "https://registry.npmjs.org/react-with-styles-interface-css/-/react-with-styles-interface-css-6.0.0.tgz" + resolved "https://registry.yarnpkg.com/react-with-styles-interface-css/-/react-with-styles-interface-css-6.0.0.tgz#b53da7fa8359d452cb934cface8738acaef7b5fe" integrity sha512-6khSG1Trf4L/uXOge/ZAlBnq2O2PEXlQEqAhCRbvzaQU4sksIkdwpCPEl6d+DtP3+IdhyffTWuHDO9lhe1iYvA== dependencies: array.prototype.flat "^1.2.1" @@ -17857,7 +18449,7 @@ react-with-styles-interface-css@^6.0.0: react-with-styles@^4.1.0: version "4.2.0" - resolved "https://registry.npmjs.org/react-with-styles/-/react-with-styles-4.2.0.tgz" + resolved "https://registry.yarnpkg.com/react-with-styles/-/react-with-styles-4.2.0.tgz#0b8a8e5d94d082518b9f564f6fcf6103e28096c5" integrity sha512-tZCTY27KriRNhwHIbg1NkSdTTOSfXDg6Z7s+Q37mtz0Ym7Sc7IOr3PzVt4qJhJMW6Nkvfi3g34FuhtiGAJCBQA== dependencies: airbnb-prop-types "^2.14.0" @@ -18074,10 +18666,19 @@ reflect.getprototypeof@^1.0.4: globalthis "^1.0.3" which-builtin-type "^1.1.3" -reflect.ownkeys@^0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz" - integrity sha512-qOLsBKHCpSOFKK1NUOCGC5VyeufB6lEsFe92AL2bhIJsacZS1qdoOZSbPk3MYKuT2cFlRDnulKXuuElIrMjGUg== +reflect.getprototypeof@^1.0.6, reflect.getprototypeof@^1.0.9: + version "1.0.10" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9" + integrity sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-abstract "^1.23.9" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + get-intrinsic "^1.2.7" + get-proto "^1.0.1" + which-builtin-type "^1.2.1" refractor@^3.6.0: version "3.6.0" @@ -18137,6 +18738,18 @@ regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2: es-errors "^1.3.0" set-function-name "^2.0.1" +regexp.prototype.flags@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz#1ad6c62d44a259007e55b3970e00f746efbcaa19" + integrity sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA== + dependencies: + call-bind "^1.0.8" + define-properties "^1.2.1" + es-errors "^1.3.0" + get-proto "^1.0.1" + gopd "^1.2.0" + set-function-name "^2.0.2" + regexpu-core@^5.3.1: version "5.3.2" resolved "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.3.2.tgz" @@ -18295,7 +18908,7 @@ resolve.exports@^2.0.0: resolved "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.4: +resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.4: version "1.22.8" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -18304,6 +18917,15 @@ resolve@^1.1.5, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.12.0: + version "1.22.11" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.11.tgz#aad857ce1ffb8bfa9b0b1ac29f1156383f68c262" + integrity sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ== + dependencies: + is-core-module "^2.16.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^1.22.8: version "1.22.10" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz" @@ -18617,6 +19239,17 @@ safe-array-concat@^1.1.2: has-symbols "^1.0.3" isarray "^2.0.5" +safe-array-concat@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.3.tgz#c9e54ec4f603b0bbb8e7e5007a5ee7aecd1538c3" + integrity sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + get-intrinsic "^1.2.6" + has-symbols "^1.1.0" + isarray "^2.0.5" + safe-buffer@5.2.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" @@ -18632,6 +19265,14 @@ safe-identifier@^0.4.2: resolved "https://registry.npmjs.org/safe-identifier/-/safe-identifier-0.4.2.tgz" integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w== +safe-push-apply@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-push-apply/-/safe-push-apply-1.0.0.tgz#01850e981c1602d398c85081f360e4e6d03d27f5" + integrity sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA== + dependencies: + es-errors "^1.3.0" + isarray "^2.0.5" + safe-regex-test@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz" @@ -18641,6 +19282,15 @@ safe-regex-test@^1.0.3: es-errors "^1.3.0" is-regex "^1.1.4" +safe-regex-test@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.1.0.tgz#7f87dfb67a3150782eaaf18583ff5d1711ac10c1" + integrity sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + is-regex "^1.2.1" + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" @@ -18802,7 +19452,7 @@ set-blocking@^2.0.0: resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -set-function-length@^1.2.1: +set-function-length@^1.2.1, set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== @@ -18824,6 +19474,15 @@ set-function-name@^2.0.1, set-function-name@^2.0.2: functions-have-names "^1.2.3" has-property-descriptors "^1.0.2" +set-proto@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/set-proto/-/set-proto-1.0.0.tgz#0760dbcff30b2d7e801fd6e19983e56da337565e" + integrity sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw== + dependencies: + dunder-proto "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" @@ -19249,6 +19908,14 @@ stop-iteration-iterator@^1.0.0: dependencies: internal-slot "^1.0.4" +stop-iteration-iterator@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz#f481ff70a548f6124d0312c3aa14cbfa7aa542ad" + integrity sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ== + dependencies: + es-errors "^1.3.0" + internal-slot "^1.1.0" + store2@^2.14.2: version "2.14.3" resolved "https://registry.npmjs.org/store2/-/store2-2.14.3.tgz" @@ -19359,6 +20026,19 @@ string.prototype.matchall@^4.0.10: set-function-name "^2.0.2" side-channel "^1.0.6" +string.prototype.trim@^1.2.10: + version "1.2.10" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz#40b2dd5ee94c959b4dcfb1d65ce72e90da480c81" + integrity sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-data-property "^1.1.4" + define-properties "^1.2.1" + es-abstract "^1.23.5" + es-object-atoms "^1.0.0" + has-property-descriptors "^1.0.2" + string.prototype.trim@^1.2.9: version "1.2.9" resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz" @@ -19378,7 +20058,17 @@ string.prototype.trimend@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string.prototype.trimstart@^1.0.7: +string.prototype.trimend@^1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz#62e2731272cd285041b36596054e9f66569b6942" + integrity sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ== + dependencies: + call-bind "^1.0.8" + call-bound "^1.0.2" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + +string.prototype.trimstart@^1.0.7, string.prototype.trimstart@^1.0.8: version "1.0.8" resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz" integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== @@ -20253,6 +20943,15 @@ typed-array-buffer@^1.0.2: es-errors "^1.3.0" is-typed-array "^1.1.13" +typed-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz#a72395450a4869ec033fd549371b47af3a2ee536" + integrity sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw== + dependencies: + call-bound "^1.0.3" + es-errors "^1.3.0" + is-typed-array "^1.1.14" + typed-array-byte-length@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz" @@ -20264,6 +20963,17 @@ typed-array-byte-length@^1.0.1: has-proto "^1.0.3" is-typed-array "^1.1.13" +typed-array-byte-length@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz#8407a04f7d78684f3d252aa1a143d2b77b4160ce" + integrity sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg== + dependencies: + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.14" + typed-array-byte-offset@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz" @@ -20276,6 +20986,19 @@ typed-array-byte-offset@^1.0.2: has-proto "^1.0.3" is-typed-array "^1.1.13" +typed-array-byte-offset@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz#ae3698b8ec91a8ab945016108aef00d5bff12355" + integrity sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + for-each "^0.3.3" + gopd "^1.2.0" + has-proto "^1.2.0" + is-typed-array "^1.1.15" + reflect.getprototypeof "^1.0.9" + typed-array-length@^1.0.5: version "1.0.6" resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz" @@ -20288,6 +21011,18 @@ typed-array-length@^1.0.5: is-typed-array "^1.1.13" possible-typed-array-names "^1.0.0" +typed-array-length@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.7.tgz#ee4deff984b64be1e118b0de8c9c877d5ce73d3d" + integrity sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + reflect.getprototypeof "^1.0.6" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" @@ -20344,15 +21079,15 @@ typescript-plugin-css-modules@^5.0.1: stylus "^0.62.0" tsconfig-paths "^4.2.0" -"typescript@>=3 < 6", typescript@^5.1.6: +"typescript@>=3 < 6": version "5.4.3" resolved "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz" integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg== -typescript@^4.7.3: - version "4.9.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.9.3: + version "5.9.3" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz#5b4f59e15310ab17a216f5d6cf53ee476ede670f" + integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw== ufo@^1.5.3: version "1.5.3" @@ -20374,6 +21109,16 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +unbox-primitive@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz#8d9d2c9edeea8460c7f35033a88867944934d1e2" + integrity sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw== + dependencies: + call-bound "^1.0.3" + has-bigints "^1.0.2" + has-symbols "^1.1.0" + which-boxed-primitive "^1.1.1" + unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz" @@ -20960,6 +21705,17 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" + integrity sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA== + dependencies: + is-bigint "^1.1.0" + is-boolean-object "^1.2.1" + is-number-object "^1.1.1" + is-string "^1.1.1" + is-symbol "^1.1.1" + which-builtin-type@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz" @@ -20978,7 +21734,26 @@ which-builtin-type@^1.1.3: which-collection "^1.0.1" which-typed-array "^1.1.9" -which-collection@^1.0.1: +which-builtin-type@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.2.1.tgz#89183da1b4907ab089a6b02029cc5d8d6574270e" + integrity sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q== + dependencies: + call-bound "^1.0.2" + function.prototype.name "^1.1.6" + has-tostringtag "^1.0.2" + is-async-function "^2.0.0" + is-date-object "^1.1.0" + is-finalizationregistry "^1.1.0" + is-generator-function "^1.0.10" + is-regex "^1.2.1" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.1.0" + which-collection "^1.0.2" + which-typed-array "^1.1.16" + +which-collection@^1.0.1, which-collection@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz" integrity sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw== @@ -21004,6 +21779,19 @@ which-typed-array@^1.1.13, which-typed-array@^1.1.14, which-typed-array@^1.1.15, gopd "^1.0.1" has-tostringtag "^1.0.2" +which-typed-array@^1.1.16, which-typed-array@^1.1.19: + version "1.1.20" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.20.tgz#3fdb7adfafe0ea69157b1509f3a1cd892bd1d122" + integrity sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.8" + call-bound "^1.0.4" + for-each "^0.3.5" + get-proto "^1.0.1" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + which@^1.0.5, which@^1.2.12, which@^1.2.14, which@^1.2.4, which@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz"