fix data component#14
Conversation
gfazioli
commented
Jan 21, 2026
- 🚀 feat(JsonTree): Add support for rendering React elements
- 🚀 feat(JsonTree): add react element color support
- 🚀 feat(JsonTree): support date, bigint, symbol, regexp, map, set and numbers
- 🐛 fix(JsonTree.story): restore Mantine imports
- 🚀 feat(docs): Add special types demo and documentation
- 📚 docs(demos): update specialTypes demo with clearer names and defaultExpanded
- 🚀 feat(docs): add React component examples to JsonTree special types demo
- Detect React elements and display component names in the tree - Style react-element nodes with distinct color and italic font - Update stories and tests to cover React component rendering - Prevent circular reference issues by treating React elements as primitives in tree data
- Introduce `--json-tree-color-react-element` CSS variable for styling React elements - Update component styles to use the new variable - Extend type definitions and varsResolver to include the new color - Update story to showcase React element rendering and nested elements
…numbers - Add CSS styles for new value types - Extend utils to detect and format dates, NaN, Infinity, BigInt, Symbol, RegExp, Map, Set - Update story to showcase special value types - Add tests covering rendering of these types
- Re-add missing imports for Loader, ScrollArea, Stack, Title - Move CSS module import after JsonTree import for consistency
- Introduce demo showing Date, NaN, Infinity, BigInt, Symbol, RegExp, Map, Set, and React elements. - Add docs section explaining supported modern JavaScript types and syntax highlighting. - Extend styles API with CSS variables for new value types.
…tExpanded - Rename `updatedAt` to `lastModified` for better clarity - Replace `uniqueKey` symbol with `registryKey` - Add `defaultExpanded: false` to demo configuration to control initial state
…demo - Import Loader component and remove unused Stack import - Add reactLoader, reactButton, and htmlDiv entries to specialTypes - Update code snippet to reflect new imports and examples - Demonstrates rendering of React components and HTML elements in JsonTree
There was a problem hiding this comment.
Pull request overview
This PR adds support for rendering special JavaScript types in the JsonTree component, including React elements, Date objects, BigInt, Symbol, RegExp, Map, Set, NaN, and Infinity. Each new type receives dedicated color styling and appropriate formatting for display.
Changes:
- Extended ValueType union to include 9 new types (react-element, date, nan, infinity, bigint, symbol, regexp, map, set)
- Added color CSS variables and styling for all new types
- Implemented type detection, formatting, and tree conversion logic for special types
- Added comprehensive tests and documentation with live demos
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| package/src/lib/utils.tsx | Core logic for detecting, formatting, and converting special types to tree data |
| package/src/JsonTree.tsx | Added CSS variable definitions for new type colors |
| package/src/JsonTree.module.css | Added styling rules for new value types |
| package/src/JsonTree.test.tsx | Added test coverage for React components and special value types |
| package/src/JsonTree.story.tsx | Added Usage example with React components and SpecialValueTypes story |
| docs/styles-api/JsonTree.styles-api.ts | Documented new CSS variables in styles API |
| docs/docs.mdx | Added documentation section for special value types |
| docs/demos/index.ts | Exported new specialTypes demo |
| docs/demos/JsonTree.demo.specialTypes.tsx | Created comprehensive demo showcasing all special types |
| describe('React components', () => { | ||
| it('handles React elements without crashing', () => { | ||
| const dataWithReactComponent = { | ||
| name: 'Test', | ||
| loader: <Loader size="xs" />, | ||
| button: <button type="button">Click me</button>, | ||
| }; | ||
|
|
||
| const { container } = render(<JsonTree data={dataWithReactComponent} defaultExpanded />); | ||
| expect(container).toBeTruthy(); | ||
| expect(container.textContent).toContain('name'); | ||
| expect(container.textContent).toContain('loader'); | ||
| expect(container.textContent).toContain('button'); | ||
| }); | ||
|
|
||
| it('displays React elements with component name', () => { | ||
| const dataWithReactComponent = { | ||
| loader: <Loader size="xs" />, | ||
| }; | ||
|
|
||
| const { container } = render(<JsonTree data={dataWithReactComponent} defaultExpanded />); | ||
| // Should show the component in a recognizable format | ||
| expect(container.textContent).toContain('loader'); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Consider adding test cases to verify that objects with type and props properties but which are not React elements are handled correctly. This would help ensure the isReactElement function doesn't produce false positives.
| &[data-type='nan'], | ||
| &[data-type='infinity'] { | ||
| color: var(--json-tree-color-nan); | ||
| font-style: italic; | ||
| } | ||
|
|
There was a problem hiding this comment.
The CSS rule combines both nan and infinity types to use --json-tree-color-nan, but a separate --json-tree-color-infinity CSS variable is defined in the varsResolver. This creates an inconsistency where the --json-tree-color-infinity variable is defined but never used. Either update the CSS to use the separate variable for infinity types, or remove the unused --json-tree-color-infinity variable from the varsResolver and type definitions.
| &[data-type='nan'], | |
| &[data-type='infinity'] { | |
| color: var(--json-tree-color-nan); | |
| font-style: italic; | |
| } | |
| &[data-type='nan'] { | |
| color: var(--json-tree-color-nan); | |
| font-style: italic; | |
| } | |
| &[data-type='infinity'] { | |
| color: var(--json-tree-color-infinity); | |
| font-style: italic; | |
| } |
| &[data-type='map'], | ||
| &[data-type='set'] { | ||
| color: var(--json-tree-color-map); | ||
| font-weight: 600; | ||
| } |
There was a problem hiding this comment.
The CSS rule combines both map and set types to use --json-tree-color-map, but a separate --json-tree-color-set CSS variable is defined in the varsResolver. This creates an inconsistency where the --json-tree-color-set variable is defined but never used. Either update the CSS to use the separate variable for set types, or remove the unused --json-tree-color-set variable from the varsResolver and type definitions.
| &[data-type='map'], | |
| &[data-type='set'] { | |
| color: var(--json-tree-color-map); | |
| font-weight: 600; | |
| } | |
| &[data-type='map'] { | |
| color: var(--json-tree-color-map); | |
| font-weight: 600; | |
| } | |
| &[data-type='set'] { | |
| color: var(--json-tree-color-set); | |
| font-weight: 600; | |
| } |
| value.$$typeof === Symbol.for('react.transitional.element') || | ||
| (typeof value.type !== 'undefined' && typeof value.props !== 'undefined')) |
There was a problem hiding this comment.
The isReactElement function has a fallback condition that could incorrectly classify objects with type and props properties as React elements. Any plain object with these properties would be incorrectly detected as a React element, even if it's not actually a React element. Consider removing the fallback condition or making it more strict by also checking for the presence of $$typeof.
| value.$$typeof === Symbol.for('react.transitional.element') || | |
| (typeof value.type !== 'undefined' && typeof value.props !== 'undefined')) | |
| value.$$typeof === Symbol.for('react.transitional.element')) |
| if (isReactElement(value)) { | ||
| return 'react-element'; | ||
| } | ||
| if (value instanceof Date) { | ||
| return 'date'; | ||
| } | ||
| if (value instanceof RegExp) { | ||
| return 'regexp'; | ||
| } | ||
| if (value instanceof Map) { | ||
| return 'map'; | ||
| } | ||
| if (value instanceof Set) { | ||
| return 'set'; | ||
| } | ||
| if (typeof value === 'bigint') { | ||
| return 'bigint'; | ||
| } | ||
| if (typeof value === 'symbol') { | ||
| return 'symbol'; | ||
| } |
There was a problem hiding this comment.
The order of type checking in getValueType could cause issues. The React element check occurs before checking for Date, RegExp, Map, and Set. If a React element happens to be an instance of one of these types (unlikely but theoretically possible in custom scenarios), it would be classified as a React element. While this is probably the intended behavior, consider documenting this precedence or reordering the checks if Date/RegExp/Map/Set should take priority.
| // React components | ||
| reactLoader: <Loader size="xs" />, | ||
| reactButton: <button type="button">Click me</button>, | ||
| htmlDiv: <div>Hello World</div>, |
There was a problem hiding this comment.
Missing newline between the comment for React components and the Date objects comment in the code string. The code string should have a blank line after line 14 to match the actual code structure below (which has a blank line after line 77).
| htmlDiv: <div>Hello World</div>, | |
| htmlDiv: <div>Hello World</div>, |
| entries = Array.from(value.entries()).map( | ||
| ([k, v], index) => [`[${index}] ${String(k)}`, v] as [string, any] | ||
| ); |
There was a problem hiding this comment.
The Map key formatting uses String(k) which will produce [object Object] for object keys, making it impossible to distinguish between different object keys. Consider using JSON.stringify(k) or a more sophisticated stringification method to better represent complex keys, or add special handling for object keys.
| entries = Array.from(value.entries()).map( | |
| ([k, v], index) => [`[${index}] ${String(k)}`, v] as [string, any] | |
| ); | |
| entries = Array.from(value.entries()).map(([k, v], index) => { | |
| let keyLabel: string; | |
| const keyType = typeof k; | |
| if (keyType === 'string' || keyType === 'number' || keyType === 'boolean' || keyType === 'bigint' || keyType === 'symbol') { | |
| keyLabel = String(k); | |
| } else { | |
| try { | |
| keyLabel = JSON.stringify(k); | |
| } catch { | |
| keyLabel = Object.prototype.toString.call(k); | |
| } | |
| } | |
| return [`[${index}] ${keyLabel}`, v] as [string, any]; | |
| }); |
| it('handles Set as expandable', () => { | ||
| const dataWithSet = { | ||
| tags: new Set(['javascript', 'typescript', 'react']), | ||
| }; | ||
|
|
||
| const { container } = render(<JsonTree data={dataWithSet} defaultExpanded />); | ||
| expect(container.textContent).toContain('tags'); | ||
| // Set should be expandable and show its values | ||
| expect(container.textContent).toContain('javascript'); | ||
| expect(container.textContent).toContain('typescript'); | ||
| expect(container.textContent).toContain('react'); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Consider adding test cases for empty Map and Set collections to ensure they render correctly and are not expandable when empty. This would complement the existing tests for non-empty collections.
| return ( | ||
| typeof value === 'object' && | ||
| value !== null && | ||
| (value.$$typeof === Symbol.for('react.element') || | ||
| value.$$typeof === Symbol.for('react.transitional.element') || | ||
| (typeof value.type !== 'undefined' && typeof value.props !== 'undefined')) |
There was a problem hiding this comment.
Variable 'value' is of type date, object or regular expression, but it is compared to an expression of type null.
| return ( | |
| typeof value === 'object' && | |
| value !== null && | |
| (value.$$typeof === Symbol.for('react.element') || | |
| value.$$typeof === Symbol.for('react.transitional.element') || | |
| (typeof value.type !== 'undefined' && typeof value.props !== 'undefined')) | |
| if (!value || typeof value !== 'object') { | |
| return false; | |
| } | |
| return ( | |
| value.$$typeof === Symbol.for('react.element') || | |
| value.$$typeof === Symbol.for('react.transitional.element') || | |
| (typeof value.type !== 'undefined' && typeof value.props !== 'undefined') |