mirror of
https://github.com/SigNoz/signoz.git
synced 2026-09-19 09:50:41 +01:00
Compare commits
6 Commits
feat/warni
...
chore/issu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c74e650246 | ||
|
|
a178c1b266 | ||
|
|
8b9fcae0cb | ||
|
|
e75abcb108 | ||
|
|
ffa15f9ec1 | ||
|
|
87562f5387 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -12,7 +12,6 @@ frontend/coverage
|
||||
|
||||
# production
|
||||
frontend/build
|
||||
frontend/.vscode
|
||||
frontend/.yarnclean
|
||||
frontend/.temp_cache
|
||||
frontend/test-results
|
||||
@@ -31,7 +30,6 @@ frontend/src/constants/env.ts
|
||||
|
||||
.idea
|
||||
|
||||
**/.vscode
|
||||
**/build
|
||||
**/storage
|
||||
**/locust-scripts/__pycache__/
|
||||
|
||||
9
.vscode/settings.json
vendored
Normal file
9
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"eslint.workingDirectories": ["./frontend"],
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"prettier.requireConfig": true
|
||||
}
|
||||
@@ -176,7 +176,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.106.0
|
||||
image: signoz/signoz:v0.107.0
|
||||
command:
|
||||
- --config=/root/config/prometheus.yml
|
||||
ports:
|
||||
|
||||
@@ -117,7 +117,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:v0.106.0
|
||||
image: signoz/signoz:v0.107.0
|
||||
command:
|
||||
- --config=/root/config/prometheus.yml
|
||||
ports:
|
||||
|
||||
@@ -179,7 +179,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.106.0}
|
||||
image: signoz/signoz:${VERSION:-v0.107.0}
|
||||
container_name: signoz
|
||||
command:
|
||||
- --config=/root/config/prometheus.yml
|
||||
|
||||
@@ -111,7 +111,7 @@ services:
|
||||
# - ../common/clickhouse/storage.xml:/etc/clickhouse-server/config.d/storage.xml
|
||||
signoz:
|
||||
!!merge <<: *db-depend
|
||||
image: signoz/signoz:${VERSION:-v0.106.0}
|
||||
image: signoz/signoz:${VERSION:-v0.107.0}
|
||||
container_name: signoz
|
||||
command:
|
||||
- --config=/root/config/prometheus.yml
|
||||
|
||||
@@ -7,8 +7,6 @@ module.exports = {
|
||||
'jest/globals': true,
|
||||
},
|
||||
extends: [
|
||||
'airbnb',
|
||||
'airbnb-typescript',
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
@@ -35,6 +33,7 @@ module.exports = {
|
||||
'react-hooks',
|
||||
'prettier',
|
||||
'jest',
|
||||
'jsx-a11y',
|
||||
],
|
||||
settings: {
|
||||
react: {
|
||||
@@ -72,9 +71,6 @@ module.exports = {
|
||||
'react-hooks/rules-of-hooks': 'error',
|
||||
'react-hooks/exhaustive-deps': 'error',
|
||||
|
||||
// airbnb
|
||||
'no-underscore-dangle': 'off',
|
||||
'no-console': 'off',
|
||||
'import/prefer-default-export': 'off',
|
||||
'import/extensions': [
|
||||
'error',
|
||||
@@ -87,6 +83,9 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
'import/no-extraneous-dependencies': ['error', { devDependencies: true }],
|
||||
// Disabled because TypeScript already handles this check more accurately,
|
||||
// and the rule has false positives with type-only imports (e.g., TooltipProps from antd)
|
||||
'import/named': 'off',
|
||||
'no-plusplus': 'off',
|
||||
'jsx-a11y/label-has-associated-control': [
|
||||
'error',
|
||||
@@ -104,7 +103,10 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': 'error',
|
||||
// Allow empty functions for mocks, default context values, and noop callbacks
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
// Allow underscore prefix for intentionally unused variables (e.g., const { id: _id, ...rest } = props)
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
'func-style': ['error', 'declaration', { allowArrowFunctions: true }],
|
||||
'arrow-body-style': ['error', 'as-needed'],
|
||||
|
||||
|
||||
@@ -4,5 +4,14 @@
|
||||
"tabWidth": 1,
|
||||
"singleQuote": true,
|
||||
"jsxSingleQuote": false,
|
||||
"semi": true
|
||||
"semi": true,
|
||||
"printWidth": 80,
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"endOfLine": "lf",
|
||||
"quoteProps": "as-needed",
|
||||
"proseWrap": "preserve",
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"embeddedLanguageFormatting": "auto"
|
||||
}
|
||||
|
||||
8
frontend/.vscode/settings.json
vendored
Normal file
8
frontend/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"prettier.requireConfig": true
|
||||
}
|
||||
@@ -34,12 +34,18 @@ Embrace the spirit of collaboration and contribute to the success of our open-so
|
||||
### Linting and Setup
|
||||
|
||||
- It is crucial to refrain from disabling ESLint and TypeScript errors within the project. If there is a specific rule that needs to be disabled, provide a clear and justified explanation for doing so. Maintaining the integrity of the linting and type-checking processes ensures code quality and consistency throughout the codebase.
|
||||
- In our project, we rely on several essential ESLint plugins, namely:
|
||||
- [plugin:@typescript-eslint](https://typescript-eslint.io/rules/)
|
||||
- [airbnb styleguide](https://github.com/airbnb/javascript)
|
||||
- [plugin:sonarjs](https://github.com/SonarSource/eslint-plugin-sonarjs)
|
||||
- In our project, we rely on several essential ESLint plugins and configurations:
|
||||
|
||||
To ensure compliance with our coding standards and best practices, we encourage you to refer to the documentation of these plugins. Familiarizing yourself with the ESLint rules they provide will help maintain code quality and consistency throughout the project.
|
||||
- [eslint:recommended](https://eslint.org/docs/latest/rules/) - Core ESLint rules for JavaScript best practices
|
||||
- [plugin:@typescript-eslint](https://typescript-eslint.io/rules/) - TypeScript-specific linting rules
|
||||
- [plugin:react](https://github.com/jsx-eslint/eslint-plugin-react) - React best practices and patterns
|
||||
- [plugin:react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks) - Rules of Hooks enforcement
|
||||
- [plugin:sonarjs](https://github.com/SonarSource/eslint-plugin-sonarjs) - Code quality and complexity analysis
|
||||
- [plugin:prettier](https://github.com/prettier/eslint-plugin-prettier) - Code formatting via Prettier
|
||||
- [simple-import-sort](https://github.com/lydell/eslint-plugin-simple-import-sort) - Automatic import organization
|
||||
- [plugin:jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y) - Accessibility rules for JSX elements
|
||||
|
||||
To ensure compliance with our coding standards and best practices, we encourage you to refer to the documentation of these plugins. Familiarizing yourself with the ESLint rules they provide will help maintain code quality and consistency throughout the project.
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
|
||||
@@ -219,16 +219,11 @@
|
||||
"compression-webpack-plugin": "9.0.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-config-airbnb-typescript": "^16.1.4",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-config-standard": "^16.0.3",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-jest": "^26.9.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-promise": "^5.1.0",
|
||||
"eslint-plugin-react": "^7.24.0",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"eslint-plugin-simple-import-sort": "^7.0.0",
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
padding-left: 30px;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Chart, ChartConfiguration, ChartData, Color } from 'chart.js';
|
||||
// eslint-disable-next-line import/namespace -- side-effect import that registers Chart.js date adapter
|
||||
import * as chartjsAdapter from 'chartjs-adapter-date-fns';
|
||||
import { Timezone } from 'components/CustomTimePicker/timezoneUtils';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { DrawerProps } from 'antd';
|
||||
import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
|
||||
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
|
||||
import { ActionItemProps } from 'container/LogDetailedView/ActionItem';
|
||||
import { IField } from 'types/api/logs/fields';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import { VIEWS } from './constants';
|
||||
|
||||
export type LogDetailProps = {
|
||||
log: ILog | null;
|
||||
selectedTab: VIEWS;
|
||||
onGroupByAttribute?: (fieldKey: string, dataType?: DataTypes) => Promise<void>;
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
isListViewPanel?: boolean;
|
||||
listViewPanelSelectedFields?: IField[] | null;
|
||||
} & Pick<AddToQueryHOCProps, 'onAddToQuery'> &
|
||||
|
||||
@@ -55,11 +55,11 @@ function LogDetailInner({
|
||||
log,
|
||||
onClose,
|
||||
onAddToQuery,
|
||||
onGroupByAttribute,
|
||||
onClickActionItem,
|
||||
selectedTab,
|
||||
isListViewPanel = false,
|
||||
listViewPanelSelectedFields,
|
||||
handleChangeSelectedView,
|
||||
}: LogDetailInnerProps): JSX.Element {
|
||||
const initialContextQuery = useInitialQuery(log);
|
||||
const [contextQuery, setContextQuery] = useState<Query | undefined>(
|
||||
@@ -365,10 +365,10 @@ function LogDetailInner({
|
||||
logData={log}
|
||||
onAddToQuery={onAddToQuery}
|
||||
onClickActionItem={onClickActionItem}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
isListViewPanel={isListViewPanel}
|
||||
selectedOptions={options}
|
||||
listViewPanelSelectedFields={listViewPanelSelectedFields}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
)}
|
||||
{selectedView === VIEW_TYPES.JSON && <JSONView logData={log} />}
|
||||
|
||||
@@ -6,6 +6,7 @@ import cx from 'classnames';
|
||||
import LogDetail from 'components/LogDetail';
|
||||
import { VIEW_TYPES } from 'components/LogDetail/constants';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
|
||||
import { getSanitizedLogBody } from 'container/LogDetailedView/utils';
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import { useActiveLog } from 'hooks/logs/useActiveLog';
|
||||
@@ -108,6 +109,7 @@ type ListLogViewProps = {
|
||||
activeLog?: ILog | null;
|
||||
linesPerRow: number;
|
||||
fontSize: FontSize;
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
};
|
||||
|
||||
function ListLogView({
|
||||
@@ -118,6 +120,7 @@ function ListLogView({
|
||||
activeLog,
|
||||
linesPerRow,
|
||||
fontSize,
|
||||
handleChangeSelectedView,
|
||||
}: ListLogViewProps): JSX.Element {
|
||||
const flattenLogData = useMemo(() => FlatLogData(logData), [logData]);
|
||||
|
||||
@@ -131,7 +134,6 @@ function ListLogView({
|
||||
onAddToQuery: handleAddToQuery,
|
||||
onSetActiveLog: handleSetActiveContextLog,
|
||||
onClearActiveLog: handleClearActiveContextLog,
|
||||
onGroupByAttribute,
|
||||
} = useActiveLog();
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
@@ -255,7 +257,7 @@ function ListLogView({
|
||||
onAddToQuery={handleAddToQuery}
|
||||
selectedTab={VIEW_TYPES.CONTEXT}
|
||||
onClose={handlerClearActiveContextLog}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
@@ -264,6 +266,7 @@ function ListLogView({
|
||||
|
||||
ListLogView.defaultProps = {
|
||||
activeLog: null,
|
||||
handleChangeSelectedView: undefined,
|
||||
};
|
||||
|
||||
LogGeneralField.defaultProps = {
|
||||
|
||||
@@ -39,6 +39,7 @@ function RawLogView({
|
||||
selectedFields = [],
|
||||
fontSize,
|
||||
onLogClick,
|
||||
handleChangeSelectedView,
|
||||
}: RawLogViewProps): JSX.Element {
|
||||
const {
|
||||
isHighlighted: isUrlHighlighted,
|
||||
@@ -52,7 +53,6 @@ function RawLogView({
|
||||
onSetActiveLog,
|
||||
onClearActiveLog,
|
||||
onAddToQuery,
|
||||
onGroupByAttribute,
|
||||
} = useActiveLog();
|
||||
|
||||
const [selectedTab, setSelectedTab] = useState<VIEWS | undefined>();
|
||||
@@ -224,13 +224,12 @@ function RawLogView({
|
||||
onClose={handleCloseLogDetail}
|
||||
onAddToQuery={onAddToQuery}
|
||||
onClickActionItem={onAddToQuery}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
)}
|
||||
</RawLogViewContainer>
|
||||
);
|
||||
}
|
||||
|
||||
RawLogView.defaultProps = {
|
||||
isActiveLog: false,
|
||||
isReadOnly: false,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import { MouseEvent } from 'react';
|
||||
import { IField } from 'types/api/logs/fields';
|
||||
@@ -14,6 +15,7 @@ export interface RawLogViewProps {
|
||||
fontSize: FontSize;
|
||||
selectedFields?: IField[];
|
||||
onLogClick?: (log: ILog, event: MouseEvent) => void;
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
}
|
||||
|
||||
export interface RawLogContentProps {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Switch, Typography } from 'antd';
|
||||
import LogsFormatOptionsMenu from 'components/LogsFormatOptionsMenu/LogsFormatOptionsMenu';
|
||||
import { MAX_LOGS_LIST_SIZE } from 'constants/liveTail';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
|
||||
import GoToTop from 'container/GoToTop';
|
||||
import { useOptionsMenu } from 'container/OptionsMenu';
|
||||
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
|
||||
@@ -21,7 +22,13 @@ import { ILiveLogsLog } from '../LiveLogsList/types';
|
||||
import LiveLogsListChart from '../LiveLogsListChart';
|
||||
import { QueryHistoryState } from '../types';
|
||||
|
||||
function LiveLogsContainer(): JSX.Element {
|
||||
interface LiveLogsContainerProps {
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
}
|
||||
|
||||
function LiveLogsContainer({
|
||||
handleChangeSelectedView,
|
||||
}: LiveLogsContainerProps): JSX.Element {
|
||||
const location = useLocation();
|
||||
const [logs, setLogs] = useState<ILiveLogsLog[]>([]);
|
||||
const { currentQuery, stagedQuery } = useQueryBuilder();
|
||||
@@ -247,6 +254,7 @@ function LiveLogsContainer(): JSX.Element {
|
||||
<LiveLogsList
|
||||
logs={logs}
|
||||
isLoading={initialLoading && logs.length === 0}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -256,4 +264,8 @@ function LiveLogsContainer(): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
LiveLogsContainer.defaultProps = {
|
||||
handleChangeSelectedView: undefined,
|
||||
};
|
||||
|
||||
export default LiveLogsContainer;
|
||||
|
||||
@@ -25,7 +25,11 @@ import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
||||
|
||||
import { LiveLogsListProps } from './types';
|
||||
|
||||
function LiveLogsList({ logs, isLoading }: LiveLogsListProps): JSX.Element {
|
||||
function LiveLogsList({
|
||||
logs,
|
||||
isLoading,
|
||||
handleChangeSelectedView,
|
||||
}: LiveLogsListProps): JSX.Element {
|
||||
const ref = useRef<VirtuosoHandle>(null);
|
||||
|
||||
const { isConnectionLoading } = useEventSource();
|
||||
@@ -36,7 +40,6 @@ function LiveLogsList({ logs, isLoading }: LiveLogsListProps): JSX.Element {
|
||||
activeLog,
|
||||
onClearActiveLog,
|
||||
onAddToQuery,
|
||||
onGroupByAttribute,
|
||||
onSetActiveLog,
|
||||
} = useActiveLog();
|
||||
|
||||
@@ -72,6 +75,7 @@ function LiveLogsList({ logs, isLoading }: LiveLogsListProps): JSX.Element {
|
||||
linesPerRow={options.maxLines}
|
||||
selectedFields={selectedFields}
|
||||
fontSize={options.fontSize}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -85,10 +89,12 @@ function LiveLogsList({ logs, isLoading }: LiveLogsListProps): JSX.Element {
|
||||
onAddToQuery={onAddToQuery}
|
||||
onSetActiveLog={onSetActiveLog}
|
||||
fontSize={options.fontSize}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[
|
||||
handleChangeSelectedView,
|
||||
onAddToQuery,
|
||||
onSetActiveLog,
|
||||
options.fontSize,
|
||||
@@ -147,6 +153,7 @@ function LiveLogsList({ logs, isLoading }: LiveLogsListProps): JSX.Element {
|
||||
appendTo: 'end',
|
||||
activeLogIndex,
|
||||
}}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
) : (
|
||||
<Card style={{ width: '100%' }} bodyStyle={CARD_BODY_STYLE}>
|
||||
@@ -170,12 +177,11 @@ function LiveLogsList({ logs, isLoading }: LiveLogsListProps): JSX.Element {
|
||||
log={activeLog}
|
||||
onClose={onClearActiveLog}
|
||||
onAddToQuery={onAddToQuery}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
onClickActionItem={onAddToQuery}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(LiveLogsList);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
|
||||
export interface ILiveLogsLog {
|
||||
@@ -8,4 +9,5 @@ export interface ILiveLogsLog {
|
||||
export type LiveLogsListProps = {
|
||||
logs: ILiveLogsLog[];
|
||||
isLoading: boolean;
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
};
|
||||
|
||||
@@ -12,13 +12,13 @@ import {
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
|
||||
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
|
||||
import { OptionsQuery } from 'container/OptionsMenu/types';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { ChevronDown, ChevronRight, Search } from 'lucide-react';
|
||||
import { ReactNode, useState } from 'react';
|
||||
import { IField } from 'types/api/logs/fields';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import { ActionItemProps } from './ActionItem';
|
||||
import TableView from './TableView';
|
||||
@@ -29,7 +29,7 @@ interface OverviewProps {
|
||||
isListViewPanel?: boolean;
|
||||
selectedOptions: OptionsQuery;
|
||||
listViewPanelSelectedFields?: IField[] | null;
|
||||
onGroupByAttribute?: (fieldKey: string, dataType?: DataTypes) => Promise<void>;
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
}
|
||||
|
||||
type Props = OverviewProps &
|
||||
@@ -42,8 +42,8 @@ function Overview({
|
||||
onClickActionItem,
|
||||
isListViewPanel = false,
|
||||
selectedOptions,
|
||||
onGroupByAttribute,
|
||||
listViewPanelSelectedFields,
|
||||
handleChangeSelectedView,
|
||||
}: Props): JSX.Element {
|
||||
const [isWrapWord, setIsWrapWord] = useState<boolean>(true);
|
||||
const [isSearchVisible, setIsSearchVisible] = useState<boolean>(false);
|
||||
@@ -208,11 +208,11 @@ function Overview({
|
||||
logData={logData}
|
||||
onAddToQuery={onAddToQuery}
|
||||
fieldSearchInput={fieldSearchInput}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
onClickActionItem={onClickActionItem}
|
||||
isListViewPanel={isListViewPanel}
|
||||
selectedOptions={selectedOptions}
|
||||
listViewPanelSelectedFields={listViewPanelSelectedFields}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
@@ -227,7 +227,7 @@ function Overview({
|
||||
Overview.defaultProps = {
|
||||
isListViewPanel: false,
|
||||
listViewPanelSelectedFields: null,
|
||||
onGroupByAttribute: undefined,
|
||||
handleChangeSelectedView: undefined,
|
||||
};
|
||||
|
||||
export default Overview;
|
||||
|
||||
@@ -13,6 +13,7 @@ import AddToQueryHOC, {
|
||||
import { ResizeTable } from 'components/ResizeTable';
|
||||
import { OPERATORS } from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
|
||||
import { RESTRICTED_SELECTED_FIELDS } from 'container/LogsFilters/config';
|
||||
import { MetricsType } from 'container/MetricsApplication/constant';
|
||||
import { FontSize, OptionsQuery } from 'container/OptionsMenu/types';
|
||||
@@ -47,7 +48,7 @@ interface TableViewProps {
|
||||
selectedOptions: OptionsQuery;
|
||||
isListViewPanel?: boolean;
|
||||
listViewPanelSelectedFields?: IField[] | null;
|
||||
onGroupByAttribute?: (fieldKey: string, dataType?: DataTypes) => Promise<void>;
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
}
|
||||
|
||||
type Props = TableViewProps &
|
||||
@@ -61,8 +62,8 @@ function TableView({
|
||||
onClickActionItem,
|
||||
isListViewPanel = false,
|
||||
selectedOptions,
|
||||
onGroupByAttribute,
|
||||
listViewPanelSelectedFields,
|
||||
handleChangeSelectedView,
|
||||
}: Props): JSX.Element | null {
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
const [isfilterInLoading, setIsFilterInLoading] = useState<boolean>(false);
|
||||
@@ -295,7 +296,7 @@ function TableView({
|
||||
isfilterInLoading={isfilterInLoading}
|
||||
isfilterOutLoading={isfilterOutLoading}
|
||||
onClickHandler={onClickHandler}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@@ -338,7 +339,7 @@ function TableView({
|
||||
TableView.defaultProps = {
|
||||
isListViewPanel: false,
|
||||
listViewPanelSelectedFields: null,
|
||||
onGroupByAttribute: undefined,
|
||||
handleChangeSelectedView: undefined,
|
||||
};
|
||||
|
||||
export interface DataType {
|
||||
|
||||
@@ -7,15 +7,24 @@ import GroupByIcon from 'assets/CustomIcons/GroupByIcon';
|
||||
import cx from 'classnames';
|
||||
import CopyClipboardHOC from 'components/Logs/CopyClipboardHOC';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { OPERATORS } from 'constants/queryBuilder';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
|
||||
import { RESTRICTED_SELECTED_FIELDS } from 'container/LogsFilters/config';
|
||||
import { MetricsType } from 'container/MetricsApplication/constant';
|
||||
import { useGetSearchQueryParam } from 'hooks/queryBuilder/useGetSearchQueryParam';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { ICurrentQueryData } from 'hooks/useHandleExplorerTabChange';
|
||||
import { ArrowDownToDot, ArrowUpFromDot, Ellipsis } from 'lucide-react';
|
||||
import { ExplorerViews } from 'pages/LogsExplorer/utils';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import {
|
||||
BaseAutocompleteData,
|
||||
DataTypes,
|
||||
} from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import { DataType } from '../TableView';
|
||||
import {
|
||||
@@ -33,7 +42,6 @@ interface ITableViewActionsProps {
|
||||
isListViewPanel: boolean;
|
||||
isfilterInLoading: boolean;
|
||||
isfilterOutLoading: boolean;
|
||||
onGroupByAttribute?: (fieldKey: string, dataType?: DataTypes) => Promise<void>;
|
||||
onClickHandler: (
|
||||
operator: string,
|
||||
fieldKey: string,
|
||||
@@ -41,6 +49,7 @@ interface ITableViewActionsProps {
|
||||
dataType: string | undefined,
|
||||
logType: MetricsType | undefined,
|
||||
) => () => void;
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
}
|
||||
|
||||
// Memoized Tree Component
|
||||
@@ -118,10 +127,12 @@ export default function TableViewActions(
|
||||
isfilterInLoading,
|
||||
isfilterOutLoading,
|
||||
onClickHandler,
|
||||
onGroupByAttribute,
|
||||
handleChangeSelectedView,
|
||||
} = props;
|
||||
|
||||
const { pathname } = useLocation();
|
||||
const { stagedQuery, updateQueriesData } = useQueryBuilder();
|
||||
const viewName = useGetSearchQueryParam(QueryParams.viewName) || '';
|
||||
const { dataType, logType: fieldType } = getFieldAttributes(record.field);
|
||||
|
||||
// there is no option for where clause in old logs explorer and live logs page
|
||||
@@ -145,6 +156,42 @@ export default function TableViewActions(
|
||||
|
||||
const fieldFilterKey = filterKeyForField(fieldData.field);
|
||||
|
||||
const handleGroupByAttribute = useCallback((): void => {
|
||||
if (!stagedQuery) return;
|
||||
const normalizedDataType: DataTypes | undefined =
|
||||
dataType && Object.values(DataTypes).includes(dataType as DataTypes)
|
||||
? (dataType as DataTypes)
|
||||
: undefined;
|
||||
|
||||
const updatedQuery = updateQueriesData(stagedQuery, 'queryData', (item) => {
|
||||
const newGroupByItem: BaseAutocompleteData = {
|
||||
key: fieldFilterKey,
|
||||
type: fieldType || '',
|
||||
dataType: normalizedDataType,
|
||||
};
|
||||
|
||||
const updatedGroupBy = [...(item.groupBy || []), newGroupByItem];
|
||||
|
||||
return { ...item, groupBy: updatedGroupBy };
|
||||
});
|
||||
|
||||
const queryData: ICurrentQueryData = {
|
||||
name: viewName,
|
||||
id: updatedQuery.id,
|
||||
query: updatedQuery,
|
||||
};
|
||||
|
||||
handleChangeSelectedView?.(ExplorerViews.TIMESERIES, queryData);
|
||||
}, [
|
||||
stagedQuery,
|
||||
updateQueriesData,
|
||||
fieldFilterKey,
|
||||
fieldType,
|
||||
dataType,
|
||||
handleChangeSelectedView,
|
||||
viewName,
|
||||
]);
|
||||
|
||||
// Memoize textToCopy computation
|
||||
const textToCopy = useMemo(() => {
|
||||
let text = fieldData.value;
|
||||
@@ -268,9 +315,7 @@ export default function TableViewActions(
|
||||
className="group-by-clause"
|
||||
type="text"
|
||||
icon={<GroupByIcon />}
|
||||
onClick={(): Promise<void> | void =>
|
||||
onGroupByAttribute?.(fieldFilterKey)
|
||||
}
|
||||
onClick={handleGroupByAttribute}
|
||||
>
|
||||
Group By Attribute
|
||||
</Button>
|
||||
@@ -348,9 +393,7 @@ export default function TableViewActions(
|
||||
className="group-by-clause"
|
||||
type="text"
|
||||
icon={<GroupByIcon />}
|
||||
onClick={(): Promise<void> | void =>
|
||||
onGroupByAttribute?.(fieldFilterKey)
|
||||
}
|
||||
onClick={handleGroupByAttribute}
|
||||
>
|
||||
Group By Attribute
|
||||
</Button>
|
||||
@@ -373,5 +416,5 @@ export default function TableViewActions(
|
||||
}
|
||||
|
||||
TableViewActions.defaultProps = {
|
||||
onGroupByAttribute: undefined,
|
||||
handleChangeSelectedView: undefined,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { RESTRICTED_SELECTED_FIELDS } from 'container/LogsFilters/config';
|
||||
import { useGetSearchQueryParam } from 'hooks/queryBuilder/useGetSearchQueryParam';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { ExplorerViews } from 'pages/LogsExplorer/utils';
|
||||
|
||||
import TableViewActions from '../TableViewActions';
|
||||
import useAsyncJSONProcessing from '../useAsyncJSONProcessing';
|
||||
@@ -49,6 +52,20 @@ jest.mock('../useAsyncJSONProcessing', () => ({
|
||||
default: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('antd', () => {
|
||||
const antd = jest.requireActual('antd');
|
||||
return {
|
||||
...antd,
|
||||
// Render popover content inline to make its children testable
|
||||
Popover: ({ content, children }: any): JSX.Element => (
|
||||
<div data-testid="popover">
|
||||
<div data-testid="popover-content">{content}</div>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('providers/Timezone', () => ({
|
||||
useTimezone: (): {
|
||||
formatTimezoneAdjustedTimestamp: (timestamp: string) => string;
|
||||
@@ -71,29 +88,35 @@ jest.mock('react-router-dom', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('hooks/queryBuilder/useQueryBuilder');
|
||||
jest.mock('hooks/queryBuilder/useGetSearchQueryParam');
|
||||
|
||||
describe('TableViewActions', () => {
|
||||
const TEST_VALUE = 'test value';
|
||||
const TEST_FIELD = 'test-field';
|
||||
const ACTION_BUTTON_TEST_ID = '.action-btn';
|
||||
const defaultProps = {
|
||||
fieldData: {
|
||||
field: 'test-field',
|
||||
field: TEST_FIELD,
|
||||
value: TEST_VALUE,
|
||||
},
|
||||
record: {
|
||||
key: 'test-key',
|
||||
field: 'test-field',
|
||||
field: TEST_FIELD,
|
||||
value: TEST_VALUE,
|
||||
},
|
||||
isListViewPanel: false,
|
||||
isfilterInLoading: false,
|
||||
isfilterOutLoading: false,
|
||||
onClickHandler: jest.fn(),
|
||||
onGroupByAttribute: jest.fn(),
|
||||
handleChangeSelectedView: jest.fn(),
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockCopyToClipboard = jest.fn();
|
||||
mockNotificationsSuccess = jest.fn();
|
||||
defaultProps.onClickHandler = jest.fn();
|
||||
defaultProps.handleChangeSelectedView = jest.fn();
|
||||
|
||||
// Default mock for useAsyncJSONProcessing
|
||||
const mockUseAsyncJSONProcessing = jest.mocked(useAsyncJSONProcessing);
|
||||
@@ -102,6 +125,24 @@ describe('TableViewActions', () => {
|
||||
treeData: null,
|
||||
error: null,
|
||||
});
|
||||
|
||||
// Default mock for useQueryBuilder
|
||||
jest.mocked(useQueryBuilder).mockReturnValue({
|
||||
stagedQuery: null,
|
||||
updateQueriesData: jest.fn((query, type, callback) => {
|
||||
const updatedBuilder = {
|
||||
...query.builder,
|
||||
[type]: query.builder[type].map(callback),
|
||||
};
|
||||
return {
|
||||
...query,
|
||||
builder: updatedBuilder,
|
||||
};
|
||||
}),
|
||||
} as any);
|
||||
|
||||
// Default mock for useGetSearchQueryParam
|
||||
jest.mocked(useGetSearchQueryParam).mockReturnValue(null);
|
||||
});
|
||||
|
||||
it('should render without crashing', () => {
|
||||
@@ -113,7 +154,7 @@ describe('TableViewActions', () => {
|
||||
isfilterInLoading={defaultProps.isfilterInLoading}
|
||||
isfilterOutLoading={defaultProps.isfilterOutLoading}
|
||||
onClickHandler={defaultProps.onClickHandler}
|
||||
onGroupByAttribute={defaultProps.onGroupByAttribute}
|
||||
handleChangeSelectedView={defaultProps.handleChangeSelectedView}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByText(TEST_VALUE)).toBeInTheDocument();
|
||||
@@ -135,7 +176,7 @@ describe('TableViewActions', () => {
|
||||
isfilterInLoading={defaultProps.isfilterInLoading}
|
||||
isfilterOutLoading={defaultProps.isfilterOutLoading}
|
||||
onClickHandler={defaultProps.onClickHandler}
|
||||
onGroupByAttribute={defaultProps.onGroupByAttribute}
|
||||
handleChangeSelectedView={defaultProps.handleChangeSelectedView}
|
||||
/>,
|
||||
);
|
||||
// Verify that action buttons are not rendered for restricted fields
|
||||
@@ -154,13 +195,100 @@ describe('TableViewActions', () => {
|
||||
isfilterInLoading={defaultProps.isfilterInLoading}
|
||||
isfilterOutLoading={defaultProps.isfilterOutLoading}
|
||||
onClickHandler={defaultProps.onClickHandler}
|
||||
onGroupByAttribute={defaultProps.onGroupByAttribute}
|
||||
handleChangeSelectedView={defaultProps.handleChangeSelectedView}
|
||||
/>,
|
||||
);
|
||||
// Verify that action buttons are rendered for non-restricted fields
|
||||
expect(container.querySelector(ACTION_BUTTON_TEST_ID)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should call handleChangeSelectedView when clicking group by', () => {
|
||||
const mockStagedQuery = {
|
||||
id: 'test-query-id',
|
||||
queryType: 'queryBuilder',
|
||||
builder: {
|
||||
queryData: [
|
||||
{
|
||||
queryName: 'A',
|
||||
dataSource: 'logs',
|
||||
aggregateOperator: 'count',
|
||||
functions: [],
|
||||
filter: {},
|
||||
groupBy: [],
|
||||
expression: '',
|
||||
disabled: false,
|
||||
having: [],
|
||||
limit: null,
|
||||
stepInterval: null,
|
||||
orderBy: [],
|
||||
legend: '',
|
||||
},
|
||||
],
|
||||
queryFormulas: [],
|
||||
queryTraceOperator: [],
|
||||
},
|
||||
promql: [],
|
||||
clickhouse_sql: [],
|
||||
};
|
||||
|
||||
const mockUpdateQueriesData = jest.fn((query, type, callback) => {
|
||||
const section = query.builder?.[type];
|
||||
if (!Array.isArray(section)) {
|
||||
return query;
|
||||
}
|
||||
return {
|
||||
...query,
|
||||
builder: {
|
||||
...query.builder,
|
||||
[type]: section.map(callback),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
jest.mocked(useQueryBuilder).mockReturnValue({
|
||||
stagedQuery: mockStagedQuery,
|
||||
updateQueriesData: mockUpdateQueriesData,
|
||||
} as any);
|
||||
|
||||
jest.mocked(useGetSearchQueryParam).mockReturnValue(null);
|
||||
|
||||
render(
|
||||
<TableViewActions
|
||||
fieldData={defaultProps.fieldData}
|
||||
record={defaultProps.record}
|
||||
isListViewPanel={defaultProps.isListViewPanel}
|
||||
isfilterInLoading={defaultProps.isfilterInLoading}
|
||||
isfilterOutLoading={defaultProps.isfilterOutLoading}
|
||||
onClickHandler={defaultProps.onClickHandler}
|
||||
handleChangeSelectedView={defaultProps.handleChangeSelectedView}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByText('Group By Attribute'));
|
||||
|
||||
expect(defaultProps.handleChangeSelectedView).toHaveBeenCalledWith(
|
||||
ExplorerViews.TIMESERIES,
|
||||
expect.objectContaining({
|
||||
name: '',
|
||||
id: 'test-query-id',
|
||||
query: expect.objectContaining({
|
||||
builder: expect.objectContaining({
|
||||
queryData: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
groupBy: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
key: TEST_FIELD,
|
||||
type: '',
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not render action buttons in list view panel', () => {
|
||||
const { container } = render(
|
||||
<TableViewActions
|
||||
@@ -170,7 +298,7 @@ describe('TableViewActions', () => {
|
||||
isfilterInLoading={defaultProps.isfilterInLoading}
|
||||
isfilterOutLoading={defaultProps.isfilterOutLoading}
|
||||
onClickHandler={defaultProps.onClickHandler}
|
||||
onGroupByAttribute={defaultProps.onGroupByAttribute}
|
||||
handleChangeSelectedView={defaultProps.handleChangeSelectedView}
|
||||
/>,
|
||||
);
|
||||
// Verify that action buttons are not rendered in list view panel
|
||||
@@ -200,7 +328,7 @@ describe('TableViewActions', () => {
|
||||
isfilterInLoading: false,
|
||||
isfilterOutLoading: false,
|
||||
onClickHandler: jest.fn(),
|
||||
onGroupByAttribute: jest.fn(),
|
||||
handleChangeSelectedView: jest.fn(),
|
||||
};
|
||||
|
||||
// Render component with body field
|
||||
@@ -212,7 +340,7 @@ describe('TableViewActions', () => {
|
||||
isfilterInLoading={bodyProps.isfilterInLoading}
|
||||
isfilterOutLoading={bodyProps.isfilterOutLoading}
|
||||
onClickHandler={bodyProps.onClickHandler}
|
||||
onGroupByAttribute={bodyProps.onGroupByAttribute}
|
||||
handleChangeSelectedView={bodyProps.handleChangeSelectedView}
|
||||
/>,
|
||||
);
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ function ColumnView({
|
||||
onSetActiveLog: handleSetActiveLog,
|
||||
onClearActiveLog: handleClearActiveLog,
|
||||
onAddToQuery: handleAddToQuery,
|
||||
onGroupByAttribute: handleGroupByAttribute,
|
||||
} = useActiveLog();
|
||||
|
||||
const [showActiveLog, setShowActiveLog] = useState<boolean>(false);
|
||||
@@ -271,7 +270,6 @@ function ColumnView({
|
||||
onClose={handleLogDetailClose}
|
||||
onAddToQuery={handleAddToQuery}
|
||||
onClickActionItem={handleAddToQuery}
|
||||
onGroupByAttribute={handleGroupByAttribute}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -58,7 +58,7 @@ const CustomTableRow: TableComponents<ILog>['TableRow'] = ({
|
||||
|
||||
const InfinityTable = forwardRef<TableVirtuosoHandle, InfinityTableProps>(
|
||||
function InfinityTableView(
|
||||
{ isLoading, tableViewProps, infitiyTableProps },
|
||||
{ isLoading, tableViewProps, infitiyTableProps, handleChangeSelectedView },
|
||||
ref,
|
||||
): JSX.Element | null {
|
||||
const {
|
||||
@@ -72,7 +72,6 @@ const InfinityTable = forwardRef<TableVirtuosoHandle, InfinityTableProps>(
|
||||
onSetActiveLog,
|
||||
onClearActiveLog,
|
||||
onAddToQuery,
|
||||
onGroupByAttribute,
|
||||
} = useActiveLog();
|
||||
|
||||
const { dataSource, columns } = useTableView({
|
||||
@@ -187,7 +186,7 @@ const InfinityTable = forwardRef<TableVirtuosoHandle, InfinityTableProps>(
|
||||
onClose={handleClearActiveContextLog}
|
||||
onAddToQuery={handleAddToQuery}
|
||||
selectedTab={VIEW_TYPES.CONTEXT}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
)}
|
||||
<LogDetail
|
||||
@@ -196,7 +195,7 @@ const InfinityTable = forwardRef<TableVirtuosoHandle, InfinityTableProps>(
|
||||
onClose={onClearActiveLog}
|
||||
onAddToQuery={onAddToQuery}
|
||||
onClickActionItem={onAddToQuery}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { UseTableViewProps } from 'components/Logs/TableView/types';
|
||||
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
|
||||
|
||||
export type InfinityTableProps = {
|
||||
isLoading?: boolean;
|
||||
@@ -6,4 +7,5 @@ export type InfinityTableProps = {
|
||||
infitiyTableProps?: {
|
||||
onEndReached: (index: number) => void;
|
||||
};
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
|
||||
import APIError from 'types/api/error';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
@@ -12,4 +13,5 @@ export type LogsExplorerListProps = {
|
||||
error?: Error | APIError;
|
||||
isFilterApplied: boolean;
|
||||
isFrequencyChartVisible: boolean;
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
};
|
||||
|
||||
@@ -48,6 +48,7 @@ function LogsExplorerList({
|
||||
isError,
|
||||
error,
|
||||
isFilterApplied,
|
||||
handleChangeSelectedView,
|
||||
}: LogsExplorerListProps): JSX.Element {
|
||||
const ref = useRef<VirtuosoHandle>(null);
|
||||
const { activeLogId } = useCopyLogLink();
|
||||
@@ -56,7 +57,6 @@ function LogsExplorerList({
|
||||
activeLog,
|
||||
onClearActiveLog,
|
||||
onAddToQuery,
|
||||
onGroupByAttribute,
|
||||
onSetActiveLog,
|
||||
} = useActiveLog();
|
||||
|
||||
@@ -100,6 +100,7 @@ function LogsExplorerList({
|
||||
linesPerRow={options.maxLines}
|
||||
selectedFields={selectedFields}
|
||||
fontSize={options.fontSize}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -114,11 +115,13 @@ function LogsExplorerList({
|
||||
activeLog={activeLog}
|
||||
fontSize={options.fontSize}
|
||||
linesPerRow={options.maxLines}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[
|
||||
activeLog,
|
||||
handleChangeSelectedView,
|
||||
onAddToQuery,
|
||||
onSetActiveLog,
|
||||
options.fontSize,
|
||||
@@ -149,10 +152,10 @@ function LogsExplorerList({
|
||||
activeLogIndex,
|
||||
}}
|
||||
infitiyTableProps={{ onEndReached }}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function getMarginTop(): string {
|
||||
switch (options.fontSize) {
|
||||
case FontSize.SMALL:
|
||||
@@ -195,6 +198,7 @@ function LogsExplorerList({
|
||||
onEndReached,
|
||||
getItemContent,
|
||||
selectedFields,
|
||||
handleChangeSelectedView,
|
||||
]);
|
||||
|
||||
const isTraceToLogsNavigation = useMemo(() => {
|
||||
@@ -273,8 +277,8 @@ function LogsExplorerList({
|
||||
log={activeLog}
|
||||
onClose={onClearActiveLog}
|
||||
onAddToQuery={onAddToQuery}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
onClickActionItem={onAddToQuery}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -447,8 +447,9 @@ function LogsExplorerViewsContainer({
|
||||
)}
|
||||
|
||||
<div className="logs-explorer-views-type-content">
|
||||
{showLiveLogs && <LiveLogs />}
|
||||
|
||||
{showLiveLogs && (
|
||||
<LiveLogs handleChangeSelectedView={handleChangeSelectedView} />
|
||||
)}
|
||||
{selectedPanelType === PANEL_TYPES.LIST && !showLiveLogs && (
|
||||
<LogsExplorerList
|
||||
isLoading={isLoading}
|
||||
@@ -460,9 +461,9 @@ function LogsExplorerViewsContainer({
|
||||
isError={isError}
|
||||
error={error as APIError}
|
||||
isFilterApplied={!isEmpty(listQuery?.filters?.items)}
|
||||
handleChangeSelectedView={handleChangeSelectedView}
|
||||
/>
|
||||
)}
|
||||
|
||||
{selectedPanelType === PANEL_TYPES.TIME_SERIES && !showLiveLogs && (
|
||||
<div className="time-series-view-container">
|
||||
<div className="time-series-view-container-header">
|
||||
@@ -483,7 +484,6 @@ function LogsExplorerViewsContainer({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedPanelType === PANEL_TYPES.TABLE && !showLiveLogs && (
|
||||
<LogsExplorerTable
|
||||
data={
|
||||
|
||||
@@ -89,7 +89,6 @@ function LogsPanelComponent({
|
||||
onSetActiveLog,
|
||||
onClearActiveLog,
|
||||
onAddToQuery,
|
||||
onGroupByAttribute,
|
||||
} = useActiveLog();
|
||||
|
||||
const handleRow = useCallback(
|
||||
@@ -171,7 +170,6 @@ function LogsPanelComponent({
|
||||
onClose={onClearActiveLog}
|
||||
onAddToQuery={onAddToQuery}
|
||||
onClickActionItem={onAddToQuery}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
isListViewPanel
|
||||
listViewPanelSelectedFields={widget?.selectedLogFields}
|
||||
/>
|
||||
|
||||
@@ -38,7 +38,6 @@ function LogsTable(props: LogsTableProps): JSX.Element {
|
||||
activeLog,
|
||||
onClearActiveLog,
|
||||
onAddToQuery,
|
||||
onGroupByAttribute,
|
||||
onSetActiveLog,
|
||||
} = useActiveLog();
|
||||
|
||||
@@ -157,7 +156,6 @@ function LogsTable(props: LogsTableProps): JSX.Element {
|
||||
selectedTab={VIEW_TYPES.OVERVIEW}
|
||||
log={activeLog}
|
||||
onClose={onClearActiveLog}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
onAddToQuery={onAddToQuery}
|
||||
onClickActionItem={onAddToQuery}
|
||||
/>
|
||||
|
||||
@@ -14,7 +14,6 @@ function LogsList({ logs }: LogsListProps): JSX.Element {
|
||||
onSetActiveLog,
|
||||
onClearActiveLog,
|
||||
onAddToQuery,
|
||||
onGroupByAttribute,
|
||||
} = useActiveLog();
|
||||
|
||||
const makeLogDetailsHandler = (log: ILog) => (): void => onSetActiveLog(log);
|
||||
@@ -49,7 +48,6 @@ function LogsList({ logs }: LogsListProps): JSX.Element {
|
||||
onClose={onClearActiveLog}
|
||||
onAddToQuery={onAddToQuery}
|
||||
onClickActionItem={onAddToQuery}
|
||||
onGroupByAttribute={onGroupByAttribute}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';
|
||||
import { liveLogsCompositeQuery } from 'container/LiveLogs/constants';
|
||||
import LiveLogsContainer from 'container/LiveLogs/LiveLogsContainer';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
@@ -6,7 +7,11 @@ import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||
import { useEffect } from 'react';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
function LiveLogs(): JSX.Element {
|
||||
interface LiveLogsProps {
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
}
|
||||
|
||||
function LiveLogs({ handleChangeSelectedView }: LiveLogsProps): JSX.Element {
|
||||
useShareBuilderUrl({ defaultValue: liveLogsCompositeQuery });
|
||||
const { handleSetConfig } = useQueryBuilder();
|
||||
|
||||
@@ -14,7 +19,13 @@ function LiveLogs(): JSX.Element {
|
||||
handleSetConfig(PANEL_TYPES.LIST, DataSource.LOGS);
|
||||
}, [handleSetConfig]);
|
||||
|
||||
return <LiveLogsContainer />;
|
||||
return (
|
||||
<LiveLogsContainer handleChangeSelectedView={handleChangeSelectedView} />
|
||||
);
|
||||
}
|
||||
|
||||
LiveLogs.defaultProps = {
|
||||
handleChangeSelectedView: undefined,
|
||||
};
|
||||
|
||||
export default LiveLogs;
|
||||
|
||||
@@ -79,21 +79,19 @@ function LogsExplorer(): JSX.Element {
|
||||
|
||||
const handleChangeSelectedView = useCallback(
|
||||
(view: ExplorerViews, querySearchParameters?: ICurrentQueryData): void => {
|
||||
handleSetConfig(
|
||||
defaultTo(explorerViewToPanelType[view], PANEL_TYPES.LIST),
|
||||
DataSource.LOGS,
|
||||
const nextPanelType = defaultTo(
|
||||
explorerViewToPanelType[view],
|
||||
PANEL_TYPES.LIST,
|
||||
);
|
||||
|
||||
handleSetConfig(nextPanelType, DataSource.LOGS);
|
||||
setSelectedView(view);
|
||||
|
||||
if (view !== ExplorerViews.LIST) {
|
||||
setShowLiveLogs(false);
|
||||
}
|
||||
|
||||
handleExplorerTabChange(
|
||||
explorerViewToPanelType[view],
|
||||
querySearchParameters,
|
||||
);
|
||||
handleExplorerTabChange(nextPanelType, querySearchParameters);
|
||||
},
|
||||
[handleSetConfig, handleExplorerTabChange, setSelectedView],
|
||||
);
|
||||
|
||||
@@ -26,18 +26,6 @@ body {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// Theme transition animations
|
||||
* {
|
||||
transition: background-color 0.3s ease, color 0.3s ease, border-color 0.3s ease,
|
||||
box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
// For components that shouldn't transition (like loading spinners, animations)
|
||||
.no-transition,
|
||||
.no-transition * {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
// Respect user's reduced motion preference
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
* {
|
||||
@@ -178,7 +166,7 @@ body {
|
||||
height: 11px;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
transition: transform 0.2s ease;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
@@ -292,7 +280,7 @@ body {
|
||||
height: 11px;
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
transition: transform 0.2s ease;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -344,6 +344,8 @@ const customRender = (
|
||||
});
|
||||
};
|
||||
|
||||
// eslint-disable-next-line import/export -- re-exporting custom render alongside @testing-library/react
|
||||
export * from '@testing-library/react';
|
||||
export { default as userEvent } from '@testing-library/user-event';
|
||||
// eslint-disable-next-line import/export -- custom render wraps the original
|
||||
export { customRender as render };
|
||||
|
||||
Reference in New Issue
Block a user