Compare commits

..

7 Commits

Author SHA1 Message Date
Ishan Uniyal
19751e4d75 feat: added testcases 2026-02-23 15:18:44 +05:30
Ishan Uniyal
a0684c68d3 feat: list panel try/catch 2026-02-23 15:18:44 +05:30
Ishan Uniyal
380b1f48d2 feat: list panel check 2026-02-23 15:18:44 +05:30
Ishan Uniyal
ce1476be0f feat: reverting to first commit 2026-02-23 15:18:44 +05:30
Ishan Uniyal
8ce44c85c3 feat: updated null checks 2026-02-23 15:18:44 +05:30
Ishan Uniyal
10a1ffaccb feat: moved logic to util 2026-02-23 15:18:44 +05:30
Ishan Uniyal
5adaa79e49 feat: add to alert bug 2026-02-23 15:18:44 +05:30
4 changed files with 67 additions and 62 deletions

View File

@@ -172,23 +172,51 @@ function ExplorerOptions({
const { user } = useAppContext();
const handleConditionalQueryModification = useCallback(
// eslint-disable-next-line sonarjs/cognitive-complexity
(defaultQuery: Query | null): string => {
const queryToUse = defaultQuery || query;
if (!queryToUse) {
throw new Error('No query provided');
}
if (
queryToUse?.builder?.queryData?.[0]?.aggregateOperator !==
StringOperators.NOOP
StringOperators.NOOP &&
sourcepage !== DataSource.LOGS
) {
return JSON.stringify(queryToUse);
}
// Modify aggregateOperator to count, as noop is not supported in alerts
// Convert NOOP to COUNT for alerts and strip orderBy for logs
const modifiedQuery = cloneDeep(queryToUse);
if (modifiedQuery && modifiedQuery.builder?.queryData) {
modifiedQuery.builder.queryData = modifiedQuery.builder.queryData.map(
(item) => {
const updatedItem = { ...item };
modifiedQuery.builder.queryData[0].aggregateOperator = StringOperators.COUNT;
if (updatedItem.aggregateOperator === StringOperators.NOOP) {
updatedItem.aggregateOperator = StringOperators.COUNT;
}
return JSON.stringify(modifiedQuery);
// Alerts do not support order by on logs explorer queries
if (sourcepage === DataSource.LOGS && panelType === PANEL_TYPES.LIST) {
updatedItem.orderBy = [];
}
return updatedItem;
},
);
}
try {
return JSON.stringify(modifiedQuery);
} catch (err) {
throw new Error(
'Failed to stringify modified query: ' +
(err instanceof Error ? err.message : String(err)),
);
}
},
[query],
[panelType, query, sourcepage],
);
const onCreateAlertsHandler = useCallback(
@@ -757,9 +785,9 @@ function ExplorerOptions({
);
}, [
disabled,
query,
isOneChartPerQuery,
onCreateAlertsHandler,
query,
splitedQueries,
]);

View File

@@ -1,3 +1,4 @@
import { useHistory } from 'react-router-dom';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { MOCK_QUERY } from 'container/QueryTable/Drilldown/__tests__/mockTableData';
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
@@ -15,6 +16,11 @@ import { getExplorerToolBarVisibility } from '../utils';
// Mock dependencies
jest.mock('hooks/dashboard/useUpdateDashboard');
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useHistory: jest.fn(),
}));
jest.mock('../utils', () => ({
getExplorerToolBarVisibility: jest.fn(),
generateRGBAFromHex: jest.fn(() => 'rgba(0, 0, 0, 0.08)'),
@@ -29,6 +35,7 @@ const mockGetExplorerToolBarVisibility = jest.mocked(
);
const mockUseUpdateDashboard = jest.mocked(useUpdateDashboard);
const mockUseHistory = jest.mocked(useHistory);
// Mock data
const TEST_QUERY_ID = 'test-query-id';
@@ -103,7 +110,6 @@ describe('ExplorerOptionWrapper', () => {
beforeEach(() => {
jest.clearAllMocks();
mockGetExplorerToolBarVisibility.mockReturnValue(true);
// Mock useUpdateDashboard to return a mutation object
mockUseUpdateDashboard.mockReturnValue(({
mutate: jest.fn(),
@@ -117,6 +123,28 @@ describe('ExplorerOptionWrapper', () => {
} as unknown) as ReturnType<typeof useUpdateDashboard>);
});
it('should navigate to alert creation page when "Create an Alert" is clicked in logs-explorer', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
const mockPush = jest.fn();
mockUseHistory.mockReturnValue(({
push: mockPush,
} as unknown) as ReturnType<typeof useHistory>);
renderExplorerOptionWrapper({ sourcepage: DataSource.LOGS });
const createAlertButton = screen.getByRole('button', {
name: 'Create an Alert',
});
await user.click(createAlertButton);
expect(mockPush).toHaveBeenCalledTimes(1);
const calledWith = mockPush.mock.calls[0][0] as string;
const [path, search = ''] = calledWith.split('?');
expect(path).toBe('/alerts/new');
const params = new URLSearchParams(search);
expect(params.has('compositeQuery')).toBe(true);
});
describe('onExport functionality', () => {
it('should call onExport when New Dashboard button is clicked in export modal', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });

View File

@@ -1,34 +0,0 @@
import { Color } from '@signozhq/design-tokens';
import { getColorsForSeverityLabels, isRedLike } from '../utils';
describe('getColorsForSeverityLabels', () => {
it('should return slate for blank labels', () => {
expect(getColorsForSeverityLabels('', 0)).toBe(Color.BG_SLATE_300);
expect(getColorsForSeverityLabels(' ', 0)).toBe(Color.BG_SLATE_300);
});
it('should return correct colors for known severity variants', () => {
expect(getColorsForSeverityLabels('INFO', 0)).toBe(Color.BG_ROBIN_600);
expect(getColorsForSeverityLabels('ERROR', 0)).toBe(Color.BG_CHERRY_600);
expect(getColorsForSeverityLabels('WARN', 0)).toBe(Color.BG_AMBER_600);
expect(getColorsForSeverityLabels('DEBUG', 0)).toBe(Color.BG_AQUA_600);
expect(getColorsForSeverityLabels('TRACE', 0)).toBe(Color.BG_FOREST_600);
expect(getColorsForSeverityLabels('FATAL', 0)).toBe(Color.BG_SAKURA_600);
});
it('should return non-red colors for unrecognized labels at any index', () => {
for (let i = 0; i < 30; i++) {
const color = getColorsForSeverityLabels('4', i);
expect(isRedLike(color)).toBe(false);
}
});
it('should return non-red colors for numeric severity text', () => {
const numericLabels = ['1', '2', '4', '9', '13', '17', '21'];
numericLabels.forEach((label) => {
const color = getColorsForSeverityLabels(label, 0);
expect(isRedLike(color)).toBe(false);
});
});
});

View File

@@ -1,16 +1,7 @@
import { Color } from '@signozhq/design-tokens';
import { themeColors } from 'constants/theme';
import { colors } from 'lib/getRandomColor';
// Function to determine if a color is "red-like" based on its RGB values
export function isRedLike(hex: string): boolean {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return r > 180 && r > g * 1.4 && r > b * 1.4;
}
const SAFE_FALLBACK_COLORS = colors.filter((c) => !isRedLike(c));
const SEVERITY_VARIANT_COLORS: Record<string, string> = {
TRACE: Color.BG_FOREST_600,
Trace: Color.BG_FOREST_500,
@@ -76,13 +67,8 @@ export function getColorsForSeverityLabels(
label: string,
index: number,
): string {
const trimmed = label.trim();
if (!trimmed) {
return Color.BG_SLATE_300;
}
const variantColor = SEVERITY_VARIANT_COLORS[trimmed];
// Check if we have a direct mapping for this severity variant
const variantColor = SEVERITY_VARIANT_COLORS[label.trim()];
if (variantColor) {
return variantColor;
}
@@ -117,8 +103,5 @@ export function getColorsForSeverityLabels(
return Color.BG_SAKURA_500;
}
return (
SAFE_FALLBACK_COLORS[index % SAFE_FALLBACK_COLORS.length] ||
Color.BG_SLATE_400
);
return colors[index % colors.length] || themeColors.red;
}