Compare commits

...

4 Commits

Author SHA1 Message Date
Yunus M
3b37b67222 test: adapt checkbox assertions to SigNoz UI Checkbox DOM
The SigNoz UI Checkbox renders <button role="checkbox" data-state="…">
via Radix instead of antd's <input type="checkbox">. Four test suites
queried the old DOM and broke after the migration.

- TracesExplorer.test.tsx — getByTestId returns the wrapper div now (the
  SigNoz UI Checkbox exposes testId on the wrapper, not the button).
  Querying the inner [role="checkbox"] and asserting data-state.
- DynamicVariableDefaultBehavior.test.tsx, PanelManagement.test.tsx,
  getChartManagerColumns.test.tsx — replace querySelector(
  'input[type="checkbox"]') with [role="checkbox"], and .checked /
  toBeChecked() with toHaveAttribute('data-state', 'checked').

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:57:29 +05:30
Yunus M
5e57a55358 refactor: migrate complex antd Checkbox callsites to @signozhq/ui (batch 3)
Handles the four callsites that needed shape changes, not just renames:

- RegionSelector — collapses separate `checked` + `indeterminate` props
  into a single `value: true | false | 'indeterminate'` (SigNoz UI uses
  CheckedState, not two props).
- InteractiveQuestion — replaces `<Checkbox.Group>` (no SigNoz UI
  equivalent) with a plain `<div>` of individual `<Checkbox>`s that
  push/pull from the existing `selected: string[]` state on each toggle.
- GridCardLayout/types.ts + CustomCheckBox — drops the antd-only
  `CheckboxChangeEvent` type from the `checkBoxOnChangeHandler`
  signature and removes the `ConfigProvider` theme override that
  previously colored each chart-legend checkbox to match its series.
  The dynamic per-series color is now injected via the SigNoz UI
  Checkbox's exposed CSS variables
  (`--checkbox-checked-background`, `--checkbox-border-color`)
  applied through a wrapper `<span style={…}>`.

After this commit `grep -rE "from 'antd'.*Checkbox" frontend/src` and
`grep -rE "CheckboxChangeEvent" frontend/src` both return zero matches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 19:27:50 +05:30
Yunus M
28274b826c refactor: drop antd CheckboxChangeEvent in checkbox callsites (batch 2)
Two callsites that read e.target.checked via antd's CheckboxChangeEvent
type now receive the CheckedState (boolean | 'indeterminate') directly
from @signozhq/ui Checkbox's onChange.

- TopNav/AutoRefreshV2/index.tsx — onChangeAutoRefreshHandler signature
  changes; derives boolean checked from CheckedState.
- TracesExplorer/Filter/SectionContent.tsx — onCheckHandler likewise;
  also swap data-testid -> testId since SigNoz UI Checkbox wraps the
  input in a div that exposes testId.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 17:20:24 +05:30
Yunus M
dc656f08be refactor: replace antd Checkbox with @signozhq/ui Checkbox (batch 1)
Migrates mechanical checkbox callsites from antd to @signozhq/ui/checkbox:

- Trace/Filters/Panel/.../Common/Checkbox.tsx
- NewWidget/LeftContainer/ExplorerAttributeColumns.tsx
- AnomalyAlertEvaluationView.tsx
- QuickFilters/FilterRenderers/Checkbox/Checkbox.tsx
- NewSelect/CustomMultiSelect.tsx
- RolesSelect/RolesSelect.tsx
- TraceDetailsV3/.../SpanPercentilePanel.tsx

API mapping: checked -> value, defaultChecked -> defaultValue, onChange(e)
where e.target.checked is read -> onChange(checked) where checked is
CheckedState. Dropped antd-only props (type="checkbox", rootClassName ->
className, form-value `value=` semantics). RolesSelect wraps the checkbox
in a div to host pointerEvents:none since style is not in CheckboxProps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 17:17:18 +05:30
17 changed files with 105 additions and 93 deletions

View File

@@ -18,7 +18,8 @@ import {
RefreshCw,
} from '@signozhq/icons';
import { Color } from '@signozhq/design-tokens';
import { Button, Checkbox, Select } from 'antd';
import { Button, Select } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
import TextToolTip from 'components/TextToolTip/TextToolTip';
@@ -749,7 +750,7 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
tabIndex={isActive ? 0 : -1}
>
<Checkbox
checked={isSelected}
value={isSelected}
className="option-checkbox"
onClick={(e): void => {
e.stopPropagation();
@@ -1584,7 +1585,7 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
}}
>
<div style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<Checkbox checked={allOptionsSelected} className="option-checkbox">
<Checkbox value={allOptionsSelected} className="option-checkbox">
<div className="option-content">
<div className="all-option-text">ALL</div>
</div>

View File

@@ -1,6 +1,7 @@
/* eslint-disable sonarjs/no-identical-functions */
import { Fragment, useMemo, useState } from 'react';
import { Button, Checkbox, Input, Skeleton } from 'antd';
import { Button, Input, Skeleton } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
import { removeKeysFromExpression } from 'components/QueryBuilderV2/utils';
@@ -634,10 +635,12 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element {
)}
<div className="value">
<Checkbox
onChange={(e): void => onChange(value, e.target.checked, false)}
checked={currentFilterState[value]}
onChange={(checked): void =>
onChange(value, checked === true, false)
}
value={currentFilterState[value]}
disabled={isFilterDisabled}
rootClassName="check-box"
className="check-box"
/>
<div

View File

@@ -1,5 +1,6 @@
import { CircleAlert, RefreshCw } from '@signozhq/icons';
import { Checkbox, Select } from 'antd';
import { Select } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import { useListRoles } from 'api/generated/services/role';
import type { AuthtypesRoleDTO } from 'api/generated/services/sigNoz.schemas';
@@ -146,12 +147,11 @@ function RolesSelect(props: RolesSelectProps): JSX.Element {
options={options}
optionFilterProp="label"
optionRender={(option): JSX.Element => (
<Checkbox
checked={value.includes(option.value as string)}
style={{ pointerEvents: 'none' }}
>
{option.label}
</Checkbox>
<div style={{ pointerEvents: 'none' }}>
<Checkbox value={value.includes(option.value as string)}>
{option.label}
</Checkbox>
</div>
)}
getPopupContainer={getPopupContainer}
disabled={disabled}

View File

@@ -2,7 +2,8 @@ import { useState } from 'react';
import cx from 'classnames';
import { Button } from '@signozhq/ui/button';
import logEvent from 'api/common/logEvent';
import { Checkbox, Radio } from 'antd';
import { Radio } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { AIAssistantEvents } from '../../../events';
import { useAIAssistantAnalyticsContext } from '../../../hooks/useAIAssistantAnalyticsContext';
@@ -96,16 +97,24 @@ export default function InteractiveQuestion({
</Radio.Group>
) : (
<>
<Checkbox.Group
className={cx(styles.options, styles.checkbox)}
onChange={(vals): void => setSelected(vals as string[])}
>
<div className={cx(styles.options, styles.checkbox)}>
{normalized.map((opt) => (
<Checkbox key={opt.value} value={opt.value} className={styles.option}>
<Checkbox
key={opt.value}
value={selected.includes(opt.value)}
onChange={(checked): void => {
setSelected((prev) =>
checked === true
? [...prev, opt.value]
: prev.filter((v) => v !== opt.value),
);
}}
className={styles.option}
>
{opt.label}
</Checkbox>
))}
</Checkbox.Group>
</div>
<Button
variant="solid"
size="sm"

View File

@@ -1,5 +1,6 @@
import { useEffect, useRef, useState } from 'react';
import { Checkbox, Input } from 'antd';
import { Input } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Typography } from '@signozhq/ui/typography';
import { useIsDarkMode } from 'hooks/useDarkMode';
import useDebouncedFn from 'hooks/useDebouncedFunction';
@@ -320,10 +321,8 @@ function AnomalyAlertEvaluationView({
{filteredSeriesKeys.length > 0 && (
<Checkbox
className="anomaly-alert-evaluation-view-series-list-item"
type="checkbox"
name="series"
value="all"
checked={selectedSeries === null}
value={selectedSeries === null}
onChange={(): void => handleSeriesChange(null)}
>
Show All
@@ -335,10 +334,8 @@ function AnomalyAlertEvaluationView({
<Checkbox
className="anomaly-alert-evaluation-view-series-list-item"
key={seriesKey}
type="checkbox"
name="series"
value={seriesKey}
checked={selectedSeries === seriesKey}
value={selectedSeries === seriesKey}
onChange={(): void => handleSeriesChange(seriesKey)}
>
<div

View File

@@ -395,8 +395,8 @@ describe('Dynamic Variable Default Behavior', () => {
// Check if the checkbox exists (it should be unchecked initially)
const checkbox = allOptionContainer?.querySelector(
'input[type="checkbox"]',
) as HTMLInputElement;
'[role="checkbox"]',
) as HTMLElement;
expect(checkbox).toBeInTheDocument();
// Should call onValueUpdate with all values (ALL selection)
@@ -516,10 +516,10 @@ describe('Dynamic Variable Default Behavior', () => {
// Check if the checkbox for ALL option is checked
const checkbox = dropdownAllOption.querySelector(
'input[type="checkbox"]',
) as HTMLInputElement;
'[role="checkbox"]',
) as HTMLElement;
expect(checkbox).toBeInTheDocument();
expect(checkbox.checked).toBe(true);
expect(checkbox).toHaveAttribute('data-state', 'checked');
});
});
});

View File

@@ -196,10 +196,10 @@ describe('Panel Management Tests', () => {
expect(allOption).toHaveClass('selected');
const allCheckbox = allOption.querySelector(
'input[type="checkbox"]',
) as HTMLInputElement;
'[role="checkbox"]',
) as HTMLElement;
expect(allCheckbox).toBeInTheDocument();
expect(allCheckbox.checked).toBe(true);
expect(allCheckbox).toHaveAttribute('data-state', 'checked');
});
});

View File

@@ -160,8 +160,8 @@ describe('getChartManagerColumns', () => {
expect(renderFn).toBeDefined();
const { container } = render(renderFn!(null, tableDataSet[1], 1));
const checkbox = container.querySelector('input[type="checkbox"]');
const checkbox = container.querySelector('[role="checkbox"]');
expect(checkbox).toBeInTheDocument();
expect(checkbox).toBeChecked(); // graphVisibilityState[1] is true
expect(checkbox).toHaveAttribute('data-state', 'checked'); // graphVisibilityState[1] is true
});
});

View File

@@ -1,6 +1,6 @@
import { grey } from '@ant-design/colors';
import { Checkbox, ConfigProvider } from 'antd';
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
import { Checkbox } from '@signozhq/ui/checkbox';
import { CSSProperties } from 'react';
import { CheckBoxProps } from '../types';
@@ -11,30 +11,22 @@ function CustomCheckBox({
checkBoxOnChangeHandler,
disabled = false,
}: CheckBoxProps): JSX.Element {
const onChangeHandler = (e: CheckboxChangeEvent): void => {
checkBoxOnChangeHandler(e, index);
};
const color = data[index]?.stroke?.toString() || grey[0];
const isChecked = graphVisibilityState[index] || false;
const colorStyle = {
'--checkbox-checked-background': color,
'--checkbox-border-color': color,
} as CSSProperties;
return (
<ConfigProvider
theme={{
token: {
colorPrimary: color,
colorBorder: color,
colorBgContainer: color,
},
}}
>
<span style={colorStyle}>
<Checkbox
onChange={onChangeHandler}
checked={isChecked}
onChange={(checked): void => checkBoxOnChangeHandler(checked, index)}
value={isChecked}
disabled={disabled}
/>
</ConfigProvider>
</span>
);
}

View File

@@ -1,5 +1,4 @@
import { Dispatch, MutableRefObject, RefObject, SetStateAction } from 'react';
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
import { ToggleGraphProps } from 'components/Graph/types';
import { UplotProps } from 'components/Uplot/Uplot';
import { PANEL_TYPES } from 'constants/queryBuilder';
@@ -77,7 +76,10 @@ export interface CheckBoxProps {
data: ExtendedChartDataset[];
index: number;
graphVisibilityState: boolean[];
checkBoxOnChangeHandler: (e: CheckboxChangeEvent, index: number) => void;
checkBoxOnChangeHandler: (
checked: boolean | 'indeterminate',
index: number,
) => void;
disabled?: boolean;
}

View File

@@ -1,5 +1,5 @@
import { Dispatch, SetStateAction } from 'react';
import { Checkbox } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { useRegionSelection } from 'hooks/integration/aws/useRegionSelection';
import { regions } from 'utils/regions';
@@ -21,18 +21,18 @@ export function RegionSelector({
setIncludeAllRegions,
});
const allSelected =
allRegionIds.length > 0 &&
allRegionIds.every((regionId) => selectedRegions.includes(regionId));
const someSelected =
selectedRegions.length > 0 && selectedRegions.length < allRegionIds.length;
return (
<div className="region-selector">
<div className="select-all">
<Checkbox
checked={
allRegionIds.length > 0 &&
allRegionIds.every((regionId) => selectedRegions.includes(regionId))
}
indeterminate={
selectedRegions.length > 0 && selectedRegions.length < allRegionIds.length
}
onChange={(e): void => handleSelectAll(e.target.checked)}
value={allSelected ? true : someSelected ? 'indeterminate' : false}
onChange={(checked): void => handleSelectAll(checked === true)}
>
Select All Regions
</Checkbox>
@@ -45,7 +45,7 @@ export function RegionSelector({
{region.subRegions.map((subRegion) => (
<Checkbox
key={subRegion.id}
checked={selectedRegions.includes(subRegion.id)}
value={selectedRegions.includes(subRegion.id)}
onChange={(): void => handleRegionSelect(subRegion.id)}
>
{subRegion.name}

View File

@@ -1,4 +1,5 @@
import { Checkbox, Empty } from 'antd';
import { Empty } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { AxiosResponse } from 'axios';
import Spinner from 'components/Spinner';
import { EXCLUDED_COLUMNS } from 'container/OptionsMenu/constants';
@@ -50,9 +51,8 @@ function ExplorerAttributeColumns({
<div className="attribute-columns">
{filteredAttributeKeys.map((attributeKey: any) => (
<Checkbox
checked={isAttributeKeySelected(attributeKey.name)}
value={isAttributeKeySelected(attributeKey.name)}
onChange={(): void => handleCheckboxChange(attributeKey.name)}
style={{ padding: 0 }}
key={attributeKey.name}
>
{attributeKey.name}

View File

@@ -4,9 +4,9 @@ import { useDispatch, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { useInterval } from 'react-use';
import { Check, ChevronDown } from '@signozhq/icons';
import { Button, Checkbox, Popover } from 'antd';
import { Button, Popover } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Typography } from '@signozhq/ui/typography';
import type { CheckboxChangeEvent } from 'antd/lib/checkbox';
import get from 'api/browser/localstorage/get';
import set from 'api/browser/localstorage/set';
import { DASHBOARD_TIME_IN_DURATION } from 'constants/app';
@@ -132,8 +132,8 @@ function AutoRefresh({
);
const onChangeAutoRefreshHandler = useCallback(
(event: CheckboxChangeEvent) => {
const { checked } = event.target;
(value: boolean | 'indeterminate') => {
const checked = value === true;
if (!checked) {
// remove the path from localstorage
set(
@@ -168,7 +168,7 @@ function AutoRefresh({
<div className="auto-refresh-menu">
<Checkbox
onChange={onChangeAutoRefreshHandler}
checked={isAutoRefreshEnabled}
value={isAutoRefreshEnabled}
disabled={isDisabled}
className="auto-refresh-checkbox"
>

View File

@@ -1,7 +1,8 @@
import { useMemo, useState } from 'react';
// eslint-disable-next-line no-restricted-imports
import { useDispatch, useSelector } from 'react-redux';
import { Checkbox, Tooltip } from 'antd';
import { Tooltip } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Typography } from '@signozhq/ui/typography';
import getFilters from 'api/trace/getFilters';
import { AxiosError } from 'axios';
@@ -174,8 +175,7 @@ function CheckBoxComponent(props: CheckBoxProps): JSX.Element {
<Checkbox
disabled={isLoading || filterLoading}
onClick={onCheckHandler}
checked={isCheckBoxSelected}
defaultChecked
value={isCheckBoxSelected}
key={keyValue}
>
<Tooltip overlay={TooTipOverLay}>

View File

@@ -1,4 +1,5 @@
import { Checkbox, Input, Select, Skeleton } from 'antd';
import { Input, Select, Skeleton } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Button } from '@signozhq/ui/button';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
@@ -104,12 +105,12 @@ function SpanPercentilePanel({
.map((attr) => (
<div className={styles.resourceSelectorItem} key={attr.key}>
<Checkbox
checked={attr.isSelected}
onChange={(e): void => {
value={attr.isSelected}
onChange={(checked): void => {
handleResourceAttributeChange(
attr.key,
attr.value,
e.target.checked,
checked === true,
);
}}
disabled={

View File

@@ -6,8 +6,8 @@ import {
useMemo,
useState,
} from 'react';
import { Button, Card, Checkbox, Input, Tooltip } from 'antd';
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
import { Button, Card, Input, Tooltip } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { ParaGraph } from 'container/Trace/Filters/Panel/PanelBody/Common/styles';
import useDebouncedFn from 'hooks/useDebouncedFunction';
import { isArray, isEmpty } from 'lodash-es';
@@ -87,8 +87,11 @@ export function SectionBody(props: SectionBodyProps): JSX.Element {
[results, searchFilter, type, visibleItemsCount],
);
const onCheckHandler = (event: CheckboxChangeEvent, value: string): void => {
const { checked } = event.target;
const onCheckHandler = (
checkedState: boolean | 'indeterminate',
value: string,
): void => {
const checked = checkedState === true;
let newValue = value;
if (type === 'hasError') {
newValue = String(value === 'Error');
@@ -147,9 +150,9 @@ export function SectionBody(props: SectionBodyProps): JSX.Element {
<Checkbox
className="submenu-checkbox"
key={`${type}-${item}`}
onChange={(e): void => onCheckHandler(e, item)}
checked={checkboxMatcher(item)}
data-testid={`${type}-${item}`}
onChange={(checked): void => onCheckHandler(checked, item)}
value={checkboxMatcher(item)}
testId={`${type}-${item}`}
>
<div className="checkbox-label">
<div className={labelClassname(item)} />

View File

@@ -327,9 +327,13 @@ describe('TracesExplorer - Filters', () => {
// check if the default query is applied - composite query has filters - serviceName : demo-app and name : HTTP GET /customer
await expect(findByText('demo-app')).resolves.toBeInTheDocument();
expect(getByTestId('serviceName-demo-app')).toBeChecked();
expect(
getByTestId('serviceName-demo-app').querySelector('[role="checkbox"]'),
).toHaveAttribute('data-state', 'checked');
await expect(findByText('HTTP GET /customer')).resolves.toBeInTheDocument();
expect(getByTestId('name-HTTP GET /customer')).toBeChecked();
expect(
getByTestId('name-HTTP GET /customer').querySelector('[role="checkbox"]'),
).toHaveAttribute('data-state', 'checked');
});
it('test edge cases of undefined filters', async () => {