diff --git a/src/DataRenderer.test.tsx b/src/DataRenderer.test.tsx index a060e1c..954fd21 100644 --- a/src/DataRenderer.test.tsx +++ b/src/DataRenderer.test.tsx @@ -47,6 +47,12 @@ const testButtonsExpanded = () => { return buttons; }; +const testButtonsIfEmpty = () => { + expect(() => { + screen.getAllByRole('button', { hidden: true }); + }).toThrow(); +}; + const testClickableNodeCollapsed = () => { const buttons = screen.getAllByRole('button', { hidden: true }); expect(buttons.length).toBe(4); @@ -143,6 +149,52 @@ describe('DataRender', () => { expect(screen.getByText(/empty key/)).toBeInTheDocument(); }); + it('should render empty objects', () => { + render(); + expect(screen.getByText('{')).toBeInTheDocument(); + expect(screen.getByText('}')).toBeInTheDocument(); + }); + + it('should render nested empty objects', () => { + render(); + expect(screen.getByText('nested:')).toBeInTheDocument(); + expect(screen.getByText('{')).toBeInTheDocument(); + expect(screen.getByText('}')).toBeInTheDocument(); + }); + + it('should not expand empty objects', () => { + render(); + testButtonsIfEmpty(); + }); + + it('should not collapse empty objects', () => { + render(); + testButtonsIfEmpty(); + }); + + it('should render empty arrays', () => { + render(); + expect(screen.getByText('[')).toBeInTheDocument(); + expect(screen.getByText(']')).toBeInTheDocument(); + }); + + it('should render nested empty arrays', () => { + render(); + expect(screen.getByText('nested:')).toBeInTheDocument(); + expect(screen.getByText('[')).toBeInTheDocument(); + expect(screen.getByText(']')).toBeInTheDocument(); + }); + + it('should not expand empty arrays', () => { + render(); + testButtonsIfEmpty(); + }); + + it('should not collapse empty arrays', () => { + render(); + testButtonsIfEmpty(); + }); + it('should render arrays', () => { render(); expect(screen.getByText('1')).toBeInTheDocument(); diff --git a/src/DataRenderer.tsx b/src/DataRenderer.tsx index da9cc2e..b1570c2 100644 --- a/src/DataRenderer.tsx +++ b/src/DataRenderer.tsx @@ -31,7 +31,7 @@ export interface JsonRenderProps { } export interface ExpandableRenderProps { - field?: string; + field: string | undefined; value: Array | object; data: Array<[string | undefined, any]>; openBracket: string; @@ -144,6 +144,25 @@ function ExpandableObject({ ); } +export interface EmptyRenderProps { + field: string | undefined; + openBracket: string; + closeBracket: string; + lastElement: boolean; + style: StyleProps; +} + +function EmptyObject({ field, openBracket, closeBracket, lastElement, style }: EmptyRenderProps) { + return ( +
+ {field && {field}:} + {openBracket} + {closeBracket} + {!lastElement && ,} +
+ ); +} + function JsonObject({ field, value, @@ -153,6 +172,16 @@ function JsonObject({ clickToExpandNode, level }: JsonRenderProps) { + if (Object.keys(value).length === 0) { + return EmptyObject({ + field, + openBracket: '{', + closeBracket: '}', + lastElement, + style + }); + } + return ExpandableObject({ field, value, @@ -176,6 +205,16 @@ function JsonArray({ shouldExpandNode, clickToExpandNode }: JsonRenderProps>) { + if (value.length === 0) { + return EmptyObject({ + field, + openBracket: '[', + closeBracket: ']', + lastElement, + style + }); + } + return ExpandableObject({ field, value, diff --git a/src/stories/JsonView.stories.tsx b/src/stories/JsonView.stories.tsx index d2f8eb0..1bb354b 100644 --- a/src/stories/JsonView.stories.tsx +++ b/src/stories/JsonView.stories.tsx @@ -56,7 +56,9 @@ const jsonData = { 'date property': new Date(0), 'boolean property': true, 'null property': null, + 'empty array': [], 'array propery': [1, 2, 3, 4, 5], + 'empty object': {}, 'nested object': { first: true, second: 'another value', diff --git a/src/styles.module.css b/src/styles.module.css index 199ec57..e895868 100644 --- a/src/styles.module.css +++ b/src/styles.module.css @@ -14,6 +14,10 @@ font-weight: bold; } +.punctuation-base + .punctuation-base { + margin-left: -5px; +} + .pointer { cursor: pointer; }