Compare commits

..

12 Commits

Author SHA1 Message Date
swapnil-signoz
2040fba075 Merge branch 'main' into refactor/aws-regions 2026-04-21 23:47:11 +05:30
Tushar Vats
e607908b29 fix: not_in expression in having input box (#11035)
Some checks are pending
build-staging / prepare (push) Waiting to run
build-staging / js-build (push) Blocked by required conditions
build-staging / go-build (push) Blocked by required conditions
build-staging / staging (push) Blocked by required conditions
Release Drafter / update_release_draft (push) Waiting to run
* fix: not_in expression in having input box

* fix: revert spacing check
2026-04-21 17:05:35 +00:00
swapnil-signoz
210ac2e74b feat: adding migration AWS cloud integration regions config (#10983)
* feat: adding migration AWS cloud integration regions config

* refactor: removing raw queries

* refactor: using table expr for table name

* refactor: using updated AWS regions declaration

* refactor: cleanup

* refactor: update AWS region migration logic to use new configuration method

* refactor: adding aws regions in migration

---------

Co-authored-by: Vikrant Gupta <vikrant@signoz.io>
2026-04-21 16:53:55 +00:00
swapnil-signoz
892bc3b2ba Merge branch 'main' into refactor/aws-regions 2026-04-21 20:31:12 +05:30
Ashwin Bhatkal
5971a9efbf feat: show related values section, always (#11032)
* feat: show related values section, always

* chore: resolve comments

* chore: add tests
2026-04-21 14:42:00 +00:00
swapnil-signoz
4245e209f4 feat: fe add new AWS regions for Mexico and Asia Pacific 2026-04-21 19:48:00 +05:30
swapnil-signoz
75653c3a97 feat: adding missing AWS regions 2026-04-21 19:48:00 +05:30
Yunus M
d43f3de049 refactor: use new components from signozhq/ui (#11008)
* refactor: use new components from signozhq/ui

* chore: remove migration doc

* refactor: update test imports and improve dialog handling

* chore: add assignee in todo items

* fix: failing test cases

* refactor: update button imports and adjust prefixIcon to prefix in AWS integration components

* refactor: use correct variables and latest components

* fix: add missing semicolon

* fix: add missing semicolon

* chore: use Button from @signozhq/ui

* refactor: update DrawerWrapper mock to include footer prop in tests

* refactor: remove richColor prop from toast

* fix: failing test cases

* chore: fix styling issues

* test: update EditMemberDrawer mock to include footer prop in tests

* chore: clean up and enhance component styles; update button variants and layout properties

* chore: add @signozhq/resizable package and update imports in TraceDetailV2 component

* refactor: move verifyFiltersAndOrderBy function to a separate file for better organization
2026-04-21 13:46:28 +00:00
Ashwin Bhatkal
7917540662 fix: alerts creation for query types other than builder (#11030)
* fix: alerts creation for query types other than builder

* chore: add tests
2026-04-21 11:33:32 +00:00
Vinicius Lourenço
addb234c8c test(infra-monitoring): fix flaky test (#11021) 2026-04-21 11:06:49 +00:00
Prakhar Dewan
be6a663e4b refactor: migrate CreateFunnel input to @signozhq/ui (#10996)
Refs SigNoz#10615
2026-04-21 11:04:32 +00:00
Abhi kumar
ed17003329 fix: y-axis crosshair sync for tooltip (#10944)
Some checks failed
build-staging / staging (push) Has been cancelled
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
* chore: added changes for crosshair sync for tooltip

* chore: minor cleanup

* chore: updated the core structure

* chore: updated the types

* chore: minor cleanup

* chore: minor changes
2026-04-21 08:36:04 +00:00
232 changed files with 1920 additions and 2464 deletions

View File

@@ -66,8 +66,6 @@ module.exports = {
rules: {
// Asset migration — base-path safety
'rulesdir/no-unsupported-asset-pattern': 'error',
// Base-path safety — window.open and origin-concat patterns; upgrade to error coming PR
'rulesdir/no-raw-absolute-path': 'warn',
// Code quality rules
'prefer-const': 'error', // Enforces const for variables never reassigned

View File

@@ -1,153 +0,0 @@
'use strict';
/**
* ESLint rule: no-raw-absolute-path
*
* Catches patterns that break at runtime when the app is served from a
* sub-path (e.g. /signoz/):
*
* 1. window.open(path, '_blank')
* → use openInNewTab(path) which calls withBasePath internally
*
* 2. window.location.origin + path / `${window.location.origin}${path}`
* → use getAbsoluteUrl(path)
*
* 3. frontendBaseUrl: window.location.origin (bare origin usage)
* → use getBaseUrl() to include the base path
*
* 4. window.location.href = path
* → use withBasePath(path) or navigate() for internal navigation
*
* External URLs (first arg starts with "http") are explicitly allowed.
*/
function isOriginAccess(node) {
return (
node.type === 'MemberExpression' &&
!node.computed &&
node.property.name === 'origin' &&
node.object.type === 'MemberExpression' &&
!node.object.computed &&
node.object.property.name === 'location' &&
node.object.object.type === 'Identifier' &&
node.object.object.name === 'window'
);
}
function isHrefAccess(node) {
return (
node.type === 'MemberExpression' &&
!node.computed &&
node.property.name === 'href' &&
node.object.type === 'MemberExpression' &&
!node.object.computed &&
node.object.property.name === 'location' &&
node.object.object.type === 'Identifier' &&
node.object.object.name === 'window'
);
}
function isExternalUrl(node) {
if (node.type === 'Literal' && typeof node.value === 'string') {
return node.value.startsWith('http://') || node.value.startsWith('https://');
}
if (node.type === 'TemplateLiteral' && node.quasis.length > 0) {
const raw = node.quasis[0].value.raw;
return raw.startsWith('http://') || raw.startsWith('https://');
}
return false;
}
// window.open(withBasePath(x)) and window.open(getAbsoluteUrl(x)) are already safe.
function isSafeHelperCall(node) {
return (
node.type === 'CallExpression' &&
node.callee.type === 'Identifier' &&
(node.callee.name === 'withBasePath' || node.callee.name === 'getAbsoluteUrl')
);
}
module.exports = {
meta: {
type: 'suggestion',
docs: {
description:
'Disallow raw window.open and origin-concatenation patterns that miss the runtime base path',
category: 'Base Path Safety',
},
schema: [],
messages: {
windowOpen:
'Use openInNewTab(path) instead of window.open(path, "_blank") — openInNewTab prepends the base path automatically.',
originConcat:
'Use getAbsoluteUrl(path) instead of window.location.origin + path — getAbsoluteUrl prepends the base path automatically.',
originDirect:
'Use getBaseUrl() instead of window.location.origin — getBaseUrl includes the base path.',
hrefAssign:
'Use withBasePath(path) or navigate() instead of window.location.href = path — ensures the base path is included.',
},
},
create(context) {
return {
// window.open(path, ...) — allow only external first-arg URLs
CallExpression(node) {
const { callee, arguments: args } = node;
if (
callee.type !== 'MemberExpression' ||
callee.object.type !== 'Identifier' ||
callee.object.name !== 'window' ||
callee.property.name !== 'open'
)
return;
if (args.length < 1) return;
if (isExternalUrl(args[0])) return;
if (isSafeHelperCall(args[0])) return;
context.report({ node, messageId: 'windowOpen' });
},
// window.location.origin + path
BinaryExpression(node) {
if (node.operator !== '+') return;
if (isOriginAccess(node.left) || isOriginAccess(node.right)) {
context.report({ node, messageId: 'originConcat' });
}
},
// `${window.location.origin}${path}`
TemplateLiteral(node) {
if (node.expressions.some(isOriginAccess)) {
context.report({ node, messageId: 'originConcat' });
}
},
// window.location.origin used directly (not in concatenation)
// Catches: frontendBaseUrl: window.location.origin
MemberExpression(node) {
if (!isOriginAccess(node)) return;
const parent = node.parent;
// Skip if parent is BinaryExpression with + (handled by BinaryExpression visitor)
if (parent.type === 'BinaryExpression' && parent.operator === '+') return;
// Skip if inside TemplateLiteral (handled by TemplateLiteral visitor)
if (parent.type === 'TemplateLiteral') return;
context.report({ node, messageId: 'originDirect' });
},
// window.location.href = path
AssignmentExpression(node) {
if (node.operator !== '=') return;
if (!isHrefAccess(node.left)) return;
// Allow external URLs
if (isExternalUrl(node.right)) return;
// Allow safe helper calls
if (isSafeHelperCall(node.right)) return;
context.report({ node, messageId: 'hrefAssign' });
},
};
},
};

View File

@@ -2,7 +2,6 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<base href="[[.BaseHref]]" />
<meta
http-equiv="Cache-Control"
content="no-cache, no-store, must-revalidate, max-age: 0"
@@ -60,7 +59,7 @@
<meta data-react-helmet="true" name="docusaurus_locale" content="en" />
<meta data-react-helmet="true" name="docusaurus_tag" content="default" />
<meta name="robots" content="noindex" />
<link data-react-helmet="true" rel="shortcut icon" href="favicon.ico" />
<link data-react-helmet="true" rel="shortcut icon" href="/favicon.ico" />
</head>
<body data-theme="default">
<script>
@@ -137,7 +136,7 @@
})(document, 'script');
}
</script>
<link rel="stylesheet" href="css/uPlot.min.css" />
<link rel="stylesheet" href="/css/uPlot.min.css" />
<script type="module" src="./src/index.tsx"></script>
</body>
</html>

View File

@@ -45,7 +45,8 @@ const config: Config.InitialOptions = {
'^.+\\.(js|jsx)$': 'babel-jest',
},
transformIgnorePatterns: [
'node_modules/(?!(lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@signozhq/design-tokens|@signozhq/table|@signozhq/calendar|@signozhq/input|@signozhq/popover|@signozhq/button|@signozhq/*|date-fns|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn|@grafana|nuqs)/)',
// @chenglou/pretext is ESM-only; @signozhq/ui pulls it in via text-ellipsis.
'node_modules/(?!(lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@chenglou/pretext|@signozhq/design-tokens|@signozhq/table|@signozhq/calendar|@signozhq/input|@signozhq/popover|@signozhq/*|date-fns|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn|@grafana|nuqs)/)',
],
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
testPathIgnorePatterns: ['/node_modules/', '/public/'],

View File

@@ -24,6 +24,10 @@ window.matchMedia =
};
};
if (!HTMLElement.prototype.scrollIntoView) {
HTMLElement.prototype.scrollIntoView = function (): void {};
}
// Patch getComputedStyle to handle CSS parsing errors from @signozhq/* packages.
// These packages inject CSS at import time via style-inject / vite-plugin-css-injected-by-js.
// jsdom's nwsapi cannot parse some of the injected selectors (e.g. Tailwind's :animate-in),

View File

@@ -48,24 +48,10 @@
"@radix-ui/react-tooltip": "1.0.7",
"@sentry/react": "8.41.0",
"@sentry/vite-plugin": "2.22.6",
"@signozhq/button": "0.0.5",
"@signozhq/calendar": "0.1.1",
"@signozhq/callout": "0.0.4",
"@signozhq/checkbox": "0.0.4",
"@signozhq/combobox": "0.0.4",
"@signozhq/command": "0.0.2",
"@signozhq/design-tokens": "2.1.4",
"@signozhq/dialog": "0.0.4",
"@signozhq/drawer": "0.0.6",
"@signozhq/icons": "0.1.0",
"@signozhq/input": "0.0.4",
"@signozhq/popover": "0.1.2",
"@signozhq/radio-group": "0.0.4",
"@signozhq/resizable": "0.0.2",
"@signozhq/tabs": "0.0.11",
"@signozhq/table": "0.3.8",
"@signozhq/toggle-group": "0.0.3",
"@signozhq/ui": "0.0.5",
"@signozhq/ui": "0.0.9",
"@tanstack/react-table": "8.21.3",
"@tanstack/react-virtual": "3.13.22",
"@uiw/codemirror-theme-copilot": "4.23.11",

View File

@@ -2,7 +2,6 @@ import { initReactI18next } from 'react-i18next';
import i18n from 'i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
import Backend from 'i18next-http-backend';
import { getBasePath } from 'utils/basePath';
import cacheBursting from '../../i18n-translations-hash.json';
@@ -25,7 +24,7 @@ i18n
const ns = namespace[0];
const pathkey = `/${language}/${ns}`;
const hash = cacheBursting[pathkey as keyof typeof cacheBursting] || '';
return `${getBasePath()}locales/${language}/${namespace}.json?h=${hash}`;
return `/locales/${language}/${namespace}.json?h=${hash}`;
},
},
react: {

View File

@@ -1,6 +1,5 @@
import {
interceptorRejected,
interceptorsRequestBasePath,
interceptorsRequestResponse,
interceptorsResponse,
} from 'api';
@@ -18,7 +17,6 @@ export const GeneratedAPIInstance = <T>(
return generatedAPIAxiosInstance({ ...config }).then(({ data }) => data);
};
generatedAPIAxiosInstance.interceptors.request.use(interceptorsRequestBasePath);
generatedAPIAxiosInstance.interceptors.request.use(interceptorsRequestResponse);
generatedAPIAxiosInstance.interceptors.response.use(
interceptorsResponse,

View File

@@ -11,7 +11,6 @@ import axios, {
import { ENVIRONMENT } from 'constants/env';
import { Events } from 'constants/events';
import { LOCALSTORAGE } from 'constants/localStorage';
import { getBasePath } from 'utils/basePath';
import { eventEmitter } from 'utils/getEventEmitter';
import apiV1, { apiAlertManager, apiV2, apiV3, apiV4, apiV5 } from './apiV1';
@@ -68,39 +67,6 @@ export const interceptorsRequestResponse = (
return value;
};
// Strips the leading '/' from path and joins with base — idempotent if already prefixed.
// e.g. prependBase('/signoz/', '/api/v1/') → '/signoz/api/v1/'
function prependBase(base: string, path: string): string {
return path.startsWith(base) ? path : base + path.slice(1);
}
// Prepends the runtime base path to outgoing requests so API calls work under
// a URL prefix (e.g. /signoz/api/v1/…). No-op for root deployments and dev
// (dev baseURL is a full http:// URL, not an absolute path).
export const interceptorsRequestBasePath = (
value: InternalAxiosRequestConfig,
): InternalAxiosRequestConfig => {
const basePath = getBasePath();
if (basePath === '/') {
return value;
}
if (value.baseURL?.startsWith('/')) {
// Production relative baseURL: '/api/v1/' → '/signoz/api/v1/'
value.baseURL = prependBase(basePath, value.baseURL);
} else if (value.baseURL?.startsWith('http')) {
// Dev absolute baseURL (VITE_FRONTEND_API_ENDPOINT): 'https://host/api/v1/' → 'https://host/signoz/api/v1/'
const url = new URL(value.baseURL);
url.pathname = prependBase(basePath, url.pathname);
value.baseURL = url.toString();
} else if (!value.baseURL && value.url?.startsWith('/')) {
// Orval-generated client (empty baseURL, path in url): '/api/signoz/v1/rules' → '/signoz/api/signoz/v1/rules'
value.url = prependBase(basePath, value.url);
}
return value;
};
export const interceptorRejected = async (
value: AxiosResponse<any>,
): Promise<AxiosResponse<any>> => {
@@ -167,7 +133,6 @@ const instance = axios.create({
});
instance.interceptors.request.use(interceptorsRequestResponse);
instance.interceptors.request.use(interceptorsRequestBasePath);
instance.interceptors.response.use(interceptorsResponse, interceptorRejected);
export const AxiosAlertManagerInstance = axios.create({
@@ -182,7 +147,6 @@ ApiV2Instance.interceptors.response.use(
interceptorRejected,
);
ApiV2Instance.interceptors.request.use(interceptorsRequestResponse);
ApiV2Instance.interceptors.request.use(interceptorsRequestBasePath);
// axios V3
export const ApiV3Instance = axios.create({
@@ -194,7 +158,6 @@ ApiV3Instance.interceptors.response.use(
interceptorRejected,
);
ApiV3Instance.interceptors.request.use(interceptorsRequestResponse);
ApiV3Instance.interceptors.request.use(interceptorsRequestBasePath);
//
// axios V4
@@ -207,7 +170,6 @@ ApiV4Instance.interceptors.response.use(
interceptorRejected,
);
ApiV4Instance.interceptors.request.use(interceptorsRequestResponse);
ApiV4Instance.interceptors.request.use(interceptorsRequestBasePath);
//
// axios V5
@@ -220,7 +182,6 @@ ApiV5Instance.interceptors.response.use(
interceptorRejected,
);
ApiV5Instance.interceptors.request.use(interceptorsRequestResponse);
ApiV5Instance.interceptors.request.use(interceptorsRequestBasePath);
//
// axios Base
@@ -233,7 +194,6 @@ LogEventAxiosInstance.interceptors.response.use(
interceptorRejectedBase,
);
LogEventAxiosInstance.interceptors.request.use(interceptorsRequestResponse);
LogEventAxiosInstance.interceptors.request.use(interceptorsRequestBasePath);
//
AxiosAlertManagerInstance.interceptors.response.use(
@@ -241,7 +201,6 @@ AxiosAlertManagerInstance.interceptors.response.use(
interceptorRejected,
);
AxiosAlertManagerInstance.interceptors.request.use(interceptorsRequestResponse);
AxiosAlertManagerInstance.interceptors.request.use(interceptorsRequestBasePath);
export { apiV1 };
export default instance;

View File

@@ -10,21 +10,7 @@
// PR for reference: https://github.com/SigNoz/signoz/pull/9694
// -------------------------------------------------------------------------
import '@signozhq/button';
import '@signozhq/calendar';
import '@signozhq/callout';
import '@signozhq/checkbox';
import '@signozhq/combobox';
import '@signozhq/command';
import '@signozhq/design-tokens';
import '@signozhq/dialog';
import '@signozhq/drawer';
import '@signozhq/icons';
import '@signozhq/input';
import '@signozhq/popover';
import '@signozhq/radio-group';
import '@signozhq/resizable';
import '@signozhq/tabs';
import '@signozhq/table';
import '@signozhq/toggle-group';
import '@signozhq/ui';

View File

@@ -80,12 +80,12 @@
mask-image: radial-gradient(
circle at 50% 0,
color-mix(in srgb, var(--background) 10%, transparent) 0,
color-mix(in srgb, var(--l1-background) 10%, transparent) 0,
transparent 100%
);
-webkit-mask-image: radial-gradient(
circle at 50% 0,
color-mix(in srgb, var(--background) 10%, transparent) 0,
color-mix(in srgb, var(--l1-background) 10%, transparent) 0,
transparent 100%
);
}

View File

@@ -4,13 +4,14 @@
animation: horizontal-shaking 300ms ease-out;
.error-content {
background: color-mix(in srgb, var(--bg-cherry-500) 10%, transparent);
border: 1px solid color-mix(in srgb, var(--bg-cherry-500) 20%, transparent);
background: color-mix(in srgb, var(--danger-background) 10%, transparent);
border: 1px solid
color-mix(in srgb, var(--danger-background) 20%, transparent);
border-radius: 4px;
&__summary-section {
border-bottom: 1px solid
color-mix(in srgb, var(--bg-cherry-500) 20%, transparent);
color-mix(in srgb, var(--danger-background) 20%, transparent);
}
&__summary {
@@ -58,7 +59,7 @@
&__message-badge-line {
background-image: radial-gradient(
circle,
color-mix(in srgb, var(--bg-cherry-500) 30%, transparent) 1px,
color-mix(in srgb, var(--danger-background) 30%, transparent) 1px,
transparent 2px
);
}
@@ -84,7 +85,7 @@
}
&__scroll-hint {
background: color-mix(in srgb, var(--bg-cherry-500) 20%, transparent);
background: color-mix(in srgb, var(--danger-background) 20%, transparent);
}
&__scroll-hint-text {

View File

@@ -1,5 +1,5 @@
import { useCallback } from 'react';
import { Button } from '@signozhq/button';
import { Button } from '@signozhq/ui';
import { LifeBuoy } from 'lucide-react';
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
@@ -23,8 +23,10 @@ function AuthHeader(): JSX.Element {
</div>
<Button
className="auth-header-help-button"
prefixIcon={<LifeBuoy size={12} />}
prefix={<LifeBuoy size={12} />}
onClick={handleGetHelp}
variant="solid"
color="none"
>
Get Help
</Button>

View File

@@ -43,12 +43,12 @@
.masked-dots {
mask-image: radial-gradient(
circle at 50% 0%,
color-mix(in srgb, var(--background) 10%, transparent) 0%,
color-mix(in srgb, var(--l1-background) 10%, transparent) 0%,
transparent 56.77%
);
-webkit-mask-image: radial-gradient(
circle at 50% 0%,
color-mix(in srgb, var(--background) 10%, transparent) 0%,
color-mix(in srgb, var(--l1-background) 10%, transparent) 0%,
transparent 56.77%
);
}

View File

@@ -12,7 +12,6 @@ import { AppState } from 'store/reducers';
import { Query, TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource, MetricAggregateOperator } from 'types/common/queryBuilder';
import { GlobalReducer } from 'types/reducer/globalTime';
import { withBasePath } from 'utils/basePath';
export interface NavigateToExplorerProps {
filters: TagFilterItem[];
@@ -134,7 +133,7 @@ export function useNavigateToExplorer(): (
QueryParams.compositeQuery
}=${JSONCompositeQuery}`;
window.open(withBasePath(newExplorerPath), sameTab ? '_self' : '_blank');
window.open(newExplorerPath, sameTab ? '_self' : '_blank');
},
[
prepareQuery,

View File

@@ -26,14 +26,14 @@
}
}
&--negative {
background: color-mix(in srgb, var(--bg-cherry-500) 10%, transparent);
background: color-mix(in srgb, var(--danger-background) 10%, transparent);
.change-percentage-pill {
&__icon {
color: var(--bg-cherry-500);
color: var(--danger-background);
}
&__label {
color: var(--bg-cherry-500);
color: var(--danger-background);
}
}
}

View File

@@ -1,10 +1,13 @@
import { Controller, useForm } from 'react-hook-form';
import { useQueryClient } from 'react-query';
import { Button } from '@signozhq/button';
import { DialogFooter, DialogWrapper } from '@signozhq/dialog';
import { X } from '@signozhq/icons';
import { Input } from '@signozhq/input';
import { toast } from '@signozhq/ui';
import {
Button,
DialogFooter,
DialogWrapper,
Input,
toast,
} from '@signozhq/ui';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import {
invalidateListServiceAccounts,
@@ -50,9 +53,7 @@ function CreateServiceAccountModal(): JSX.Element {
} = useCreateServiceAccount({
mutation: {
onSuccess: async () => {
toast.success('Service account created successfully', {
richColors: true,
});
toast.success('Service account created successfully');
reset();
await setIsOpen(null);
await invalidateListServiceAccounts(queryClient);
@@ -128,7 +129,6 @@ function CreateServiceAccountModal(): JSX.Element {
type="button"
variant="solid"
color="secondary"
size="sm"
onClick={handleClose}
>
<X size={12} />
@@ -137,10 +137,10 @@ function CreateServiceAccountModal(): JSX.Element {
<Button
type="submit"
// @ts-expect-error -- form prop not in @signozhq/ui Button type - TODO: Fix this - @SagarRajput
form="create-sa-form"
variant="solid"
color="primary"
size="sm"
loading={isSubmitting}
disabled={!isValid}
>

View File

@@ -1,7 +1,13 @@
import { toast } from '@signozhq/ui';
import { rest, server } from 'mocks-server/server';
import { NuqsTestingAdapter } from 'nuqs/adapters/testing';
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
import {
render,
screen,
userEvent,
waitFor,
waitForElementToBeRemoved,
} from 'tests/test-utils';
import CreateServiceAccountModal from '../CreateServiceAccountModal';
@@ -69,7 +75,6 @@ describe('CreateServiceAccountModal', () => {
await waitFor(() => {
expect(mockToast.success).toHaveBeenCalledWith(
'Service account created successfully',
expect.anything(),
);
});
@@ -121,12 +126,12 @@ describe('CreateServiceAccountModal', () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
renderModal();
await screen.findByRole('dialog', { name: /New Service Account/i });
const dialog = await screen.findByRole('dialog', {
name: /New Service Account/i,
});
await user.click(screen.getByRole('button', { name: /Cancel/i }));
expect(
screen.queryByRole('dialog', { name: /New Service Account/i }),
).not.toBeInTheDocument();
await waitForElementToBeRemoved(dialog);
});
it('shows "Name is required" after clearing the name field', async () => {

View File

@@ -1,4 +1,4 @@
import { Calendar } from '@signozhq/calendar';
import { Calendar } from '@signozhq/ui';
import { Button } from 'antd';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import dayjs from 'dayjs';

View File

@@ -7,7 +7,7 @@ import {
useState,
} from 'react';
import { useLocation } from 'react-router-dom';
import { Button } from '@signozhq/button';
import { Button } from '@signozhq/ui';
import { Input, InputRef, Popover, Tooltip } from 'antd';
import cx from 'classnames';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
@@ -661,7 +661,9 @@ function CustomTimePicker({
onClick={handleZoomOut}
disabled={zoomOutDisabled}
data-testid="zoom-out-btn"
prefixIcon={<ZoomOut size={14} />}
prefix={<ZoomOut size={14} />}
variant="solid"
color="none"
/>
</Tooltip>
)}

View File

@@ -1,6 +1,5 @@
import { Button } from '@signozhq/button';
import { DialogFooter, DialogWrapper } from '@signozhq/dialog';
import { Trash2, X } from '@signozhq/icons';
import { Button, DialogWrapper } from '@signozhq/ui';
import { MemberRow } from 'components/MembersTable/MembersTable';
interface DeleteMemberDialogProps {
@@ -36,6 +35,24 @@ function DeleteMemberDialog({
</>
);
const footer = (
<>
<Button variant="solid" color="secondary" onClick={onClose}>
<X size={12} />
Cancel
</Button>
<Button
variant="solid"
color="destructive"
disabled={isDeleting}
onClick={onConfirm}
>
<Trash2 size={12} />
{isDeleting ? 'Processing...' : title}
</Button>
</>
);
return (
<DialogWrapper
open={open}
@@ -49,25 +66,9 @@ function DeleteMemberDialog({
className="alert-dialog delete-dialog"
showCloseButton={false}
disableOutsideClick={false}
footer={footer}
>
<p className="delete-dialog__body">{body}</p>
<DialogFooter className="delete-dialog__footer">
<Button variant="solid" color="secondary" size="sm" onClick={onClose}>
<X size={12} />
Cancel
</Button>
<Button
variant="solid"
color="destructive"
size="sm"
disabled={isDeleting}
onClick={onConfirm}
>
<Trash2 size={12} />
{isDeleting ? 'Processing...' : title}
</Button>
</DialogFooter>
{body}
</DialogWrapper>
);
}

View File

@@ -2,7 +2,7 @@
&__layout {
display: flex;
flex-direction: column;
height: calc(100vh - 48px);
height: 100%;
}
&__body {
@@ -11,7 +11,6 @@
display: flex;
flex-direction: column;
gap: var(--spacing-8);
padding: var(--padding-5) var(--padding-4);
}
&__field {
@@ -50,6 +49,7 @@
border-radius: 2px;
background: var(--l2-background);
border: 1px solid var(--l1-border);
box-sizing: border-box;
&--disabled {
cursor: not-allowed;
@@ -120,17 +120,11 @@
align-items: center;
justify-content: space-between;
width: 100%;
height: 56px;
padding: 0 var(--padding-4);
border-top: 1px solid var(--l1-border);
flex-shrink: 0;
background: var(--card);
}
&__footer-left {
display: flex;
align-items: center;
gap: var(--spacing-8);
}
&__footer-right {
@@ -223,10 +217,6 @@
color: var(--l1-foreground);
}
[data-slot='dialog-description'] {
width: 510px;
}
&__content {
display: flex;
flex-direction: column;

View File

@@ -1,10 +1,7 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import { Button } from '@signozhq/button';
import { DrawerWrapper } from '@signozhq/drawer';
import { LockKeyhole, RefreshCw, Trash2, X } from '@signozhq/icons';
import { Input } from '@signozhq/input';
import { Badge, toast } from '@signozhq/ui';
import { Badge, Button, DrawerWrapper, Input, toast } from '@signozhq/ui';
import { Skeleton, Tooltip } from 'antd';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import type { RenderErrorResponseDTO } from 'api/generated/services/sigNoz.schemas';
@@ -207,7 +204,7 @@ function EditMemberDrawer({
onSuccess: (): void => {
toast.success(
isInvited ? 'Invite revoked successfully' : 'Member deleted successfully',
{ richColors: true, position: 'top-right' },
{ position: 'top-right' },
);
setShowDeleteConfirm(false);
onComplete();
@@ -342,10 +339,7 @@ function EditMemberDrawer({
if (errors.length > 0) {
setSaveErrors(errors);
} else {
toast.success('Member details updated successfully', {
richColors: true,
position: 'top-right',
});
toast.success('Member details updated successfully');
onComplete();
}
@@ -403,7 +397,6 @@ function EditMemberDrawer({
onClose();
} else {
toast.error('Failed to generate password reset link', {
richColors: true,
position: 'top-right',
});
}
@@ -427,15 +420,12 @@ function EditMemberDrawer({
linkType === 'invite'
? 'Invite link copied to clipboard'
: 'Reset link copied to clipboard';
toast.success(message, { richColors: true, position: 'top-right' });
toast.success(message);
}, [resetLink, copyToClipboard, linkType]);
useEffect(() => {
if (copyState.error) {
toast.error('Failed to copy link', {
richColors: true,
position: 'top-right',
});
toast.error('Failed to copy link');
}
}, [copyState.error]);
@@ -596,16 +586,21 @@ function EditMemberDrawer({
const drawerContent = (
<div className="edit-member-drawer__layout">
<div className="edit-member-drawer__body">{drawerBody}</div>
</div>
);
const footer = (
<div className="edit-member-drawer__footer">
{!isDeleted && (
<div className="edit-member-drawer__footer">
<>
<div className="edit-member-drawer__footer-left">
<Tooltip title={getDeleteTooltip(isRootUser, isSelf)}>
<span className="edit-member-drawer__tooltip-wrapper">
<Button
className="edit-member-drawer__footer-btn edit-member-drawer__footer-btn--danger"
onClick={(): void => setShowDeleteConfirm(true)}
disabled={isRootUser || isSelf}
variant="link"
color="destructive"
>
<Trash2 size={12} />
{isInvited ? 'Revoke Invite' : 'Delete Member'}
@@ -617,9 +612,10 @@ function EditMemberDrawer({
<Tooltip title={isRootUser ? ROOT_USER_TOOLTIP : undefined}>
<span className="edit-member-drawer__tooltip-wrapper">
<Button
className="edit-member-drawer__footer-btn edit-member-drawer__footer-btn--warning"
onClick={handleGenerateResetLink}
disabled={isGeneratingLink || isRootUser || isLoadingTokenStatus}
variant="link"
color="warning"
>
<RefreshCw size={12} />
{isGeneratingLink
@@ -638,7 +634,7 @@ function EditMemberDrawer({
</div>
<div className="edit-member-drawer__footer-right">
<Button variant="solid" color="secondary" size="sm" onClick={handleClose}>
<Button variant="solid" color="secondary" onClick={handleClose}>
<X size={14} />
Cancel
</Button>
@@ -646,14 +642,13 @@ function EditMemberDrawer({
<Button
variant="solid"
color="primary"
size="sm"
disabled={!isDirty || isSaving || isRootUser}
onClick={handleSave}
>
{isSaving ? 'Saving...' : 'Save Member Details'}
</Button>
</div>
</div>
</>
)}
</div>
);
@@ -668,14 +663,14 @@ function EditMemberDrawer({
}
}}
direction="right"
type="panel"
showCloseButton
showOverlay={false}
allowOutsideClick
header={{ title: 'Member Details' }}
content={drawerContent}
className="edit-member-drawer"
/>
title="Member Details"
footer={footer}
width="wide"
>
{drawerContent}
</DrawerWrapper>
<ResetLinkDialog
open={showResetLinkDialog}

View File

@@ -1,6 +1,5 @@
import { Button } from '@signozhq/button';
import { DialogWrapper } from '@signozhq/dialog';
import { Check, Copy } from '@signozhq/icons';
import { Button, DialogWrapper } from '@signozhq/ui';
interface ResetLinkDialogProps {
open: boolean;
@@ -49,7 +48,7 @@ function ResetLinkDialog({
color="secondary"
size="sm"
onClick={onCopy}
prefixIcon={hasCopied ? <Check size={12} /> : <Copy size={12} />}
prefix={hasCopied ? <Check size={12} /> : <Copy size={12} />}
className="reset-link-dialog__copy-btn"
>
{hasCopied ? 'Copied!' : 'Copy'}

View File

@@ -20,36 +20,6 @@ import { render, screen, userEvent, waitFor } from 'tests/test-utils';
import EditMemberDrawer, { EditMemberDrawerProps } from '../EditMemberDrawer';
jest.mock('@signozhq/drawer', () => ({
DrawerWrapper: ({
content,
open,
}: {
content?: ReactNode;
open: boolean;
}): JSX.Element | null => (open ? <div>{content}</div> : null),
}));
jest.mock('@signozhq/dialog', () => ({
DialogWrapper: ({
children,
open,
title,
}: {
children?: ReactNode;
open: boolean;
title?: string;
}): JSX.Element | null =>
open ? (
<div role="dialog" aria-label={title}>
{children}
</div>
) : null,
DialogFooter: ({ children }: { children?: ReactNode }): JSX.Element => (
<div>{children}</div>
),
}));
jest.mock('api/generated/services/users', () => ({
useDeleteUser: jest.fn(),
useGetUser: jest.fn(),
@@ -66,6 +36,41 @@ jest.mock('api/ErrorResponseHandlerForGeneratedAPIs', () => ({
jest.mock('@signozhq/ui', () => ({
...jest.requireActual('@signozhq/ui'),
DrawerWrapper: ({
children,
footer,
open,
}: {
children?: ReactNode;
footer?: ReactNode;
open: boolean;
}): JSX.Element | null =>
open ? (
<div>
{children}
{footer}
</div>
) : null,
DialogWrapper: ({
children,
footer,
open,
title,
}: {
children?: ReactNode;
footer?: ReactNode;
open: boolean;
title?: string;
}): JSX.Element | null =>
open ? (
<div role="dialog" aria-label={title}>
{children}
{footer}
</div>
) : null,
DialogFooter: ({ children }: { children?: ReactNode }): JSX.Element => (
<div>{children}</div>
),
toast: {
success: jest.fn(),
error: jest.fn(),
@@ -160,6 +165,8 @@ function renderDrawer(
describe('EditMemberDrawer', () => {
beforeEach(() => {
jest.clearAllMocks();
mockCopyState.value = undefined;
mockCopyState.error = undefined;
showErrorModal.mockClear();
server.use(
rest.get(ROLES_ENDPOINT, (_, res, ctx) =>
@@ -726,16 +733,16 @@ describe('EditMemberDrawer', () => {
await user.click(screen.getByRole('button', { name: /^copy$/i }));
await waitFor(() => {
expect(mockCopyToClipboard).toHaveBeenCalledWith(
expect.stringContaining('reset-tok-abc'),
);
expect(
screen.getByRole('button', { name: /copied!/i }),
).toBeInTheDocument();
expect(mockToast.success).toHaveBeenCalledWith(
'Reset link copied to clipboard',
expect.anything(),
);
});
expect(mockCopyToClipboard).toHaveBeenCalledWith(
expect.stringContaining('reset-tok-abc'),
);
expect(screen.getByRole('button', { name: /copied!/i })).toBeInTheDocument();
});
});
});

View File

@@ -5,12 +5,12 @@
align-items: center;
gap: 4px;
border-radius: 20px;
background: color-mix(in srgb, var(--bg-cherry-500) 20%, transparent);
background: color-mix(in srgb, var(--danger-background) 20%, transparent);
padding-left: 3px;
padding-right: 8px;
cursor: pointer;
span {
color: var(--bg-cherry-500);
color: var(--danger-background);
font-size: 10px;
font-weight: 500;
line-height: 20px; /* 200% */
@@ -21,7 +21,7 @@
&__wrap {
background: linear-gradient(
180deg,
color-mix(in srgb, var(--background) 12%, transparent) 0.07%,
color-mix(in srgb, var(--l1-background) 12%, transparent) 0.07%,
color-mix(in srgb, var(--bg-sakura-950) 24%, transparent) 50.04%,
color-mix(in srgb, var(--bg-sakura-800) 36%, transparent) 75.02%,
color-mix(in srgb, var(--bg-sakura-600) 48%, transparent) 87.51%,
@@ -40,15 +40,17 @@
margin: auto;
}
}
&__body {
padding: 0;
background: var(--l2-background);
overflow: hidden;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
&__header {
background: none !important;
.ant-modal-title {
display: flex;
justify-content: space-between;
@@ -80,6 +82,7 @@
pointer-events: none;
}
}
.close-button {
padding: 3px 7px;
background: var(--l2-background);
@@ -90,15 +93,15 @@
box-shadow: none;
}
}
&__footer {
margin: 0 !important;
height: 6px;
background: var(--bg-sakura-500);
}
&__content {
padding: 0 !important;
border-radius: 4px;
overflow: hidden;
background: none !important;
}
}

View File

@@ -5,7 +5,6 @@
&__summary-section {
display: flex;
flex-direction: column;
border-bottom: 1px solid var(--l1-border);
}
&__summary {

View File

@@ -5,7 +5,6 @@
border-radius: 2px 0px 0px 2px;
.label {
color: var(--l2-foreground);
font-size: 12px;
font-style: normal;
font-weight: 500;
@@ -21,8 +20,9 @@
padding: 0px 8px;
border-radius: 2px 0px 0px 2px;
border: 1px solid var(--l1-border);
background: var(--l3-background);
border: 1px solid var(--l2-border);
background: var(--l2-background);
color: var(--l2-foreground);
display: flex;
justify-content: flex-start;
@@ -37,6 +37,7 @@
border-radius: 2px 0px 0px 2px;
border: 1px solid var(--l1-border);
background: var(--l2-background);
border-right: none;
border-left: none;
@@ -46,6 +47,7 @@
border-bottom-left-radius: 0px;
font-size: 12px !important;
line-height: 27px;
&::placeholder {
color: var(--l2-foreground) !important;
font-size: 12px !important;
@@ -61,8 +63,8 @@
.close-btn {
border-radius: 0px 2px 2px 0px;
border: 1px solid var(--l1-border);
background: var(--l3-background);
border: 1px solid var(--l2-border);
background: var(--l2-background);
height: 38px;
width: 38px;
}
@@ -71,7 +73,7 @@
.input {
border-radius: 0px 2px 2px 0px;
border: 1px solid var(--l1-border);
background: var(--l3-background);
background: var(--l2-background);
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
}

View File

@@ -181,7 +181,7 @@
box-shadow: none;
&:hover {
background: color-mix(in srgb, var(--bg-cherry-500) 10%, transparent);
background: color-mix(in srgb, var(--danger-background) 10%, transparent);
opacity: 0.9;
}
}
@@ -196,12 +196,16 @@
}
.invite-team-members-error-callout {
background: color-mix(in srgb, var(--bg-cherry-500) 10%, transparent);
border: 1px solid color-mix(in srgb, var(--bg-cherry-500) 20%, transparent);
background: color-mix(in srgb, var(--danger-background) 10%, transparent);
border: 1px solid color-mix(in srgb, var(--danger-background) 20%, transparent);
border-radius: 4px;
animation: horizontal-shaking 300ms ease-out;
}
.invite-members-modal__error-callout {
display: flex;
}
@keyframes horizontal-shaking {
0% {
transform: translateX(0);

View File

@@ -1,11 +1,14 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { Button } from '@signozhq/button';
import { Callout } from '@signozhq/callout';
import { Style } from '@signozhq/design-tokens';
import { DialogFooter, DialogWrapper } from '@signozhq/dialog';
import { ChevronDown, CircleAlert, Plus, Trash2, X } from '@signozhq/icons';
import { Input } from '@signozhq/input';
import { toast } from '@signozhq/ui';
import {
Button,
Callout,
DialogFooter,
DialogWrapper,
Input,
toast,
} from '@signozhq/ui';
import { Select } from 'antd';
import inviteUsers from 'api/v1/invite/bulk/create';
import sendInvite from 'api/v1/invite/create';
@@ -200,10 +203,7 @@ function InviteMembersModal({
})),
});
}
toast.success('Invites sent successfully', {
richColors: true,
position: 'top-right',
});
toast.success('Invites sent successfully', { position: 'top-right' });
resetAndClose();
onComplete?.();
} catch (err) {
@@ -274,7 +274,6 @@ function InviteMembersModal({
<Button
variant="ghost"
color="destructive"
className="remove-team-member-button"
onClick={(): void => removeRow(row.id)}
aria-label="Remove row"
>
@@ -289,14 +288,16 @@ function InviteMembersModal({
</div>
{(hasInvalidEmails || hasInvalidRoles) && (
<Callout
type="error"
size="small"
showIcon
icon={<CircleAlert size={12} />}
className="invite-team-members-error-callout"
description={getValidationErrorMessage()}
/>
<div className="invite-members-modal__error-callout">
<Callout
type="error"
size="small"
showIcon
icon={<CircleAlert size={12} />}
>
{getValidationErrorMessage()}
</Callout>
</div>
)}
</div>
@@ -304,9 +305,8 @@ function InviteMembersModal({
<Button
variant="dashed"
color="secondary"
size="sm"
className="add-another-member-button"
prefixIcon={<Plus size={12} color={Style.L1_FOREGROUND} />}
prefix={<Plus size={12} color={Style.L1_FOREGROUND} />}
onClick={addRow}
>
Add another
@@ -317,7 +317,6 @@ function InviteMembersModal({
type="button"
variant="solid"
color="secondary"
size="sm"
onClick={resetAndClose}
>
<X size={12} />
@@ -327,7 +326,6 @@ function InviteMembersModal({
<Button
variant="solid"
color="primary"
size="sm"
onClick={handleSubmit}
disabled={isSubmitDisabled}
loading={isSubmitting}

View File

@@ -110,7 +110,7 @@
}
&.ERROR {
border-color: var(--bg-cherry-500);
border-color: var(--danger-background);
}
}

View File

@@ -94,7 +94,7 @@
background-color: var(--bg-cherry-600);
}
&.severity-error-1 {
background-color: var(--bg-cherry-500);
background-color: var(--danger-background);
}
&.severity-error-2 {
background-color: var(--bg-cherry-400);

View File

@@ -21,7 +21,7 @@
.ant-table-thead {
> tr > th,
> tr > td {
background: var(--background);
background: var(--l1-background);
font-size: var(--paragraph-small-600-font-size);
font-weight: var(--paragraph-small-600-font-weight);
line-height: var(--paragraph-small-600-line-height);

View File

@@ -1445,11 +1445,22 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
// Custom dropdown render with sections support
const customDropdownRender = useCallback((): React.ReactElement => {
// Process options based on current search
const processedOptions =
selectedValues.length > 0 && isEmpty(searchText)
? prioritizeOrAddOptionForMultiSelect(filteredOptions, selectedValues)
: filteredOptions;
// When ALL is selected and the options contain sections (groups),
// skip prioritization so section headers (e.g. "Related values" /
// "All values") remain visible instead of being collapsed away by
// every option getting hoisted to the top. For flat option lists we
// still prioritize so selected/synthesized values stay rendered.
const hasSections = filteredOptions.some(
(opt) => 'options' in opt && Array.isArray(opt.options),
);
const shouldPrioritize =
selectedValues.length > 0 &&
isEmpty(searchText) &&
!(hasSections && (allOptionShown || isAllSelected));
const processedOptions = shouldPrioritize
? prioritizeOrAddOptionForMultiSelect(filteredOptions, selectedValues)
: filteredOptions;
const { sectionOptions, nonSectionOptions } = splitOptions(processedOptions);
@@ -1747,6 +1758,8 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
}, [
selectedValues,
searchText,
allOptionShown,
isAllSelected,
filteredOptions,
splitOptions,
isLabelPresent,

View File

@@ -6,6 +6,7 @@ import {
screen,
waitFor,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import CustomMultiSelect from '../CustomMultiSelect';
@@ -283,4 +284,68 @@ describe('CustomMultiSelect Component', () => {
// When all options are selected, component shows ALL tag instead
expect(screen.getByText('ALL')).toBeInTheDocument();
});
describe('section visibility when ALL is selected', () => {
it('keeps group headers visible when every grouped value is selected', async () => {
const user = userEvent.setup();
renderWithVirtuoso(
<CustomMultiSelect
options={mockGroupedOptions}
value={['g1-option1', 'g1-option2', 'g2-option1', 'g2-option2']}
/>,
);
await user.click(screen.getByRole('combobox'));
await waitFor(() => {
expect(screen.getByText('Group 1')).toBeInTheDocument();
expect(screen.getByText('Group 2')).toBeInTheDocument();
});
});
it('keeps every grouped option visible within its section when all are selected', async () => {
const user = userEvent.setup();
renderWithVirtuoso(
<CustomMultiSelect
options={mockGroupedOptions}
value={['g1-option1', 'g1-option2', 'g2-option1', 'g2-option2']}
/>,
);
await user.click(screen.getByRole('combobox'));
await waitFor(() => {
const group1Region = screen.getByRole('group', {
name: 'Group 1 options',
});
const group2Region = screen.getByRole('group', {
name: 'Group 2 options',
});
// Each option stays inside its original section rather than being
// hoisted into a flat selected-first list.
expect(group1Region).toHaveTextContent('Group 1 - Option 1');
expect(group1Region).toHaveTextContent('Group 1 - Option 2');
expect(group2Region).toHaveTextContent('Group 2 - Option 1');
expect(group2Region).toHaveTextContent('Group 2 - Option 2');
});
});
it('keeps group headers visible when value is the ALL sentinel', async () => {
const user = userEvent.setup();
renderWithVirtuoso(
<CustomMultiSelect
options={mockGroupedOptions}
value={('__ALL__' as unknown) as string[]}
/>,
);
await user.click(screen.getByRole('combobox'));
await waitFor(() => {
expect(screen.getByText('Group 1')).toBeInTheDocument();
expect(screen.getByText('Group 2')).toBeInTheDocument();
});
});
});
});

View File

@@ -355,7 +355,7 @@ $custom-border-color: #2c3044;
.navigation-error {
.navigation-text,
.navigation-icons {
color: var(--bg-cherry-500) !important;
color: var(--danger-background) !important;
}
display: flex;
align-items: center;

View File

@@ -1,3 +1,5 @@
// TODO: Improve the styling of the query aggregation container and its components. - @YounixM , @H4ad
.query-builder-v2 {
display: flex;
flex-direction: row;
@@ -274,7 +276,7 @@
.ant-input-group-addon {
border-top-left-radius: 0px !important;
border-top-right-radius: 0px !important;
background: var(--l3-background);
background: var(--l2-background);
color: var(--l2-foreground);
font-size: 12px;
font-weight: 300;

View File

@@ -50,8 +50,8 @@ const havingOperators = [
value: 'IN',
},
{
label: 'NOT_IN',
value: 'NOT_IN',
label: 'NOT IN',
value: 'NOT IN',
},
];
@@ -129,7 +129,7 @@ function HavingFilter({
const operator = havingOperators[j];
newOptions.push({
label: `${opt.func}(${opt.arg}) ${operator.label}`,
value: `${opt.func}(${opt.arg}) ${operator.label} `,
value: `${opt.func}(${opt.arg}) ${operator.value} `,
apply: (
view: EditorView,
completion: { label: string; value: string },

View File

@@ -1,3 +1,5 @@
// TODO: Improve the styling of the query aggregation container and its components. - @YounixM , @H4ad
.query-add-ons {
width: 100%;
}
@@ -102,7 +104,7 @@
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
padding: 0px !important;
background-color: var(--card) !important;
background-color: var(--l2-background) !important;
&:focus-within {
border-color: var(--l1-border);
@@ -211,7 +213,7 @@
.cm-line {
line-height: 36px !important;
font-family: 'Space Mono', monospace !important;
background-color: var(--card) !important;
background-color: var(--l2-background) !important;
::-moz-selection {
background: var(--l3-background) !important;
@@ -249,8 +251,8 @@
.close-btn {
border-radius: 0px 2px 2px 0px;
border: 1px solid var(--l1-border);
background: var(--l3-background);
border: 1px solid var(--l2-border);
background: var(--l2-background);
height: 38px;
width: 38px;
@@ -284,108 +286,3 @@
}
}
}
.lightMode {
.add-ons-list {
.add-ons-tabs {
.add-on-tab-title {
color: var(--l1-foreground) !important;
}
.tab {
border: 1px solid var(--l1-border) !important;
background: var(--l1-background) !important;
&:first-child {
border-left: 1px solid var(--l1-border) !important;
}
}
.tab::before {
background: var(--l3-background) !important;
}
.selected-view {
color: var(--primary-background) !important;
border: 1px solid var(--l1-border) !important;
}
.selected-view::before {
background: var(--l3-background) !important;
}
}
.compass-button {
border: 1px solid var(--l1-border) !important;
background: var(--l1-background) !important;
}
}
.having-filter-container {
.having-filter-select-container {
.having-filter-select-editor {
.cm-editor {
&:focus-within {
border-color: var(--l1-border) !important;
}
.cm-content {
border: 1px solid var(--l1-border) !important;
background: var(--l1-background) !important;
&:focus-within {
border-color: var(--l1-border) !important;
}
}
.cm-tooltip-autocomplete {
background: var(--l1-background) !important;
border: 1px solid var(--l1-border) !important;
color: var(--l1-foreground) !important;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1) !important;
ul {
li {
color: var(--l1-foreground) !important;
&:hover {
background: var(--l3-background) !important;
}
&[aria-selected='true'] {
background: var(--l3-background) !important;
font-weight: 600 !important;
}
}
}
}
.cm-line {
background-color: var(--l1-background) !important;
::-moz-selection {
background: var(--l1-background) !important;
}
::selection {
background: var(--l3-background) !important;
}
.chip-decorator {
background: var(--l3-background) !important;
color: var(--l1-foreground) !important;
}
}
.cm-selectionBackground {
background: var(--l1-background) !important;
}
}
}
.close-btn {
border: 1px solid var(--l1-border) !important;
background: var(--l1-background) !important;
}
}
}
}

View File

@@ -1,3 +1,5 @@
// TODO: Improve the styling of the query aggregation container and its components. - @YounixM , @H4ad
.query-aggregation-container {
display: block;
@@ -26,7 +28,7 @@
&.error {
.cm-editor {
.cm-content {
border-color: var(--bg-cherry-500) !important;
border-color: var(--danger-background) !important;
}
}
}
@@ -140,7 +142,7 @@
.cm-line {
line-height: 36px !important;
font-family: 'Space Mono', monospace !important;
background-color: var(--l1-background) !important;
background-color: var(--l2-background) !important;
::-moz-selection {
background: var(--l3-background) !important;
@@ -184,7 +186,7 @@
max-width: 300px;
.query-aggregation-error-message {
color: var(--bg-cherry-500);
color: var(--danger-background);
font-size: 12px;
line-height: 16px;
}
@@ -196,6 +198,7 @@
min-width: auto;
}
}
.close-btn {
border-radius: 0px 2px 2px 0px;
border: 1px solid var(--l1-border);
@@ -270,7 +273,7 @@
.cm-line {
::-moz-selection {
background: var(--l1-background) !important;
background: var(--l2-background) !important;
opacity: 0.5 !important;
}

View File

@@ -1,3 +1,5 @@
// TODO: Improve the styling of the query aggregation container and its components. - @YounixM , @H4ad
.code-mirror-where-clause {
width: 100%;
display: flex;
@@ -30,7 +32,7 @@
border-left: none !important;
&.hasErrors {
border-color: var(--bg-cherry-500);
border-color: var(--danger-background);
}
}
}
@@ -39,7 +41,7 @@
&.hasErrors {
.cm-editor {
.cm-content {
border-color: var(--bg-cherry-500);
border-color: var(--danger-background);
border-top-right-radius: 0px !important;
border-bottom-right-radius: 0px !important;
}
@@ -156,7 +158,7 @@
.cm-line {
line-height: 34px !important;
font-family: 'Space Mono', monospace !important;
background-color: var(--l1-background) !important;
background-color: var(--l2-background) !important;
::-moz-selection {
background: var(--l3-background) !important;
@@ -211,7 +213,7 @@
.invalid {
background-color: color-mix(in srgb, var(--bg-cherry-400) 10%, transparent);
color: var(--bg-cherry-500);
color: var(--danger-background);
}
.query-validation-status {
@@ -232,7 +234,7 @@
font-size: 12px;
font-family: 'Space Mono', monospace !important;
color: var(--bg-cherry-500);
color: var(--danger-background);
padding: 8px;
}
}
@@ -454,30 +456,3 @@
margin-top: -6px !important;
}
}
.lightMode {
.code-mirror-where-clause {
.cm-editor {
.cm-tooltip-autocomplete {
background: var(--l1-background) !important;
border: 1px solid var(--l1-border);
backdrop-filter: blur(20px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1) !important;
}
.cm-line {
::-moz-selection {
background: var(--bg-robin-200) !important;
}
::selection {
background: var(--bg-robin-200) !important;
}
}
.cm-selectionBackground {
background: var(--bg-robin-200) !important;
}
}
}
}

View File

@@ -177,7 +177,7 @@
width: 2px;
height: 11px;
border-radius: 2px;
background: var(--bg-cherry-500);
background: var(--danger-background);
}
.label-true {

View File

@@ -158,12 +158,12 @@
mask-image: radial-gradient(
circle at 50% 0,
color-mix(in srgb, var(--background) 10%, transparent) 0,
color-mix(in srgb, var(--l1-background) 10%, transparent) 0,
transparent 100%
);
-webkit-mask-image: radial-gradient(
circle at 50% 0,
color-mix(in srgb, var(--background) 10%, transparent) 0,
color-mix(in srgb, var(--l1-background) 10%, transparent) 0,
transparent 100%
);
}

View File

@@ -11,7 +11,7 @@ import {
ComboboxItem,
ComboboxList,
ComboboxTrigger,
} from '@signozhq/combobox';
} from '@signozhq/ui';
import { Skeleton, Switch, Tooltip, Typography } from 'antd';
import getLocalStorageKey from 'api/browser/localstorage/get';
import setLocalStorageKey from 'api/browser/localstorage/set';
@@ -200,7 +200,6 @@ export default function QuickFilters(props: IQuickFiltersProps): JSX.Element {
setOpen(false);
}}
isSelected={validQueryIndex === option.value}
showCheck={false}
>
{option.label}
</ComboboxItem>

View File

@@ -17,7 +17,7 @@
}
&__label {
font-size: var(--font-size-sm);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-normal);
color: var(--foreground);
line-height: var(--line-height-20);
@@ -142,6 +142,10 @@
gap: var(--spacing-2);
}
&__callout-wrapper {
display: flex;
}
&__expiry-label {
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);

View File

@@ -1,7 +1,5 @@
import { Button } from '@signozhq/button';
import { Callout } from '@signozhq/callout';
import { Check, Copy } from '@signozhq/icons';
import { Badge } from '@signozhq/ui';
import { Badge, Button, Callout } from '@signozhq/ui';
import type { ServiceaccounttypesGettableFactorAPIKeyWithKeyDTO } from 'api/generated/services/sigNoz.schemas';
export interface KeyCreatedPhaseProps {
@@ -40,11 +38,13 @@ function KeyCreatedPhase({
<Badge color="vanilla">{expiryLabel}</Badge>
</div>
<Callout
type="info"
showIcon
message="Store the key securely. This is the only time it will be displayed."
/>
<div className="add-key-modal__callout-wrapper">
<Callout
type="info"
showIcon
title="Store the key securely. This is the only time it will be displayed."
/>
</div>
</div>
);
}

View File

@@ -1,8 +1,6 @@
import type { Control, UseFormRegister } from 'react-hook-form';
import { Controller } from 'react-hook-form';
import { Button } from '@signozhq/button';
import { Input } from '@signozhq/input';
import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group';
import { Button, Input, ToggleGroup, ToggleGroupItem } from '@signozhq/ui';
import { DatePicker } from 'antd';
import { popupContainer } from 'utils/selectPopupContainer';
@@ -56,11 +54,12 @@ function KeyFormPhase({
<ToggleGroup
type="single"
value={field.value}
onValueChange={(val): void => {
onChange={(val): void => {
if (val) {
field.onChange(val);
}
}}
size="sm"
className="add-key-modal__expiry-toggle"
>
<ToggleGroupItem
@@ -112,6 +111,7 @@ function KeyFormPhase({
</Button>
<Button
type="submit"
// @ts-expect-error -- form prop not in @signozhq/ui Button type - TODO: Fix this - @SagarRajput
form={FORM_ID}
variant="solid"
color="primary"

View File

@@ -2,8 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useQueryClient } from 'react-query';
import { useCopyToClipboard } from 'react-use';
import { DialogWrapper } from '@signozhq/dialog';
import { toast } from '@signozhq/ui';
import { DialogWrapper, toast } from '@signozhq/ui';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import {
invalidateListServiceAccountKeys,
@@ -118,12 +117,12 @@ function AddKeyModal(): JSX.Element {
copyToClipboard(createdKey.key);
setHasCopied(true);
setTimeout(() => setHasCopied(false), 2000);
toast.success('Key copied to clipboard', { richColors: true });
toast.success('Key copied to clipboard');
}, [copyToClipboard, createdKey?.key]);
useEffect(() => {
if (copyState.error) {
toast.error('Failed to copy key', { richColors: true });
toast.error('Failed to copy key');
}
}, [copyState.error]);

View File

@@ -1,8 +1,6 @@
import { useQueryClient } from 'react-query';
import { Button } from '@signozhq/button';
import { DialogFooter, DialogWrapper } from '@signozhq/dialog';
import { Trash2, X } from '@signozhq/icons';
import { toast } from '@signozhq/ui';
import { Button, DialogWrapper, toast } from '@signozhq/ui';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import {
getGetServiceAccountQueryKey,
@@ -42,7 +40,7 @@ function DeleteAccountModal(): JSX.Element {
} = useDeleteServiceAccount({
mutation: {
onSuccess: async () => {
toast.success('Service account deleted', { richColors: true });
toast.success('Service account deleted');
await setIsDeleteOpen(null);
await setAccountId(null);
await invalidateListServiceAccounts(queryClient);
@@ -70,6 +68,32 @@ function DeleteAccountModal(): JSX.Element {
setIsDeleteOpen(null);
}
const content = (
<p className="sa-delete-dialog__body">
Are you sure you want to delete <strong>{accountName}</strong>? This action
cannot be undone. All keys associated with this service account will be
permanently removed.
</p>
);
const footer = (
<div className="sa-delete-dialog__footer">
<Button variant="solid" color="secondary" onClick={handleCancel}>
<X size={12} />
Cancel
</Button>
<Button
variant="solid"
color="destructive"
loading={isDeleting}
onClick={handleConfirm}
>
<Trash2 size={12} />
Delete
</Button>
</div>
);
return (
<DialogWrapper
open={open}
@@ -83,28 +107,9 @@ function DeleteAccountModal(): JSX.Element {
className="alert-dialog sa-delete-dialog"
showCloseButton={false}
disableOutsideClick={isErrorModalVisible}
footer={footer}
>
<p className="sa-delete-dialog__body">
Are you sure you want to delete <strong>{accountName}</strong>? This action
cannot be undone. All keys associated with this service account will be
permanently removed.
</p>
<DialogFooter className="sa-delete-dialog__footer">
<Button variant="solid" color="secondary" size="sm" onClick={handleCancel}>
<X size={12} />
Cancel
</Button>
<Button
variant="solid"
color="destructive"
size="sm"
loading={isDeleting}
onClick={handleConfirm}
>
<Trash2 size={12} />
Delete
</Button>
</DialogFooter>
{content}
</DialogWrapper>
);
}

View File

@@ -1,10 +1,13 @@
import type { Control, UseFormRegister } from 'react-hook-form';
import { Controller } from 'react-hook-form';
import { Button } from '@signozhq/button';
import { LockKeyhole, Trash2, X } from '@signozhq/icons';
import { Input } from '@signozhq/input';
import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group';
import { Badge } from '@signozhq/ui';
import {
Badge,
Button,
Input,
ToggleGroup,
ToggleGroupItem,
} from '@signozhq/ui';
import { DatePicker } from 'antd';
import type { ServiceaccounttypesGettableFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas';
import { popupContainer } from 'utils/selectPopupContainer';
@@ -72,11 +75,12 @@ function EditKeyForm({
<ToggleGroup
type="single"
value={field.value}
onValueChange={(val): void => {
onChange={(val): void => {
if (val) {
field.onChange(val);
}
}}
size="sm"
className="edit-key-modal__expiry-toggle"
>
<ToggleGroupItem
@@ -132,25 +136,21 @@ function EditKeyForm({
</form>
<div className="edit-key-modal__footer">
<Button
type="button"
className="edit-key-modal__footer-danger"
onClick={onRevokeClick}
>
<Button variant="ghost" color="destructive" onClick={onRevokeClick}>
<Trash2 size={12} />
Revoke Key
</Button>
<div className="edit-key-modal__footer-right">
<Button variant="solid" color="secondary" size="sm" onClick={onClose}>
<Button variant="solid" color="secondary" onClick={onClose}>
<X size={12} />
Cancel
</Button>
<Button
type="submit"
// @ts-expect-error -- form prop not in @signozhq/ui Button type - TODO: Fix this - @SagarRajput
form={FORM_ID}
variant="solid"
color="primary"
size="sm"
loading={isSaving}
disabled={!isDirty}
>

View File

@@ -138,7 +138,7 @@
&__meta {
display: flex;
flex-direction: column;
gap: var(--spacing-2);
gap: var(--spacing-4);
}
&__meta-label {

View File

@@ -1,8 +1,7 @@
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useQueryClient } from 'react-query';
import { DialogWrapper } from '@signozhq/dialog';
import { toast } from '@signozhq/ui';
import { DialogWrapper, toast } from '@signozhq/ui';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import {
invalidateListServiceAccountKeys,
@@ -72,7 +71,7 @@ function EditKeyModal({ keyItem }: EditKeyModalProps): JSX.Element {
const { mutate: updateKey, isLoading: isSaving } = useUpdateServiceAccountKey({
mutation: {
onSuccess: async () => {
toast.success('Key updated successfully', { richColors: true });
toast.success('Key updated successfully');
await setEditKeyId(null);
if (selectedAccountId) {
await invalidateListServiceAccountKeys(queryClient, {
@@ -96,7 +95,7 @@ function EditKeyModal({ keyItem }: EditKeyModalProps): JSX.Element {
} = useRevokeServiceAccountKey({
mutation: {
onSuccess: async () => {
toast.success('Key revoked successfully', { richColors: true });
toast.success('Key revoked successfully');
setIsRevokeConfirmOpen(false);
await setEditKeyId(null);
if (selectedAccountId) {

View File

@@ -1,6 +1,6 @@
import { useCallback, useMemo } from 'react';
import { Button } from '@signozhq/button';
import { KeyRound, X } from '@signozhq/icons';
import { Button } from '@signozhq/ui';
import { Skeleton, Table, Tooltip } from 'antd';
import type { ColumnsType } from 'antd/es/table/interface';
import type { ServiceaccounttypesGettableFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas';
@@ -96,7 +96,7 @@ function buildColumns({
<Tooltip title={isDisabled ? 'Service account disabled' : 'Revoke Key'}>
<Button
variant="ghost"
size="xs"
size="sm"
color="destructive"
disabled={isDisabled}
onClick={(e): void => {
@@ -177,8 +177,8 @@ function KeysTab({
</a>
</p>
<Button
type="button"
className="keys-tab__learn-more"
variant="link"
color="primary"
onClick={async (): Promise<void> => {
await setIsAddKeyOpen(true);
}}

View File

@@ -1,7 +1,6 @@
import { useCallback } from 'react';
import { LockKeyhole } from '@signozhq/icons';
import { Input } from '@signozhq/input';
import { Badge } from '@signozhq/ui';
import { Badge, Input } from '@signozhq/ui';
import type { AuthtypesRoleDTO } from 'api/generated/services/sigNoz.schemas';
import RolesSelect from 'components/RolesSelect';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';

View File

@@ -1,8 +1,6 @@
import { useQueryClient } from 'react-query';
import { Button } from '@signozhq/button';
import { DialogFooter, DialogWrapper } from '@signozhq/dialog';
import { Trash2, X } from '@signozhq/icons';
import { toast } from '@signozhq/ui';
import { Button, DialogWrapper, toast } from '@signozhq/ui';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import {
getListServiceAccountKeysQueryKey,
@@ -36,7 +34,7 @@ export function RevokeKeyContent({
Revoking this key will permanently invalidate it. Any systems using this key
will lose access immediately.
</p>
<DialogFooter className="delete-dialog__footer">
<div className="delete-dialog__footer">
<Button variant="solid" color="secondary" size="sm" onClick={onCancel}>
<X size={12} />
Cancel
@@ -51,7 +49,7 @@ export function RevokeKeyContent({
<Trash2 size={12} />
Revoke Key
</Button>
</DialogFooter>
</div>
</>
);
}
@@ -79,7 +77,7 @@ function RevokeKeyModal(): JSX.Element {
} = useRevokeServiceAccountKey({
mutation: {
onSuccess: async () => {
toast.success('Key revoked successfully', { richColors: true });
toast.success('Key revoked successfully');
await setRevokeKeyId(null);
if (accountId) {
await invalidateListServiceAccountKeys(queryClient, { id: accountId });

View File

@@ -1,7 +1,7 @@
import { useState } from 'react';
import { Button } from '@signozhq/button';
import { Color } from '@signozhq/design-tokens';
import { ChevronDown, ChevronUp, CircleAlert, RotateCw } from '@signozhq/icons';
import { Button } from '@signozhq/ui';
import ErrorContent from 'components/ErrorModal/components/ErrorContent';
import APIError from 'types/api/error';
@@ -40,9 +40,9 @@ function SaveErrorItem({
</span>
{onRetry && !isRetrying && (
<Button
type="button"
variant="link"
color="none"
aria-label="Retry"
size="xs"
onClick={async (e): Promise<void> => {
e.stopPropagation();
setIsRetrying(true);

View File

@@ -5,31 +5,21 @@
margin-left: var(--margin-2);
}
&__layout {
display: flex;
flex-direction: column;
height: calc(100vh - 48px);
}
&__tabs {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--padding-3) var(--padding-4) var(--padding-2) var(--padding-4);
flex-shrink: 0;
}
&__tab-group {
[data-slot='toggle-group'] {
height: 32px;
border-radius: 2px;
border: 1px solid var(--l1-border);
background: var(--l2-background);
gap: 0;
}
[data-slot='toggle-group-item'] {
height: 32px;
border-radius: 0;
border-left: 1px solid var(--l1-border);
background: transparent;
@@ -40,6 +30,7 @@
padding: 0 var(--padding-7);
gap: var(--spacing-3);
box-shadow: none;
border: none;
&:first-child {
border-left: none;
@@ -88,7 +79,7 @@
&__body {
flex: 1;
overflow-y: auto;
padding: var(--padding-5) var(--padding-4);
padding-top: var(--padding-5);
display: flex;
flex-direction: column;
gap: var(--spacing-8);
@@ -112,14 +103,11 @@
}
&__footer {
height: 56px;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 var(--padding-4);
border-top: 1px solid var(--secondary);
background: var(--card);
width: 100%;
}
&__keys-pagination {
@@ -302,7 +290,7 @@
&__icon {
flex-shrink: 0;
color: var(--bg-cherry-500);
color: var(--danger-background);
}
&__title {
@@ -310,7 +298,7 @@
min-width: 0;
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
color: var(--bg-cherry-500);
color: var(--danger-background);
line-height: var(--line-height-18);
letter-spacing: -0.06px;
text-align: left;
@@ -587,6 +575,5 @@
display: flex;
justify-content: flex-end;
gap: var(--spacing-4);
margin-top: var(--margin-6);
}
}

View File

@@ -1,10 +1,13 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useQueryClient } from 'react-query';
import { Button } from '@signozhq/button';
import { DrawerWrapper } from '@signozhq/drawer';
import { Key, LayoutGrid, Plus, Trash2, X } from '@signozhq/icons';
import { ToggleGroup, ToggleGroupItem } from '@signozhq/toggle-group';
import { toast } from '@signozhq/ui';
import {
Button,
DrawerWrapper,
toast,
ToggleGroup,
ToggleGroupItem,
} from '@signozhq/ui';
import { Pagination, Skeleton } from 'antd';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import {
@@ -331,7 +334,6 @@ function ServiceAccountDrawer({
setSaveErrors(errors);
} else {
toast.success('Service account updated successfully', {
richColors: true,
position: 'top-right',
});
onSuccess({ closeDrawer: false });
@@ -379,7 +381,7 @@ function ServiceAccountDrawer({
<ToggleGroup
type="single"
value={activeTab}
onValueChange={(val): void => {
onChange={(val): void => {
if (val) {
setActiveTab(val as ServiceAccountDrawerTab);
if (val !== ServiceAccountDrawerTab.Keys) {
@@ -471,69 +473,64 @@ function ServiceAccountDrawer({
</>
)}
</div>
</div>
);
<div className="sa-drawer__footer">
{activeTab === ServiceAccountDrawerTab.Keys ? (
<Pagination
current={keysPage}
pageSize={PAGE_SIZE}
total={keys.length}
showTotal={(total: number, range: number[]): JSX.Element => (
<>
<span className="sa-drawer__pagination-range">
{range[0]} &#8212; {range[1]}
</span>
<span className="sa-drawer__pagination-total"> of {total}</span>
</>
)}
showSizeChanger={false}
hideOnSinglePage
onChange={(page): void => {
void setKeysPage(page);
}}
className="sa-drawer__keys-pagination"
/>
) : (
<>
{!isDeleted && (
<Button
variant="ghost"
color="destructive"
className="sa-drawer__footer-btn"
onClick={(): void => {
setIsDeleteOpen(true);
}}
>
<Trash2 size={12} />
Delete Service Account
const footer = (
<div className="sa-drawer__footer">
{activeTab === ServiceAccountDrawerTab.Keys ? (
<Pagination
current={keysPage}
pageSize={PAGE_SIZE}
total={keys.length}
showTotal={(total: number, range: number[]): JSX.Element => (
<>
<span className="sa-drawer__pagination-range">
{range[0]} &#8212; {range[1]}
</span>
<span className="sa-drawer__pagination-total"> of {total}</span>
</>
)}
showSizeChanger={false}
hideOnSinglePage
onChange={(page): void => {
void setKeysPage(page);
}}
className="sa-drawer__keys-pagination"
/>
) : (
<>
{!isDeleted && (
<Button
variant="link"
color="destructive"
onClick={(): void => {
setIsDeleteOpen(true);
}}
>
<Trash2 size={12} />
Delete Service Account
</Button>
)}
{!isDeleted && (
<div className="sa-drawer__footer-right">
<Button variant="solid" color="secondary" onClick={handleClose}>
<X size={14} />
Cancel
</Button>
)}
{!isDeleted && (
<div className="sa-drawer__footer-right">
<Button
variant="solid"
color="secondary"
size="sm"
onClick={handleClose}
>
<X size={14} />
Cancel
</Button>
<Button
variant="solid"
color="primary"
size="sm"
loading={isSaving}
disabled={!isDirty}
onClick={handleSave}
>
Save Changes
</Button>
</div>
)}
</>
)}
</div>
<Button
variant="solid"
color="primary"
loading={isSaving}
disabled={!isDirty}
onClick={handleSave}
>
Save Changes
</Button>
</div>
)}
</>
)}
</div>
);
@@ -547,14 +544,15 @@ function ServiceAccountDrawer({
}
}}
direction="right"
type="panel"
showCloseButton
showOverlay={false}
allowOutsideClick
header={{ title: 'Service Account Details' }}
content={drawerContent}
title="Service Account Details"
className="sa-drawer"
/>
width="wide"
footer={footer}
>
{drawerContent}
</DrawerWrapper>
<DeleteAccountModal />

View File

@@ -1,7 +1,13 @@
import { toast } from '@signozhq/ui';
import { rest, server } from 'mocks-server/server';
import { NuqsTestingAdapter } from 'nuqs/adapters/testing';
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
import {
render,
screen,
userEvent,
waitFor,
waitForElementToBeRemoved,
} from 'tests/test-utils';
import AddKeyModal from '../AddKeyModal';
@@ -117,10 +123,7 @@ describe('AddKeyModal', () => {
await waitFor(() => {
expect(mockCopyToClipboard).toHaveBeenCalledWith('snz_abc123xyz456secret');
expect(mockToast.success).toHaveBeenCalledWith(
'Key copied to clipboard',
expect.anything(),
);
expect(mockToast.success).toHaveBeenCalledWith('Key copied to clipboard');
});
});
@@ -128,11 +131,9 @@ describe('AddKeyModal', () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
renderModal();
await screen.findByRole('dialog', { name: /Add a New Key/i });
const dialog = await screen.findByRole('dialog', { name: /Add a New Key/i });
await user.click(screen.getByRole('button', { name: /Cancel/i }));
expect(
screen.queryByRole('dialog', { name: /Add a New Key/i }),
).not.toBeInTheDocument();
await waitForElementToBeRemoved(dialog);
});
});

View File

@@ -29,9 +29,14 @@ function renderModal(
account: 'sa-1',
'edit-key': 'key-1',
},
onUrlUpdate?: jest.Mock,
): ReturnType<typeof render> {
return render(
<NuqsTestingAdapter searchParams={searchParams} hasMemory>
<NuqsTestingAdapter
searchParams={searchParams}
hasMemory
onUrlUpdate={onUrlUpdate}
>
<EditKeyModal keyItem={keyItem} />
</NuqsTestingAdapter>,
);
@@ -82,10 +87,7 @@ describe('EditKeyModal (URL-controlled)', () => {
await user.click(screen.getByRole('button', { name: /Save Changes/i }));
await waitFor(() => {
expect(mockToast.success).toHaveBeenCalledWith(
'Key updated successfully',
expect.anything(),
);
expect(mockToast.success).toHaveBeenCalledWith('Key updated successfully');
});
await waitFor(() => {
@@ -97,14 +99,31 @@ describe('EditKeyModal (URL-controlled)', () => {
it('cancel clears edit-key param and closes modal', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
renderModal();
const onUrlUpdate = jest.fn();
renderModal(mockKey, undefined, onUrlUpdate);
await screen.findByDisplayValue('Original Key Name');
await user.click(screen.getByRole('button', { name: /Cancel/i }));
expect(
screen.queryByRole('dialog', { name: /Edit Key Details/i }),
).not.toBeInTheDocument();
await waitFor(() => {
expect(onUrlUpdate).toHaveBeenCalled();
});
const latestUrlUpdate =
onUrlUpdate.mock.calls[onUrlUpdate.mock.calls.length - 1]?.[0];
expect(latestUrlUpdate).toEqual(
expect.objectContaining({
queryString: expect.any(String),
}),
);
expect(latestUrlUpdate.queryString).toContain('account=sa-1');
expect(latestUrlUpdate.queryString).not.toContain('edit-key=');
await waitFor(() => {
expect(
screen.queryByRole('dialog', { name: /Edit Key Details/i }),
).not.toBeInTheDocument();
});
});
it('revoke flow: clicking Revoke Key shows confirmation inside same dialog', async () => {
@@ -136,10 +155,7 @@ describe('EditKeyModal (URL-controlled)', () => {
await user.click(confirmBtn);
await waitFor(() => {
expect(mockToast.success).toHaveBeenCalledWith(
'Key revoked successfully',
expect.anything(),
);
expect(mockToast.success).toHaveBeenCalledWith('Key revoked successfully');
});
await waitFor(() => {

View File

@@ -164,10 +164,7 @@ describe('KeysTab', () => {
await user.click(confirmBtn);
await waitFor(() => {
expect(mockToast.success).toHaveBeenCalledWith(
'Key revoked successfully',
expect.anything(),
);
expect(mockToast.success).toHaveBeenCalledWith('Key revoked successfully');
});
});

View File

@@ -6,18 +6,23 @@ import { render, screen, userEvent, waitFor } from 'tests/test-utils';
import ServiceAccountDrawer from '../ServiceAccountDrawer';
jest.mock('@signozhq/drawer', () => ({
DrawerWrapper: ({
content,
open,
}: {
content?: ReactNode;
open: boolean;
}): JSX.Element | null => (open ? <div>{content}</div> : null),
}));
jest.mock('@signozhq/ui', () => ({
...jest.requireActual('@signozhq/ui'),
DrawerWrapper: ({
children,
footer,
open,
}: {
children?: ReactNode;
footer?: ReactNode;
open: boolean;
}): JSX.Element | null =>
open ? (
<div>
{children}
{footer}
</div>
) : null,
toast: { success: jest.fn(), error: jest.fn() },
}));

View File

@@ -21,7 +21,7 @@
.ant-table-thead {
> tr > th,
> tr > td {
background: var(--background);
background: var(--l1-background);
font-size: var(--paragraph-small-600-font-size);
font-weight: var(--paragraph-small-600-font-weight);
line-height: var(--paragraph-small-600-line-height);

View File

@@ -11,7 +11,7 @@
gap: 20px;
padding: 8px 12px;
background: var(--background);
background: var(--l1-background);
color: var(--l2-foreground);
border-radius: 8px;

View File

@@ -50,7 +50,7 @@
&__error {
font-size: 0.75rem;
color: var(--bg-cherry-500);
color: var(--danger-background);
font-weight: 500;
}
@@ -94,7 +94,7 @@
height: 20px;
flex-shrink: 0;
border-radius: 2px;
background: var(--bg-cherry-500);
background: var(--danger-background);
}
}

View File

@@ -1,11 +1,11 @@
.warning-content {
display: flex;
flex-direction: column;
// === SECTION: Summary (Top)
&__summary-section {
display: flex;
flex-direction: column;
border-bottom: 1px solid var(--l1-border);
}
&__summary {

View File

@@ -161,10 +161,11 @@ describe('CmdKPalette', () => {
});
test('clicking a navigation item calls history.push with correct route', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(<CmdKPalette userRole="ADMIN" />);
const homeItem = screen.getByText(HOME_LABEL);
await userEvent.click(homeItem);
await user.click(homeItem);
expect(history.push).toHaveBeenCalledWith(ROUTES.HOME);
});
@@ -194,10 +195,11 @@ describe('CmdKPalette', () => {
});
test('closing the palette via handleInvoke sets open to false', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(<CmdKPalette userRole="ADMIN" />);
const dashItem = screen.getByText('Go to Dashboards');
await userEvent.click(dashItem);
await user.click(dashItem);
// last call from handleInvoke should set open to false
expect(mockSetOpen).toHaveBeenCalledWith(false);

View File

@@ -41,7 +41,7 @@
.cmdk-item-light:hover {
cursor: pointer;
background-color: var(--background) !important;
background-color: var(--l1-background) !important;
}
.cmdk-item-light[data-selected='true'] {

View File

@@ -7,7 +7,7 @@ import {
CommandItem,
CommandList,
CommandShortcut,
} from '@signozhq/command';
} from '@signozhq/ui';
import logEvent from 'api/common/logEvent';
import { useThemeMode } from 'hooks/useDarkMode';
import history from 'lib/history';

View File

@@ -50,6 +50,7 @@
.app-content {
width: calc(100% - 54px); // width of the sidebar
z-index: 0;
background: var(--l1-background);
margin: 0 auto;
@@ -139,22 +140,6 @@
}
}
.isDarkMode {
.app-layout {
.app-content {
background: var(--background);
}
}
}
.isLightMode {
.app-layout {
.app-content {
background: var(--card);
}
}
}
.trial-expiry-banner,
.slow-api-warning-banner,
.workspace-restricted-banner {

View File

@@ -82,7 +82,7 @@
.sentence-text {
color: var(--l2-foreground);
font-size: 14px;
font-size: 13px;
line-height: 1.5;
display: flex;
align-items: center;

View File

@@ -1,5 +1,5 @@
import { useCallback, useMemo } from 'react';
import { Button } from 'antd';
import { Button, Input } from '@signozhq/ui';
import logEvent from 'api/common/logEvent';
import classNames from 'classnames';
import { QueryParams } from 'constants/query';
@@ -59,15 +59,18 @@ function CreateAlertHeader(): JSX.Element {
<div className="alert-header__tab-bar">
<div className="alert-header__tab">New Alert Rule</div>
<Button
icon={<RotateCcw size={16} />}
prefix={<RotateCcw size={12} />}
onClick={handleSwitchToClassicExperience}
variant="solid"
color="secondary"
size="sm"
>
Switch to Classic Experience
</Button>
</div>
)}
<div className="alert-header__content">
<input
<Input
type="text"
value={alertState.name}
onChange={(e): void =>

View File

@@ -2,6 +2,7 @@
background-color: var(--l1-background);
font-family: inherit;
color: var(--l1-foreground);
padding: 12px 16px;
&__tab-bar {
display: flex;
@@ -14,23 +15,20 @@
display: flex;
align-items: center;
background-color: var(--l1-background);
padding: 0 12px;
height: 32px;
font-size: 13px;
color: var(--l1-foreground);
margin-left: 12px;
margin-top: 12px;
}
&__tab::before {
content: '';
margin-right: 6px;
font-size: 14px;
font-size: 13px;
color: var(--l3-foreground);
}
&__content {
padding: 16px;
padding: 8px 0;
background: var(--l1-background);
display: flex;
flex-direction: column;
@@ -40,22 +38,14 @@
}
&__input.title {
font-size: 18px;
font-weight: 500;
background-color: transparent;
color: var(--l1-foreground);
width: 100%;
min-width: 300px;
}
&__input:focus,
&__input:active {
border: none;
outline: none;
}
&__input.description {
font-size: 14px;
font-size: 13px;
background-color: transparent;
color: var(--l2-foreground);
}

View File

@@ -13,7 +13,7 @@
.advanced-option-item-title {
color: var(--l2-foreground);
font-family: Inter;
font-size: 14px;
font-size: 13px;
font-weight: 500;
display: flex;
align-items: center;
@@ -23,7 +23,7 @@
.advanced-option-item-description {
color: var(--muted-foreground);
font-family: Inter;
font-size: 14px;
font-size: 13px;
font-weight: 400;
}

View File

@@ -22,7 +22,7 @@
color: var(--l2-foreground);
font-weight: 500;
margin: 0 4px;
font-size: 14px;
font-size: 13px;
}
}
@@ -30,7 +30,7 @@
.ant-btn {
border: 1px solid var(--l1-border);
color: var(--l2-foreground);
font-size: 14px;
font-size: 13px;
display: flex;
align-items: center;
gap: 8px;
@@ -48,7 +48,7 @@
.evaluation-cadence-details-title {
color: var(--l1-foreground);
font-size: 14px;
font-size: 13px;
font-weight: 500;
padding-left: 16px;
padding-top: 16px;
@@ -138,7 +138,7 @@
border-radius: 4px;
color: var(--l2-foreground) !important;
font-family: 'Space Mono';
font-size: 14px;
font-size: 13px;
&::placeholder {
font-family: 'Space Mono';
@@ -202,7 +202,7 @@
gap: 8px;
height: 100%;
color: var(--l1-foreground);
font-size: 14px;
font-size: 13px;
}
.schedule-preview {
@@ -225,7 +225,7 @@
.schedule-preview-title {
color: var(--l2-foreground);
font-size: 14px;
font-size: 13px;
font-weight: 500;
}
}
@@ -281,7 +281,7 @@
.schedule-preview-date {
color: var(--l2-foreground);
font-size: 14px;
font-size: 13px;
font-weight: 400;
white-space: nowrap;
}
@@ -396,7 +396,7 @@
.schedule-preview-title {
color: var(--l1-foreground);
font-size: 14px;
font-size: 13px;
font-weight: 500;
}
}
@@ -422,7 +422,7 @@
.schedule-preview-content {
.schedule-preview-date {
color: var(--l1-foreground);
font-size: 14px;
font-size: 13px;
font-weight: 500;
}

View File

@@ -10,7 +10,7 @@
border: 1px solid var(--l1-border);
color: var(--l1-foreground);
font-family: 'Space Mono', monospace;
font-size: 14px;
font-size: 13px;
font-weight: 600;
text-align: center;
border-radius: 4px;
@@ -43,7 +43,7 @@
.time-input-separator {
color: var(--l2-foreground);
font-size: 14px;
font-size: 13px;
font-weight: 600;
margin: 0 4px;
user-select: none;

View File

@@ -13,7 +13,7 @@
.ant-typography {
color: var(--l2-foreground);
font-size: 14px;
font-size: 13px;
}
.evaluate-alert-conditions-separator {
@@ -175,7 +175,7 @@
background-color: var(--l3-background);
border: 1px solid var(--l1-border);
color: var(--l2-foreground);
font-size: 14px;
font-size: 13px;
}
}
}

View File

@@ -1,6 +1,6 @@
import { useCallback, useMemo } from 'react';
import { toast } from '@signozhq/ui';
import { Button, Tooltip, Typography } from 'antd';
import { Button, toast } from '@signozhq/ui';
import { Tooltip } from 'antd';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import { Check, Loader, Send, X } from 'lucide-react';
@@ -147,7 +147,8 @@ function Footer(): JSX.Element {
const saveAlertButton = useMemo(() => {
let button = (
<Button
type="primary"
variant="solid"
color="primary"
onClick={handleSaveAlert}
disabled={disableButtons || Boolean(alertValidationMessage)}
>
@@ -156,7 +157,7 @@ function Footer(): JSX.Element {
) : (
<Check size={14} />
)}
<Typography.Text>Save Alert Rule</Typography.Text>
Save Alert Rule
</Button>
);
if (alertValidationMessage) {
@@ -174,12 +175,13 @@ function Footer(): JSX.Element {
const testAlertButton = useMemo(() => {
let button = (
<Button
type="default"
variant="solid"
color="secondary"
onClick={handleTestNotification}
disabled={disableButtons || Boolean(alertValidationMessage)}
>
{isTestingAlertRule ? <Loader size={14} /> : <Send size={14} />}
<Typography.Text>Test Notification</Typography.Text>
Test Notification
</Button>
);
if (alertValidationMessage) {
@@ -195,7 +197,12 @@ function Footer(): JSX.Element {
return (
<div className="create-alert-v2-footer">
<Button type="default" onClick={handleDiscard} disabled={disableButtons}>
<Button
variant="solid"
color="secondary"
onClick={handleDiscard}
disabled={disableButtons}
>
<X size={14} /> Discard
</Button>
<div className="button-group">

View File

@@ -4,9 +4,8 @@
left: 63px;
right: 0;
background-color: var(--l1-background);
height: 70px;
border-top: 1px solid var(--l1-border);
padding: 16px 24px;
padding: 12px;
z-index: 1000;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
display: flex;

View File

@@ -29,14 +29,14 @@
align-items: center;
color: var(--l1-foreground);
font-family: Inter;
font-size: 14px;
font-size: 13px;
font-weight: 500;
}
.notification-message-header-description {
color: var(--l2-foreground);
font-family: Inter;
font-size: 14px;
font-size: 13px;
font-weight: 400;
}
}
@@ -59,7 +59,7 @@
border-radius: 4px;
color: var(--l2-foreground) !important;
font-family: Inter;
font-size: 14px;
font-size: 13px;
}
}
@@ -130,7 +130,7 @@
.multiple-notifications-header-title {
color: var(--l1-foreground);
font-family: Inter;
font-size: 14px;
font-size: 13px;
font-weight: 500;
display: flex;
align-items: center;
@@ -140,7 +140,7 @@
.multiple-notifications-header-description {
color: var(--l2-foreground);
font-family: Inter;
font-size: 14px;
font-size: 13px;
font-weight: 400;
}
}
@@ -179,14 +179,14 @@
.advanced-option-item-title {
color: var(--l2-foreground);
font-family: Inter;
font-size: 14px;
font-size: 13px;
font-weight: 500;
}
.advanced-option-item-description {
color: var(--muted-foreground);
font-family: Inter;
font-size: 14px;
font-size: 13px;
font-weight: 400;
}
}
@@ -206,7 +206,7 @@
flex-wrap: nowrap;
.ant-typography {
font-size: 14px;
font-size: 13px;
font-weight: 400;
color: var(--l2-foreground);
white-space: nowrap;

View File

@@ -16,8 +16,8 @@
margin-bottom: -1px;
.prom-ql-icon {
height: 14px;
width: 14px;
height: 13px;
width: 13px;
}
.explorer-view-option {

View File

@@ -17,7 +17,7 @@
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
font-size: 13px;
flex-shrink: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

View File

@@ -1,9 +1,7 @@
import { useEffect, useState } from 'react';
import { Button } from '@signozhq/button';
import { Color } from '@signozhq/design-tokens';
import { DialogWrapper } from '@signozhq/dialog';
import { CircleAlert, CircleCheck, LoaderCircle } from '@signozhq/icons';
import { Input } from '@signozhq/input';
import { Button, DialogWrapper, Input } from '@signozhq/ui';
import { RenderErrorResponseDTO } from 'api/generated/services/sigNoz.schemas';
import { AxiosError } from 'axios';
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';

View File

@@ -78,7 +78,6 @@
display: flex;
align-items: center;
gap: var(--spacing-10);
padding-left: 26px;
}
.custom-domain-card-meta-row.workspace-name-hidden {
@@ -272,7 +271,6 @@
display: flex;
align-items: center;
gap: var(--spacing-3);
height: 44px;
padding: 6px var(--padding-3);
background: var(--l1-background);
border: 1px solid var(--l1-border);

View File

@@ -1,6 +1,4 @@
import { useEffect, useMemo, useState } from 'react';
import { Button } from '@signozhq/button';
import { Callout } from '@signozhq/callout';
import {
Check,
ChevronDown,
@@ -11,7 +9,7 @@ import {
SolidAlertCircle,
X,
} from '@signozhq/icons';
import { toast } from '@signozhq/ui';
import { Button, Callout, toast } from '@signozhq/ui';
import { Dropdown, Skeleton } from 'antd';
import {
RenderErrorResponseDTO,
@@ -44,9 +42,9 @@ function DomainUpdateToast({
<div className="custom-domain-toast-actions">
<Button
variant="ghost"
size="xs"
size="sm"
className="custom-domain-toast-visit-btn"
suffixIcon={<ExternalLink size={12} />}
suffix={<ExternalLink size={12} />}
onClick={(): void => {
window.open(url, '_blank', 'noopener,noreferrer');
}}
@@ -61,7 +59,7 @@ function DomainUpdateToast({
toast.dismiss(toastId);
}}
aria-label="Dismiss"
prefixIcon={<X size={14} />}
prefix={<X size={14} />}
/>
</div>
</div>
@@ -245,10 +243,10 @@ export default function CustomDomainSettings(): JSX.Element {
)}
>
<Button
type="button"
size="xs"
className="workspace-url-trigger"
disabled={isFetchingHosts}
variant="link"
color="none"
>
<Link2 size={12} />
<span>{stripProtocol(activeHost?.url ?? '')}</span>
@@ -264,9 +262,8 @@ export default function CustomDomainSettings(): JSX.Element {
<Button
variant="solid"
size="sm"
className="custom-domain-edit-button"
prefixIcon={<FilePenLine size={12} />}
color="secondary"
prefix={<FilePenLine size={12} />}
disabled={isFetchingHosts || isPollingEnabled}
onClick={(): void => setIsEditModalOpen(true)}
>
@@ -281,7 +278,7 @@ export default function CustomDomainSettings(): JSX.Element {
className="custom-domain-callout"
size="small"
icon={<SolidAlertCircle size={13} color="primary" />}
message={`Updating your URL to ⎯ ${customDomainSubdomain}.${dnsSuffix}. This may take a few mins.`}
title={`Updating your URL to ⎯ ${customDomainSubdomain}.${dnsSuffix}. This may take a few mins.`}
/>
)}

View File

@@ -69,7 +69,7 @@
width: 26px;
height: 22px;
border-radius: 2px;
background: color-mix(in srgb, var(--bg-cherry-500) 10%, transparent);
background: color-mix(in srgb, var(--danger-background) 10%, transparent);
display: flex;
padding: 4px 6px;
align-items: center;

View File

@@ -1,8 +1,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useMutation } from 'react-query';
import { useCopyToClipboard } from 'react-use';
import { Checkbox } from '@signozhq/checkbox';
import { toast } from '@signozhq/ui';
import { Checkbox, toast } from '@signozhq/ui';
import { Button, Select, Typography } from 'antd';
import createPublicDashboardAPI from 'api/dashboard/public/createPublicDashboard';
import revokePublicDashboardAccessAPI from 'api/dashboard/public/revokePublicDashboardAccess';
@@ -247,10 +246,11 @@ function PublicDashboardSetting(): JSX.Element {
<div className="timerange-enabled-checkbox">
<Checkbox
id="enable-time-range"
checked={timeRangeEnabled}
onCheckedChange={handleTimeRangeEnabled}
labelName="Enable time range"
/>
value={timeRangeEnabled}
onChange={handleTimeRangeEnabled}
>
Enable time range
</Checkbox>
</div>
<div className="default-time-range-select">

View File

@@ -1,4 +1,4 @@
import { useCallback, useRef } from 'react';
import { useCallback, useMemo, useRef } from 'react';
import ChartLayout from 'container/DashboardContainer/visualization/layout/ChartLayout/ChartLayout';
import Legend from 'lib/uPlotV2/components/Legend/Legend';
import {
@@ -30,6 +30,7 @@ export default function ChartWrapper({
onDestroy = noop,
children,
layoutChildren,
yAxisUnit,
customTooltip,
pinnedTooltipElement,
'data-testid': testId,
@@ -62,6 +63,13 @@ export default function ChartWrapper({
[customTooltip],
);
const syncMetadata = useMemo(
() => ({
yAxisUnit,
}),
[yAxisUnit],
);
return (
<PlotContextProvider>
<ChartLayout
@@ -99,6 +107,7 @@ export default function ChartWrapper({
averageLegendWidth + TOOLTIP_WIDTH_PADDING,
)}
syncKey={syncKey}
syncMetadata={syncMetadata}
render={renderTooltipCallback}
pinnedTooltipElement={pinnedTooltipElement}
/>

View File

@@ -24,13 +24,12 @@ export default function Histogram(props: HistogramChartProps): JSX.Element {
}
const tooltipProps: HistogramTooltipProps = {
...props,
timezone: rest.timezone,
yAxisUnit: rest.yAxisUnit,
decimalPrecision: rest.decimalPrecision,
};
return <HistogramTooltip {...tooltipProps} />;
},
[customTooltip, rest.timezone, rest.yAxisUnit, rest.decimalPrecision],
[customTooltip, rest.yAxisUnit, rest.decimalPrecision],
);
return (

View File

@@ -12,10 +12,7 @@ interface BaseChartProps {
height: number;
showTooltip?: boolean;
showLegend?: boolean;
timezone?: Timezone;
canPinTooltip?: boolean;
yAxisUnit?: string;
decimalPrecision?: PrecisionOption;
pinnedTooltipElement?: (clickData: TooltipClickData) => React.ReactNode;
customTooltip?: (props: TooltipRenderArgs) => React.ReactNode;
'data-testid'?: string;
@@ -32,18 +29,31 @@ interface UPlotBasedChartProps {
layoutChildren?: React.ReactNode;
}
interface UPlotChartDataProps {
yAxisUnit?: string;
decimalPrecision?: PrecisionOption;
}
export interface TimeSeriesChartProps
extends BaseChartProps,
UPlotBasedChartProps {}
UPlotBasedChartProps,
UPlotChartDataProps {
timezone?: Timezone;
}
export interface HistogramChartProps
extends BaseChartProps,
UPlotBasedChartProps {
UPlotBasedChartProps,
UPlotChartDataProps {
isQueriesMerged?: boolean;
}
export interface BarChartProps extends BaseChartProps, UPlotBasedChartProps {
export interface BarChartProps
extends BaseChartProps,
UPlotBasedChartProps,
UPlotChartDataProps {
isStackedBarChart?: boolean;
timezone?: Timezone;
}
export type ChartProps =

View File

@@ -123,13 +123,13 @@ function BarPanel(props: PanelWrapperProps): JSX.Element {
}}
plotRef={onPlotRef}
onDestroy={onPlotDestroy}
yAxisUnit={widget.yAxisUnit}
decimalPrecision={widget.decimalPrecision}
data={chartData as uPlot.AlignedData}
width={containerDimensions.width}
height={containerDimensions.height}
layoutChildren={layoutChildren}
isStackedBarChart={widget.stackedBarChart ?? false}
yAxisUnit={widget.yAxisUnit}
decimalPrecision={widget.decimalPrecision}
timezone={timezone}
>
<ContextMenu

View File

@@ -3,8 +3,6 @@ import { PanelWrapperProps } from 'container/PanelWrapper/panelWrapper.types';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions';
import { LegendPosition } from 'lib/uPlotV2/components/types';
import { DashboardCursorSync } from 'lib/uPlotV2/plugins/TooltipPlugin/types';
import { useTimezone } from 'providers/Timezone';
import uPlot from 'uplot';
import Histogram from '../../charts/Histogram/Histogram';
@@ -29,7 +27,6 @@ function HistogramPanel(props: PanelWrapperProps): JSX.Element {
const containerDimensions = useResizeObserver(graphRef);
const isDarkMode = useIsDarkMode();
const { timezone } = useTimezone();
const config = useMemo(() => {
return prepareHistogramPanelConfig({
@@ -92,11 +89,9 @@ function HistogramPanel(props: PanelWrapperProps): JSX.Element {
onDestroy={(): void => {
uPlotRef.current = null;
}}
isQueriesMerged={widget.mergeAllActiveQueries}
yAxisUnit={widget.yAxisUnit}
decimalPrecision={widget.decimalPrecision}
syncMode={DashboardCursorSync.Crosshair}
timezone={timezone}
isQueriesMerged={widget.mergeAllActiveQueries}
data={chartData as uPlot.AlignedData}
width={containerDimensions.width}
height={containerDimensions.height}

View File

@@ -48,8 +48,8 @@ jest.mock(
{JSON.stringify({
legendPosition: props.legendConfig?.position,
isQueriesMerged: props.isQueriesMerged,
yAxisUnit: props.yAxisUnit,
decimalPrecision: props.decimalPrecision,
yAxisUnit: props?.yAxisUnit,
decimalPrecision: props?.decimalPrecision,
})}
</div>
{props.layoutChildren}

View File

@@ -112,9 +112,9 @@ function TimeSeriesPanel(props: PanelWrapperProps): JSX.Element {
legendConfig={{
position: widget?.legendPosition ?? LegendPosition.BOTTOM,
}}
timezone={timezone}
yAxisUnit={widget.yAxisUnit}
decimalPrecision={widget.decimalPrecision}
timezone={timezone}
data={chartData as uPlot.AlignedData}
width={containerDimensions.width}
height={containerDimensions.height}

View File

@@ -1,5 +1,5 @@
import { Button } from '@signozhq/button';
import { ArrowLeft, Mail } from '@signozhq/icons';
import { Button } from '@signozhq/ui';
interface SuccessScreenProps {
onBackToLogin: () => void;
@@ -28,7 +28,7 @@ function SuccessScreen({ onBackToLogin }: SuccessScreenProps): JSX.Element {
data-testid="back-to-login"
className="login-submit-btn"
onClick={onBackToLogin}
prefixIcon={<ArrowLeft size={12} />}
prefix={<ArrowLeft size={12} />}
>
Back to login
</Button>

View File

@@ -1,7 +1,6 @@
import { useCallback, useEffect, useMemo } from 'react';
import { Button } from '@signozhq/button';
import { ArrowLeft, ArrowRight } from '@signozhq/icons';
import { Input } from '@signozhq/input';
import { Button, Input } from '@signozhq/ui';
import { Form, Select } from 'antd';
import { ErrorResponseHandlerForGeneratedAPIs } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import { useForgotPassword } from 'api/generated/services/users';
@@ -191,7 +190,7 @@ function ForgotPassword({
data-testid="forgot-password-back"
className="forgot-password-back-button"
onClick={handleBackToLogin}
prefixIcon={<ArrowLeft size={12} />}
prefix={<ArrowLeft size={12} />}
>
Back to login
</Button>
@@ -204,7 +203,7 @@ function ForgotPassword({
type="submit"
data-testid="forgot-password-submit"
className="login-submit-btn"
suffixIcon={<ArrowRight size={12} />}
suffix={<ArrowRight size={12} />}
>
{isLoading ? 'Sending...' : 'Send reset link'}
</Button>

View File

@@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next';
import { UseQueryResult } from 'react-query';
import { useInterval } from 'react-use';
import { LoadingOutlined } from '@ant-design/icons';
import { Button } from '@signozhq/button';
import { Compass, ScrollText } from '@signozhq/icons';
import { Button } from '@signozhq/ui';
import { Modal, Spin } from 'antd';
import setRetentionApi from 'api/settings/setRetention';
import setRetentionApiV2 from 'api/settings/setRetentionV2';

View File

@@ -1,7 +1,6 @@
import { useCopyToClipboard } from 'react-use';
import { Button } from '@signozhq/button';
import { Copy, KeyRound } from '@signozhq/icons';
import { toast } from '@signozhq/ui';
import { Button, toast } from '@signozhq/ui';
import { useAppContext } from 'providers/App/App';
import { getMaskedKey } from 'utils/maskedKey';
@@ -17,7 +16,7 @@ function LicenseKeyRow(): JSX.Element | null {
const handleCopyLicenseKey = (text: string): void => {
copyToClipboard(text);
toast.success('License key copied to clipboard.', { richColors: true });
toast.success('License key copied to clipboard.');
};
return (
@@ -32,7 +31,7 @@ function LicenseKeyRow(): JSX.Element | null {
</code>
<Button
type="button"
size="xs"
size="sm"
aria-label="Copy license key"
data-testid="license-key-row-copy-btn"
className="license-key-row__copy-btn"

View File

@@ -1,6 +1,6 @@
import { useState } from 'react';
import { Link } from 'react-router-dom';
import { Callout } from '@signozhq/callout';
import { Callout } from '@signozhq/ui';
import getLocalStorageApi from 'api/browser/localstorage/get';
import setLocalStorageApi from 'api/browser/localstorage/set';
import { FeatureKeys } from 'constants/features';
@@ -44,39 +44,38 @@ function LicenseRowDismissibleCallout(): JSX.Element | null {
type="info"
size="small"
showIcon
dismissable
onClose={handleDismissCallout}
action="dismissible"
onClick={handleDismissCallout}
className="license-key-callout"
description={
<div className="license-key-callout__description">
This is <strong>NOT</strong> your ingestion or Service account key.
{(hasServiceAccountsAccess || hasIngestionAccess) && (
<>
{' '}
Find your{' '}
{hasServiceAccountsAccess && (
<Link
to={ROUTES.SERVICE_ACCOUNTS_SETTINGS}
className="license-key-callout__link"
>
Service account here
</Link>
)}
{hasServiceAccountsAccess && hasIngestionAccess && ' and '}
{hasIngestionAccess && (
<Link
to={ROUTES.INGESTION_SETTINGS}
className="license-key-callout__link"
>
Ingestion key here
</Link>
)}
.
</>
)}
</div>
}
/>
>
<div className="license-key-callout__description">
This is <strong>NOT</strong> your ingestion or Service account key.
{(hasServiceAccountsAccess || hasIngestionAccess) && (
<>
{' '}
Find your{' '}
{hasServiceAccountsAccess && (
<Link
to={ROUTES.SERVICE_ACCOUNTS_SETTINGS}
className="license-key-callout__link"
>
Service account here
</Link>
)}
{hasServiceAccountsAccess && hasIngestionAccess && ' and '}
{hasIngestionAccess && (
<Link
to={ROUTES.INGESTION_SETTINGS}
className="license-key-callout__link"
>
Ingestion key here
</Link>
)}
.
</>
)}
</div>
</Callout>
) : null;
}

View File

@@ -53,9 +53,6 @@ describe('LicenseKeyRow', () => {
expect(mockCopyToClipboard).toHaveBeenCalledWith('test-key');
expect(mockToastSuccess).toHaveBeenCalledWith(
'License key copied to clipboard.',
{
richColors: true,
},
);
});
});

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