chore: make settings drawer reusable (#9985)

* chore: make settings drawer reusable

* chore: clean up styles as well

* fix: resolve comments

* chore: trigger build

* chore: resolve comments
This commit is contained in:
Ashwin Bhatkal
2026-01-13 15:03:05 +05:30
committed by GitHub
parent 15fe3a7163
commit b4282be3ac
10 changed files with 211 additions and 161 deletions

View File

@@ -1,47 +1,3 @@
.settings-container-root {
.ant-drawer-wrapper-body {
border-left: 1px solid var(--bg-slate-500);
background: var(--bg-ink-400);
box-shadow: -4px 10px 16px 2px rgba(0, 0, 0, 0.2);
.ant-drawer-header {
height: 48px;
border-bottom: 1px solid var(--bg-slate-500);
padding: 14px 14px 14px 11px;
.ant-drawer-header-title {
gap: 16px;
.ant-drawer-title {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: -0.07px;
padding-left: 16px;
border-left: 1px solid #161922;
}
.ant-drawer-close {
height: 16px;
width: 16px;
margin-inline-end: 0px !important;
}
}
}
.ant-drawer-body {
padding: 16px;
&::-webkit-scrollbar {
width: 0.1rem;
}
}
}
}
.dashboard-description-container {
box-shadow: none;
border: none;
@@ -576,28 +532,6 @@
}
.lightMode {
.settings-container-root {
.ant-drawer-wrapper-body {
border-left: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-100);
.ant-drawer-header {
border-bottom: 1px solid var(--bg-vanilla-300);
.ant-drawer-header-title {
.ant-drawer-title {
color: var(--bg-ink-400);
border-left: 1px solid var(--bg-vanilla-300);
}
.ant-drawer-close {
color: var(--bg-ink-300);
}
}
}
}
}
.dashboard-description-container {
color: var(--bg-ink-400);

View File

@@ -1,53 +0,0 @@
import './Description.styles.scss';
import { Button } from 'antd';
import ConfigureIcon from 'assets/Integrations/ConfigureIcon';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { useRef, useState } from 'react';
import DashboardSettingsContent from '../DashboardSettings';
import { DrawerContainer } from './styles';
function SettingsDrawer({ drawerTitle }: { drawerTitle: string }): JSX.Element {
const [visible, setVisible] = useState<boolean>(false);
const variableViewModeRef = useRef<() => void>();
const showDrawer = (): void => {
setVisible(true);
};
const handleClose = (): void => {
setVisible(false);
variableViewModeRef?.current?.();
};
return (
<>
<Button
type="text"
className="configure-button"
icon={<ConfigureIcon />}
data-testid="show-drawer"
onClick={showDrawer}
>
Configure
</Button>
<DrawerContainer
title={drawerTitle}
placement="right"
width="50%"
onClose={handleClose}
open={visible}
rootClassName="settings-container-root"
>
<OverlayScrollbar>
<DashboardSettingsContent variableViewModeRef={variableViewModeRef} />
</OverlayScrollbar>
</DrawerContainer>
</>
);
}
export default SettingsDrawer;

View File

@@ -0,0 +1,67 @@
.settings-container-root {
.ant-drawer-wrapper-body {
border-left: 1px solid var(--bg-slate-500);
background: var(--bg-ink-400);
box-shadow: -4px 10px 16px 2px rgba(0, 0, 0, 0.2);
.ant-drawer-header {
height: 48px;
border-bottom: 1px solid var(--bg-slate-500);
padding: 14px 14px 14px 11px;
.ant-drawer-header-title {
gap: 16px;
.ant-drawer-title {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: -0.07px;
padding-left: 16px;
border-left: 1px solid var(--bg-slate-500);
}
.ant-drawer-close {
height: 16px;
width: 16px;
margin-inline-end: 0px !important;
}
}
}
.ant-drawer-body {
padding: 16px;
&::-webkit-scrollbar {
width: 0.1rem;
}
}
}
}
.lightMode {
.settings-container-root {
.ant-drawer-wrapper-body {
border-left: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-100);
.ant-drawer-header {
border-bottom: 1px solid var(--bg-vanilla-300);
.ant-drawer-header-title {
.ant-drawer-title {
color: var(--bg-ink-400);
border-left: 1px solid var(--bg-vanilla-300);
}
.ant-drawer-close {
color: var(--bg-ink-300);
}
}
}
}
}
}

View File

@@ -0,0 +1,34 @@
import './SettingsDrawer.styles.scss';
import { Drawer } from 'antd';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { memo, PropsWithChildren, ReactElement } from 'react';
type SettingsDrawerProps = PropsWithChildren<{
drawerTitle: string;
isOpen: boolean;
onClose: () => void;
}>;
function SettingsDrawer({
children,
drawerTitle,
isOpen,
onClose,
}: SettingsDrawerProps): JSX.Element {
return (
<Drawer
title={drawerTitle}
placement="right"
width="50%"
onClose={onClose}
open={isOpen}
rootClassName="settings-container-root"
>
{/* Need to type cast because of OverlayScrollbar type definition. We should be good once we remove it. */}
<OverlayScrollbar>{children as ReactElement}</OverlayScrollbar>
</Drawer>
);
}
export default memo(SettingsDrawer);

View File

@@ -12,6 +12,7 @@ import {
Typography,
} from 'antd';
import logEvent from 'api/common/logEvent';
import ConfigureIcon from 'assets/Integrations/ConfigureIcon';
import HeaderRightSection from 'components/HeaderRightSection/HeaderRightSection';
import { PANEL_GROUP_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
@@ -40,7 +41,7 @@ import {
import { useAppContext } from 'providers/App/App';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { sortLayout } from 'providers/Dashboard/util';
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { FullScreenHandle } from 'react-full-screen';
import { Layout } from 'react-grid-layout';
import { useTranslation } from 'react-i18next';
@@ -52,9 +53,11 @@ import { ComponentTypes } from 'utils/permission';
import { v4 as uuid } from 'uuid';
import DashboardGraphSlider from '../ComponentsSlider';
import DashboardSettings from '../DashboardSettings';
import { Base64Icons } from '../DashboardSettings/General/utils';
import DashboardVariableSelection from '../DashboardVariablesSelection';
import SettingsDrawer from './SettingsDrawer';
import { VariablesSettingsTab } from './types';
import { DEFAULT_ROW_NAME, downloadObjectAsJson } from './utils';
interface DashboardDescriptionProps {
@@ -101,6 +104,11 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
handleDashboardLockToggle,
} = useDashboard();
const variablesSettingsTabHandle = useRef<VariablesSettingsTab>(null);
const [isSettingsDrawerOpen, setIsSettingsDrawerOpen] = useState<boolean>(
false,
);
const { isCloudUser, isEnterpriseSelfHostedUser } = useGetTenantLicense();
const isPublicDashboardEnabled = isCloudUser || isEnterpriseSelfHostedUser;
@@ -340,6 +348,18 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
publicDashboardResponse?.data,
]);
const onConfigureClick = useCallback((): void => {
setIsSettingsDrawerOpen(true);
}, []);
const onSettingsDrawerClose = useCallback((): void => {
setIsSettingsDrawerOpen(false);
// good use case for a state library like Jotai
if (variablesSettingsTabHandle.current) {
variablesSettingsTabHandle.current.resetState();
}
}, []);
return (
<Card className="dashboard-description-container">
<div className="dashboard-header">
@@ -504,7 +524,26 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
/>
</Popover>
{!isDashboardLocked && editDashboard && (
<SettingsDrawer drawerTitle="Dashboard Configuration" />
<>
<Button
type="text"
className="configure-button"
icon={<ConfigureIcon />}
data-testid="show-drawer"
onClick={onConfigureClick}
>
Configure
</Button>
<SettingsDrawer
drawerTitle="Dashboard Configuration"
isOpen={isSettingsDrawerOpen}
onClose={onSettingsDrawerClose}
>
<DashboardSettings
variablesSettingsTabHandle={variablesSettingsTabHandle}
/>
</SettingsDrawer>
</>
)}
{!isDashboardLocked && addPanelPermission && (
<Button

View File

@@ -1,24 +0,0 @@
import { Button as ButtonComponent, Drawer } from 'antd';
import styled from 'styled-components';
export const Container = styled.div`
margin-top: 0.5rem;
`;
export const Button = styled(ButtonComponent)`
&&& {
display: flex;
align-items: center;
}
`;
export const DrawerContainer = styled(Drawer)`
.ant-drawer-header {
padding: 16px;
border: none;
}
.ant-drawer-body {
padding-top: 0;
}
`;

View File

@@ -0,0 +1,7 @@
import { MutableRefObject } from 'react';
export interface VariablesSettingsTab {
resetState: () => void;
}
export type VariablesSettingsTabHandle = MutableRefObject<VariablesSettingsTab | null>;

View File

@@ -14,6 +14,7 @@ import { arrayMove, SortableContext, useSortable } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { Button, Modal, Row, Space, Table, Typography } from 'antd';
import { RowProps } from 'antd/lib';
import { VariablesSettingsTabHandle } from 'container/DashboardContainer/DashboardDescription/types';
import { convertVariablesToDbFormat } from 'container/DashboardContainer/DashboardVariablesSelection/util';
import { useAddDynamicVariableToPanels } from 'hooks/dashboard/useAddDynamicVariableToPanels';
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
@@ -77,10 +78,10 @@ function TableRow({ children, ...props }: RowProps): JSX.Element {
);
}
function VariablesSetting({
variableViewModeRef,
function VariablesSettings({
variablesSettingsTabHandle,
}: {
variableViewModeRef: React.MutableRefObject<(() => void) | undefined>;
variablesSettingsTabHandle: VariablesSettingsTabHandle;
}): JSX.Element {
const variableToDelete = useRef<IDashboardVariable | null>(null);
const [deleteVariableModal, setDeleteVariableModal] = useState(false);
@@ -126,11 +127,13 @@ function VariablesSetting({
};
useEffect(() => {
if (variableViewModeRef) {
// eslint-disable-next-line no-param-reassign
variableViewModeRef.current = onDoneVariableViewMode;
}
}, [variableViewModeRef]);
if (!variablesSettingsTabHandle) return;
// eslint-disable-next-line no-param-reassign
variablesSettingsTabHandle.current = {
resetState: onDoneVariableViewMode,
};
}, [variablesSettingsTabHandle]);
const updateMutation = useUpdateDashboard();
@@ -510,4 +513,4 @@ function VariablesSetting({
);
}
export default VariablesSetting;
export default VariablesSettings;

View File

@@ -6,14 +6,15 @@ import { Braces, Globe, Table } from 'lucide-react';
import { useAppContext } from 'providers/App/App';
import { USER_ROLES } from 'types/roles';
import { VariablesSettingsTabHandle } from '../DashboardDescription/types';
import GeneralDashboardSettings from './General';
import PublicDashboardSetting from './PublicDashboard';
import VariablesSetting from './Variables';
function DashboardSettingsContent({
variableViewModeRef,
function DashboardSettings({
variablesSettingsTabHandle,
}: {
variableViewModeRef: React.MutableRefObject<(() => void) | undefined>;
variablesSettingsTabHandle: VariablesSettingsTabHandle;
}): JSX.Element {
const { user } = useAppContext();
const { isCloudUser, isEnterpriseSelfHostedUser } = useGetTenantLicense();
@@ -63,7 +64,9 @@ function DashboardSettingsContent({
</Button>
),
key: 'variables',
children: <VariablesSetting variableViewModeRef={variableViewModeRef} />,
children: (
<VariablesSetting variablesSettingsTabHandle={variablesSettingsTabHandle} />
),
},
...(enablePublicDashboard ? [publicDashboardItem] : []),
];
@@ -71,4 +74,4 @@ function DashboardSettingsContent({
return <Tabs items={items} animated className="settings-tabs" />;
}
export default DashboardSettingsContent;
export default DashboardSettings;

View File

@@ -4,11 +4,14 @@ import './DashboardEmptyState.styles.scss';
import { PlusOutlined } from '@ant-design/icons';
import { Button, Typography } from 'antd';
import logEvent from 'api/common/logEvent';
import ConfigureIcon from 'assets/Integrations/ConfigureIcon';
import SettingsDrawer from 'container/DashboardContainer/DashboardDescription/SettingsDrawer';
import { VariablesSettingsTab } from 'container/DashboardContainer/DashboardDescription/types';
import DashboardSettings from 'container/DashboardContainer/DashboardSettings';
import useComponentPermission from 'hooks/useComponentPermission';
import { useAppContext } from 'providers/App/App';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useCallback } from 'react';
import { useCallback, useRef, useState } from 'react';
import { ROLES, USER_ROLES } from 'types/roles';
import { ComponentTypes } from 'utils/permission';
@@ -20,6 +23,11 @@ export default function DashboardEmptyState(): JSX.Element {
setSelectedRowWidgetId,
} = useDashboard();
const variablesSettingsTabHandle = useRef<VariablesSettingsTab>(null);
const [isSettingsDrawerOpen, setIsSettingsDrawerOpen] = useState<boolean>(
false,
);
const { user } = useAppContext();
let permissions: ComponentTypes[] = ['add_panel'];
@@ -44,6 +52,19 @@ export default function DashboardEmptyState(): JSX.Element {
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [handleToggleDashboardSlider]);
const onConfigureClick = useCallback((): void => {
setIsSettingsDrawerOpen(true);
}, []);
const onSettingsDrawerClose = useCallback((): void => {
setIsSettingsDrawerOpen(false);
if (variablesSettingsTabHandle.current) {
variablesSettingsTabHandle.current.resetState();
}
}, []);
return (
<section className="dashboard-empty-state">
<div className="dashboard-content">
@@ -77,7 +98,26 @@ export default function DashboardEmptyState(): JSX.Element {
Give it a name, add description, tags and variables
</Typography.Text>
</div>
<SettingsDrawer drawerTitle="Dashboard Configuration" />
{/* This Empty State needs to be consolidated. The SettingsDrawer should be global to the
whole dashboard page instead of confined to this Empty State */}
<Button
type="text"
className="configure-button"
icon={<ConfigureIcon />}
data-testid="show-drawer"
onClick={onConfigureClick}
>
Configure
</Button>
<SettingsDrawer
drawerTitle="Dashboard Configuration"
isOpen={isSettingsDrawerOpen}
onClose={onSettingsDrawerClose}
>
<DashboardSettings
variablesSettingsTabHandle={variablesSettingsTabHandle}
/>
</SettingsDrawer>
</div>
<div className="actions-1">
<div className="actions-add-panel">