mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-14 10:41:32 +08:00
feat: added goto top feature in list logs veiw (#3146)
This commit is contained in:
parent
7f9ba6c43a
commit
22d0aa951c
29
frontend/src/container/GoToTop/index.tsx
Normal file
29
frontend/src/container/GoToTop/index.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { ArrowUpOutlined } from '@ant-design/icons';
|
||||||
|
import { FloatButton } from 'antd';
|
||||||
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||||
|
// hooks
|
||||||
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import useScrollToTop from 'hooks/useScrollToTop';
|
||||||
|
|
||||||
|
function GoToTop(): JSX.Element | null {
|
||||||
|
const { isVisible, scrollToTop } = useScrollToTop();
|
||||||
|
|
||||||
|
const { panelType } = useQueryBuilder();
|
||||||
|
|
||||||
|
if (!isVisible) return null;
|
||||||
|
|
||||||
|
if (panelType === PANEL_TYPES.LIST) {
|
||||||
|
return (
|
||||||
|
<FloatButton
|
||||||
|
onClick={scrollToTop}
|
||||||
|
shape="circle"
|
||||||
|
type="primary"
|
||||||
|
icon={<ArrowUpOutlined />}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GoToTop;
|
@ -1,5 +1,4 @@
|
|||||||
import { TabsProps } from 'antd';
|
import { TabsProps } from 'antd';
|
||||||
import axios from 'axios';
|
|
||||||
import LogDetail from 'components/LogDetail';
|
import LogDetail from 'components/LogDetail';
|
||||||
import TabLabel from 'components/TabLabel';
|
import TabLabel from 'components/TabLabel';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
@ -13,6 +12,7 @@ import { queryParamNamesMap } from 'constants/queryBuilderQueryNames';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
|
import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
|
||||||
import ExportPanel from 'container/ExportPanel';
|
import ExportPanel from 'container/ExportPanel';
|
||||||
|
import GoToTop from 'container/GoToTop';
|
||||||
import LogsExplorerChart from 'container/LogsExplorerChart';
|
import LogsExplorerChart from 'container/LogsExplorerChart';
|
||||||
import LogsExplorerList from 'container/LogsExplorerList';
|
import LogsExplorerList from 'container/LogsExplorerList';
|
||||||
import LogsExplorerTable from 'container/LogsExplorerTable';
|
import LogsExplorerTable from 'container/LogsExplorerTable';
|
||||||
@ -22,6 +22,7 @@ import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
|||||||
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
|
import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils';
|
||||||
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
|
import { useGetExplorerQueryRange } from 'hooks/queryBuilder/useGetExplorerQueryRange';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import useAxiosError from 'hooks/useAxiosError';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||||
import { chooseAutocompleteFromCustomValue } from 'lib/newQueryBuilder/chooseAutocompleteFromCustomValue';
|
import { chooseAutocompleteFromCustomValue } from 'lib/newQueryBuilder/chooseAutocompleteFromCustomValue';
|
||||||
@ -81,6 +82,8 @@ function LogsExplorerViews(): JSX.Element {
|
|||||||
const [logs, setLogs] = useState<ILog[]>([]);
|
const [logs, setLogs] = useState<ILog[]>([]);
|
||||||
const [requestData, setRequestData] = useState<Query | null>(null);
|
const [requestData, setRequestData] = useState<Query | null>(null);
|
||||||
|
|
||||||
|
const handleAxisError = useAxiosError();
|
||||||
|
|
||||||
const currentStagedQueryData = useMemo(() => {
|
const currentStagedQueryData = useMemo(() => {
|
||||||
if (!stagedQuery || stagedQuery.builder.queryData.length !== 1) return null;
|
if (!stagedQuery || stagedQuery.builder.queryData.length !== 1) return null;
|
||||||
|
|
||||||
@ -357,16 +360,16 @@ function LogsExplorerViews(): JSX.Element {
|
|||||||
|
|
||||||
history.push(dashboardEditView);
|
history.push(dashboardEditView);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: handleAxisError,
|
||||||
if (axios.isAxiosError(error)) {
|
|
||||||
notifications.error({
|
|
||||||
message: error.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[exportDefaultQuery, history, notifications, updateDashboard],
|
[
|
||||||
|
exportDefaultQuery,
|
||||||
|
history,
|
||||||
|
notifications,
|
||||||
|
updateDashboard,
|
||||||
|
handleAxisError,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -511,6 +514,8 @@ function LogsExplorerViews(): JSX.Element {
|
|||||||
onAddToQuery={handleAddToQuery}
|
onAddToQuery={handleAddToQuery}
|
||||||
onClickActionItem={handleAddToQuery}
|
onClickActionItem={handleAddToQuery}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<GoToTop />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
29
frontend/src/hooks/useScrollToTop/index.tsx
Normal file
29
frontend/src/hooks/useScrollToTop/index.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import throttle from 'lodash-es/throttle';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { UseScrollToTop } from './types';
|
||||||
|
|
||||||
|
function useScrollToTop(visibleOffset = 200): UseScrollToTop {
|
||||||
|
const [isVisible, setIsVisible] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const scrollToTop = (): void => {
|
||||||
|
window.scrollTo({
|
||||||
|
top: 0,
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const toggleVisibility = throttle(() => {
|
||||||
|
setIsVisible(window.pageYOffset > visibleOffset);
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
window.addEventListener('scroll', toggleVisibility);
|
||||||
|
|
||||||
|
return (): void => window.removeEventListener('scroll', toggleVisibility);
|
||||||
|
}, [visibleOffset]);
|
||||||
|
|
||||||
|
return { isVisible, scrollToTop };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useScrollToTop;
|
4
frontend/src/hooks/useScrollToTop/types.ts
Normal file
4
frontend/src/hooks/useScrollToTop/types.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export interface UseScrollToTop {
|
||||||
|
isVisible: boolean;
|
||||||
|
scrollToTop: VoidFunction;
|
||||||
|
}
|
58
frontend/src/hooks/useScrollToTop/useScrollToTop.test.ts
Normal file
58
frontend/src/hooks/useScrollToTop/useScrollToTop.test.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { act, renderHook } from '@testing-library/react';
|
||||||
|
|
||||||
|
import useScrollToTop from './index';
|
||||||
|
|
||||||
|
// Mocking window.scrollTo method
|
||||||
|
global.scrollTo = jest.fn();
|
||||||
|
|
||||||
|
describe('useScrollToTop hook', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change visibility and scroll to top on call', () => {
|
||||||
|
const { result } = renderHook(() => useScrollToTop(100));
|
||||||
|
|
||||||
|
// Simulate scrolling 150px down
|
||||||
|
act(() => {
|
||||||
|
global.pageYOffset = 150;
|
||||||
|
global.dispatchEvent(new Event('scroll'));
|
||||||
|
jest.advanceTimersByTime(300);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isVisible).toBe(true);
|
||||||
|
|
||||||
|
// Simulate scrolling to top
|
||||||
|
act(() => {
|
||||||
|
result.current.scrollToTop();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(global.scrollTo).toHaveBeenCalledWith({ top: 0, behavior: 'smooth' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be invisible when scrolled less than offset', () => {
|
||||||
|
const { result } = renderHook(() => useScrollToTop(100));
|
||||||
|
|
||||||
|
// Simulate scrolling 50px down
|
||||||
|
act(() => {
|
||||||
|
global.pageYOffset = 50;
|
||||||
|
global.dispatchEvent(new Event('scroll'));
|
||||||
|
jest.advanceTimersByTime(300);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isVisible).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be visible when scrolled more than offset', () => {
|
||||||
|
const { result } = renderHook(() => useScrollToTop(100));
|
||||||
|
|
||||||
|
// Simulate scrolling 50px down
|
||||||
|
act(() => {
|
||||||
|
global.pageYOffset = 200;
|
||||||
|
global.dispatchEvent(new Event('scroll'));
|
||||||
|
jest.advanceTimersByTime(300);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.current.isVisible).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user