Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/shy-carrots-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hyperdx/app": patch
---

fix: Update "Copy Object" in line viewer to work with nested objects and arrays
77 changes: 68 additions & 9 deletions packages/app/src/components/DBRowJsonViewer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,6 @@ describe('DBRowJsonViewer', () => {
jest.clearAllMocks();
});

// Helper function to simulate clicking a button in a line
const clickLineButton = (fieldText: string, buttonText: string) => {
const line = screen.getByText(fieldText).closest('.line')! as HTMLElement;
fireEvent.mouseEnter(line);
const lineMenu = line.querySelector('.lineMenu')! as HTMLElement;
const button = within(lineMenu).getByText(buttonText);
fireEvent.click(button);
};

// Helper to render component
const renderComponent = (data: any) => {
return renderWithMantine(
Expand All @@ -70,6 +61,33 @@ describe('DBRowJsonViewer', () => {
);
};

// Helper to click a button on a line
const clickLineButton = (fieldText: string, buttonText: string) => {
const line = screen.getByText(fieldText).closest('.line')! as HTMLElement;
fireEvent.mouseEnter(line);
const button = within(line).getByText(buttonText);
fireEvent.click(button);
};

// Helper to expand a field and click a button on a nested field
const expandAndClickButton = (
parentField: string,
childField: string,
buttonText: string,
) => {
const parentLine = screen
.getByText(parentField)
.closest('.line')! as HTMLElement;
fireEvent.click(parentLine);

const childLine = screen
.getByText(childField)
.closest('.line')! as HTMLElement;
fireEvent.mouseEnter(childLine);
const button = within(childLine).getByText(buttonText);
fireEvent.click(button);
};

it('formats log attributes correctly', () => {
renderComponent(logData);
clickLineButton('field1', 'Search');
Expand Down Expand Up @@ -136,4 +154,45 @@ describe('DBRowJsonViewer', () => {
},
);
});

describe('copy functionality', () => {
const mockClipboard = jest.fn();

beforeEach(() => {
Object.assign(navigator, {
clipboard: { writeText: mockClipboard },
});
});

it('copies array elements from expanded stringified JSON', () => {
const arrayObject = { status: 'True', type: 'PodReady' };
const data = { conditions: JSON.stringify([arrayObject]) };

renderComponent(data);
expandAndClickButton('conditions', '0', 'Copy Object');

expect(mockClipboard).toHaveBeenCalledWith(
JSON.stringify(arrayObject, null, 2),
);
});

it('copies entire stringified value when not expanded', () => {
const arrayData = [{ type: 'Ready' }, { type: 'Init' }];
const data = { conditions: JSON.stringify(arrayData) };

renderComponent(data);
clickLineButton('conditions', 'Copy Value');

expect(mockClipboard).toHaveBeenCalledWith(JSON.stringify(arrayData));
});

it('copies regular nested objects', () => {
renderComponent(logData);
clickLineButton('nested', 'Copy Object');

expect(mockClipboard).toHaveBeenCalledWith(
JSON.stringify({ field3: 'nested value' }, null, 2),
);
});
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used TDD here to prove that the old functionality broke these tests.

});
14 changes: 11 additions & 3 deletions packages/app/src/components/DBRowJsonViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useAtom, useAtomValue } from 'jotai';
import { atomWithStorage } from 'jotai/utils';
import get from 'lodash/get';
import {
ActionIcon,
Box,
Button,
Group,
Expand Down Expand Up @@ -344,8 +343,17 @@ export function DBRowJsonViewer({
}

const handleCopyObject = () => {
const copiedObj =
keyPath.length === 0 ? rowData : get(rowData, keyPath);
let copiedObj;

// When in parsed JSON context (e.g., expanded stringified JSON),
// use the value directly since keyPath doesn't match rowData structure
if (isInParsedJson && parsedJsonRootPath) {
copiedObj = value;
} else {
// For regular nested objects, use keyPath to navigate rowData
copiedObj = keyPath.length === 0 ? rowData : get(rowData, keyPath);
}

window.navigator.clipboard.writeText(
JSON.stringify(copiedObj, null, 2),
);
Expand Down