Compare commits

..

11 Commits

Author SHA1 Message Date
Manika Malhotra
911ee2912d feat(calendar): select the calendar through data-slot 2026-09-23 21:57:22 +05:30
Manika Malhotra
d4a4217865 feat(dropdown): adopt Dropdown in place of DropdownMenu 2026-09-23 21:56:48 +05:30
Manika Malhotra
925cbafde6 feat(tabs): adopt the items API 2026-09-23 21:56:30 +05:30
Manika Malhotra
1f57634472 feat(radio-group): adopt the items API 2026-09-23 21:56:09 +05:30
Manika Malhotra
662c91884a feat(toggle-group): adopt the items API 2026-09-23 21:55:50 +05:30
Manika Malhotra
95bbd1b501 feat(tooltip): adopt the single Tooltip API 2026-09-23 21:55:23 +05:30
Manika Malhotra
f0037ae4bd feat(checkbox): adopt the reworked Checkbox API 2026-09-23 21:55:00 +05:30
Manika Malhotra
8c3688131e feat(switch): adopt the reworked Switch API 2026-09-23 21:54:38 +05:30
Manika Malhotra
db286c4f92 feat(badge): adopt the reworked Badge palette 2026-09-23 21:54:16 +05:30
Manika Malhotra
e14a6add4e feat(button-group): import ButtonGroup from its own entry 2026-09-23 21:53:56 +05:30
Manika Malhotra
3e2caa8110 feat(button): adopt the reworked Button API 2026-09-23 21:53:31 +05:30
529 changed files with 5190 additions and 7795 deletions

11
.github/CODEOWNERS vendored
View File

@@ -200,15 +200,6 @@ go.mod @therealpandey
/frontend/src/container/ListAlertRules/ @SigNoz/pulse-frontend
/frontend/src/container/TriggeredAlerts/ @SigNoz/pulse-frontend
/frontend/src/container/AnomalyAlertEvaluationView/ @SigNoz/pulse-frontend
/frontend/src/container/RoutingPolicies/ @SigNoz/pulse-frontend
/frontend/src/components/AlertBreadcrumb/ @SigNoz/pulse-frontend
/frontend/src/container/EditRules/ @SigNoz/pulse-frontend
/frontend/src/components/AlertDetailsFilters/ @SigNoz/pulse-frontend
/frontend/src/components/Alerts/ @SigNoz/pulse-frontend
/frontend/src/hooks/routingPolicies/ @SigNoz/pulse-frontend
/frontend/src/types/api/alerts/ @SigNoz/pulse-frontend
/frontend/src/providers/Alert.tsx @SigNoz/pulse-frontend
/frontend/src/constants/alerts.ts @SigNoz/pulse-frontend
## Notification Channels
/frontend/src/pages/ChannelsEdit/ @SigNoz/pulse-frontend
@@ -216,8 +207,6 @@ go.mod @therealpandey
/frontend/src/container/AllAlertChannels/ @SigNoz/pulse-frontend
/frontend/src/container/CreateAlertChannels/ @SigNoz/pulse-frontend
/frontend/src/container/EditAlertChannels/ @SigNoz/pulse-frontend
/frontend/src/container/FormAlertChannels/ @SigNoz/pulse-frontend
/frontend/src/hooks/notificationChannels/ @SigNoz/pulse-frontend
## OpenAPI Schema - Generated
/frontend/src/api/generated/services/ @therealpandey @vikrantgupta25 @srikanthccv

File diff suppressed because it is too large Load Diff

View File

@@ -179,7 +179,6 @@ The `handler.New` function ties the HTTP handler to OpenAPI metadata via `OpenAP
- **SuccessStatusCode**: The HTTP status for successful responses (for example, `http.StatusOK`, `http.StatusCreated`, `http.StatusNoContent`).
- **ErrorStatusCodes**: Additional error status codes beyond the standard ones automatically added by `handler.New`.
- **SecuritySchemes**: Auth mechanisms and scopes required by the operation.
- **Stability**: Maturity marker (`handler.StabilityDevelopment`, `handler.StabilityAlpha`, `handler.StabilityBeta`, `handler.StabilityStable`, the OpenTelemetry Collector levels) emitted as the `x-signoz-stability` extension on every operation. Unset is emitted as `alpha`.
The generic handler:

View File

@@ -23,15 +23,6 @@ func (f *formatter) JSONExtractString(column, path string) []byte {
return append(f.TextToJsonColumn(column), ops...)
}
func (f *formatter) JSONExtractMapValue(column, mapField, key string) []byte {
sql := f.TextToJsonColumn(column)
sql = append(sql, "->"...)
sql = schema.Append(f.bunf, sql, mapField)
sql = append(sql, "->>"...)
sql = schema.Append(f.bunf, sql, key)
return sql
}
func (f *formatter) JSONType(column, path string) []byte {
var sql []byte
sql = append(sql, "jsonb_typeof("...)

View File

@@ -55,67 +55,6 @@ func TestJSONExtractString(t *testing.T) {
}
}
func TestJSONExtractMapValue(t *testing.T) {
tests := []struct {
name string
column string
mapField string
key string
expected string
}{
{
name: "PlainKey",
column: "data",
mapField: "labels",
key: "team",
expected: `"data"::jsonb->'labels'->>'team'`,
},
{
name: "DottedKey_OneMapEntry",
column: "data",
mapField: "labels",
key: "k8s.cluster",
expected: `"data"::jsonb->'labels'->>'k8s.cluster'`,
},
{
name: "SingleQuoteInKey_Doubled",
column: "data",
mapField: "labels",
key: "o'brien",
expected: `"data"::jsonb->'labels'->>'o''brien'`,
},
{
name: "BackslashInKey_Literal",
column: "data",
mapField: "labels",
key: `a\b`,
expected: `"data"::jsonb->'labels'->>'a\b'`,
},
{
name: "DoubleQuoteInKey_Literal",
column: "data",
mapField: "labels",
key: `a"b`,
expected: `"data"::jsonb->'labels'->>'a"b'`,
},
{
name: "QualifiedColumn",
column: "rule.data",
mapField: "labels",
key: "severity",
expected: `"rule"."data"::jsonb->'labels'->>'severity'`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := newFormatter(pgdialect.New())
got := string(f.JSONExtractMapValue(tt.column, tt.mapField, tt.key))
assert.Equal(t, tt.expected, got)
})
}
}
func TestJSONType(t *testing.T) {
tests := []struct {
name string

View File

@@ -41,8 +41,6 @@ import type {
GetRuleHistoryTopContributorsParams,
GetRuleHistoryTopContributorsPathParameters,
ListRules200,
ListRulesV3200,
ListRulesV3Params,
PatchRuleByID200,
PatchRuleByIDPathParameters,
RenderErrorResponseDTO,
@@ -75,8 +73,7 @@ const withQueryKey = <T extends object, K>(
};
/**
* This endpoint lists all alert rules with their current evaluation state. Deprecated: use ListRulesV3, which supports filtering, sorting and pagination.
* @deprecated
* This endpoint lists all alert rules with their current evaluation state
* @summary List alert rules
*/
export const listRules = (signal?: AbortSignal) => {
@@ -118,7 +115,6 @@ export type ListRulesQueryResult = NonNullable<
export type ListRulesQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @deprecated
* @summary List alert rules
*/
@@ -138,7 +134,6 @@ export function useListRules<
}
/**
* @deprecated
* @summary List alert rules
*/
export const invalidateListRules = async (
@@ -1393,97 +1388,3 @@ export const useTestRule = <
> => {
return useMutation(getTestRuleMutationOptions(options));
};
/**
* Returns a page of alert rules with their current evaluation state, trimmed to the fields the list page renders. Supports a filter DSL (`query`), a repeated `states` filter applied after the state overlay, sort (`updated_at`/`created_at`/`name`/`state`/`severity`), order (`asc`/`desc`), and offset-based pagination (`limit`/`offset`). In the filter DSL, a non-reserved key is matched as a rule label directly (`team = infra`); a key that collides with a reserved keyword matches either interpretation (negative operators exclude both), and `labels.<key>` targets only the label. The response also carries the org's label pairs and the reserved filter keys for building filter suggestions.
* @summary List alert rules (v3)
*/
export const listRulesV3 = (
params?: ListRulesV3Params,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<ListRulesV3200>({
url: `/api/v3/rules`,
method: 'GET',
params,
signal,
});
};
export const getListRulesV3QueryKey = (params?: ListRulesV3Params) => {
return [`/api/v3/rules`, ...(params ? [params] : [])] as const;
};
export const getListRulesV3QueryOptions = <
TData = Awaited<ReturnType<typeof listRulesV3>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(
params?: ListRulesV3Params,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listRulesV3>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getListRulesV3QueryKey(params);
const queryFn: QueryFunction<Awaited<ReturnType<typeof listRulesV3>>> = ({
signal,
}) => listRulesV3(params, signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof listRulesV3>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type ListRulesV3QueryResult = NonNullable<
Awaited<ReturnType<typeof listRulesV3>>
>;
export type ListRulesV3QueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary List alert rules (v3)
*/
export function useListRulesV3<
TData = Awaited<ReturnType<typeof listRulesV3>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(
params?: ListRulesV3Params,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listRulesV3>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getListRulesV3QueryOptions(params, options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
return withQueryKey(query, queryOptions.queryKey);
}
/**
* @summary List alert rules (v3)
*/
export const invalidateListRulesV3 = async (
queryClient: QueryClient,
params?: ListRulesV3Params,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getListRulesV3QueryKey(params) },
options,
);
return queryClient;
};

View File

@@ -10188,99 +10188,6 @@ export interface RuletypesGettableTestRuleDTO {
message?: string;
}
export interface RuletypesLabelPairDTO {
/**
* @type string
*/
key: string;
/**
* @type string
*/
value: string;
}
export enum RuletypesListOrderDTO {
asc = 'asc',
desc = 'desc',
}
export enum RuletypesListSortDTO {
updated_at = 'updated_at',
created_at = 'created_at',
name = 'name',
state = 'state',
severity = 'severity',
}
export type RuletypesListableRuleDTOLabels = { [key: string]: string };
export enum RuletypesRuleTypeDTO {
threshold_rule = 'threshold_rule',
promql_rule = 'promql_rule',
anomaly_rule = 'anomaly_rule',
}
export interface RuletypesListableRuleDTO {
/**
* @type string
*/
alert: string;
alertType: RuletypesAlertTypeDTO;
/**
* @type string
* @format date-time
*/
createdAt?: string;
/**
* @type string
*/
createdBy?: string;
/**
* @type string
*/
description?: string;
/**
* @type boolean
*/
disabled?: boolean;
/**
* @type string
*/
id: string;
/**
* @type object
*/
labels?: RuletypesListableRuleDTOLabels;
ruleType: RuletypesRuleTypeDTO;
state: RuletypesAlertStateDTO;
/**
* @type string
* @format date-time
*/
updatedAt?: string;
/**
* @type string
*/
updatedBy?: string;
}
export interface RuletypesListableRulesDTO {
/**
* @type array
*/
labels: RuletypesLabelPairDTO[];
/**
* @type array
*/
reservedKeywords: string[];
/**
* @type array
*/
rules: RuletypesListableRuleDTO[];
/**
* @type integer
* @format int64
*/
total: number;
}
export interface RuletypesRenotifyDTO {
/**
* @type array,null
@@ -10377,6 +10284,11 @@ export interface RuletypesRuleConditionDTO {
thresholds?: RuletypesRuleThresholdDataDTO;
}
export enum RuletypesRuleTypeDTO {
threshold_rule = 'threshold_rule',
promql_rule = 'promql_rule',
anomaly_rule = 'anomaly_rule',
}
export interface RuletypesPostableRuleDTO {
/**
* @type string
@@ -14277,45 +14189,6 @@ export type GetMetricDashboardsV2200 = {
status: string;
};
export type ListRulesV3Params = {
/**
* @type string
* @description undefined
*/
query?: string;
/**
* @type array
* @description undefined
*/
states?: string[];
/**
* @description undefined
*/
sort?: RuletypesListSortDTO;
/**
* @description undefined
*/
order?: RuletypesListOrderDTO;
/**
* @type integer
* @description undefined
*/
limit?: number;
/**
* @type integer
* @description undefined
*/
offset?: number;
};
export type ListRulesV3200 = {
data: RuletypesListableRulesDTO;
/**
* @type string
*/
status: string;
};
export type GetFlamegraphPathParameters = {
traceID: string;
};

View File

@@ -26,6 +26,7 @@ function BreadcrumbItem({
return (
<Button
size="md"
variant="ghost"
color="secondary"
className={styles.item}

View File

@@ -34,21 +34,23 @@ function ErrorEmptyState({
</div>
<div className={styles.actions}>
<Button
size="md"
variant="solid"
color="secondary"
prefix={<LifeBuoy size={14} />}
onClick={onContactSupport}
data-testid="error-contact-support-button"
testId="error-contact-support-button"
>
Contact Support
</Button>
{onRefresh && (
<Button
size="md"
variant="outlined"
color="secondary"
prefix={<RefreshCw size={14} />}
onClick={onRefresh}
data-testid="error-refresh-button"
testId="error-refresh-button"
>
Refresh
</Button>

View File

@@ -1,11 +1,7 @@
import { Copy } from '@signozhq/icons';
import { Badge } from '@signozhq/ui/badge';
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
import { toast } from '@signozhq/ui/sonner';
import {
TooltipContent,
TooltipRoot,
TooltipTrigger,
} from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
@@ -16,20 +12,7 @@ import { BADGE_GAP, estimateBadgeWidth, OVERFLOW_BADGE_WIDTH } from './utils';
export interface LabelColumnProps {
labels: string[];
color?:
| 'primary'
| 'secondary'
| 'success'
| 'error'
| 'warning'
| 'robin'
| 'forest'
| 'amber'
| 'sienna'
| 'cherry'
| 'sakura'
| 'aqua'
| 'vanilla';
color?: BadgeColorType;
value?: { [key: string]: string };
}
@@ -104,20 +87,10 @@ function LabelColumn({
<LabelTag key={label} label={label} color={color} value={value?.[label]} />
))}
{remainingLabels.length > 0 && (
<TooltipRoot>
<TooltipTrigger asChild>
<span>
<Badge
color={color}
className={styles.overflowBadge}
variant="outline"
data-testid="label-overflow-badge"
>
+{remainingLabels.length}
</Badge>
</span>
</TooltipTrigger>
<TooltipContent side="bottom" align="end">
<Tooltip
side="bottom"
align="end"
title={
<div className={styles.tooltipContent}>
<span>
{remainingLabels
@@ -140,8 +113,19 @@ function LabelColumn({
<Copy size={12} />
</button>
</div>
</TooltipContent>
</TooltipRoot>
}
>
<span>
<Badge
color={color}
className={styles.overflowBadge}
variant="outlined"
testId="label-overflow-badge"
>
+{remainingLabels.length}
</Badge>
</span>
</Tooltip>
)}
</div>
);

View File

@@ -1,31 +1,14 @@
import { Copy } from '@signozhq/icons';
import { Badge } from '@signozhq/ui/badge';
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
import { toast } from '@signozhq/ui/sonner';
import {
TooltipContent,
TooltipRoot,
TooltipTrigger,
} from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import { useCopyToClipboard } from 'react-use';
import styles from './LabelTag.module.scss';
export interface LabelTagProps {
label: string;
color?:
| 'primary'
| 'secondary'
| 'success'
| 'error'
| 'warning'
| 'robin'
| 'forest'
| 'amber'
| 'sienna'
| 'cherry'
| 'sakura'
| 'aqua'
| 'vanilla';
color?: BadgeColorType;
value?: string;
}
@@ -41,20 +24,8 @@ function LabelTag({ label, value, color }: LabelTagProps): JSX.Element {
};
return (
<TooltipRoot>
<TooltipTrigger asChild>
<span>
<Badge
color={color}
className={styles.labelBadge}
variant="outline"
data-testid={`label-tag-${label}`}
>
<span className={styles.labelValue}>{displayText}</span>
</Badge>
</span>
</TooltipTrigger>
<TooltipContent>
<Tooltip
title={
<div className={styles.tooltipContent}>
<span>{displayText}</span>
<button
@@ -66,8 +37,19 @@ function LabelTag({ label, value, color }: LabelTagProps): JSX.Element {
<Copy size={12} />
</button>
</div>
</TooltipContent>
</TooltipRoot>
}
>
<span>
<Badge
color={color ?? 'secondary'}
className={styles.labelBadge}
variant="outlined"
testId={`label-tag-${label}`}
>
<span className={styles.labelValue}>{displayText}</span>
</Badge>
</span>
</Tooltip>
);
}

View File

@@ -30,21 +30,23 @@ function NoResultsEmptyState({
<div className={styles.actions}>
{onClear && (
<Button
size="md"
variant="outlined"
color="secondary"
onClick={onClear}
data-testid="no-results-clear-button"
testId="no-results-clear-button"
>
{clearButtonText}
</Button>
)}
{onRefresh && (
<Button
size="md"
variant="outlined"
color="secondary"
prefix={<RefreshCw size={14} />}
onClick={onRefresh}
data-testid="no-results-refresh-button"
testId="no-results-refresh-button"
>
Refresh
</Button>

View File

@@ -1,4 +1,4 @@
import type { BadgeColor } from '@signozhq/ui/badge';
import type { BadgeColorType } from '@signozhq/ui/badge';
export const STATE_ORDER = ['firing', 'pending', 'inactive', 'disabled'];
export const SEVERITY_ORDER = ['critical', 'error', 'warning', 'info'];
@@ -24,9 +24,9 @@ export const SEVERITY_COLORS: Record<string, string> = {
info: 'var(--bg-robin-500)',
};
export const SEVERITY_BADGE_COLORS: Record<string, BadgeColor> = {
critical: 'error',
error: 'error',
export const SEVERITY_BADGE_COLORS: Record<string, BadgeColorType> = {
critical: 'danger',
error: 'danger',
warning: 'warning',
info: 'primary',
};

View File

@@ -22,11 +22,12 @@ function AuthHeader(): JSX.Element {
<span className="auth-header-logo-text">SigNoz</span>
</div>
<Button
size="md"
className="auth-header-help-button"
prefix={<LifeBuoy size={12} />}
onClick={handleGetHelp}
variant="solid"
color="none"
color="secondary"
>
Get Help
</Button>

View File

@@ -48,14 +48,22 @@ function Badges({ tags, setTags }: AddTagsProps): JSX.Element {
<div className="tags-container">
{tags.map<React.ReactNode>((tag) => (
<Badge
variant="solid"
key={tag}
color="vanilla"
color="secondary"
style={{ userSelect: 'none' }}
closable
onClose={(e): void => {
e.preventDefault();
handleClose(tag);
}}
suffix={
<button
type="button"
aria-label={`Remove ${tag}`}
onClick={(e): void => {
e.preventDefault();
handleClose(tag);
}}
>
<X size={12} />
</button>
}
>
{tag}
</Badge>

View File

@@ -4,7 +4,7 @@ import {
CloudintegrationtypesCollectedMetricDTO,
} from 'api/generated/services/sigNoz.schemas';
import { BarChart, Info, ScrollText } from '@signozhq/icons';
import { TooltipProvider, TooltipSimple } from '@signozhq/ui/tooltip';
import { TooltipProvider, Tooltip } from '@signozhq/ui/tooltip';
import './CloudServiceDataCollected.styles.scss';
@@ -89,12 +89,10 @@ function CloudServiceDataCollected({
Metrics
{metricsInfoTooltip && (
<TooltipProvider>
<TooltipSimple
<Tooltip
className={'cloud-service-data-collected-table-tooltip'}
title={metricsInfoTooltip}
side="top"
tooltipContentProps={{
className: 'cloud-service-data-collected-table-tooltip',
}}
>
<span
className="cloud-service-data-collected-table-heading-info"
@@ -103,7 +101,7 @@ function CloudServiceDataCollected({
>
<Info size={12} />
</span>
</TooltipSimple>
</Tooltip>
</TooltipProvider>
)}
</div>

View File

@@ -2,6 +2,7 @@ import { useMemo, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import { Check, Copy } from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { Tooltip } from '@signozhq/ui/tooltip';
import SyntaxHighlighter, {
a11yDark,
} from 'components/MarkdownRenderer/syntaxHighlighter';
@@ -52,16 +53,19 @@ function CodeBlock({
data-testid="code-block-container"
>
{showCopyButton ? (
<Button
variant="ghost"
color="secondary"
size="sm"
onClick={handleCopy}
prefix={isCopied ? <Check size={14} /> : <Copy size={14} />}
aria-label="Copy code"
title={isCopied ? 'Copied' : 'Copy'}
style={{ position: 'absolute', right: 8, top: 8, zIndex: 1 }}
/>
<Tooltip title={isCopied ? 'Copied' : 'Copy'}>
<Button
variant="ghost"
color="secondary"
size="sm"
icon
onClick={handleCopy}
aria-label="Copy code"
style={{ position: 'absolute', right: 8, top: 8, zIndex: 1 }}
>
{isCopied ? <Check size={14} /> : <Copy size={14} />}
</Button>
</Tooltip>
) : null}
<SyntaxHighlighter
style={a11yDark}

View File

@@ -134,26 +134,33 @@ function CreateServiceAccountModal(): JSX.Element {
<DialogFooter className="create-sa-modal__footer">
<Button
size="md"
type="button"
variant="solid"
color="secondary"
onClick={handleClose}
data-testid="create-sa-cancel-btn"
testId="create-sa-cancel-btn"
>
<X size={12} />
Cancel
</Button>
<AuthZButton
size="md"
checks={[SACreatePermission]}
withPortal={false}
type="submit"
form="create-sa-form"
type="button"
variant="solid"
color="primary"
loading={isSubmitting}
disabled={!isValid}
data-testid="create-sa-submit-btn"
testId="create-sa-submit-btn"
onClick={(): void => {
const form = document.getElementById('create-sa-form');
if (form instanceof HTMLFormElement) {
form.requestSubmit();
}
}}
>
Create Service Account
</AuthZButton>

View File

@@ -656,14 +656,19 @@ function CustomTimePicker({
}
>
<Button
disabledTooltip={undefined}
size="md"
className="zoom-out-btn"
onClick={handleZoomOut}
disabled={zoomOutDisabled}
data-testid="zoom-out-btn"
prefix={<ZoomOut size={14} />}
testId="zoom-out-btn"
icon
aria-label="Zoom out"
variant="solid"
color="none"
/>
color="secondary"
>
<ZoomOut size={14} />
</Button>
</Tooltip>
)}
</div>

View File

@@ -27,12 +27,14 @@ function DetailsHeader({
const closeButton = (
<Button
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={onClose}
aria-label="Close"
prefix={<X size={14} />}
></Button>
>
<X size={14} />
</Button>
);
return (

View File

@@ -1,6 +1,6 @@
import { useCallback, useMemo, useState } from 'react';
import { Button, Popover, Tooltip } from 'antd';
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
import { RadioGroup } from '@signozhq/ui/radio-group';
import { Typography } from '@signozhq/ui/typography';
import { TelemetryFieldKey } from 'api/v5/v5';
import { PANEL_TYPES } from 'constants/queryBuilder';
@@ -68,10 +68,15 @@ export default function DownloadOptionsMenu({
>
<div className="export-format">
<Typography.Text className="title">FORMAT</Typography.Text>
<RadioGroup value={exportFormat} onChange={setExportFormat}>
<RadioGroupItem value={DownloadFormats.CSV}>csv</RadioGroupItem>
<RadioGroupItem value={DownloadFormats.JSONL}>jsonl</RadioGroupItem>
</RadioGroup>
<RadioGroup
color="primary"
value={exportFormat}
onChange={setExportFormat}
items={[
{ value: DownloadFormats.CSV, label: 'csv' },
{ value: DownloadFormats.JSONL, label: 'jsonl' },
]}
/>
</div>
<div className="horizontal-line" />
@@ -79,19 +84,15 @@ export default function DownloadOptionsMenu({
<div className="row-limit">
<Typography.Text className="title">Number of Rows</Typography.Text>
<RadioGroup
color="primary"
value={String(rowLimit)}
onChange={(value): void => setRowLimit(Number(value))}
>
<RadioGroupItem value={String(DownloadRowCounts.TEN_K)}>
10k
</RadioGroupItem>
<RadioGroupItem value={String(DownloadRowCounts.THIRTY_K)}>
30k
</RadioGroupItem>
<RadioGroupItem value={String(DownloadRowCounts.FIFTY_K)}>
50k
</RadioGroupItem>
</RadioGroup>
items={[
{ value: String(DownloadRowCounts.TEN_K), label: '10k' },
{ value: String(DownloadRowCounts.THIRTY_K), label: '30k' },
{ value: String(DownloadRowCounts.FIFTY_K), label: '50k' },
]}
/>
</div>
{dataSource !== DataSource.TRACES && (
@@ -100,12 +101,15 @@ export default function DownloadOptionsMenu({
<div className="columns-scope">
<Typography.Text className="title">Columns</Typography.Text>
<RadioGroup value={columnsScope} onChange={setColumnsScope}>
<RadioGroupItem value={DownloadColumnsScopes.ALL}>All</RadioGroupItem>
<RadioGroupItem value={DownloadColumnsScopes.SELECTED}>
Selected
</RadioGroupItem>
</RadioGroup>
<RadioGroup
color="primary"
value={columnsScope}
onChange={setColumnsScope}
items={[
{ value: DownloadColumnsScopes.ALL, label: 'All' },
{ value: DownloadColumnsScopes.SELECTED, label: 'Selected' },
]}
/>
</div>
</>
)}

View File

@@ -0,0 +1,221 @@
import { isValidElement, type ReactElement, type ReactNode } from 'react';
import {
Dropdown,
type DropdownItemType,
type DropdownProps,
} from '@signozhq/ui/dropdown';
/**
* The menu-item shape SigNoz built against `@signozhq/ui/dropdown-menu`.
* `Dropdown` only accepts its own `items` array, so this module maps the old
* rows onto that array and renders them.
*/
export type BaseMenuItem = {
key?: string;
label?: ReactNode;
disabled?: boolean;
disabledTooltip?: ReactNode;
icon?: ReactNode;
rightIcon?: ReactNode;
shortcut?: ReactNode;
onClick?: (info: { key: string; keyPath: string[] }) => void;
danger?: boolean;
className?: string;
};
export type MenuGroup = BaseMenuItem & {
type: 'group';
label: string;
children: MenuItem[];
};
export type MenuDivider = {
type: 'divider';
key?: string;
};
export type SubMenuItem = BaseMenuItem & {
children: MenuItem[];
};
export type CheckboxMenuItem = BaseMenuItem & {
type: 'checkbox';
key: string;
label: ReactNode;
checked?: boolean;
onCheckedChange?: (checked: boolean) => void;
};
export type RadioMenuItem = {
type: 'radio';
key: string;
label: ReactNode;
value: string;
disabled?: boolean;
className?: string;
};
export type RadioGroupMenuItem = {
type: 'radio-group';
key?: string;
value?: string;
onChange?: (value: string) => void;
children: RadioMenuItem[];
};
export type MenuItem =
| MenuGroup
| MenuDivider
| CheckboxMenuItem
| RadioGroupMenuItem
| (SubMenuItem & { type?: never })
| (BaseMenuItem & { type?: never; children?: never });
export type MenuProps = {
items: MenuItem[];
search?: {
placeholder?: string;
onSearchChange?: (value: string) => void;
};
loading?: boolean | { text?: string };
};
type Align = DropdownProps['align'];
type Side = DropdownProps['side'];
function elementOf(node: ReactNode): ReactElement | undefined {
return isValidElement(node) ? node : undefined;
}
function disabledFields(item: {
disabled?: boolean;
disabledTooltip?: ReactNode;
}): { disabled: boolean; disabledTooltip: ReactNode } | Record<string, never> {
if (item.disabled === undefined && item.disabledTooltip === undefined) {
return {};
}
return {
disabled: Boolean(item.disabled),
disabledTooltip: item.disabledTooltip,
};
}
function mapItem(item: MenuItem, index: number): DropdownItemType {
if ('type' in item && item.type === 'divider') {
return { type: 'separator', value: item.key ?? `separator-${index}` };
}
if ('type' in item && item.type === 'group') {
return {
type: 'group',
value: item.key ?? `group-${index}`,
label: item.label,
items: item.children.map((child, childIndex) => mapItem(child, childIndex)),
} as DropdownItemType;
}
if ('type' in item && item.type === 'checkbox') {
return {
type: 'checkbox',
name: item.key,
label: item.label,
value: item.checked,
onChange: item.onCheckedChange,
prefix: elementOf(item.icon),
...disabledFields(item),
};
}
if ('type' in item && item.type === 'radio-group') {
return {
type: 'radio-group',
name: item.key ?? `radio-${index}`,
value: item.value,
onChange: item.onChange,
items: item.children.map((child) => ({
value: child.value,
label: child.label,
...disabledFields(child),
})),
};
}
if ('children' in item && item.children) {
const key = item.key ?? `submenu-${index}`;
return {
type: 'submenu',
value: key,
label: item.label ?? '',
prefix: elementOf(item.icon),
items: item.children.map((child, childIndex) => mapItem(child, childIndex)),
...disabledFields(item),
} as DropdownItemType;
}
const key = item.key ?? `item-${index}`;
const shortcut = 'shortcut' in item ? item.shortcut : undefined;
const suffix = elementOf('rightIcon' in item ? item.rightIcon : undefined);
return {
type: 'item',
value: key,
label: item.label ?? '',
danger: 'danger' in item ? item.danger : undefined,
prefix: elementOf('icon' in item ? item.icon : undefined),
...(shortcut != null ? { shortcut } : { suffix }),
onClick:
'onClick' in item && item.onClick
? (): void => {
item.onClick?.({ key, keyPath: [key] });
}
: undefined,
...disabledFields(item),
};
}
interface DropdownMenuSimpleProps {
menu: MenuProps;
children: ReactNode;
className?: string;
align?: Align;
side?: Side;
testId?: string;
nativeButton?: boolean;
}
export function DropdownMenuSimple({
menu,
children,
className,
align = 'end',
side = 'bottom',
testId,
nativeButton = true,
}: DropdownMenuSimpleProps): JSX.Element {
const loading = menu.loading;
const loadingText = typeof loading === 'object' ? loading.text : undefined;
return (
<Dropdown
items={menu.items.map(mapItem) as DropdownItemType[]}
nativeButton={nativeButton}
align={align}
side={side}
className={className}
testId={testId}
loading={Boolean(loading)}
loadingContent={loadingText}
searchInputProps={
menu.search
? {
placeholder: menu.search.placeholder,
onChange: menu.search.onSearchChange,
}
: undefined
}
>
{children}
</Dropdown>
);
}
export default DropdownMenuSimple;

View File

@@ -38,13 +38,15 @@ function DeleteMemberDialog({
const footer = (
<>
<Button variant="solid" color="secondary" onClick={onClose}>
<Button size="md" variant="solid" color="secondary" onClick={onClose}>
<X size={12} />
Cancel
</Button>
<Button
disabledTooltip={undefined}
size="md"
variant="solid"
color="destructive"
color="danger"
disabled={isDeleting}
onClick={onConfirm}
loading={isDeleting}

View File

@@ -519,7 +519,7 @@ function EditMemberDrawer({
localRoles.map((roleId) => {
const role = availableRoles.find((r) => r.id === roleId);
return (
<Badge key={roleId} color="vanilla">
<Badge variant="solid" key={roleId} color="secondary">
{role?.name ?? roleId}
</Badge>
);
@@ -559,15 +559,15 @@ function EditMemberDrawer({
<div className="edit-member-drawer__meta-item">
<span className="edit-member-drawer__meta-label">Status</span>
{member?.status === MemberStatus.Active ? (
<Badge color="forest" variant="outline">
<Badge color="success" variant="outlined">
ACTIVE
</Badge>
) : member?.status === MemberStatus.Deleted ? (
<Badge color="cherry" variant="outline">
<Badge color="danger" variant="outlined">
DELETED
</Badge>
) : (
<Badge color="amber" variant="outline">
<Badge color="warning" variant="outlined">
INVITED
</Badge>
)}
@@ -575,12 +575,16 @@ function EditMemberDrawer({
<div className="edit-member-drawer__meta-item">
<span className="edit-member-drawer__meta-label">{joinedOnLabel}</span>
<Badge color="vanilla">{formatTimestamp(member?.joinedOn)}</Badge>
<Badge variant="solid" color="secondary">
{formatTimestamp(member?.joinedOn)}
</Badge>
</div>
{!isInvited && (
<div className="edit-member-drawer__meta-item">
<span className="edit-member-drawer__meta-label">Last Modified</span>
<Badge color="vanilla">{formatTimestamp(member?.updatedAt)}</Badge>
<Badge variant="solid" color="secondary">
{formatTimestamp(member?.updatedAt)}
</Badge>
</div>
)}
</div>
@@ -614,10 +618,12 @@ function EditMemberDrawer({
<Tooltip title={getDeleteTooltip(isRootUser, isSelf)}>
<span className="edit-member-drawer__tooltip-wrapper">
<Button
disabledTooltip={undefined}
size="md"
onClick={(): void => setShowDeleteConfirm(true)}
disabled={isRootUser || isSelf}
variant="link"
color="destructive"
color="danger"
>
<Trash2 size={12} />
{isInvited ? 'Revoke Invite' : 'Delete Member'}
@@ -629,6 +635,8 @@ function EditMemberDrawer({
<Tooltip title={isRootUser ? ROOT_USER_TOOLTIP : undefined}>
<span className="edit-member-drawer__tooltip-wrapper">
<Button
disabledTooltip={undefined}
size="md"
onClick={handleGenerateResetLink}
disabled={isGeneratingLink || isRootUser || isLoadingTokenStatus}
variant="link"
@@ -651,12 +659,19 @@ function EditMemberDrawer({
</div>
<div className="edit-member-drawer__footer-right">
<Button variant="outlined" color="secondary" onClick={handleClose}>
<Button
size="md"
variant="outlined"
color="secondary"
onClick={handleClose}
>
<X size={14} />
Cancel
</Button>
<Button
disabledTooltip={undefined}
size="md"
variant="solid"
color="primary"
disabled={!isDirty || isSaving || isRootUser}

View File

@@ -45,6 +45,7 @@ function ResetLinkDialog({
<span className="reset-link-dialog__link-text">{resetLink}</span>
</div>
<Button
size="md"
variant="link"
color="secondary"
onClick={onCopy}

View File

@@ -53,7 +53,7 @@ function ErrorModal({
onClick={(): void => setVisible(true)}
onKeyDown={undefined}
>
<Badge color="error">
<Badge variant="solid" color="danger">
<CircleAlert size={14} color={Color.BG_CHERRY_500} /> error
</Badge>
</span>

View File

@@ -1,7 +1,10 @@
import { useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import { Button, Col, Popover, Row, Select, Space } from 'antd';
import { DropdownMenuSimple, type MenuProps } from '@signozhq/ui/dropdown-menu';
import {
DropdownMenuSimple,
type MenuProps,
} from 'components/DropdownMenu/DropdownMenuSimple';
import { Typography } from '@signozhq/ui/typography';
import axios from 'axios';
import TextToolTip from 'components/TextToolTip';

View File

@@ -1,8 +1,8 @@
import { Download } from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { Popover, PopoverContent, PopoverTrigger } from '@signozhq/ui/popover';
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { RadioGroup } from '@signozhq/ui/radio-group';
import { Tooltip } from '@signozhq/ui/tooltip';
import { Typography } from '@signozhq/ui/typography';
import {
ClientExportData,
@@ -51,31 +51,40 @@ export default function ExportMenu({
return (
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
<TooltipSimple title="Download">
<Tooltip title="Download">
<PopoverTrigger asChild>
<Button
disabledTooltip={undefined}
variant="ghost"
color="secondary"
size="icon"
size="sm"
icon
aria-label="Download"
data-testid={`export-menu-${dataSource}`}
testId={`export-menu-${dataSource}`}
disabled={isExporting}
loading={isExporting}
>
<Download size={14} />
</Button>
</PopoverTrigger>
</TooltipSimple>
</Tooltip>
<PopoverContent align="end" className="export-menu-popover">
<div className="export-format">
<Typography.Text className="title">FORMAT</Typography.Text>
<RadioGroup value={exportFormat} onChange={setExportFormat}>
<RadioGroupItem value={ExportFormat.Csv}>csv</RadioGroupItem>
<RadioGroupItem value={ExportFormat.Jsonl}>jsonl</RadioGroupItem>
</RadioGroup>
<RadioGroup
color="primary"
value={exportFormat}
onChange={setExportFormat}
items={[
{ value: ExportFormat.Csv, label: 'csv' },
{ value: ExportFormat.Jsonl, label: 'jsonl' },
]}
/>
</div>
<Button
disabledTooltip={undefined}
size="md"
variant="solid"
color="primary"
className="export-button"

View File

@@ -58,8 +58,8 @@ function SortableField({
{!isRequired && (
<Button
className={cx(styles.removeBtn, 'periscope-btn')}
variant="outlined"
color="destructive"
variant="solid"
color="danger"
size="sm"
onClick={(): void => onRemove(field)}
>

View File

@@ -173,6 +173,7 @@ function FieldsSelectorContent({
{hasUnsavedChanges && (
<div className={styles.footer}>
<Button
size="md"
variant="outlined"
color="secondary"
onClick={handleDiscard}
@@ -181,6 +182,7 @@ function FieldsSelectorContent({
Discard
</Button>
<Button
size="md"
variant="solid"
color="primary"
onClick={handleSave}

View File

@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { toast } from '@signozhq/ui/sonner';
import { Button, Input } from 'antd';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup } from '@signozhq/ui/toggle-group';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import { handleContactSupport } from 'container/Integrations/utils';
@@ -102,7 +102,10 @@ function FeedbackModal({ onClose }: { onClose: () => void }): JSX.Element {
return (
<div className="feedback-modal-container">
<div className="feedback-modal-header">
<ToggleGroupSimple
<ToggleGroup
variant="outlined"
color="secondary"
size="sm"
type="single"
value={activeTab}
className="feedback-modal-tabs"

View File

@@ -2,7 +2,7 @@ import { useCallback, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { Dot } from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import Noz from 'components/Noz/Noz';
import { NOZ_TOOLTIP_TITLE } from 'components/Noz/Noz.constants';
import { Popover } from 'antd';
@@ -113,8 +113,9 @@ function HeaderRightSection({
</span>
) : null}
<TooltipSimple title={NOZ_TOOLTIP_TITLE}>
<Tooltip title={NOZ_TOOLTIP_TITLE}>
<Button
size="md"
variant="solid"
color="secondary"
className="noz-wave"
@@ -130,7 +131,7 @@ function HeaderRightSection({
>
<Typography.Text>Noz</Typography.Text>
</Button>
</TooltipSimple>
</Tooltip>
</div>
)}
@@ -147,13 +148,16 @@ function HeaderRightSection({
onOpenChange={handleOpenFeedbackModalChange}
>
<Button
color="secondary"
variant="ghost"
size="icon"
size="sm"
icon
className="share-feedback-btn"
aria-label="Feedback"
prefix={<SquarePen size={14} />}
onClick={handleOpenFeedbackModal}
/>
>
<SquarePen size={14} />
</Button>
</Popover>
)}
@@ -170,16 +174,19 @@ function HeaderRightSection({
onOpenChange={handleOpenAnnouncementsModalChange}
>
<Button
color="secondary"
variant="ghost"
size="icon"
size="sm"
icon
aria-label="Announcements"
prefix={<Inbox size={14} />}
onClick={(): void => {
logEvent('Announcements: Clicked', {
page: location.pathname,
});
}}
/>
>
<Inbox size={14} />
</Button>
</Popover>
)}
@@ -196,12 +203,15 @@ function HeaderRightSection({
onOpenChange={handleOpenShareURLModalChange}
>
<Button
color="secondary"
variant="ghost"
size="icon"
size="sm"
icon
aria-label="Share"
prefix={<Globe size={14} />}
onClick={handleOpenShareURLModal}
/>
>
<Globe size={14} />
</Button>
</Popover>
)}
</div>

View File

@@ -149,6 +149,9 @@ function ShareURLModal({ extraOption }: ShareURLModalProps): JSX.Element {
<Info size={14} color={Color.BG_AMBER_600} />
)}
<Switch
color="primary"
textPlacement="right"
disabledTooltip={undefined}
value={enableAbsoluteTime}
disabled={!isValidateRelativeTime}
onChange={(): void => {
@@ -173,6 +176,8 @@ function ShareURLModal({ extraOption }: ShareURLModalProps): JSX.Element {
</Typography.Text>
<div className="absolute-relative-time-toggler">
<Switch
color="primary"
textPlacement="right"
value={enableExtraOption}
onChange={(): void => setEnableExtraOption((prev) => !prev)}
/>

View File

@@ -1,14 +1,23 @@
import { Badge } from '@signozhq/ui/badge';
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
type BadgeColor =
| 'vanilla'
| 'robin'
| 'forest'
| 'amber'
| 'sienna'
| 'cherry'
| 'sakura'
| 'aqua';
function getStatusCodeColor(statusCode: number): BadgeColorType {
if (statusCode >= 200 && statusCode < 300) {
return 'success';
}
if (statusCode >= 300 && statusCode < 400) {
return 'primary';
}
if (statusCode >= 400 && statusCode < 500) {
return 'warning';
}
if (statusCode >= 500) {
return 'danger';
}
if (statusCode >= 100 && statusCode < 200) {
return 'secondary';
}
return 'primary';
}
interface HttpStatusBadgeProps {
statusCode: string | number;
@@ -16,25 +25,6 @@ interface HttpStatusBadgeProps {
className?: string;
}
function getStatusCodeColor(statusCode: number): BadgeColor {
if (statusCode >= 200 && statusCode < 300) {
return 'forest'; // Success - green
}
if (statusCode >= 300 && statusCode < 400) {
return 'robin'; // Redirect - blue
}
if (statusCode >= 400 && statusCode < 500) {
return 'amber'; // Client error - amber
}
if (statusCode >= 500) {
return 'cherry'; // Server error - red
}
if (statusCode >= 100 && statusCode < 200) {
return 'vanilla'; // Informational - neutral
}
return 'robin'; // Default fallback
}
function HttpStatusBadge({
statusCode,
testId,
@@ -49,12 +39,7 @@ function HttpStatusBadge({
const color = getStatusCodeColor(numericStatusCode);
return (
<Badge
color={color}
variant="outline"
data-testid={testId}
className={className}
>
<Badge color={color} variant="outlined" testId={testId} className={className}>
{statusCode}
</Badge>
);

View File

@@ -119,11 +119,12 @@ function InviteMembers({
<div className={styles.cellAction}>
{canRemoveRow && (
<Button
variant="ghost"
color="destructive"
size="md"
variant="solid"
color="danger"
onClick={(): void => removeRow(row.id)}
aria-label="Remove row"
data-testid={`invite-remove-${row.id}`}
testId={`invite-remove-${row.id}`}
>
<Trash2 size={12} />
</Button>
@@ -136,11 +137,12 @@ function InviteMembers({
{showAddButton && (
<div className={styles.addRow}>
<Button
size="md"
variant="dashed"
color="secondary"
prefix={<Plus size={12} />}
onClick={addRow}
data-testid="invite-add-row"
testId="invite-add-row"
>
Add another
</Button>

View File

@@ -1,8 +1,8 @@
import { Button } from '@signozhq/ui/button';
import { Divider } from '@signozhq/ui/divider';
import { DropdownMenuSimple as Dropdown } from '@signozhq/ui/dropdown-menu';
import { DropdownMenuSimple as Dropdown } from 'components/DropdownMenu/DropdownMenuSimple';
import { Typography } from '@signozhq/ui/typography';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { aggregateAttributesResourcesToString } from 'container/LogDetailedView/utils';
import { toast } from '@signozhq/ui/sonner';
@@ -23,8 +23,6 @@ import { useCopyToClipboard } from 'react-use';
import styles from './LogDetailsHeader.module.scss';
const TOOLTIP_CONTENT_PROPS = { className: styles.tooltipContent };
interface LogDetailsHeaderProps {
log: ILog;
onNavigatePrev: () => void;
@@ -91,6 +89,7 @@ function LogDetailsHeader({
<div className={styles.actions}>
{showOpenInExplorer && (
<Button
size="md"
variant="outlined"
color="secondary"
prefix={<Compass size={16} />}
@@ -104,47 +103,59 @@ function LogDetailsHeader({
menu={{ items: menuItems }}
align="end"
className={styles.dropdownContent}
onClick={(e: MouseEvent): void => e.stopPropagation()}
>
<Button
size="md"
variant="link"
color="secondary"
prefix={<Ellipsis size={16} />}
data-testid="log-details-header-menu"
/>
icon
aria-label="Log actions"
testId="log-details-header-menu"
onClick={(e: MouseEvent): void => e.stopPropagation()}
>
<Ellipsis size={16} />
</Button>
</Dropdown>
<div className={styles.arrows}>
<TooltipSimple
<Tooltip
title="Move to previous log"
side="top"
open={isPrevDisabled ? false : undefined}
tooltipContentProps={TOOLTIP_CONTENT_PROPS}
>
<Button
disabledTooltip={undefined}
size="md"
variant="outlined"
color="secondary"
prefix={<ChevronUp size={14} />}
icon
aria-label="Move to previous log"
disabled={isPrevDisabled}
onClick={onNavigatePrev}
data-testid="log-details-header-prev"
/>
</TooltipSimple>
<TooltipSimple
testId="log-details-header-prev"
>
<ChevronUp size={14} />
</Button>
</Tooltip>
<Tooltip
title="Move to next log"
side="top"
open={isNextDisabled ? false : undefined}
tooltipContentProps={TOOLTIP_CONTENT_PROPS}
>
<Button
disabledTooltip={undefined}
size="md"
variant="outlined"
color="secondary"
prefix={<ChevronDown size={14} />}
icon
aria-label="Move to next log"
disabled={isNextDisabled}
onClick={onNavigateNext}
data-testid="log-details-header-next"
/>
</TooltipSimple>
testId="log-details-header-next"
>
<ChevronDown size={14} />
</Button>
</Tooltip>
</div>
</div>
</div>

View File

@@ -1,5 +1,5 @@
import { ReactNode } from 'react';
import { Badge, BadgeColor } from '@signozhq/ui/badge';
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator';
import { getLogIndicatorType } from 'components/Logs/LogStateIndicator/utils';
import { ILog } from 'types/api/logs/log';
@@ -8,13 +8,13 @@ import styles from './LogHighlights.module.scss';
import TraceIdField from './TraceIdField';
// Severity badge color mirrors the LogStateIndicator bar
const SEVERITY_COLOR: Record<string, BadgeColor> = {
[LogType.TRACE]: 'forest',
[LogType.DEBUG]: 'aqua',
[LogType.INFO]: 'robin',
[LogType.WARN]: 'amber',
[LogType.ERROR]: 'cherry',
[LogType.FATAL]: 'sakura',
const SEVERITY_COLOR: Record<string, BadgeColorType> = {
[LogType.TRACE]: 'success',
[LogType.DEBUG]: 'info',
[LogType.INFO]: 'primary',
[LogType.WARN]: 'warning',
[LogType.ERROR]: 'danger',
[LogType.FATAL]: 'highlight-danger',
};
export interface LogHighlightConfig {
@@ -32,9 +32,13 @@ const getAttr = (log: ILog, key: string): string =>
const valueBadge = (
value: string,
options?: { prefix?: ReactNode; color?: BadgeColor },
options?: { prefix?: ReactNode; color?: BadgeColorType },
): ReactNode => (
<Badge color={options?.color ?? 'vanilla'} className={styles.valueBadge}>
<Badge
variant="solid"
color={options?.color ?? 'secondary'}
className={styles.valueBadge}
>
{options?.prefix}
<span className={styles.badgeText} title={value}>
{value}

View File

@@ -4,7 +4,7 @@ import { useCopyToClipboard } from 'react-use';
import { Color, Spacing } from '@signozhq/design-tokens';
import { Button } from '@signozhq/ui/button';
import { Drawer, Tooltip } from 'antd';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup } from '@signozhq/ui/toggle-group';
import { Divider } from '@signozhq/ui/divider';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
@@ -328,13 +328,18 @@ function LogDetailInner({
mouseLeaveDelay={0}
>
<Button
disabledTooltip={undefined}
size="md"
variant="outlined"
color="secondary"
prefix={<ChevronUp size={14} />}
icon
aria-label="Move to previous log"
className="log-arrow-btn log-arrow-btn-up"
disabled={isPrevDisabled}
onClick={goToPrev}
/>
>
<ChevronUp size={14} />
</Button>
</Tooltip>
<Tooltip
title={isNextDisabled ? '' : 'Move to next log'}
@@ -342,18 +347,24 @@ function LogDetailInner({
mouseLeaveDelay={0}
>
<Button
disabledTooltip={undefined}
size="md"
variant="outlined"
color="secondary"
prefix={<ChevronDown size={14} />}
icon
aria-label="Move to next log"
className="log-arrow-btn log-arrow-btn-down"
disabled={isNextDisabled}
onClick={goToNext}
/>
>
<ChevronDown size={14} />
</Button>
</Tooltip>
</div>
{handleOpenInExplorer && (
<div>
<Button
size="md"
variant="outlined"
color="secondary"
prefix={<Compass size={16} />}
@@ -410,7 +421,10 @@ function LogDetailInner({
{isLogDetailsV2 && <div className="log-detail-drawer__section-divider" />}
<div className="tabs-and-search">
<ToggleGroupSimple
<ToggleGroup
variant="outlined"
color="secondary"
size="sm"
type="single"
className="views-tabs"
onChange={handleModeChange}
@@ -473,9 +487,12 @@ function LogDetailInner({
variant="link"
color="secondary"
size="sm"
prefix={<Filter size="lg" />}
icon
aria-label="Show Filters"
onClick={handleFilterVisible}
/>
>
<Filter size="lg" />
</Button>
</Tooltip>
)}
@@ -493,9 +510,14 @@ function LogDetailInner({
variant="link"
color="secondary"
size="sm"
prefix={<Copy size={12} />}
icon
aria-label={
selectedView === VIEW_TYPES.JSON ? 'Copy JSON' : 'Copy Log Link'
}
onClick={selectedView === VIEW_TYPES.JSON ? handleJSONCopy : onLogCopy}
/>
>
<Copy size={12} />
</Button>
</Tooltip>
)}
</div>

View File

@@ -1,4 +1,4 @@
import type { ReactNode } from 'react';
import type { ReactElement, ReactNode } from 'react';
import {
Bold,
CodeXml,
@@ -11,7 +11,7 @@ import {
Type,
} from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import { Typography } from '@signozhq/ui/typography';
import InsertVariableMenu from './InsertVariableMenu';
@@ -20,7 +20,7 @@ import type { EditorCommand, EditorVariable } from './types';
import styles from './MarkdownEditor.module.scss';
const COMMAND_ICONS: Record<string, ReactNode> = {
const COMMAND_ICONS: Record<string, ReactElement> = {
heading: <Heading size={14} />,
bold: <Bold size={14} />,
italic: <Italic size={14} />,
@@ -61,20 +61,22 @@ function EditorToolbar({
<span className={styles.toolbarDivider} />
<div className={styles.commands}>
{commands.map((command) => (
<TooltipSimple key={command.id} title={command.label}>
<Tooltip key={command.id} title={command.label}>
<Button
disabledTooltip={undefined}
type="button"
variant="ghost"
color="secondary"
size="icon"
size="sm"
icon
disabled={disabled}
aria-label={command.label}
data-testid={`markdown-command-${command.id}`}
testId={`markdown-command-${command.id}`}
onClick={(): void => onRunCommand(command)}
>
{COMMAND_ICONS[command.id]}
</Button>
</TooltipSimple>
</Tooltip>
))}
</div>
<div className={styles.toolbarEnd}>

View File

@@ -1,7 +1,10 @@
import { useMemo, useState } from 'react';
import { ChevronDown, DollarSign, Search } from '@signozhq/icons';
import { ChevronDown, DollarSign } from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { DropdownMenuSimple, type MenuItem } from '@signozhq/ui/dropdown-menu';
import {
DropdownMenuSimple,
type MenuItem,
} from 'components/DropdownMenu/DropdownMenuSimple';
import type { EditorVariable } from './types';
@@ -66,12 +69,12 @@ function InsertVariableMenu({
items,
search: {
placeholder: 'Search variables',
searchIcon: <Search size={14} />,
onSearchChange: setSearch,
},
}}
>
<Button
disabledTooltip={undefined}
type="button"
variant="outlined"
color="secondary"
@@ -80,7 +83,7 @@ function InsertVariableMenu({
prefix={<DollarSign size={14} className={styles.insertVariableIcon} />}
suffix={<ChevronDown size={14} />}
className={styles.insertVariable}
data-testid="markdown-insert-variable"
testId="markdown-insert-variable"
>
Insert variable
</Button>

View File

@@ -15,9 +15,10 @@ function MarkdownHelp(): JSX.Element {
type="button"
variant="ghost"
color="secondary"
size="icon"
size="sm"
icon
aria-label="Markdown syntax help"
data-testid="markdown-help-trigger"
testId="markdown-help-trigger"
>
<CircleHelp size={14} />
</Button>

View File

@@ -55,14 +55,14 @@ function NameEmailCell({
function StatusBadge({ status }: { status: MemberRow['status'] }): JSX.Element {
if (status === MemberStatus.Active) {
return (
<Badge color="forest" variant="outline">
<Badge color="success" variant="outlined">
ACTIVE
</Badge>
);
}
if (status === MemberStatus.Deleted) {
return (
<Badge color="cherry" variant="outline">
<Badge color="danger" variant="outlined">
DELETED
</Badge>
);
@@ -70,13 +70,17 @@ function StatusBadge({ status }: { status: MemberRow['status'] }): JSX.Element {
if (status === MemberStatus.Invited) {
return (
<Badge color="amber" variant="outline">
<Badge color="warning" variant="outlined">
INVITED
</Badge>
);
}
return <Badge color="vanilla">⎯</Badge>;
return (
<Badge variant="solid" color="secondary">
⎯
</Badge>
);
}
function MembersEmptyState({

View File

@@ -20,7 +20,7 @@ import {
import { Color } from '@signozhq/design-tokens';
import { Button, Select } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { TooltipProvider, TooltipSimple } from '@signozhq/ui/tooltip';
import { TooltipProvider, Tooltip } from '@signozhq/ui/tooltip';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
import TextToolTip from 'components/TextToolTip/TextToolTip';
@@ -758,9 +758,14 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
tabIndex={isActive ? 0 : -1}
>
<Checkbox
color="primary"
value={isSelected}
className="option-checkbox"
onClick={(e): void => selectFromButton(e, 'checkbox')}
onChange={(): void => {
handleItemSelection('checkbox');
setActiveChipIndex(-1);
setActiveIndex(-1);
}}
>
<div className="option-content">
<Typography.Text truncate={1} className="option-label-text">
@@ -1595,7 +1600,11 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
}}
>
<div style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<Checkbox value={allOptionsSelected} className="option-checkbox">
<Checkbox
color="primary"
value={allOptionsSelected}
className="option-checkbox"
>
<div className="option-content">
<div className="all-option-text">ALL</div>
</div>
@@ -1973,13 +1982,9 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
// `label` arrives already cut to maxTagTextLength, so the reveal reads the
// option's own text (falling back to the raw value for freeform tags).
return (
<TooltipSimple
side="top"
delayDuration={300}
title={findOptionLabelText(options, value)}
>
<Tooltip side="top" title={findOptionLabelText(options, value)}>
{tag}
</TooltipSimple>
</Tooltip>
);
}

View File

@@ -1,7 +1,7 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Button, Tooltip } from 'antd';
import cx from 'classnames';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup } from '@signozhq/ui/toggle-group';
import InputWithLabel from 'components/InputWithLabel/InputWithLabel';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { GroupByFilter } from 'container/QueryBuilder/filters/GroupByFilter/GroupByFilter';
@@ -562,7 +562,10 @@ function QueryAddOns({
</div>
)}
<ToggleGroupSimple
<ToggleGroup
variant="outlined"
color="secondary"
size="sm"
type="multiple"
className="add-ons-tabs"
value={selectedViews.map((view) => view.key)}

View File

@@ -724,26 +724,62 @@ function QuerySearch({
// Helper function to render a badge for the current context mode
const renderContextBadge = (): JSX.Element => {
if (!editingMode) {
return <Badge color="vanilla">Unknown</Badge>;
return (
<Badge variant="solid" color="secondary">
Unknown
</Badge>
);
}
switch (editingMode) {
case 'key':
return <Badge color="robin">Key</Badge>;
return (
<Badge variant="solid" color="primary">
Key
</Badge>
);
case 'operator':
return <Badge color="sakura">Operator</Badge>;
return (
<Badge variant="solid" color="highlight-danger">
Operator
</Badge>
);
case 'value':
return <Badge color="forest">Value</Badge>;
return (
<Badge variant="solid" color="success">
Value
</Badge>
);
case 'conjunction':
return <Badge color="amber">Conjunction</Badge>;
return (
<Badge variant="solid" color="warning">
Conjunction
</Badge>
);
case 'function':
return <Badge color="aqua">Function</Badge>;
return (
<Badge variant="solid" color="info">
Function
</Badge>
);
case 'parenthesis':
return <Badge color="sakura">Parenthesis</Badge>;
return (
<Badge variant="solid" color="highlight-danger">
Parenthesis
</Badge>
);
case 'bracketList':
return <Badge color="cherry">Bracket List</Badge>;
return (
<Badge variant="solid" color="danger">
Bracket List
</Badge>
);
default:
return <Badge color="vanilla">Unknown</Badge>;
return (
<Badge variant="solid" color="secondary">
Unknown
</Badge>
);
}
};
@@ -1465,27 +1501,44 @@ function QuerySearch({
Currently editing: {renderContextBadge()}
{queryContext?.keyToken && (
<span className="triplet-info">
Key: <Badge color="vanilla">{queryContext.keyToken}</Badge>
Key:{' '}
<Badge variant="solid" color="secondary">
{queryContext.keyToken}
</Badge>
</span>
)}
{queryContext?.operatorToken && (
<span className="triplet-info">
Operator: <Badge color="vanilla">{queryContext.operatorToken}</Badge>
Operator:{' '}
<Badge variant="solid" color="secondary">
{queryContext.operatorToken}
</Badge>
</span>
)}
{queryContext?.valueToken && (
<span className="triplet-info">
Value: <Badge color="vanilla">{queryContext.valueToken}</Badge>
Value:{' '}
<Badge variant="solid" color="secondary">
{queryContext.valueToken}
</Badge>
</span>
)}
{queryContext?.currentPair && (
<span className="triplet-info query-pair-info">
Current pair: <Badge color="robin">{queryContext.currentPair.key}</Badge>
<Badge color="sakura">{queryContext.currentPair.operator}</Badge>
Current pair:{' '}
<Badge variant="solid" color="primary">
{queryContext.currentPair.key}
</Badge>
<Badge variant="solid" color="highlight-danger">
{queryContext.currentPair.operator}
</Badge>
{queryContext.currentPair.value && (
<Badge color="forest">{queryContext.currentPair.value}</Badge>
<Badge variant="solid" color="success">
{queryContext.currentPair.value}
</Badge>
)}
<Badge
variant="solid"
color={queryContext.currentPair.isComplete ? 'success' : 'warning'}
>
{queryContext.currentPair.isComplete ? 'Complete' : 'Incomplete'}
@@ -1495,7 +1548,9 @@ function QuerySearch({
{queryContext?.queryPairs && queryContext.queryPairs.length > 0 && (
<span className="triplet-info">
Total pairs:{' '}
<Badge color="robin">{queryContext.queryPairs.length}</Badge>
<Badge variant="solid" color="primary">
{queryContext.queryPairs.length}
</Badge>
</span>
)}
</div>

View File

@@ -6,7 +6,7 @@ import {
useMemo,
useState,
} from 'react';
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
import { DropdownMenuSimple } from 'components/DropdownMenu/DropdownMenuSimple';
import cx from 'classnames';
import { ENTITY_VERSION_V4, ENTITY_VERSION_V5 } from 'constants/app';
import { PANEL_TYPES } from 'constants/queryBuilder';

View File

@@ -2,7 +2,7 @@ import { Button } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
interface CheckboxValueRowProps {
value: string;
@@ -28,6 +28,8 @@ function CheckboxValueRow({
return (
<div className="value">
<Checkbox
color="primary"
disabledTooltip={undefined}
onChange={(isChecked): void => onCheckboxChange(isChecked === true)}
value={checked}
disabled={disabled}
@@ -47,11 +49,11 @@ function CheckboxValueRow({
{customRendererForValue ? (
customRendererForValue(value)
) : (
<TooltipSimple title={String(value)} side="top" align="start">
<Tooltip title={String(value)} side="top" align="start">
<Typography.Text className="value-string" truncate={1}>
{String(value)}
</Typography.Text>
</TooltipSimple>
</Tooltip>
)}
<div className="value-actions">
<Button type="text" className="only-btn">

View File

@@ -1,5 +1,5 @@
import { useState } from 'react';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import { Typography } from '@signozhq/ui/typography';
import { ChevronDown, ChevronRight, Search, Undo2 } from '@signozhq/icons';
@@ -63,9 +63,7 @@ export function CheckboxFilterV2Header({
<ChevronRight size={13} cursor="pointer" />
)}
{isTitleTruncated ? (
<TooltipSimple title={title} delayDuration={400}>
{titleText}
</TooltipSimple>
<Tooltip title={title}>{titleText}</Tooltip>
) : (
titleText
)}

View File

@@ -54,6 +54,7 @@ export function CheckboxFilterV2ValueRow({
>
<div className={styles.checkbox}>
<Checkbox
disabledTooltip={undefined}
onChange={(isChecked): void =>
onCheckboxChange(isChecked === true, checkedState)
}
@@ -97,7 +98,7 @@ export function CheckboxFilterV2ValueRow({
<div className={styles.actions}>
{badge && (
<Badge
variant="outline"
variant="outlined"
color={badge.color}
className={styles.badge}
testId={`badge-${badge.key}`}
@@ -105,10 +106,20 @@ export function CheckboxFilterV2ValueRow({
{badge.label}
</Badge>
)}
<Button variant="ghost" color="secondary" className={styles.onlyButton}>
<Button
size="md"
variant="ghost"
color="secondary"
className={styles.onlyButton}
>
{onlyButtonLabel}
</Button>
<Button variant="ghost" color="secondary" className={styles.toggleButton}>
<Button
size="md"
variant="ghost"
color="secondary"
className={styles.toggleButton}
>
Toggle
</Button>
</div>

View File

@@ -64,7 +64,7 @@ describe('CheckboxFilterV2ValueRow', () => {
render(
<CheckboxFilterV2ValueRow
{...defaultProps}
badge={{ key: 'related', label: 'Related', color: 'robin' }}
badge={{ key: 'related', label: 'Related', color: 'primary' }}
/>,
);

View File

@@ -9,7 +9,7 @@ export enum SectionType {
export interface BadgeConfig {
key: string;
label: string;
color: 'robin' | 'warning' | 'secondary';
color: 'primary' | 'warning' | 'secondary';
}
export interface ItemConfig {

View File

@@ -23,21 +23,22 @@ export function SectionActionButton({
}: SectionActionButtonProps): JSX.Element {
return (
<Tooltip title={tooltip}>
<Button
variant="link"
color="secondary"
size="sm"
className={classNames(styles.iconBtn, className)}
onMouseDown={(e): void => e.preventDefault()}
onClick={(e): void => {
e.stopPropagation();
e.preventDefault();
onClick();
}}
data-testid={testId}
>
{icon}
</Button>
<span onMouseDown={(e): void => e.preventDefault()}>
<Button
variant="link"
color="secondary"
size="sm"
className={classNames(styles.iconBtn, className)}
onClick={(e): void => {
e.stopPropagation();
e.preventDefault();
onClick();
}}
testId={testId}
>
{icon}
</Button>
</span>
</Tooltip>
);
}

View File

@@ -2,8 +2,6 @@
display: flex;
flex-direction: row;
position: relative;
flex: 1;
min-height: 0;
.quick-filters-settings-container {
flex: 0 0 0;

View File

@@ -232,48 +232,55 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
<section className="right-actions">
<Tooltip title="Reset All">
<Button
size="md"
variant="link"
color="secondary"
icon
aria-label="Reset All"
className="right-action-icon-container"
onClick={handleReset}
prefix={<RefreshCw className="sync-icon" size="md" />}
/>
>
<RefreshCw className="sync-icon" size="md" />
</Button>
</Tooltip>
{showFilterCollapse && (
<Tooltip title="Collapse Filters">
<Button
size="md"
variant="link"
color="secondary"
icon
aria-label="Collapse Filters"
className="right-action-icon-container"
onClick={handleFilterVisibilityChange}
prefix={<ArrowUpToLine style={{ rotate: '270deg' }} size="md" />}
/>
>
<ArrowUpToLine style={{ rotate: '270deg' }} size="md" />
</Button>
</Tooltip>
)}
{isDynamicFilters && (
<AuthZButton
size="md"
checks={QuickFilterManagePermissions}
variant="link"
color="secondary"
icon
aria-label="Settings"
className={classNames('right-action-icon-container', {
active: isSettingsOpen,
})}
onClick={(): void => setIsSettingsOpen(true)}
testId="settings-icon-container"
prefix={
<Tooltip title="Settings" open={isSettingsDisabled ? false : undefined}>
<SettingsIcon
className="settings-icon"
data-testid="settings-icon"
width={14}
height={14}
/>
</Tooltip>
}
/>
>
<Tooltip title="Settings" open={isSettingsDisabled ? false : undefined}>
<SettingsIcon
className="settings-icon"
data-testid="settings-icon"
width={14}
height={14}
/>
</Tooltip>
</AuthZButton>
)}
</section>
);
@@ -284,6 +291,8 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
<div className="api-quick-filters-header">
<Typography.Text>Show IP addresses</Typography.Text>
<Switch
color="primary"
textPlacement="right"
style={{ marginLeft: 'auto' }}
value={showIP ?? true}
onChange={(checked): void => {

View File

@@ -1,33 +0,0 @@
// The one `overflow: hidden` in the chain. Ancestors (RouteTab, AppLayout)
// only hand height down; each pane below owns its own scroll.
.layout {
display: flex;
flex: 1;
height: 100%;
min-height: 0;
overflow: hidden;
}
// Positioned so overlays (settings drawer) paint above the content pane
// without changing this pane's layout width.
.filters {
width: 280px;
flex-shrink: 0;
display: flex;
flex-direction: column;
min-height: 0;
position: relative;
overflow: visible;
z-index: 2;
}
// Bounded box for the OverlayScrollbar inside it (`.overlay-scrollbar` is
// `height: 100%`), which owns the scrolling.
.content {
flex: 1;
min-width: 0;
min-height: 0;
display: flex;
flex-direction: column;
overflow: hidden;
}

View File

@@ -1,54 +0,0 @@
import { ComponentProps, ReactNode } from 'react';
import cx from 'classnames';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import QuickFilters from '../QuickFilters';
import styles from './QuickFiltersLayout.module.scss';
// Same optionality as `<QuickFilters />` in JSX (honours its defaultProps).
type QuickFiltersElementProps = JSX.LibraryManagedAttributes<
typeof QuickFilters,
ComponentProps<typeof QuickFilters>
>;
export interface QuickFiltersLayoutProps {
quickFilterProps: QuickFiltersElementProps;
showFilters: boolean;
className?: string;
contentClassName?: string;
testId?: string;
children: ReactNode;
}
function QuickFiltersLayout({
quickFilterProps,
showFilters,
className,
contentClassName,
testId,
children,
}: QuickFiltersLayoutProps): JSX.Element {
return (
<div className={cx(styles.layout, className)} data-testid={testId}>
{showFilters && (
<aside
className={styles.filters}
data-testid="quick-filters-layout-filters"
>
<QuickFilters {...quickFilterProps} />
</aside>
)}
<section
className={cx(styles.content, contentClassName)}
data-testid="quick-filters-layout-content"
>
<OverlayScrollbar>
<div>{children}</div>
</OverlayScrollbar>
</section>
</div>
);
}
export default QuickFiltersLayout;

View File

@@ -1,79 +0,0 @@
import { render, screen } from 'tests/test-utils';
import { QuickFiltersSource } from '../../types';
import QuickFiltersLayout from '../QuickFiltersLayout';
jest.mock('../QuickFiltersLayout.module.scss', () => ({
__esModule: true,
default: {
layout: 'layout',
filters: 'filters',
content: 'content',
},
}));
jest.mock('../../QuickFilters', () => ({
__esModule: true,
default: ({ source }: { source: string }): JSX.Element => (
<div data-testid="quick-filters">{source}</div>
),
}));
const quickFilterProps = {
source: QuickFiltersSource.TRACES_EXPLORER,
handleFilterVisibilityChange: jest.fn(),
};
describe('QuickFiltersLayout', () => {
it('renders QuickFilters with the given props inside the filters pane', () => {
render(
<QuickFiltersLayout showFilters quickFilterProps={quickFilterProps}>
<div>content</div>
</QuickFiltersLayout>,
);
const filtersPane = screen.getByTestId('quick-filters-layout-filters');
expect(filtersPane).toContainElement(screen.getByTestId('quick-filters'));
expect(screen.getByTestId('quick-filters')).toHaveTextContent(
QuickFiltersSource.TRACES_EXPLORER,
);
expect(screen.getByTestId('quick-filters-layout-content')).toHaveTextContent(
'content',
);
});
it('does not render the filters pane when showFilters is false', () => {
render(
<QuickFiltersLayout showFilters={false} quickFilterProps={quickFilterProps}>
<div>content</div>
</QuickFiltersLayout>,
);
expect(
screen.queryByTestId('quick-filters-layout-filters'),
).not.toBeInTheDocument();
expect(screen.queryByTestId('quick-filters')).not.toBeInTheDocument();
expect(screen.getByText('content')).toBeInTheDocument();
});
it('merges classNames onto the root and content panes', () => {
render(
<QuickFiltersLayout
showFilters
quickFilterProps={quickFilterProps}
className="page-root"
contentClassName="page-content"
testId="page"
>
<div>content</div>
</QuickFiltersLayout>,
);
const root = screen.getByTestId('page');
expect(root).toHaveClass('layout', 'page-root');
expect(screen.getByTestId('quick-filters-layout-content')).toHaveClass(
'content',
'page-content',
);
});
});

View File

@@ -61,6 +61,7 @@ function AnnouncementTooltip({
<p className="announcement-tooltip__message">{message}</p>
<div className="announcement-tooltip__footer">
<Button
size="md"
variant="solid"
color="primary"
onClick={closeTooltip}

View File

@@ -6,12 +6,27 @@
left: 0;
z-index: 999;
width: 342px;
height: 100%;
background: var(--l1-background);
transition: width 0.05s ease-in-out;
overflow: hidden;
color: var(--l1-foreground);
&.qf-logs-explorer {
height: calc(100vh - 45px);
}
&.qf-exceptions {
height: 100vh;
}
&.qf-api-monitoring {
height: calc(100vh - 45px);
}
&.qf-traces-explorer {
height: calc(100vh - 45px);
}
&.hidden {
width: 0;
}

View File

@@ -2,7 +2,7 @@ import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { refreshLicense } from 'api/generated/services/licenses';
import { Button } from '@signozhq/ui/button';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import { RefreshCcw } from '@signozhq/icons';
import AuthZTooltip from 'lib/authz/components/AuthZTooltip/AuthZTooltip';
import { buildLicenseUpdatePermission } from 'lib/authz/hooks/useAuthZ/permissions/license.permissions';
@@ -49,7 +49,7 @@ function RefreshPaymentStatus({
>
<Button
variant="link"
color={type === 'text' ? 'none' : 'secondary'}
color="secondary"
size="md"
className={className}
onClick={handleRefreshPaymentStatus}
@@ -64,7 +64,7 @@ function RefreshPaymentStatus({
return (
<span className="refresh-payment-status-btn-wrapper">
{type === 'tooltip' ? (
<TooltipSimple title={t('refreshPaymentStatus')}>{button}</TooltipSimple>
<Tooltip title={t('refreshPaymentStatus')}>{button}</Tooltip>
) : (
button
)}

View File

@@ -5,7 +5,10 @@ import type {
TableColumnType as ColumnType,
} from 'antd';
import { Button, Flex } from 'antd';
import { DropdownMenuSimple, type MenuItem } from '@signozhq/ui/dropdown-menu';
import {
DropdownMenuSimple,
type MenuItem,
} from 'components/DropdownMenu/DropdownMenuSimple';
import { Switch } from '@signozhq/ui/switch';
import logEvent from 'api/common/logEvent';
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
@@ -95,6 +98,8 @@ function DynamicColumnTable({
>
<div>{column.title?.toString()}</div>
<Switch
color="primary"
textPlacement="right"
value={columnsData?.findIndex((c) => c.key === column.key) !== -1}
onChange={onToggleHandler(index, column)}
/>

View File

@@ -152,7 +152,7 @@ function RolesSelect(props: RolesSelectProps): JSX.Element {
optionFilterProp="label"
optionRender={(option): JSX.Element => (
<div style={{ pointerEvents: 'none' }}>
<Checkbox value={value.includes(option.value as string)}>
<Checkbox color="primary" value={value.includes(option.value as string)}>
{option.label}
</Checkbox>
</div>

View File

@@ -1,38 +0,0 @@
// Hands the parent's height down to the active pane and lets the pane scroll
// its own content, so TopNav and the tab bar stay put. Child combinators only
// (nested Tabs must not be caught).
.routeTab {
flex: 1;
min-height: 0;
}
.routeTab > :global(.ant-tabs-content-holder) {
display: flex;
flex-direction: column;
}
.routeTab > :global(.ant-tabs-content-holder) > :global(.ant-tabs-content) {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.routeTab
> :global(.ant-tabs-content-holder)
> :global(.ant-tabs-content)
> :global(.ant-tabs-tabpane-active) {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}
.routeTab
> :global(.ant-tabs-content-holder)
> :global(.ant-tabs-content)
> :global(.ant-tabs-tabpane-active)
> :global(.overlay-scrollbar) {
flex: 1;
min-height: 0;
}

View File

@@ -5,11 +5,6 @@ import { fireEvent, render, screen } from 'tests/test-utils';
import RouteTab from './index';
import { RouteTabProps } from './types';
jest.mock('./RouteTab.module.scss', () => ({
__esModule: true,
default: { routeTab: 'routeTab' },
}));
function DummyComponent1(): JSX.Element {
return <div>Dummy Component 1</div>;
}
@@ -79,36 +74,6 @@ describe('RouteTab component', () => {
expect(history.location.pathname).toBe('/tab2');
});
it('applies the layout class alongside a custom className', () => {
const history = createMemoryHistory();
const { container } = render(
<Router history={history}>
<RouteTab
history={history}
routes={testRoutes}
activeKey="Tab1"
className="custom-tabs"
/>
</Router>,
);
expect(container.querySelector('.ant-tabs')).toHaveClass(
'routeTab',
'custom-tabs',
);
});
it('renders the active tab content inside an overlay scrollbar', () => {
const history = createMemoryHistory();
const { container } = render(
<Router history={history}>
<RouteTab history={history} routes={testRoutes} activeKey="Tab1" />
</Router>,
);
expect(
container.querySelector('.ant-tabs-tabpane-active > .overlay-scrollbar'),
).toHaveTextContent('Dummy Component 1');
});
it('calls onChangeHandler on tab change', () => {
const onChangeHandler = jest.fn();
const history = createMemoryHistory();

View File

@@ -5,32 +5,20 @@ import {
useParams,
} from 'react-router-dom';
import { Tabs, TabsProps } from 'antd';
import cx from 'classnames';
import HeaderRightSection from 'components/HeaderRightSection/HeaderRightSection';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { RouteTabProps } from './types';
import styles from './RouteTab.module.scss';
interface Params {
[key: string]: string;
}
/**
* Each pane scrolls its own content inside an OverlayScrollbar, so the tab bar
* stays put. Mounted as the page root the pane is bounded to the viewport; inside
* a plain block wrapper the scroller is inert and the page scrolls as usual.
* Pane content that needs a bounded box must size itself with `height: 100%`
* (the scroller's viewport is block flow, so `flex: 1` has no effect there).
*/
function RouteTab({
routes,
activeKey,
onChangeHandler,
history,
showRightSection,
className,
...rest
}: RouteTabProps & TabsProps): JSX.Element {
const params = useParams<Params>();
@@ -62,16 +50,11 @@ function RouteTab({
label: name,
key,
tabKey: route,
children: (
<OverlayScrollbar>
<Component />
</OverlayScrollbar>
),
children: <Component />,
}));
return (
<Tabs
className={cx(styles.routeTab, className)}
onChange={onChange}
destroyInactiveTabPane
activeKey={currentRoute?.key || activeKey}

View File

@@ -24,6 +24,7 @@ function KeyCreatedPhase({
<div className="add-key-modal__key-display">
<span className="add-key-modal__key-text">{createdKey.key}</span>
<Button
size="md"
variant="link"
color="secondary"
onClick={onCopy}
@@ -36,7 +37,9 @@ function KeyCreatedPhase({
<div className="add-key-modal__expiry-meta">
<span className="add-key-modal__expiry-label">Expiration</span>
<Badge color="vanilla">{expiryLabel}</Badge>
<Badge variant="solid" color="secondary">
{expiryLabel}
</Badge>
</div>
<div className="add-key-modal__callout-wrapper">

View File

@@ -2,7 +2,7 @@ import type { Control, UseFormRegister } from 'react-hook-form';
import { Controller } from 'react-hook-form';
import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup } from '@signozhq/ui/toggle-group';
import { DatePicker } from 'antd';
import AuthZButton from 'lib/authz/components/AuthZButton/AuthZButton';
import { AuthZGuardContent } from 'lib/authz/components/AuthZGuard/AuthZGuardContent';
@@ -68,7 +68,9 @@ function KeyFormPhase({
name="expiryMode"
control={control}
render={({ field }): JSX.Element => (
<ToggleGroupSimple
<ToggleGroup
variant="outlined"
color="secondary"
type="single"
value={field.value}
onChange={(val: string): void => {
@@ -117,6 +119,7 @@ function KeyFormPhase({
<div className="add-key-modal__footer">
<div className="add-key-modal__footer-right">
<Button
size="md"
variant="solid"
color="secondary"
onClick={onClose}
@@ -125,16 +128,22 @@ function KeyFormPhase({
Cancel
</Button>
<AuthZButton
size="md"
checks={checks}
authZEnabled={!!accountId}
withPortal={false}
type="submit"
form={FORM_ID}
type="button"
variant="solid"
color="primary"
loading={isSubmitting}
disabled={!isValid}
testId="add-key-submit-btn"
onClick={(): void => {
const form = document.getElementById(FORM_ID);
if (form instanceof HTMLFormElement) {
form.requestSubmit();
}
}}
>
Create Key
</AuthZButton>

View File

@@ -80,15 +80,16 @@ function DeleteAccountModal(): JSX.Element {
const footer = (
<div className="sa-delete-dialog__footer">
<Button variant="solid" color="secondary" onClick={handleCancel}>
<Button size="md" variant="solid" color="secondary" onClick={handleCancel}>
<X size={12} />
Cancel
</Button>
<AuthZButton
size="md"
checks={[buildSADeletePermission(accountId ?? '')]}
authZEnabled={!!accountId}
variant="solid"
color="destructive"
color="danger"
loading={isDeleting}
onClick={handleConfirm}
data-testid="confirm-delete-btn"

View File

@@ -4,7 +4,7 @@ import { LockKeyhole, Trash2, X } from '@signozhq/icons';
import { Badge } from '@signozhq/ui/badge';
import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup } from '@signozhq/ui/toggle-group';
import { DatePicker } from 'antd';
import type { ServiceaccounttypesGettableFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas';
import AuthZButton from 'lib/authz/components/AuthZButton/AuthZButton';
@@ -103,7 +103,9 @@ function EditKeyForm({
name="expiryMode"
control={control}
render={({ field }): JSX.Element => (
<ToggleGroupSimple
<ToggleGroup
variant="outlined"
color="secondary"
type="single"
value={field.value}
onChange={(val: string): void => {
@@ -113,6 +115,9 @@ function EditKeyForm({
}}
size="sm"
disabled={!canUpdate}
disabledTooltip={
canUpdate ? undefined : 'You do not have permission to update this key'
}
className="edit-key-modal__expiry-toggle"
items={[
{ value: ExpiryMode.NONE, label: 'No Expiration' },
@@ -150,7 +155,7 @@ function EditKeyForm({
<div className="edit-key-modal__meta">
<span className="edit-key-modal__meta-label">Last Observed At</span>
<Badge color="vanilla">
<Badge variant="solid" color="secondary">
{formatLastObservedAt(
keyItem?.lastObservedAt ?? null,
formatTimezoneAdjustedTimestamp,
@@ -161,13 +166,14 @@ function EditKeyForm({
<div className="edit-key-modal__footer">
<AuthZButton
size="md"
checks={[
buildAPIKeyDeletePermission(keyItem?.id ?? ''),
buildSADetachPermission(accountId ?? ''),
]}
authZEnabled={!!accountId && !!keyItem?.id}
variant="link"
color="destructive"
color="danger"
onClick={onRevokeClick}
withPortal={false}
>
@@ -175,20 +181,26 @@ function EditKeyForm({
Revoke Key
</AuthZButton>
<div className="edit-key-modal__footer-right">
<Button variant="solid" color="secondary" onClick={onClose}>
<Button size="md" variant="solid" color="secondary" onClick={onClose}>
<X size={12} />
Cancel
</Button>
<AuthZButton
size="md"
checks={[buildAPIKeyUpdatePermission(keyItem?.id ?? '')]}
authZEnabled={!!accountId && !!keyItem?.id}
type="submit"
form={FORM_ID}
type="button"
variant="solid"
color="primary"
loading={isSaving}
disabled={!isDirty}
withPortal={false}
onClick={(): void => {
const form = document.getElementById(FORM_ID);
if (form instanceof HTMLFormElement) {
form.requestSubmit();
}
}}
>
Save Changes
</AuthZButton>

View File

@@ -122,9 +122,11 @@ function buildColumns({
]}
authZEnabled={!isDisabled && !!accountId}
withPortal={false}
variant="ghost"
variant="solid"
size="sm"
color="destructive"
color="danger"
icon
aria-label="Revoke Key"
disabled={isDisabled}
onClick={(e): void => {
e.stopPropagation();
@@ -213,6 +215,7 @@ function KeysTab({
</a>
</p>
<AuthZButton
size="md"
checks={[APIKeyCreatePermission, buildSAAttachPermission(accountId)]}
authZEnabled={!isDisabled && !!accountId}
withPortal={false}

View File

@@ -122,11 +122,12 @@ function OverviewTab({
<span className="sa-drawer__input-text">{account.id || '—'}</span>
{account.id && (
<Button
size="md"
variant="link"
color="secondary"
onClick={handleCopyId}
className="sa-drawer__copy-btn"
data-testid="copy-id-btn"
testId="copy-id-btn"
>
{hasCopiedId ? <Check size={14} /> : <Copy size={14} />}
</Button>
@@ -156,7 +157,7 @@ function OverviewTab({
localRoles.map((roleId) => {
const role = availableRoles.find((r) => r.id === roleId);
return (
<Badge key={roleId} color="vanilla">
<Badge variant="solid" key={roleId} color="secondary">
{role?.name ?? roleId}
</Badge>
);
@@ -187,15 +188,15 @@ function OverviewTab({
<div className="sa-drawer__meta-item">
<span className="sa-drawer__meta-label">Status</span>
{account.status?.toUpperCase() === 'ACTIVE' ? (
<Badge color="forest" variant="outline">
<Badge color="success" variant="outlined">
ACTIVE
</Badge>
) : account.status?.toUpperCase() === 'DELETED' ? (
<Badge color="cherry" variant="outline">
<Badge color="danger" variant="outlined">
DELETED
</Badge>
) : (
<Badge color="vanilla" variant="outline" className="sa-status-badge">
<Badge color="secondary" variant="outlined" className="sa-status-badge">
{account.status ? account.status.toUpperCase() : 'UNKNOWN'}
</Badge>
)}
@@ -203,12 +204,16 @@ function OverviewTab({
<div className="sa-drawer__meta-item">
<span className="sa-drawer__meta-label">Created At</span>
<Badge color="vanilla">{formatTimestamp(account.createdAt)}</Badge>
<Badge variant="solid" color="secondary">
{formatTimestamp(account.createdAt)}
</Badge>
</div>
<div className="sa-drawer__meta-item">
<span className="sa-drawer__meta-label">Updated At</span>
<Badge color="vanilla">{formatTimestamp(account.updatedAt)}</Badge>
<Badge variant="solid" color="secondary">
{formatTimestamp(account.updatedAt)}
</Badge>
</div>
</div>

View File

@@ -41,18 +41,19 @@ export function RevokeKeyFooter({
}: RevokeKeyFooterProps): JSX.Element {
return (
<>
<Button variant="solid" color="secondary" onClick={onCancel}>
<Button size="md" variant="solid" color="secondary" onClick={onCancel}>
<X size={12} />
Cancel
</Button>
<AuthZButton
size="md"
checks={[
buildAPIKeyDeletePermission(keyId ?? ''),
buildSADetachPermission(accountId ?? ''),
]}
authZEnabled={!!accountId && !!keyId}
variant="solid"
color="destructive"
color="danger"
loading={isRevoking}
onClick={onConfirm}
withPortal={false}

View File

@@ -40,8 +40,9 @@ function SaveErrorItem({
</span>
{onRetry && !isRetrying && (
<Button
size="md"
variant="link"
color="none"
color="secondary"
aria-label="Retry"
onClick={async (e): Promise<void> => {
e.stopPropagation();

View File

@@ -10,7 +10,7 @@ import { Key, LayoutGrid, Plus, Trash2, X } from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { DrawerWrapper } from '@signozhq/ui/drawer';
import { toast } from '@signozhq/ui/sonner';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup } from '@signozhq/ui/toggle-group';
import { Skeleton } from 'antd';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import {
@@ -375,11 +375,12 @@ function ServiceAccountDrawer({
activeTab === ServiceAccountDrawerTab.Overview && !isDeleted && open ? (
<div className="sa-drawer__footer">
<AuthZButton
size="md"
checks={[buildSADeletePermission(selectedAccountId ?? '')]}
authZEnabled={!!selectedAccountId}
withPortal={false}
variant="link"
color="destructive"
color="danger"
onClick={(): void => {
void setIsDeleteOpen(true);
}}
@@ -388,11 +389,17 @@ function ServiceAccountDrawer({
Delete Service Account
</AuthZButton>
<div className="sa-drawer__footer-right">
<Button variant="outlined" color="secondary" onClick={handleClose}>
<Button
size="md"
variant="outlined"
color="secondary"
onClick={handleClose}
>
<X size={14} />
Cancel
</Button>
<AuthZButton
size="md"
checks={[
buildSAReadPermission(selectedAccountId ?? ''),
buildSAUpdatePermission(selectedAccountId ?? ''),
@@ -426,7 +433,9 @@ function ServiceAccountDrawer({
const body = (
<div className="sa-drawer__layout">
<div className="sa-drawer__tabs">
<ToggleGroupSimple
<ToggleGroup
variant="outlined"
color="secondary"
type="single"
value={activeTab}
size="sm"

View File

@@ -28,20 +28,20 @@ export function NameEmailCell({
export function StatusBadge({ status }: { status: string }): JSX.Element {
if (status?.toUpperCase() === 'ACTIVE') {
return (
<Badge color="forest" variant="outline">
<Badge color="success" variant="outlined">
ACTIVE
</Badge>
);
}
if (status?.toUpperCase() === 'DELETED') {
return (
<Badge color="cherry" variant="outline">
<Badge color="danger" variant="outlined">
DELETED
</Badge>
);
}
return (
<Badge color="vanilla" variant="outline" className="sa-status-badge">
<Badge color="secondary" variant="outlined" className="sa-status-badge">
{status ? status.toUpperCase() : 'UNKNOWN'}
</Badge>
);

View File

@@ -13,7 +13,13 @@ function ModalFixture(): JSX.Element {
return (
<>
<Button data-testid="open-signoz-modal" onClick={(): void => setOpen(true)}>
<Button
size="md"
variant="solid"
color="primary"
testId="open-signoz-modal"
onClick={(): void => setOpen(true)}
>
Open modal
</Button>
<SignozModal

View File

@@ -1,4 +1,4 @@
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup } from '@signozhq/ui/toggle-group';
import './SignozRadioGroup.styles.scss';
@@ -24,12 +24,16 @@ function SignozRadioGroup({
disabled = false,
}: SignozRadioGroupProps): JSX.Element {
return (
<ToggleGroupSimple
<ToggleGroup
variant="outlined"
color="secondary"
size="sm"
type="single"
value={value}
className={`signoz-radio-group ${className}`}
onChange={onChange}
disabled={disabled}
disabledTooltip={undefined}
items={options.map((option) => ({
value: option.value,
label: (

View File

@@ -12,7 +12,7 @@ function BadgeWithTooltip({
return (
<div key={label}>
<Tooltip title={tooltipTitle}>
<Badge className="label-column--tag" color="vanilla">
<Badge variant="solid" className="label-column--tag" color="secondary">
{getLabelRenderingValue(label, value && value[label])}
</Badge>
</Tooltip>

View File

@@ -27,7 +27,11 @@ function LabelColumn({ labels, value }: LabelColumnProps): JSX.Element {
{labels.map(
(label: string): JSX.Element => (
<div key={label}>
<Badge className="label-column--tag" color="vanilla">
<Badge
variant="solid"
className="label-column--tag"
color="secondary"
>
{getLabelAndValueContent(label, value && value[label])}
</Badge>
</div>
@@ -37,7 +41,7 @@ function LabelColumn({ labels, value }: LabelColumnProps): JSX.Element {
}
trigger="hover"
>
<Badge className="label-column--tag" color="vanilla">
<Badge variant="solid" className="label-column--tag" color="secondary">
+{remainingLabels.length}
</Badge>
</Popover>

View File

@@ -1,4 +1,5 @@
import { type MouseEvent, type ReactNode } from 'react';
import { X } from '@signozhq/icons';
import { Badge } from '@signozhq/ui/badge';
import cx from 'classnames';
@@ -23,11 +24,22 @@ function TagBadge({
}: TagBadgeProps): JSX.Element {
return (
<Badge
color="sienna"
variant="outline"
color="archive"
variant="outlined"
className={cx(styles.static, className)}
closable={closable}
onClose={onClose}
suffix={
closable ? (
<button
type="button"
aria-label="Remove"
onClick={(event): void => {
onClose?.(event);
}}
>
<X size={12} />
</button>
) : undefined
}
>
{children}
</Badge>

View File

@@ -1,5 +1,6 @@
import { type ChangeEvent, type KeyboardEvent, useState } from 'react';
import { Button } from '@signozhq/ui/button';
import { Tooltip } from '@signozhq/ui/tooltip';
import { Input } from '@signozhq/ui/input';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
@@ -138,16 +139,18 @@ function TagKeyValueInput({
closable
onClose={(): void => removeTag(tag)}
>
<Button
variant="ghost"
color="secondary"
className={styles.tagLabel}
title="Double-click to edit"
testId={`${testId}-chip`}
onDoubleClick={(): void => startEdit(index)}
>
{tag}
</Button>
<Tooltip title="Double-click to edit">
<Button
size="md"
variant="ghost"
color="secondary"
className={styles.tagLabel}
testId={`${testId}-chip`}
onDoubleClick={(): void => startEdit(index)}
>
{tag}
</Button>
</Tooltip>
</TagBadge>
),
)}

View File

@@ -1,14 +1,10 @@
import { type ReactNode, useLayoutEffect, useMemo } from 'react';
import { type ReactNode, useLayoutEffect } from 'react';
import { chromePerformanceTanstackTableEndHover } from './perfDevtools';
import { useIsRowHovered } from './TanStackTableStateContext';
import {
TooltipContentProps,
TooltipSimple,
TooltipSimpleProps,
} from '@signozhq/ui/tooltip';
import { Tooltip, TooltipProps, TooltipProvider } from '@signozhq/ui/tooltip';
export type HoverTooltipProps = Omit<TooltipSimpleProps, 'open'> & {
export type HoverTooltipProps = Omit<TooltipProps, 'open'> & {
rowId: string;
children: ReactNode;
};
@@ -26,28 +22,13 @@ export function TanStackHoverTooltip({
}
}, [isHovered, rowId]);
const tooltipContentProps = useMemo(
() =>
({
onPointerDownOutside: (e): void => {
e.preventDefault();
e.stopPropagation();
},
}) satisfies TooltipContentProps,
[],
);
if (!isHovered) {
return <>{children}</>;
}
return (
<TooltipSimple
delayDuration={700}
tooltipContentProps={tooltipContentProps}
{...tooltipProps}
>
{children}
</TooltipSimple>
<TooltipProvider delay={700}>
<Tooltip {...tooltipProps}>{children}</Tooltip>
</TooltipProvider>
);
}

View File

@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite';
import { Color } from '@signozhq/design-tokens';
import { Ellipsis } from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
import { DropdownMenuSimple } from 'components/DropdownMenu/DropdownMenuSimple';
import { GroupedStatusCounts } from 'container/InfraMonitoringK8sV2/components/GroupedStatusCounts';
import type { StatusCountItem } from 'container/InfraMonitoringK8sV2/components/GroupedStatusCounts';
import { ValidateColumnValueWrapper } from 'container/InfraMonitoringK8sV2/components/ValidateColumnValueWrapper';
@@ -104,7 +104,8 @@ const rowActions = (): JSX.Element => (
<Button
aria-label="Service actions"
color="secondary"
size="icon"
size="sm"
icon
variant="outlined"
>
<Ellipsis size={16} />

View File

@@ -1,6 +1,6 @@
import { Dispatch, SetStateAction, useCallback, useMemo } from 'react';
import { ChevronDown, Globe } from '@signozhq/icons';
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
import { DropdownMenuSimple } from 'components/DropdownMenu/DropdownMenuSimple';
import { Button } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import TimeItems, {

View File

@@ -129,10 +129,6 @@ const themeColors = {
salmon2: '#FFAB91',
salmon3: '#E0876A',
},
/* Series palette (dark). Hues in the red band are deliberately absent: red is
reserved for thresholds and error states, so an arbitrary series must never
claim it. generateColor indexes by `hash % Object.keys(...).length`, so
adding or removing an entry recolors every existing chart. */
chartcolors: {
// Blues (3)
dodgerBlue: '#2F80ED',
@@ -156,13 +152,13 @@ const themeColors = {
// Oranges (3)
festivalOrange: '#F2994A',
amber1: '#E1A155',
coralOrange: '#E17055',
pumpkin: '#FF7F50',
// Olives / Greens (3)
olive1: '#DFC33A',
olive2: '#D5E55D',
green7: '#81C220',
// Reds (3)
radicalRed: '#FF1A66',
crimsonRed: '#EB5757',
fireRed: '#E10600',
// Pinks (3)
hotPink: '#E84393',
@@ -195,9 +191,9 @@ const themeColors = {
orange1: '#D35400',
orange2: '#E67E22',
orange3: '#F5B041',
green8: '#5AC02B',
green9: '#48E043',
green10: '#68E788',
red1: '#C0392B',
red2: '#E74C3C',
red3: '#EC7063',
pink1: '#D81B60',
pink2: '#E91E63',
pink3: '#F06292',
@@ -216,9 +212,9 @@ const themeColors = {
coral1: '#E67E22',
coral2: '#F39C12',
coral3: '#F5B041',
teal7: '#2BC07B',
teal8: '#43E0C5',
teal9: '#68D9E7',
crimson1: '#C0392B',
crimson2: '#E74C3C',
crimson3: '#EC7063',
violet1: '#8E44AD',
violet2: '#9B59B6',
violet3: '#BB8FCE',
@@ -228,18 +224,18 @@ const themeColors = {
forest1: '#27AE60',
forest2: '#2ECC71',
forest3: '#58D68D',
cyan4: '#83C2EB',
blush1: '#FF6F91',
blush2: '#FF85A2',
blush3: '#FFA0B3',
lavender1: '#9B59B6',
lavender2: '#AF7AC5',
lavender3: '#C39BD3',
blue7: '#4375E0',
blue8: '#686DE7',
indigo1: '#A68EED',
indigo2: '#B980EA',
purple6: '#EE98D9',
olive3: '#F2F0AE',
tomato1: '#E74C3C',
tomato2: '#EC7063',
tomato3: '#F1948A',
salmon1: '#FF6B6B',
salmon2: '#FF8787',
salmon3: '#FFA1A1',
mustard1: '#F1C40F',
mustard2: '#F7DC6F',
mustard3: '#F9E79F',
@@ -258,9 +254,9 @@ const themeColors = {
blue4: '#2874A6',
blue5: '#2E86C1',
blue6: '#3498DB',
purple4: '#A52BC0',
purple5: '#E043D0',
magenta4: '#E768B5',
red4: '#C0392B',
red5: '#E74C3C',
red6: '#EC7063',
orange4: '#D35400',
orange5: '#E67E22',
orange6: '#EB984E',
@@ -271,19 +267,18 @@ const themeColors = {
gold5: '#F1C40F',
gold6: '#F4D03F',
},
/* Series palette (light). Same red-free constraint as chartcolors above. */
lightModeColor: {
magenta1: '#D81B60',
radicalRed: '#D81B60',
dodgerBlueDark: '#1E5BD9',
steelgrey: '#344B6B',
steelpurple: '#5E548E',
steelindigo: '#8E4A7C',
steelpink: '#B63A6F',
amber1: '#E1A14B',
steelcoral: '#E14B5A',
steelorange: '#E76F2F',
steelgold: '#E09B00',
olive1: '#C9BD3A',
steelrust: '#C93A50',
steelgreen: '#2F7D69',
mediumOrchidDark: '#8E24AA',
@@ -291,17 +286,17 @@ const themeColors = {
seaGreen: '#1E7F5A',
turquoiseBlueDark: '#007EA7',
silverDark: '#5F5F5F',
green1: '#ACDB24',
green2: '#66CC21',
outrageousOrangeDark: '#E64A19',
roseBudDark: '#D84315',
deepSkyBlueDark: '#0277BD',
royalBlue: '#2A4FDB',
avocadoDark: '#6B6B1E',
mintGreenDark: '#2E9E55',
green3: '#3F8B3A',
chestnut: '#8B3A3A',
limaDark: '#5C7F00',
olive: '#6E7F00',
green4: '#3CC964',
beautyBushDark: '#C93C3C',
danube: '#4F6FB3',
oliveDrab: '#4F7F1A',
@@ -309,13 +304,13 @@ const themeColors = {
electricLimeDark: '#6B8F00',
robin: '#2F4FCC',
teal1: '#1FBF83',
harleyOrange: '#CC2E12',
gladeGreen: '#4F7F46',
hemlock: '#5C5C45',
vidaLoca: '#3D6B00',
rust: '#993300',
teal2: '#28C6C1',
red: '#C62828',
blue: '#1A237E',
green: '#1B7F3A',
purple: '#6A1B9A',
@@ -325,7 +320,7 @@ const themeColors = {
brown: '#7A3A1E',
teal: '#006D6F',
limeDark: '#4C8C2B',
cyan1: '#1B546D',
maroon: '#6D1B1B',
navy: '#0D1B5E',
gray: '#616161',
@@ -333,25 +328,25 @@ const themeColors = {
indigo: '#303F9F',
slateGray: '#556B7C',
chocolate: '#9C4A1A',
blue1: '#3B74DF',
tomato: '#E53935',
steelBlue: '#3A6EA5',
peruDark: '#B35E00',
darkOliveGreen: '#445B1F',
blue2: '#4041B0',
indianRed: '#B04040',
mediumSlateBlue: '#5C6BC0',
indigo1: '#6644A9',
rosyBrownDark: '#A94444',
darkSlateGray: '#2E4A4A',
fuchsia: '#C511C5',
indigo2: '#AD42E0',
purple1: '#C83AC5',
salmonDark: '#E64A3C',
darkSalmonDark: '#C85A3A',
paleVioletRedDark: '#C2186A',
mediumPurple: '#7E57C2',
darkOrchid: '#7B1FA2',
mediumSeaGreenDark: '#2E8B57',
purple2: '#E573BC',
lightCoralDark: '#E57373',
gold: '#D4AF37',
sandyBrownDark: '#C76A15',

View File

@@ -1,7 +1,7 @@
import { useCallback } from 'react';
import { useHistory } from 'react-router-dom';
import { Button } from '@signozhq/ui/button';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import { Drawer } from 'antd';
import ROUTES from 'constants/routes';
import { Maximize2, Plus, X } from '@signozhq/icons';
@@ -53,22 +53,25 @@ export default function AIAssistantDrawer(): JSX.Element {
</div>
<div>
<TooltipSimple title="New conversation">
<Tooltip title="New conversation">
<Button
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={handleNewConversation}
aria-label="New conversation"
>
<Plus size={16} />
</Button>
</TooltipSimple>
</Tooltip>
<TooltipSimple title="Open full screen">
<Tooltip title="Open full screen">
<Button
disabledTooltip={undefined}
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={handleExpand}
disabled={!activeConversationId}
@@ -76,19 +79,20 @@ export default function AIAssistantDrawer(): JSX.Element {
>
<Maximize2 size={16} />
</Button>
</TooltipSimple>
</Tooltip>
<TooltipSimple title="Close">
<Tooltip title="Close">
<Button
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={closeDrawer}
aria-label="Close drawer"
>
<X size={16} />
</Button>
</TooltipSimple>
</Tooltip>
</div>
</div>
}

View File

@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import { useHistory, useLocation } from 'react-router-dom';
import { Button } from '@signozhq/ui/button';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import ROUTES from 'constants/routes';
import { History, Maximize2, Minus, Plus, X } from '@signozhq/icons';
import Noz from 'components/Noz/Noz';
@@ -159,62 +159,73 @@ export default function AIAssistantModal(): JSX.Element | null {
</div>
<div className={styles.actions}>
<TooltipSimple title={showHistory ? 'Back to chat' : 'Conversations'}>
<Tooltip title={showHistory ? 'Back to chat' : 'Conversations'}>
<Button
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={(): void => setShowHistory((v) => !v)}
aria-label="Toggle conversations"
className={showHistory ? styles.toggleBtnActive : ''}
prefix={<History size={14} />}
/>
</TooltipSimple>
>
<History size={14} />
</Button>
</Tooltip>
<TooltipSimple title="New conversation">
<Tooltip title="New conversation">
<Button
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={handleNew}
aria-label="New conversation"
prefix={<Plus size={14} />}
/>
</TooltipSimple>
>
<Plus size={14} />
</Button>
</Tooltip>
<TooltipSimple title="Open full screen">
<Tooltip title="Open full screen">
<Button
disabledTooltip={undefined}
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={handleExpand}
disabled={!activeConversationId}
aria-label="Open full screen"
prefix={<Maximize2 size={14} />}
/>
</TooltipSimple>
>
<Maximize2 size={14} />
</Button>
</Tooltip>
<TooltipSimple title="Minimize to side panel">
<Tooltip title="Minimize to side panel">
<Button
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={handleMinimize}
aria-label="Minimize to side panel"
prefix={<Minus size={14} />}
/>
</TooltipSimple>
>
<Minus size={14} />
</Button>
</Tooltip>
<TooltipSimple title="Close">
<Tooltip title="Close">
<Button
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={closeModal}
aria-label="Close"
prefix={<X size={14} />}
/>
</TooltipSimple>
>
<X size={14} />
</Button>
</Tooltip>
</div>
</div>

View File

@@ -1,7 +1,7 @@
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
import { matchPath, useHistory, useLocation } from 'react-router-dom';
import { Button } from '@signozhq/ui/button';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import ROUTES from 'constants/routes';
import { History, Maximize2, Plus, X } from '@signozhq/icons';
import Noz from 'components/Noz/Noz';
@@ -144,50 +144,59 @@ export default function AIAssistantPanel(): JSX.Element | null {
</div>
<div className={styles.actions}>
<TooltipSimple title={showHistory ? 'Back to chat' : 'Conversations'}>
<Tooltip title={showHistory ? 'Back to chat' : 'Conversations'}>
<Button
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={(): void => setShowHistory((v) => !v)}
aria-label="Toggle conversations"
prefix={<History size={14} />}
/>
</TooltipSimple>
>
<History size={14} />
</Button>
</Tooltip>
<TooltipSimple title="New conversation">
<Tooltip title="New conversation">
<Button
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={handleNew}
aria-label="New conversation"
prefix={<Plus size={14} />}
/>
</TooltipSimple>
>
<Plus size={14} />
</Button>
</Tooltip>
<TooltipSimple title="Open full screen">
<Tooltip title="Open full screen">
<Button
disabledTooltip={undefined}
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={handleExpand}
disabled={!activeConversationId}
aria-label="Open full screen"
prefix={<Maximize2 size={14} />}
/>
</TooltipSimple>
>
<Maximize2 size={14} />
</Button>
</Tooltip>
<TooltipSimple title="Close">
<Tooltip title="Close">
<Button
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
onClick={closeDrawer}
aria-label="Close panel"
prefix={<X size={14} />}
/>
</TooltipSimple>
>
<X size={14} />
</Button>
</Tooltip>
</div>
</div>

View File

@@ -1,7 +1,7 @@
import { useCallback } from 'react';
import { matchPath, useLocation } from 'react-router-dom';
import { Button } from '@signozhq/ui/button';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import logEvent from 'api/common/logEvent';
import ROUTES from 'constants/routes';
import Noz from 'components/Noz/Noz';
@@ -43,15 +43,18 @@ export default function AIAssistantTrigger(): JSX.Element | null {
}
return (
<TooltipSimple title={NOZ_TOOLTIP_TITLE}>
<Tooltip title={NOZ_TOOLTIP_TITLE}>
<Button
size="md"
variant="solid"
color="primary"
icon
className={`${styles.trigger} noz-wave`}
onClick={handleOpen}
aria-label="Open Noz"
prefix={<Noz size={24} />}
/>
</TooltipSimple>
>
<Noz size={24} />
</Button>
</Tooltip>
);
}

View File

@@ -12,7 +12,7 @@ import {
import cx from 'classnames';
import { v4 as uuidv4 } from 'uuid';
import { Button } from '@signozhq/ui/button';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import type { MessageActionDTO } from 'api/ai-assistant/sigNozAIAssistantAPI.schemas';
import {
ApplyFilterSignalDTO,
@@ -631,6 +631,7 @@ export default function ActionsSection({
const chip = (
<Button
disabledTooltip={undefined}
variant="outlined"
color="secondary"
size="sm"
@@ -645,9 +646,9 @@ export default function ActionsSection({
);
return tooltip ? (
<TooltipSimple key={key} title={tooltip}>
<Tooltip key={key} title={tooltip}>
{chip}
</TooltipSimple>
</Tooltip>
) : (
<span key={key}>{chip}</span>
);

View File

@@ -10,8 +10,8 @@ import {
DialogSubtitle,
DialogTitle,
} from '@signozhq/ui/dialog';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { ToggleGroup } from '@signozhq/ui/toggle-group';
import { Tooltip } from '@signozhq/ui/tooltip';
import type {
ApprovalEventDTO,
ApprovalEventDTODiff,
@@ -101,16 +101,18 @@ export default function ApprovalCard({
<div className={styles.diffSection}>
<div className={styles.diffHeader}>
<span className={styles.diffHeaderLabel}>Diff</span>
<TooltipSimple title="Expand diff">
<Tooltip title="Expand diff">
<Button
variant="link"
size="sm"
color="secondary"
icon
onClick={(): void => setDiffExpanded(true)}
aria-label="Expand diff"
prefix={<Maximize2 size={12} />}
/>
</TooltipSimple>
>
<Maximize2 size={12} />
</Button>
</Tooltip>
</div>
<DiffView diff={approval.diff} />
</div>
@@ -132,7 +134,9 @@ export default function ApprovalCard({
<div className={styles.diffModalBody}>
<p className={styles.diffModalSummary}>{approval.summary}</p>
<div className={styles.diffToolbarRow}>
<ToggleGroupSimple
<ToggleGroup
variant="outlined"
color="secondary"
type="single"
size="sm"
value={viewMode}
@@ -156,7 +160,9 @@ export default function ApprovalCard({
},
]}
/>
<ToggleGroupSimple
<ToggleGroup
variant="outlined"
color="secondary"
type="multiple"
size="sm"
value={wrapText ? ['wrap'] : []}
@@ -185,6 +191,8 @@ export default function ApprovalCard({
<div className={styles.actions}>
<Button
disabledTooltip={undefined}
color="primary"
variant="solid"
size="sm"
onClick={handleApprove}
@@ -194,6 +202,7 @@ export default function ApprovalCard({
Approve
</Button>
<Button
disabledTooltip={undefined}
variant="outlined"
size="sm"
color="secondary"
@@ -465,7 +474,7 @@ function CopyButton({ text, label }: CopyButtonProps): JSX.Element {
};
return (
<TooltipSimple title={copied ? `Copied ${label}` : `Copy ${label}`}>
<Tooltip title={copied ? `Copied ${label}` : `Copy ${label}`}>
<Button
variant="ghost"
size="sm"
@@ -475,6 +484,6 @@ function CopyButton({ text, label }: CopyButtonProps): JSX.Element {
>
{copied ? <Check size={12} /> : <Copy size={12} />}
</Button>
</TooltipSimple>
</Tooltip>
);
}

View File

@@ -12,7 +12,7 @@ import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import { Popover, PopoverContent, PopoverTrigger } from '@signozhq/ui/popover';
import { toast } from '@signozhq/ui/sonner';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import type { UploadFile } from 'antd';
import getSessionStorage from 'api/browser/sessionstorage/get';
import setSessionStorage from 'api/browser/sessionstorage/set';
@@ -900,8 +900,10 @@ export default function ChatInput({
<div key={f.uid} className={styles.attachmentChip}>
<span className={styles.attachmentName}>{f.name}</span>
<Button
color="secondary"
variant="ghost"
size="icon"
size="sm"
icon
className={styles.attachmentRemove}
onClick={(): void => removeFile(f.uid)}
aria-label={`Remove ${f.name}`}
@@ -925,7 +927,7 @@ export default function ChatInput({
<div className={styles.contextTagContent}>
<Badge
color="secondary"
variant="outline"
variant="outlined"
className={styles.contextTagCategory}
>
{category}
@@ -935,13 +937,15 @@ export default function ChatInput({
{onDismissAutoContext && (
<Button
variant="link"
size="icon"
size="sm"
icon
color="secondary"
className={styles.contextTagRemove}
onClick={(): void => onDismissAutoContext(key)}
aria-label={`Remove ${category}: ${label} context`}
prefix={<X size={10} />}
></Button>
>
<X size={10} />
</Button>
)}
</div>
);
@@ -954,7 +958,7 @@ export default function ChatInput({
<div className={styles.contextTagContent}>
<Badge
color="primary"
variant="outline"
variant="outlined"
className={styles.contextTagCategory}
>
{contextItem.category}
@@ -963,15 +967,17 @@ export default function ChatInput({
</div>
<Button
variant="link"
size="icon"
size="sm"
icon
color="secondary"
className={styles.contextTagRemove}
onClick={(): void =>
removeContext(contextItem.category, contextItem.entityId)
}
aria-label={`Remove ${contextItem.category}: ${contextItem.value} context`}
prefix={<X size={10} />}
></Button>
>
<X size={10} />
</Button>
</div>
))}
</div>
@@ -1020,6 +1026,7 @@ export default function ChatInput({
>
<PopoverTrigger asChild>
<Button
disabledTooltip={undefined}
variant="solid"
color="secondary"
size="sm"
@@ -1059,7 +1066,6 @@ export default function ChatInput({
variant="ghost"
color="secondary"
size="sm"
role="tab"
id={`ai-context-tab-${category}`}
// Single stable panel id shared by every tab: only the
// active category's tabpanel is rendered, so per-category
@@ -1178,18 +1184,20 @@ export default function ChatInput({
aria-live="polite"
aria-label="Recording voice input"
>
<TooltipSimple title="Discard recording">
<Tooltip title="Discard recording">
<Button
type="button"
variant="ghost"
size="icon"
size="sm"
icon
color="secondary"
className={cx(styles.micDiscard, styles.secondary)}
onClick={handleDiscard}
aria-label="Discard recording"
prefix={<X size={12} />}
/>
</TooltipSimple>
>
<X size={12} />
</Button>
</Tooltip>
<span className={styles.micWaves} aria-hidden="true">
<span />
<span />
@@ -1200,56 +1208,67 @@ export default function ChatInput({
<span />
<span />
</span>
<TooltipSimple title="Stop and send">
<Tooltip title="Stop and send">
<Button
type="button"
variant="ghost"
size="icon"
color="destructive"
variant="solid"
size="sm"
icon
color="danger"
className={cx(styles.micStop, styles.destructive)}
onClick={handleStopAndSend}
aria-label="Stop and send"
prefix={<Square size={9} fill="currentColor" strokeWidth={0} />}
/>
</TooltipSimple>
>
<Square size={9} fill="currentColor" strokeWidth={0} />
</Button>
</Tooltip>
</div>
) : (
<TooltipSimple title="Voice input">
<Tooltip title="Voice input">
<Button
disabledTooltip={undefined}
color="secondary"
variant="ghost"
size="icon"
size="sm"
icon
onClick={(): void => startVoiceInput(VoiceInputSource.Button)}
disabled={disabled}
aria-label="Start voice input"
className={styles.micBtn}
prefix={<Mic size={14} />}
/>
</TooltipSimple>
>
<Mic size={14} />
</Button>
</Tooltip>
))}
{isStreaming && onCancel ? (
<TooltipSimple title="Stop generating">
<Tooltip title="Stop generating">
<Button
variant="solid"
size="icon"
color="destructive"
size="sm"
icon
color="danger"
onClick={onCancel}
aria-label="Stop generating"
prefix={<Square size={10} fill="currentColor" strokeWidth={0} />}
/>
</TooltipSimple>
>
<Square size={10} fill="currentColor" strokeWidth={0} />
</Button>
</Tooltip>
) : (
<TooltipSimple title="Send message">
<Tooltip title="Send message">
<Button
disabledTooltip={undefined}
variant="solid"
size="icon"
size="sm"
icon
color="primary"
onClick={isListening ? handleStopAndSend : handleSend}
disabled={disabled || (!text.trim() && pendingFiles.length === 0)}
aria-label="Send message"
prefix={<Send size={14} />}
/>
</TooltipSimple>
>
<Send size={14} />
</Button>
</Tooltip>
)}
</div>
</div>

View File

@@ -58,7 +58,7 @@ export default function ContextPickerEmptyState({
color="primary"
className={styles.contextPopoverEmptyCta}
onClick={(): void => onPrefill(prefill)}
data-testid={`ai-context-empty-cta-${category}`}
testId={`ai-context-empty-cta-${category}`}
prefix={<Sparkles size={14} />}
>
<span className={styles.contextPopoverEmptyCtaLabel}>{ctaLabel}</span>

View File

@@ -140,6 +140,8 @@ export default function ClarificationForm({
<div className={styles.actions}>
<Button
disabledTooltip={undefined}
size="md"
variant="solid"
color="primary"
onClick={handleSubmit}
@@ -149,6 +151,8 @@ export default function ClarificationForm({
Submit
</Button>
<Button
disabledTooltip={undefined}
size="md"
variant="outlined"
color="secondary"
onClick={handleCancel}
@@ -310,6 +314,7 @@ function FieldInput({ field, value, onChange }: FieldInputProps): JSX.Element {
return (
<div className={styles.field}>
<Checkbox
color="primary"
className={styles.checkboxLabel}
value={checked}
onChange={(): void => onChange(!checked)}
@@ -382,6 +387,7 @@ function FieldInput({ field, value, onChange }: FieldInputProps): JSX.Element {
<div className={styles.checkboxGroup}>
{options?.map((opt) => (
<Checkbox
color="primary"
key={opt}
className={styles.checkboxLabel}
value={regularSelected.includes(opt)}
@@ -392,6 +398,7 @@ function FieldInput({ field, value, onChange }: FieldInputProps): JSX.Element {
))}
{allowCustom && (
<Checkbox
color="primary"
className={styles.checkboxLabel}
value={isCustom}
onChange={toggleCustom}

View File

@@ -6,7 +6,7 @@ import { getAbsoluteUrl } from 'utils/basePath';
import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import { toast } from '@signozhq/ui/sonner';
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
import { DropdownMenuSimple } from 'components/DropdownMenu/DropdownMenuSimple';
import {
Archive,
@@ -207,17 +207,18 @@ export default function ConversationItem({
<DropdownMenuSimple
menu={{ items: menuItems }}
align="end"
sideOffset={4}
className={styles.menu}
>
<Button
variant="link"
size="icon"
color="none"
size="sm"
icon
color="secondary"
className={styles.btn}
aria-label="Conversation actions"
prefix={<EllipsisVertical size={12} />}
/>
>
<EllipsisVertical size={12} />
</Button>
</DropdownMenuSimple>
</div>
)}

View File

@@ -2,7 +2,7 @@ import { useEffect, useMemo, useState } from 'react';
import cx from 'classnames';
import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import { Plus, Search } from '@signozhq/icons';
import logEvent from 'api/common/logEvent';
@@ -171,7 +171,7 @@ export default function ConversationsList({
{isLoadingThreads && <HeaderLoadingDots />}
{!isLoadingThreads && showAddNewConversation && (
<TooltipSimple title="New conversation">
<Tooltip title="New conversation">
<Button
variant="solid"
size="sm"
@@ -181,7 +181,7 @@ export default function ConversationsList({
>
<Plus size={12} />
</Button>
</TooltipSimple>
</Tooltip>
)}
</div>

View File

@@ -3,7 +3,7 @@ import cx from 'classnames';
import { useCopyToClipboard } from 'react-use';
import { Button } from '@signozhq/ui/button';
import { DialogWrapper } from '@signozhq/ui/dialog';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { Check, Copy, RefreshCw, ThumbsDown, ThumbsUp } from '@signozhq/icons';
import { useTimezone } from 'providers/Timezone';
@@ -174,10 +174,11 @@ export default function MessageFeedback({
<>
<div className={cx(styles.feedback, { [styles.visible]: isLastAssistant })}>
<div className={styles.actions}>
<TooltipSimple title={copied ? 'Copied!' : 'Copy'}>
<Tooltip title={copied ? 'Copied!' : 'Copy'}>
<Button
className={styles.btn}
size="icon"
size="sm"
icon
variant="ghost"
onClick={handleCopy}
color="secondary"
@@ -185,14 +186,15 @@ export default function MessageFeedback({
>
{copied ? <Check size={12} /> : <Copy size={12} />}
</Button>
</TooltipSimple>
</Tooltip>
<TooltipSimple title={VOTE_LABEL[FeedbackRatingDTO.positive].tooltip}>
<Tooltip title={VOTE_LABEL[FeedbackRatingDTO.positive].tooltip}>
<Button
className={cx(styles.btn, {
[styles.votedUp]: vote === FeedbackRatingDTO.positive,
})}
size="icon"
size="sm"
icon
variant="ghost"
color="secondary"
onClick={(): void => handleVote(FeedbackRatingDTO.positive)}
@@ -201,14 +203,15 @@ export default function MessageFeedback({
>
<ThumbsUp size={12} />
</Button>
</TooltipSimple>
</Tooltip>
<TooltipSimple title={VOTE_LABEL[FeedbackRatingDTO.negative].tooltip}>
<Tooltip title={VOTE_LABEL[FeedbackRatingDTO.negative].tooltip}>
<Button
className={cx(styles.btn, {
[styles.votedDown]: vote === FeedbackRatingDTO.negative,
})}
size="icon"
size="sm"
icon
variant="ghost"
color="secondary"
onClick={(): void => handleVote(FeedbackRatingDTO.negative)}
@@ -217,13 +220,14 @@ export default function MessageFeedback({
>
<ThumbsDown size={12} />
</Button>
</TooltipSimple>
</Tooltip>
{onRegenerate && (
<TooltipSimple title="Regenerate">
<Tooltip title="Regenerate">
<Button
className={styles.btn}
size="icon"
size="sm"
icon
variant="ghost"
color="secondary"
onClick={onRegenerate}
@@ -231,7 +235,7 @@ export default function MessageFeedback({
>
<RefreshCw size={12} />
</Button>
</TooltipSimple>
</Tooltip>
)}
</div>
@@ -249,13 +253,19 @@ export default function MessageFeedback({
footer={
<div className={styles.feedbackDialogFooter}>
<Button
size="md"
variant="solid"
color="secondary"
onClick={(): void => setIsNegativeDialogOpen(false)}
>
Cancel
</Button>
<Button variant="solid" color="primary" onClick={handleSubmitNegative}>
<Button
size="md"
variant="solid"
color="primary"
onClick={handleSubmitNegative}
>
Send feedback
</Button>
</div>

View File

@@ -1,7 +1,7 @@
import { useCallback, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import { Button } from '@signozhq/ui/button';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Tooltip } from '@signozhq/ui/tooltip';
import { Check, Copy } from '@signozhq/icons';
import logEvent from 'api/common/logEvent';
@@ -40,10 +40,11 @@ export default function UserMessageActions({
return (
<div className={styles.actions}>
<TooltipSimple title={copied ? 'Copied!' : 'Copy'}>
<Tooltip title={copied ? 'Copied!' : 'Copy'}>
<Button
className={styles.btn}
size="icon"
size="sm"
icon
variant="ghost"
color="secondary"
onClick={handleCopy}
@@ -51,7 +52,7 @@ export default function UserMessageActions({
>
{copied ? <Check size={12} /> : <Copy size={12} />}
</Button>
</TooltipSimple>
</Tooltip>
</div>
);
}

View File

@@ -189,11 +189,16 @@ export default function ActionBlock({
)}
<div className={styles.actions}>
<Button variant="solid" size="sm" onClick={execute}>
<Button color="primary" variant="solid" size="sm" onClick={execute}>
<Check size={12} />
Apply
</Button>
<Button variant="outlined" size="sm" onClick={handleDismiss}>
<Button
color="secondary"
variant="outlined"
size="sm"
onClick={handleDismiss}
>
<X size={12} />
Dismiss
</Button>

View File

@@ -83,11 +83,17 @@ export default function ConfirmBlock({
<div className={blockStyles.block}>
{message && <p className={styles.message}>{message}</p>}
<div className={styles.actions}>
<Button variant="solid" size="sm" onClick={(): void => handle('accepted')}>
<Button
color="primary"
variant="solid"
size="sm"
onClick={(): void => handle('accepted')}
>
<Check size={12} />
{acceptLabel}
</Button>
<Button
color="secondary"
variant="outlined"
size="sm"
onClick={(): void => handle('rejected')}

Some files were not shown because too many files have changed in this diff Show More