@automattic/charts 0.29.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -2
- package/agents.md +155 -0
- package/dist/cjs/components/bar-chart/bar-chart.js +26 -17
- package/dist/cjs/components/bar-list-chart/bar-list-chart.js +23 -4
- package/dist/cjs/components/conversion-funnel-chart/conversion-funnel-chart.js +245 -0
- package/dist/cjs/components/conversion-funnel-chart/conversion-funnel-chart.module.scss.js +7 -0
- package/dist/cjs/components/conversion-funnel-chart/private/use-funnel-selection.js +61 -0
- package/dist/cjs/components/leaderboard-chart/index.js +4 -1
- package/dist/cjs/components/leaderboard-chart/leaderboard-chart.js +13 -6
- package/dist/cjs/components/leaderboard-chart/leaderboard-chart.module.scss.js +1 -1
- package/dist/cjs/components/legend/{use-chart-legend-data.js → hooks/use-chart-legend-items.js} +31 -26
- package/dist/cjs/components/legend/index.js +2 -4
- package/dist/cjs/components/legend/legend.js +2 -2
- package/dist/cjs/components/legend/private/base-legend.js +78 -0
- package/dist/cjs/components/legend/private/base-legend.module.scss.js +7 -0
- package/dist/cjs/components/legend/{utils.js → utils/label-transform-factory.js} +0 -20
- package/dist/cjs/components/legend/utils/value-or-identity.js +23 -0
- package/dist/cjs/components/line-chart/line-chart.js +43 -20
- package/dist/cjs/components/line-chart/{line-chart-annotation-label-popover.js → private/line-chart-annotation-label-popover.js} +7 -3
- package/dist/cjs/components/line-chart/{line-chart-annotation.js → private/line-chart-annotation.js} +9 -4
- package/dist/cjs/components/line-chart/{line-chart-annotations-overlay.js → private/line-chart-annotations-overlay.js} +4 -3
- package/dist/cjs/components/pie-chart/pie-chart.js +32 -19
- package/dist/cjs/components/pie-semi-circle-chart/pie-semi-circle-chart.js +22 -18
- package/dist/cjs/components/private/single-chart-context/single-chart-context.js +10 -0
- package/dist/cjs/components/{shared/single-chart-context.js → private/single-chart-context/use-single-chart-context.js} +2 -6
- package/dist/cjs/components/tooltip/index.js +3 -0
- package/dist/cjs/{components/shared → hooks}/use-chart-data-transform.js +4 -1
- package/dist/cjs/{components/shared → hooks}/use-chart-margin.js +5 -2
- package/dist/cjs/hooks/use-xychart-theme.js +11 -2
- package/dist/cjs/index.js +13 -19
- package/dist/cjs/providers/chart-context/global-charts-provider.js +29 -9
- package/dist/cjs/providers/chart-context/hooks/use-chart-id.js +10 -0
- package/dist/cjs/providers/chart-context/{utils.js → hooks/use-chart-registration.js} +10 -10
- package/dist/cjs/providers/chart-context/hooks/use-global-charts-context.js +14 -0
- package/dist/cjs/{hooks/use-global-chart-theme.js → providers/chart-context/hooks/use-global-charts-theme.js} +9 -6
- package/dist/cjs/providers/chart-context/index.js +8 -4
- package/dist/cjs/providers/theme/themes.js +6 -3
- package/dist/cjs/style.css +1 -1
- package/dist/cjs/utils/color-utils.js +39 -0
- package/dist/cjs/{components/shared/utils.js → utils/get-longest-tick-width.js} +0 -7
- package/dist/cjs/utils/get-styles.js +0 -17
- package/dist/cjs/utils/is-safari.js +10 -0
- package/dist/mjs/components/bar-chart/bar-chart.js +24 -15
- package/dist/mjs/components/bar-list-chart/bar-list-chart.js +23 -5
- package/dist/mjs/components/conversion-funnel-chart/conversion-funnel-chart.js +240 -0
- package/dist/mjs/components/conversion-funnel-chart/conversion-funnel-chart.module.scss.js +3 -0
- package/dist/mjs/components/conversion-funnel-chart/private/use-funnel-selection.js +59 -0
- package/dist/mjs/components/leaderboard-chart/index.js +4 -1
- package/dist/mjs/components/leaderboard-chart/leaderboard-chart.js +14 -7
- package/dist/mjs/components/leaderboard-chart/leaderboard-chart.module.scss.js +1 -1
- package/dist/mjs/components/legend/{use-chart-legend-data.js → hooks/use-chart-legend-items.js} +31 -26
- package/dist/mjs/components/legend/index.js +1 -2
- package/dist/mjs/components/legend/legend.js +2 -2
- package/dist/mjs/components/legend/{base-legend.js → private/base-legend.js} +32 -8
- package/dist/mjs/components/legend/private/base-legend.module.scss.js +3 -0
- package/dist/mjs/components/legend/{utils.js → utils/label-transform-factory.js} +1 -19
- package/dist/mjs/components/legend/utils/value-or-identity.js +20 -0
- package/dist/mjs/components/line-chart/line-chart.js +42 -19
- package/dist/mjs/components/line-chart/{line-chart-annotation-label-popover.js → private/line-chart-annotation-label-popover.js} +6 -2
- package/dist/mjs/components/line-chart/{line-chart-annotation.js → private/line-chart-annotation.js} +8 -3
- package/dist/mjs/components/line-chart/{line-chart-annotations-overlay.js → private/line-chart-annotations-overlay.js} +3 -2
- package/dist/mjs/components/pie-chart/pie-chart.js +30 -17
- package/dist/mjs/components/pie-semi-circle-chart/pie-semi-circle-chart.js +20 -16
- package/dist/mjs/components/private/single-chart-context/single-chart-context.js +7 -0
- package/dist/mjs/components/{shared/single-chart-context.js → private/single-chart-context/use-single-chart-context.js} +3 -5
- package/dist/mjs/components/tooltip/index.js +1 -0
- package/dist/mjs/{components/shared → hooks}/use-chart-data-transform.js +4 -1
- package/dist/mjs/{components/shared → hooks}/use-chart-margin.js +4 -1
- package/dist/mjs/hooks/use-xychart-theme.js +11 -2
- package/dist/mjs/index.js +9 -9
- package/dist/mjs/providers/chart-context/global-charts-provider.js +31 -10
- package/dist/mjs/providers/chart-context/hooks/use-chart-id.js +8 -0
- package/dist/mjs/providers/chart-context/{utils.js → hooks/use-chart-registration.js} +11 -10
- package/dist/mjs/providers/chart-context/hooks/use-global-charts-context.js +12 -0
- package/dist/mjs/{hooks/use-global-chart-theme.js → providers/chart-context/hooks/use-global-charts-theme.js} +9 -6
- package/dist/mjs/providers/chart-context/index.js +5 -2
- package/dist/mjs/providers/theme/themes.js +6 -3
- package/dist/mjs/style.css +1 -1
- package/dist/mjs/utils/color-utils.js +37 -0
- package/dist/mjs/{components/shared/utils.js → utils/get-longest-tick-width.js} +1 -7
- package/dist/mjs/utils/get-styles.js +1 -17
- package/dist/mjs/utils/is-safari.js +8 -0
- package/dist/types/components/bar-chart/bar-chart.d.ts +2 -2
- package/dist/types/components/bar-chart/index.d.ts +1 -1
- package/dist/types/components/bar-list-chart/bar-list-chart.d.ts +3 -3
- package/dist/types/components/bar-list-chart/index.d.ts +1 -1
- package/dist/types/components/conversion-funnel-chart/conversion-funnel-chart.d.ts +63 -1
- package/dist/types/components/leaderboard-chart/index.d.ts +1 -1
- package/dist/types/components/leaderboard-chart/leaderboard-chart.d.ts +4 -0
- package/dist/types/components/legend/{use-chart-legend-data.d.ts → hooks/use-chart-legend-items.d.ts} +7 -5
- package/dist/types/components/legend/index.d.ts +2 -3
- package/dist/types/components/legend/legend.d.ts +2 -2
- package/dist/types/components/legend/types.d.ts +15 -20
- package/dist/types/components/line-chart/index.d.ts +1 -0
- package/dist/types/components/line-chart/line-chart.d.ts +7 -28
- package/dist/types/components/line-chart/private/line-chart-annotation.d.ts +6 -0
- package/dist/types/components/line-chart/types.d.ts +61 -0
- package/dist/types/components/pie-chart/index.d.ts +1 -1
- package/dist/types/components/pie-chart/pie-chart.d.ts +4 -4
- package/dist/types/components/pie-semi-circle-chart/index.d.ts +1 -1
- package/dist/types/components/pie-semi-circle-chart/pie-semi-circle-chart.d.ts +6 -4
- package/dist/types/components/tooltip/accessible-tooltip.d.ts +52 -0
- package/dist/types/components/tooltip/index.d.ts +2 -0
- package/dist/types/components/tooltip/types.d.ts +8 -0
- package/dist/types/index.d.ts +7 -15
- package/dist/types/providers/chart-context/global-charts-provider.d.ts +3 -4
- package/dist/types/providers/chart-context/hooks/use-chart-id.d.ts +3 -0
- package/dist/types/providers/chart-context/{utils.d.ts → hooks/use-chart-registration.d.ts} +2 -3
- package/dist/types/providers/chart-context/hooks/use-global-charts-context.d.ts +5 -0
- package/dist/types/{hooks/use-global-chart-theme.d.ts → providers/chart-context/hooks/use-global-charts-theme.d.ts} +3 -3
- package/dist/types/providers/chart-context/index.d.ts +6 -3
- package/dist/types/providers/chart-context/types.d.ts +13 -2
- package/dist/types/types.d.ts +9 -3
- package/package.json +1 -1
- package/dist/cjs/components/legend/base-legend.js +0 -54
- package/dist/cjs/components/legend/legend.module.scss.js +0 -7
- package/dist/mjs/components/legend/legend.module.scss.js +0 -3
- package/dist/types/components/legend/base-legend.d.ts +0 -53
- package/dist/types/components/line-chart/line-chart-annotation.d.ts +0 -38
- package/dist/types/hooks/use-chart-mouse-handler.d.ts +0 -44
- package/dist/types/hooks/use-deep-memo.d.ts +0 -10
- package/dist/types/hooks/use-xychart-theme.d.ts +0 -6
- package/dist/types/utils/merge-themes.d.ts +0 -13
- package/index.ts +0 -16
- /package/dist/cjs/components/bar-chart/{use-bar-chart-options.js → private/use-bar-chart-options.js} +0 -0
- /package/dist/cjs/components/{shared → private}/chart-composition/chart-html.js +0 -0
- /package/dist/cjs/components/{shared → private}/chart-composition/chart-svg.js +0 -0
- /package/dist/cjs/components/{shared → private}/chart-composition/use-chart-children.js +0 -0
- /package/dist/cjs/components/{shared → private/default-glyph}/default-glyph.js +0 -0
- /package/dist/cjs/components/{shared → private/with-responsive}/with-responsive.js +0 -0
- /package/dist/cjs/{components/shared → hooks}/use-element-height.js +0 -0
- /package/dist/cjs/{components/shared → hooks}/use-zero-value-display.js +0 -0
- /package/dist/cjs/{components/shared → utils}/date-parsing.js +0 -0
- /package/dist/cjs/{components/shared → utils}/format-metric-value.js +0 -0
- /package/dist/mjs/components/bar-chart/{use-bar-chart-options.js → private/use-bar-chart-options.js} +0 -0
- /package/dist/mjs/components/{shared → private}/chart-composition/chart-html.js +0 -0
- /package/dist/mjs/components/{shared → private}/chart-composition/chart-svg.js +0 -0
- /package/dist/mjs/components/{shared → private}/chart-composition/use-chart-children.js +0 -0
- /package/dist/mjs/components/{shared → private/default-glyph}/default-glyph.js +0 -0
- /package/dist/mjs/components/{shared → private/with-responsive}/with-responsive.js +0 -0
- /package/dist/mjs/{components/shared → hooks}/use-element-height.js +0 -0
- /package/dist/mjs/{components/shared → hooks}/use-zero-value-display.js +0 -0
- /package/dist/mjs/{components/shared → utils}/date-parsing.js +0 -0
- /package/dist/mjs/{components/shared → utils}/format-metric-value.js +0 -0
- /package/dist/types/components/line-chart/{line-chart-annotations-overlay.d.ts → private/line-chart-annotations-overlay.d.ts} +0 -0
- /package/dist/types/components/{shared → private}/chart-composition/types.d.ts +0 -0
- /package/dist/types/components/{shared → private/single-chart-context}/single-chart-context.d.ts +0 -0
- /package/dist/types/components/{shared → private/with-responsive}/with-responsive.d.ts +0 -0
- /package/dist/types/{components/shared → utils}/format-metric-value.d.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,13 +5,34 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.31.0] - 2025-09-01
|
|
9
|
+
### Added
|
|
10
|
+
- Charts: adds an agents.md file to project root [#44954]
|
|
11
|
+
- Charts: Refactor shared components, hooks and utils [#44971]
|
|
12
|
+
- Charts: Stable colors for series groups [#44730]
|
|
13
|
+
- Refactor Pie Chart to improve readability. [#44989]
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
- Enhanced ConversionFunnelChart with render props and TooltipInPortal [#45019]
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
- Charts: fix label background colour and text colour [#44990]
|
|
20
|
+
- Refactored leaderboard chart to remove progressbar. [#44982]
|
|
21
|
+
|
|
22
|
+
## [0.30.0] - 2025-08-27
|
|
23
|
+
### Added
|
|
24
|
+
- Export ConversionFunnelChart for usage outside. [#44952]
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Consolidate sample data across Storybook stories for consistency and maintainability [#44903]
|
|
28
|
+
|
|
8
29
|
## [0.29.0] - 2025-08-25
|
|
9
30
|
### Changed
|
|
10
|
-
- Charts:
|
|
31
|
+
- Charts: Consolidate and clean up pie chart composition API. [#44856]
|
|
11
32
|
|
|
12
33
|
## [0.28.0] - 2025-08-21
|
|
13
34
|
### Added
|
|
14
|
-
- Charts:
|
|
35
|
+
- Charts: Add composition legend to pie family charts. [#44796]
|
|
15
36
|
- Charts: Add theme to global context and use instead of that from theme provider [#44809]
|
|
16
37
|
|
|
17
38
|
## [0.27.0] - 2025-08-18
|
|
@@ -384,6 +405,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
384
405
|
- Fixed lints following ESLint rule changes for TS [#40584]
|
|
385
406
|
- Fixing a bug in Chart storybook data. [#40640]
|
|
386
407
|
|
|
408
|
+
[0.31.0]: https://github.com/Automattic/charts/compare/v0.30.0...v0.31.0
|
|
409
|
+
[0.30.0]: https://github.com/Automattic/charts/compare/v0.29.0...v0.30.0
|
|
387
410
|
[0.29.0]: https://github.com/Automattic/charts/compare/v0.28.0...v0.29.0
|
|
388
411
|
[0.28.0]: https://github.com/Automattic/charts/compare/v0.27.0...v0.28.0
|
|
389
412
|
[0.27.0]: https://github.com/Automattic/charts/compare/v0.26.0...v0.27.0
|
package/agents.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
This file provides AI coding agents with specific instructions and context for working on the @automattic/charts library.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
**@automattic/charts** is a React/TypeScript charting library built for interactive data visualizations within Automattic products. The library is built on visx and emphasizes accessibility, responsiveness, and developer experience through a component composition API.
|
|
8
|
+
|
|
9
|
+
**Key Technologies:**
|
|
10
|
+
- React 18+ with TypeScript (strict mode)
|
|
11
|
+
- visx for chart primitives
|
|
12
|
+
- PostCSS/Sass with BEM naming convention
|
|
13
|
+
- Jest + Testing Library for testing
|
|
14
|
+
- Storybook for documentation and development
|
|
15
|
+
|
|
16
|
+
## Essential Commands
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Development
|
|
20
|
+
pnpm run build:dev # Development build with source maps
|
|
21
|
+
pnpm run storybook # Start Storybook development server
|
|
22
|
+
pnpm run typecheck # TypeScript type checking
|
|
23
|
+
|
|
24
|
+
# Testing & Quality
|
|
25
|
+
pnpm run test # Run Jest test suite (TZ=UTC)
|
|
26
|
+
pnpm run test-coverage # Run tests with coverage report
|
|
27
|
+
|
|
28
|
+
# Production
|
|
29
|
+
pnpm run build:prod # Production build (clean + optimized)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Documentation Standards
|
|
33
|
+
|
|
34
|
+
**IMPORTANT:** This project has comprehensive documentation standards. Before creating or modifying any components, agents must reference:
|
|
35
|
+
|
|
36
|
+
- **[docs/ai-documentation-guide.md](docs/ai-documentation-guide.md)** - Comprehensive guide covering documentation standards, writing patterns, and quality requirements for chart components
|
|
37
|
+
- **[docs/feature-documentation.mdx.template](docs/feature-documentation.mdx.template)** - Standard MDX template for new component documentation
|
|
38
|
+
- **[Storybook Documentation](https://automattic.github.io/jetpack-storybook/?path=/docs/js-packages-charts)** - Live examples and API references
|
|
39
|
+
|
|
40
|
+
## Code Standards & Architecture
|
|
41
|
+
|
|
42
|
+
### Component Architecture
|
|
43
|
+
- **Compound Components**: Follow established patterns where parent components provide context and child components handle specific functionality
|
|
44
|
+
- **Theme System**: Use the centralized theme system for colors, spacing, and styling
|
|
45
|
+
- **Accessibility First**: Ensure WCAG 2.1 AA compliance for all chart components
|
|
46
|
+
|
|
47
|
+
### TypeScript Standards
|
|
48
|
+
- Strict TypeScript mode enabled
|
|
49
|
+
- Export types for all public APIs
|
|
50
|
+
- Use proper generic constraints for data types
|
|
51
|
+
- Define clear prop interfaces with JSDoc comments
|
|
52
|
+
|
|
53
|
+
### Styling Standards
|
|
54
|
+
- **BEM CSS naming convention** (per global guidelines)
|
|
55
|
+
- PostCSS with Sass support
|
|
56
|
+
- CSS custom properties for theming
|
|
57
|
+
- **Never use `!important`** (per global guidelines)
|
|
58
|
+
- Responsive design patterns
|
|
59
|
+
|
|
60
|
+
### Testing Requirements
|
|
61
|
+
- Jest configuration: `tests/jest.config.cjs`
|
|
62
|
+
- UTC timezone for consistent test results
|
|
63
|
+
- @testing-library/react for component testing
|
|
64
|
+
- Test both interaction and visual rendering
|
|
65
|
+
- Maintain existing coverage standards
|
|
66
|
+
|
|
67
|
+
## Development Workflow
|
|
68
|
+
|
|
69
|
+
### For New Components
|
|
70
|
+
1. **Check existing components** - Determine if functionality can be added to existing components rather than creating new ones
|
|
71
|
+
2. **Use the template** - Start with `docs/feature-documentation.mdx.template`
|
|
72
|
+
3. **Follow compound patterns** - Study existing chart components for composition patterns
|
|
73
|
+
4. **Integrate with providers** - Ensure compatibility with existing context providers
|
|
74
|
+
|
|
75
|
+
### For Component Modifications
|
|
76
|
+
1. **Study surrounding code** - Understand existing patterns and conventions
|
|
77
|
+
2. **Maintain backward compatibility** - Breaking changes to public APIs only when necessary
|
|
78
|
+
3. **Update documentation** - Follow the ai-documentation-guide.md standards
|
|
79
|
+
4. **Test thoroughly** - Verify existing functionality remains intact
|
|
80
|
+
|
|
81
|
+
### For New Chart Types
|
|
82
|
+
1. **Use visx primitives** - Build on established visx patterns
|
|
83
|
+
2. **Follow theme system** - Integrate with existing color and styling systems
|
|
84
|
+
3. **Accessibility review** - Ensure screen reader compatibility and keyboard navigation
|
|
85
|
+
|
|
86
|
+
## Build System
|
|
87
|
+
|
|
88
|
+
- **Rollup** for production builds (CJS/ESM/Types)
|
|
89
|
+
- **Multiple export patterns** in package.json:
|
|
90
|
+
- `./` - Main library entry
|
|
91
|
+
- `./*` - Individual components
|
|
92
|
+
- `./providers/*` - Context providers
|
|
93
|
+
- `./visx/*` - visx-related utilities
|
|
94
|
+
|
|
95
|
+
## Security & Compliance
|
|
96
|
+
|
|
97
|
+
- GPL-2.0-or-later license
|
|
98
|
+
- No secrets or sensitive data in components
|
|
99
|
+
- WordPress security standards where applicable
|
|
100
|
+
- Report security issues via [automattic.com/security](https://automattic.com/security)
|
|
101
|
+
|
|
102
|
+
## Pull Request Guidelines
|
|
103
|
+
|
|
104
|
+
When contributing to the Charts library, follow the Jetpack monorepo's standard PR process:
|
|
105
|
+
|
|
106
|
+
### Required Elements
|
|
107
|
+
- **PR Title**: Use format "Charts: [clear description of change]"
|
|
108
|
+
- **Changelog Entry**: Run `pnpm changelog add` in the charts directory
|
|
109
|
+
- **Testing Instructions**: Include specific steps for testing chart components
|
|
110
|
+
- **Visual Changes**: Provide screenshots/GIFs for any UI modifications
|
|
111
|
+
|
|
112
|
+
### Charts-Specific Considerations
|
|
113
|
+
- **Storybook Links**: Include links to new/modified component stories
|
|
114
|
+
- **Accessibility Notes**: Document accessibility features and testing approach
|
|
115
|
+
- **Performance Impact**: Note any considerations for large datasets or complex visualizations
|
|
116
|
+
- **Browser Compatibility**: Highlight any browser-specific concerns
|
|
117
|
+
- **Theme Integration**: Verify changes work across different theme configurations
|
|
118
|
+
|
|
119
|
+
### Reference Documentation
|
|
120
|
+
- [Jetpack Contributing Guide](../../../../docs/CONTRIBUTING.md) - Main contribution standards
|
|
121
|
+
- [PR Lifecycle Documentation](../../../../docs/pull-request.md) - Detailed PR process
|
|
122
|
+
- [Changelog Guidelines](../../../../docs/writing-a-good-changelog-entry.md) - Required changelog format
|
|
123
|
+
|
|
124
|
+
**Note**: All PRs automatically use the monorepo-wide PR template from `.github/PULL_REQUEST_TEMPLATE.md`.
|
|
125
|
+
|
|
126
|
+
## Agent-Specific Guidelines
|
|
127
|
+
|
|
128
|
+
### Before Making Changes
|
|
129
|
+
- Read existing component documentation and stories
|
|
130
|
+
- Check Storybook for usage patterns and examples
|
|
131
|
+
- Verify TypeScript types compile without errors
|
|
132
|
+
- Run tests to ensure no regressions
|
|
133
|
+
|
|
134
|
+
### When Adding Features
|
|
135
|
+
- Prefer extending existing components over creating new ones
|
|
136
|
+
- Use established theme and provider patterns
|
|
137
|
+
- Follow the documentation template for new features
|
|
138
|
+
- Consider performance impact on large datasets
|
|
139
|
+
|
|
140
|
+
### Quality Checklist
|
|
141
|
+
- [ ] TypeScript compiles without errors (`pnpm run typecheck`)
|
|
142
|
+
- [ ] Tests pass (`pnpm run test`)
|
|
143
|
+
- [ ] Documentation follows ai-documentation-guide.md standards
|
|
144
|
+
- [ ] Component works with existing providers and themes
|
|
145
|
+
- [ ] Accessibility requirements met
|
|
146
|
+
- [ ] Storybook stories updated/created
|
|
147
|
+
|
|
148
|
+
## Getting Help
|
|
149
|
+
|
|
150
|
+
- **Documentation**: Start with `docs/ai-documentation-guide.md`
|
|
151
|
+
- **Examples**: Review existing chart components and their stories
|
|
152
|
+
- **Build Issues**: Check package.json scripts and build configurations
|
|
153
|
+
- **Testing**: Follow patterns in existing test files
|
|
154
|
+
|
|
155
|
+
This library prioritizes maintainability, accessibility, and developer experience. Always consider how changes affect the broader ecosystem and existing users.
|
|
@@ -9,25 +9,30 @@ var i18n = require('@wordpress/i18n');
|
|
|
9
9
|
var clsx = require('clsx');
|
|
10
10
|
var react = require('react');
|
|
11
11
|
require('fast-deep-equal');
|
|
12
|
-
var useGlobalChartTheme = require('../../hooks/use-global-chart-theme.js');
|
|
13
12
|
require('@visx/event');
|
|
14
13
|
require('@visx/tooltip');
|
|
15
14
|
var useXychartTheme = require('../../hooks/use-xychart-theme.js');
|
|
15
|
+
var useChartDataTransform = require('../../hooks/use-chart-data-transform.js');
|
|
16
|
+
var useChartMargin = require('../../hooks/use-chart-margin.js');
|
|
17
|
+
var useElementHeight = require('../../hooks/use-element-height.js');
|
|
18
|
+
var useZeroValueDisplay = require('../../hooks/use-zero-value-display.js');
|
|
16
19
|
var globalChartsProvider = require('../../providers/chart-context/global-charts-provider.js');
|
|
17
|
-
var
|
|
20
|
+
var useGlobalChartsContext = require('../../providers/chart-context/hooks/use-global-charts-context.js');
|
|
21
|
+
var useChartId = require('../../providers/chart-context/hooks/use-chart-id.js');
|
|
22
|
+
var useChartRegistration = require('../../providers/chart-context/hooks/use-chart-registration.js');
|
|
18
23
|
var createComposition = require('../../utils/create-composition.js');
|
|
24
|
+
require('date-fns');
|
|
25
|
+
require('@automattic/number-formatters');
|
|
26
|
+
require('@visx/text');
|
|
27
|
+
require('deepmerge');
|
|
28
|
+
require('../../providers/theme/theme-provider.js');
|
|
19
29
|
var legend = require('../legend/legend.js');
|
|
20
|
-
require('../legend/
|
|
21
|
-
var
|
|
22
|
-
var
|
|
23
|
-
var useChartDataTransform = require('../shared/use-chart-data-transform.js');
|
|
24
|
-
var useChartMargin = require('../shared/use-chart-margin.js');
|
|
25
|
-
var useElementHeight = require('../shared/use-element-height.js');
|
|
26
|
-
var useZeroValueDisplay = require('../shared/use-zero-value-display.js');
|
|
27
|
-
var withResponsive = require('../shared/with-responsive.js');
|
|
30
|
+
var useChartLegendItems = require('../legend/hooks/use-chart-legend-items.js');
|
|
31
|
+
var singleChartContext = require('../private/single-chart-context/single-chart-context.js');
|
|
32
|
+
var withResponsive = require('../private/with-responsive/with-responsive.js');
|
|
28
33
|
var accessibleTooltip = require('../tooltip/accessible-tooltip.js');
|
|
29
34
|
var barChart_module = require('./bar-chart.module.scss.js');
|
|
30
|
-
var useBarChartOptions = require('./use-bar-chart-options.js');
|
|
35
|
+
var useBarChartOptions = require('./private/use-bar-chart-options.js');
|
|
31
36
|
|
|
32
37
|
// Validation function similar to LineChart
|
|
33
38
|
const validateData = (data) => {
|
|
@@ -45,8 +50,7 @@ const validateData = (data) => {
|
|
|
45
50
|
const getPatternId = (chartId, index) => `bar-pattern-${chartId}-${index}`;
|
|
46
51
|
const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400, className, margin, withTooltips = false, showLegend = false, legendOrientation = 'horizontal', legendPosition = 'bottom', legendAlignment = 'center', legendShape = 'rect', gridVisibility: gridVisibilityProp, renderTooltip, options = {}, orientation = 'vertical', withPatterns = false, showZeroValues = false, children, }) => {
|
|
47
52
|
const horizontal = orientation === 'horizontal';
|
|
48
|
-
const chartId =
|
|
49
|
-
const providerTheme = useGlobalChartTheme.useGlobalChartTheme();
|
|
53
|
+
const chartId = useChartId.useChartId(providedChartId);
|
|
50
54
|
const theme = useXychartTheme.useXYChartTheme(data);
|
|
51
55
|
const dataSorted = useChartDataTransform.useChartDataTransform(data);
|
|
52
56
|
// Transform data to add a small value for zero bars to make them visible
|
|
@@ -54,7 +58,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400,
|
|
|
54
58
|
enabled: showZeroValues,
|
|
55
59
|
});
|
|
56
60
|
// Create legend items using the reusable hook
|
|
57
|
-
const legendItems =
|
|
61
|
+
const legendItems = useChartLegendItems.useChartLegendItems(dataSorted);
|
|
58
62
|
const chartOptions = useBarChartOptions.useBarChartOptions(dataWithVisibleZeros, horizontal, options);
|
|
59
63
|
const defaultMargin = useChartMargin.useChartMargin(height, chartOptions, dataSorted, theme, horizontal);
|
|
60
64
|
const [legendRef, legendHeight] = useElementHeight.useElementHeight();
|
|
@@ -71,7 +75,12 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400,
|
|
|
71
75
|
chartRef,
|
|
72
76
|
totalPoints,
|
|
73
77
|
});
|
|
74
|
-
const
|
|
78
|
+
const { resolveGroupColor } = useGlobalChartsContext.useGlobalChartsContext();
|
|
79
|
+
const getColor = react.useCallback((seriesData, index) => resolveGroupColor({
|
|
80
|
+
group: seriesData.group,
|
|
81
|
+
index,
|
|
82
|
+
overrideColor: seriesData.options?.stroke,
|
|
83
|
+
}), [resolveGroupColor]);
|
|
75
84
|
const getBarBackground = react.useCallback((index) => () => withPatterns
|
|
76
85
|
? `url(#${getPatternId(chartId, index)})`
|
|
77
86
|
: getColor(dataSorted[index], index), [withPatterns, getColor, dataSorted, chartId]);
|
|
@@ -150,7 +159,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400,
|
|
|
150
159
|
withPatterns,
|
|
151
160
|
}), [orientation, withPatterns]);
|
|
152
161
|
// Register chart with context only if data is valid
|
|
153
|
-
|
|
162
|
+
useChartRegistration.useChartRegistration({
|
|
154
163
|
chartId,
|
|
155
164
|
legendItems,
|
|
156
165
|
chartType: 'bar',
|
|
@@ -177,7 +186,7 @@ const BarChartInternal = ({ data, chartId: providedChartId, width, height = 400,
|
|
|
177
186
|
...(showLegend && legendPosition === 'top'
|
|
178
187
|
? { top: (defaultMargin.top || 0) + legendHeight }
|
|
179
188
|
: {}),
|
|
180
|
-
}, xScale: chartOptions.xScale, yScale: chartOptions.yScale, horizontal: horizontal, pointerEventsDataKey: "nearest", children: [jsxRuntime.jsx(xychart.Grid, { columns: gridVisibility.includes('y'), rows: gridVisibility.includes('x'), numTicks: 4 }), withPatterns && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("defs", { "data-testid": "bar-chart-patterns", children: dataSorted.map((seriesData, index) => renderPattern(index, getColor(seriesData, index))) }), jsxRuntime.jsx("style", { children: dataSorted.map((seriesData, index) => createPatternBorderStyle(index, getColor(seriesData, index))) })] })), highlightedBarStyle && jsxRuntime.jsx("style", { children: highlightedBarStyle }), jsxRuntime.jsx(xychart.BarGroup, { padding: chartOptions.barGroup.padding, children: dataWithVisibleZeros.map((seriesData, index) => (jsxRuntime.jsx(xychart.BarSeries, { dataKey: seriesData?.label, data: seriesData.data, yAccessor: chartOptions.accessors.yAccessor, xAccessor: chartOptions.accessors.xAccessor, colorAccessor: getBarBackground(index) }, seriesData?.label))) }), jsxRuntime.jsx(xychart.Axis, { ...chartOptions.axis.x }), jsxRuntime.jsx(xychart.Axis, { ...chartOptions.axis.y }), withTooltips && (jsxRuntime.jsx(accessibleTooltip.AccessibleTooltip, { detectBounds: true, snapTooltipToDatumX: true, snapTooltipToDatumY: true, renderTooltip: renderTooltip || renderDefaultTooltip, selectedIndex: selectedIndex, tooltipRef: tooltipRef, keyboardFocusedClassName: barChart_module.default['bar-chart__tooltip--keyboard-focused'], series: data, mode: "individual" }))] }), showLegend && (jsxRuntime.jsx(legend.Legend, {
|
|
189
|
+
}, xScale: chartOptions.xScale, yScale: chartOptions.yScale, horizontal: horizontal, pointerEventsDataKey: "nearest", children: [jsxRuntime.jsx(xychart.Grid, { columns: gridVisibility.includes('y'), rows: gridVisibility.includes('x'), numTicks: 4 }), withPatterns && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("defs", { "data-testid": "bar-chart-patterns", children: dataSorted.map((seriesData, index) => renderPattern(index, getColor(seriesData, index))) }), jsxRuntime.jsx("style", { children: dataSorted.map((seriesData, index) => createPatternBorderStyle(index, getColor(seriesData, index))) })] })), highlightedBarStyle && jsxRuntime.jsx("style", { children: highlightedBarStyle }), jsxRuntime.jsx(xychart.BarGroup, { padding: chartOptions.barGroup.padding, children: dataWithVisibleZeros.map((seriesData, index) => (jsxRuntime.jsx(xychart.BarSeries, { dataKey: seriesData?.label, data: seriesData.data, yAccessor: chartOptions.accessors.yAccessor, xAccessor: chartOptions.accessors.xAccessor, colorAccessor: getBarBackground(index) }, seriesData?.label))) }), jsxRuntime.jsx(xychart.Axis, { ...chartOptions.axis.x }), jsxRuntime.jsx(xychart.Axis, { ...chartOptions.axis.y }), withTooltips && (jsxRuntime.jsx(accessibleTooltip.AccessibleTooltip, { detectBounds: true, snapTooltipToDatumX: true, snapTooltipToDatumY: true, renderTooltip: renderTooltip || renderDefaultTooltip, selectedIndex: selectedIndex, tooltipRef: tooltipRef, keyboardFocusedClassName: barChart_module.default['bar-chart__tooltip--keyboard-focused'], series: data, mode: "individual" }))] }), showLegend && (jsxRuntime.jsx(legend.Legend, { orientation: legendOrientation, position: legendPosition, alignment: legendAlignment, className: barChart_module.default['bar-chart__legend'], shape: legendShape, ref: legendRef, chartId: chartId })), children] }) }));
|
|
181
190
|
};
|
|
182
191
|
const BarChartWithProvider = props => {
|
|
183
192
|
const existingContext = react.useContext(globalChartsProvider.GlobalChartsContext);
|
|
@@ -8,8 +8,16 @@ var group = require('@visx/group');
|
|
|
8
8
|
var scale = require('@visx/scale');
|
|
9
9
|
var text = require('@visx/text');
|
|
10
10
|
var react = require('react');
|
|
11
|
+
var globalChartsProvider = require('../../providers/chart-context/global-charts-provider.js');
|
|
12
|
+
require('fast-deep-equal');
|
|
13
|
+
require('@visx/event');
|
|
14
|
+
require('@visx/tooltip');
|
|
15
|
+
require('@visx/xychart');
|
|
16
|
+
require('date-fns');
|
|
17
|
+
require('deepmerge');
|
|
18
|
+
require('../../providers/theme/theme-provider.js');
|
|
11
19
|
var barChart = require('../bar-chart/bar-chart.js');
|
|
12
|
-
var withResponsive = require('../
|
|
20
|
+
var withResponsive = require('../private/with-responsive/with-responsive.js');
|
|
13
21
|
|
|
14
22
|
/**
|
|
15
23
|
* Get the bandwidth of a scale
|
|
@@ -72,7 +80,7 @@ const getDefaultYOffset = (data, yScaleConfig, height, isMultiSeries) => {
|
|
|
72
80
|
// Use negative value to move the label up.
|
|
73
81
|
return -(barThickness + GAP_BETWEEN_BARS);
|
|
74
82
|
};
|
|
75
|
-
const
|
|
83
|
+
const BarListChartInternal = ({ data, width, height, options = {}, margin = {
|
|
76
84
|
left: 0,
|
|
77
85
|
right: 20,
|
|
78
86
|
bottom: 0,
|
|
@@ -120,6 +128,17 @@ const BarListChart = ({ data, width, height, options = {}, margin = {
|
|
|
120
128
|
yScale: chartOptions.yScale,
|
|
121
129
|
}, ...rest }));
|
|
122
130
|
};
|
|
123
|
-
|
|
131
|
+
const BarListChart = props => {
|
|
132
|
+
const existingContext = react.useContext(globalChartsProvider.GlobalChartsContext);
|
|
133
|
+
// If we're already in a GlobalChartsProvider context, render the core component directly
|
|
134
|
+
if (existingContext) {
|
|
135
|
+
return jsxRuntime.jsx(BarListChartInternal, { ...props });
|
|
136
|
+
}
|
|
137
|
+
// Otherwise, wrap with our own GlobalChartsProvider
|
|
138
|
+
return (jsxRuntime.jsx(globalChartsProvider.GlobalChartsProvider, { children: jsxRuntime.jsx(BarListChartInternal, { ...props }) }));
|
|
139
|
+
};
|
|
140
|
+
BarListChart.displayName = 'BarListChart';
|
|
141
|
+
const BarListChartResponsive = withResponsive.withResponsive(BarListChart);
|
|
124
142
|
|
|
125
|
-
exports.
|
|
143
|
+
exports.BarListChartUnresponsive = BarListChart;
|
|
144
|
+
exports.default = BarListChartResponsive;
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
var event = require('@visx/event');
|
|
7
|
+
var tooltip = require('@visx/tooltip');
|
|
8
|
+
var clsx = require('clsx');
|
|
9
|
+
var react = require('react');
|
|
10
|
+
require('../../providers/chart-context/global-charts-provider.js');
|
|
11
|
+
require('fast-deep-equal');
|
|
12
|
+
require('@visx/xychart');
|
|
13
|
+
require('date-fns');
|
|
14
|
+
require('@automattic/number-formatters');
|
|
15
|
+
require('@visx/text');
|
|
16
|
+
require('deepmerge');
|
|
17
|
+
var colorUtils = require('../../utils/color-utils.js');
|
|
18
|
+
require('@visx/scale');
|
|
19
|
+
var useGlobalChartsTheme = require('../../providers/chart-context/hooks/use-global-charts-theme.js');
|
|
20
|
+
var conversionFunnelChart_module = require('./conversion-funnel-chart.module.scss.js');
|
|
21
|
+
var useFunnelSelection = require('./private/use-funnel-selection.js');
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Default settings for ConversionFunnelChart component
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_FUNNEL_SETTINGS = {
|
|
27
|
+
primaryColor: '#4F46E5',
|
|
28
|
+
backgroundColor: '#F3F4F6',
|
|
29
|
+
positiveChangeColor: '#10B981',
|
|
30
|
+
negativeChangeColor: '#EF4444',
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* ConversionFunnelChart component displays a conversion funnel with main metric and visualization
|
|
34
|
+
*
|
|
35
|
+
* @param props - Component props
|
|
36
|
+
* @param props.mainRate - Main conversion rate to highlight
|
|
37
|
+
* @param props.changeIndicator - Change indicator (e.g., +2%, -1.5%)
|
|
38
|
+
* @param props.steps - Array of funnel steps
|
|
39
|
+
* @param props.loading - Whether the chart is in loading state
|
|
40
|
+
* @param props.className - Additional CSS class name
|
|
41
|
+
* @param props.style - Custom styling
|
|
42
|
+
* @param props.renderStepLabel - Custom render function for step labels
|
|
43
|
+
* @param props.renderStepRate - Custom render function for step rates
|
|
44
|
+
* @param props.renderMainMetric - Custom render function for the entire main metric section
|
|
45
|
+
* @param props.renderTooltip - Custom render function for tooltip content
|
|
46
|
+
* @return JSX element representing the conversion funnel chart
|
|
47
|
+
*/
|
|
48
|
+
const ConversionFunnelChart = ({ mainRate, changeIndicator, steps, loading = false, className, style, renderStepLabel, renderStepRate, renderMainMetric, renderTooltip, }) => {
|
|
49
|
+
const theme = useGlobalChartsTheme.useGlobalChartsTheme();
|
|
50
|
+
const chartRef = react.useRef(null);
|
|
51
|
+
const selectedBarRef = react.useRef(null);
|
|
52
|
+
// Use @visx/tooltip hooks for tooltip positioning
|
|
53
|
+
const { tooltipData, tooltipLeft, tooltipTop, tooltipOpen, showTooltip, hideTooltip } = tooltip.useTooltip();
|
|
54
|
+
// Use custom hook for selection management
|
|
55
|
+
const { handleBarClick, handleBarKeyDown, clearSelection, getStepState } = useFunnelSelection.useFunnelSelection(hideTooltip);
|
|
56
|
+
const { containerRef: portalContainerRef, TooltipInPortal } = tooltip.useTooltipInPortal({
|
|
57
|
+
// use TooltipWithBounds for boundary detection
|
|
58
|
+
detectBounds: true,
|
|
59
|
+
// when tooltip containers are scrolled, this will correctly update the Tooltip position
|
|
60
|
+
scroll: true,
|
|
61
|
+
});
|
|
62
|
+
// Wrapper to clear selectedBarRef after clearing selection
|
|
63
|
+
const clearSelectionAndRef = react.useCallback(() => {
|
|
64
|
+
clearSelection();
|
|
65
|
+
selectedBarRef.current = null;
|
|
66
|
+
hideTooltip();
|
|
67
|
+
}, [clearSelection, hideTooltip]);
|
|
68
|
+
// Helper function to show tooltip at specific coordinates
|
|
69
|
+
const showTooltipAt = react.useCallback((step, x, y) => {
|
|
70
|
+
showTooltip({
|
|
71
|
+
tooltipData: step,
|
|
72
|
+
tooltipLeft: x,
|
|
73
|
+
tooltipTop: y - 10,
|
|
74
|
+
});
|
|
75
|
+
}, [showTooltip]);
|
|
76
|
+
// Helper function to get tooltip coordinates for mouse events
|
|
77
|
+
const getMouseTooltipCoords = react.useCallback((event$1) => {
|
|
78
|
+
const containerElement = chartRef.current;
|
|
79
|
+
if (containerElement) {
|
|
80
|
+
const coords = event.localPoint(containerElement, event$1.nativeEvent);
|
|
81
|
+
if (coords) {
|
|
82
|
+
return { x: coords.x, y: coords.y };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}, []);
|
|
87
|
+
// Helper function to get tooltip coordinates for keyboard events
|
|
88
|
+
const getKeyboardTooltipCoords = react.useCallback((event) => {
|
|
89
|
+
const rect = event.currentTarget.getBoundingClientRect();
|
|
90
|
+
const containerElement = chartRef.current;
|
|
91
|
+
if (containerElement) {
|
|
92
|
+
const containerRect = containerElement.getBoundingClientRect();
|
|
93
|
+
const x = rect.left + rect.width / 2 - containerRect.left;
|
|
94
|
+
const y = rect.top - containerRect.top;
|
|
95
|
+
return { x, y };
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}, []);
|
|
99
|
+
// Helper function to handle step interaction (both click and keyboard)
|
|
100
|
+
const handleStepInteraction = react.useCallback((step, event, interactionType) => {
|
|
101
|
+
// Store reference to the interacted element
|
|
102
|
+
selectedBarRef.current = event.currentTarget;
|
|
103
|
+
// Check if deselecting the same step
|
|
104
|
+
const { isClicked } = getStepState(step.id);
|
|
105
|
+
if (isClicked) {
|
|
106
|
+
// Deselecting - clear selection (tooltip will be hidden by hook)
|
|
107
|
+
if (interactionType === 'click') {
|
|
108
|
+
handleBarClick(step.id);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
handleBarKeyDown(step.id, event);
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
// Selecting - handle selection and show tooltip
|
|
116
|
+
if (interactionType === 'click') {
|
|
117
|
+
handleBarClick(step.id);
|
|
118
|
+
const coords = getMouseTooltipCoords(event);
|
|
119
|
+
if (coords) {
|
|
120
|
+
showTooltipAt(step, coords.x, coords.y);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
handleBarKeyDown(step.id, event);
|
|
125
|
+
const coords = getKeyboardTooltipCoords(event);
|
|
126
|
+
if (coords) {
|
|
127
|
+
showTooltipAt(step, coords.x, coords.y);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}, [
|
|
131
|
+
getStepState,
|
|
132
|
+
handleBarClick,
|
|
133
|
+
handleBarKeyDown,
|
|
134
|
+
showTooltipAt,
|
|
135
|
+
getMouseTooltipCoords,
|
|
136
|
+
getKeyboardTooltipCoords,
|
|
137
|
+
]);
|
|
138
|
+
// Create handler factories to avoid arrow functions in JSX
|
|
139
|
+
const stepHandlers = react.useMemo(() => {
|
|
140
|
+
const handlers = new Map();
|
|
141
|
+
steps.forEach(step => {
|
|
142
|
+
const onClick = (event) => {
|
|
143
|
+
event.stopPropagation();
|
|
144
|
+
handleStepInteraction(step, event, 'click');
|
|
145
|
+
};
|
|
146
|
+
const onKeyDown = (event) => {
|
|
147
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
148
|
+
handleStepInteraction(step, event, 'keyboard');
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
// For other keys (like Escape), just handle the selection
|
|
152
|
+
selectedBarRef.current = event.currentTarget;
|
|
153
|
+
handleBarKeyDown(step.id, event);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
handlers.set(step.id, { onClick, onKeyDown });
|
|
157
|
+
});
|
|
158
|
+
return handlers;
|
|
159
|
+
}, [steps, handleStepInteraction, handleBarKeyDown]);
|
|
160
|
+
// Handle document-level click to clear selection when clicking outside selected bar
|
|
161
|
+
react.useEffect(() => {
|
|
162
|
+
const handleDocumentClick = (event) => {
|
|
163
|
+
// Only clear selection if there's an active selection and click is outside the selected bar
|
|
164
|
+
if (selectedBarRef.current && !selectedBarRef.current.contains(event.target)) {
|
|
165
|
+
clearSelectionAndRef();
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
document.addEventListener('mousedown', handleDocumentClick);
|
|
169
|
+
return () => {
|
|
170
|
+
document.removeEventListener('mousedown', handleDocumentClick);
|
|
171
|
+
};
|
|
172
|
+
}, [clearSelectionAndRef]);
|
|
173
|
+
// Get component settings from theme with fallbacks
|
|
174
|
+
const funnelSettings = theme.conversionFunnelChart;
|
|
175
|
+
const primaryColor = funnelSettings?.primaryColor || DEFAULT_FUNNEL_SETTINGS.primaryColor;
|
|
176
|
+
const positiveChangeColor = funnelSettings?.positiveChangeColor || DEFAULT_FUNNEL_SETTINGS.positiveChangeColor;
|
|
177
|
+
const negativeChangeColor = funnelSettings?.negativeChangeColor || DEFAULT_FUNNEL_SETTINGS.negativeChangeColor;
|
|
178
|
+
// Determine change indicator color
|
|
179
|
+
const isPositiveChange = changeIndicator?.startsWith('+');
|
|
180
|
+
const changeColor = isPositiveChange ? positiveChangeColor : negativeChangeColor;
|
|
181
|
+
// Create light background version of primary color
|
|
182
|
+
const lightBackgroundColor = colorUtils.hexToRgba(primaryColor, 0.08);
|
|
183
|
+
const chartStyle = {
|
|
184
|
+
'--primary-color': primaryColor,
|
|
185
|
+
'--light-background-color': lightBackgroundColor,
|
|
186
|
+
'--change-indicator-color': changeColor,
|
|
187
|
+
...style,
|
|
188
|
+
};
|
|
189
|
+
// Default main metric rendering function
|
|
190
|
+
const renderDefaultMainMetric = () => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("span", { className: conversionFunnelChart_module.default['main-rate'], children: [mainRate.toFixed(1), "%"] }), changeIndicator && (jsxRuntime.jsx("span", { className: conversionFunnelChart_module.default['change-indicator'], children: changeIndicator }))] }));
|
|
191
|
+
// Default tooltip rendering function
|
|
192
|
+
const renderDefaultTooltip = (step) => (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: conversionFunnelChart_module.default['tooltip-title'], children: step.label }), jsxRuntime.jsxs("div", { className: conversionFunnelChart_module.default['tooltip-content'], children: [step.rate.toFixed(1), "%", step.count && ` • ${step.count.toLocaleString()} items`] })] }));
|
|
193
|
+
// Handle empty or undefined data
|
|
194
|
+
if (!steps || steps.length === 0) {
|
|
195
|
+
return (jsxRuntime.jsx("div", { className: clsx(conversionFunnelChart_module.default.conversionFunnelChart, loading && conversionFunnelChart_module.default.loading, className), style: chartStyle, children: jsxRuntime.jsx("div", { className: conversionFunnelChart_module.default['empty-state'], children: loading ? 'Loading...' : 'No data available' }) }));
|
|
196
|
+
}
|
|
197
|
+
// Calculate bar heights relative to the maximum (first step)
|
|
198
|
+
const maxRate = Math.max(...steps.map(step => step.rate));
|
|
199
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { ref: node => {
|
|
200
|
+
// Set containerRef for @visx coordinate system
|
|
201
|
+
portalContainerRef(node);
|
|
202
|
+
chartRef.current = node;
|
|
203
|
+
}, className: clsx(conversionFunnelChart_module.default.conversionFunnelChart, loading && conversionFunnelChart_module.default.loading, className), style: chartStyle, children: [renderMainMetric ? (renderMainMetric({
|
|
204
|
+
mainRate,
|
|
205
|
+
changeIndicator,
|
|
206
|
+
className: conversionFunnelChart_module.default['main-metric'],
|
|
207
|
+
changeColor,
|
|
208
|
+
})) : (jsxRuntime.jsx("div", { className: conversionFunnelChart_module.default['main-metric'], children: renderDefaultMainMetric() })), jsxRuntime.jsx("div", { className: conversionFunnelChart_module.default['funnel-container'], children: steps.map((step, index) => {
|
|
209
|
+
const barHeight = (step.rate / maxRate) * 100;
|
|
210
|
+
const { isClicked, isBlurred } = getStepState(step.id);
|
|
211
|
+
return (jsxRuntime.jsxs("div", { className: clsx(conversionFunnelChart_module.default['funnel-step'], isBlurred && conversionFunnelChart_module.default.blurred), children: [jsxRuntime.jsxs("div", { className: conversionFunnelChart_module.default['step-header'], children: [renderStepLabel ? (renderStepLabel({
|
|
212
|
+
step,
|
|
213
|
+
index,
|
|
214
|
+
className: conversionFunnelChart_module.default['step-label'],
|
|
215
|
+
})) : (jsxRuntime.jsx("span", { className: conversionFunnelChart_module.default['step-label'], children: step.label })), renderStepRate ? (renderStepRate({
|
|
216
|
+
step,
|
|
217
|
+
index,
|
|
218
|
+
className: conversionFunnelChart_module.default['step-rate'],
|
|
219
|
+
})) : (jsxRuntime.jsxs("span", { className: conversionFunnelChart_module.default['step-rate'], children: [step.rate.toFixed(1), "%"] }))] }), jsxRuntime.jsx("div", { className: clsx(conversionFunnelChart_module.default['bar-container'], isClicked && conversionFunnelChart_module.default.selected, isBlurred && conversionFunnelChart_module.default.disabled), onClick: stepHandlers.get(step.id)?.onClick, onKeyDown: stepHandlers.get(step.id)?.onKeyDown, role: "button", tabIndex: isBlurred ? -1 : 0, "aria-label": step.label, children: jsxRuntime.jsx("div", { className: clsx(conversionFunnelChart_module.default['funnel-bar'], isClicked && conversionFunnelChart_module.default.selected), style: {
|
|
220
|
+
height: `${barHeight}%`,
|
|
221
|
+
backgroundColor: primaryColor,
|
|
222
|
+
} }) })] }, step.id));
|
|
223
|
+
}) })] }), tooltipOpen &&
|
|
224
|
+
tooltipData &&
|
|
225
|
+
(() => {
|
|
226
|
+
const tooltipContent = renderTooltip
|
|
227
|
+
? renderTooltip({
|
|
228
|
+
step: tooltipData,
|
|
229
|
+
index: steps.findIndex(s => s.id === tooltipData.id),
|
|
230
|
+
top: tooltipTop,
|
|
231
|
+
left: tooltipLeft,
|
|
232
|
+
className: conversionFunnelChart_module.default['tooltip-wrapper'],
|
|
233
|
+
})
|
|
234
|
+
: renderDefaultTooltip(tooltipData);
|
|
235
|
+
// Don't render tooltip if renderTooltip returns falsy
|
|
236
|
+
if (!tooltipContent)
|
|
237
|
+
return null;
|
|
238
|
+
return (jsxRuntime.jsx(TooltipInPortal
|
|
239
|
+
// set this to random so it correctly updates with parent bounds
|
|
240
|
+
, { top: tooltipTop, left: tooltipLeft, className: conversionFunnelChart_module.default['tooltip-wrapper'], children: tooltipContent }, Math.random()));
|
|
241
|
+
})()] }));
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
exports.ConversionFunnelChart = ConversionFunnelChart;
|
|
245
|
+
exports.default = ConversionFunnelChart;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var styles = {"conversionFunnelChart":"conversion-funnel-chart-module_conversionFunnelChart__-9Qsb","loading":"conversion-funnel-chart-module_loading__Kw-iZ","main-metric":"conversion-funnel-chart-module_main-metric__8mIwV","main-rate":"conversion-funnel-chart-module_main-rate__D93Ub","change-indicator":"conversion-funnel-chart-module_change-indicator__QWypV","funnel-container":"conversion-funnel-chart-module_funnel-container__RR7xa","funnel-step":"conversion-funnel-chart-module_funnel-step__VIVzt","blurred":"conversion-funnel-chart-module_blurred__Ax4cu","step-header":"conversion-funnel-chart-module_step-header__bUrZ0","step-label":"conversion-funnel-chart-module_step-label__SCy8F","step-rate":"conversion-funnel-chart-module_step-rate__A0irB","bar-container":"conversion-funnel-chart-module_bar-container__5Dl5-","selected":"conversion-funnel-chart-module_selected__W40FY","disabled":"conversion-funnel-chart-module_disabled__Reovk","funnel-bar":"conversion-funnel-chart-module_funnel-bar__tG5m3","tooltip-wrapper":"conversion-funnel-chart-module_tooltip-wrapper__NohPt","tooltip-title":"conversion-funnel-chart-module_tooltip-title__hjZr3","tooltip-content":"conversion-funnel-chart-module_tooltip-content__ocwAP","empty-state":"conversion-funnel-chart-module_empty-state__9c0ps"};
|
|
6
|
+
|
|
7
|
+
exports.default = styles;
|