Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added feat to add new panel in a section #6999

Merged
merged 7 commits into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default function DashboardEmptyState(): JSX.Element {
selectedDashboard,
isDashboardLocked,
handleToggleDashboardSlider,
setSelectedRowWidgetId,
} = useDashboard();

const { user } = useAppContext();
Expand All @@ -34,6 +35,7 @@ export default function DashboardEmptyState(): JSX.Element {
const [addPanelPermission] = useComponentPermission(permissions, userRole);

const onEmptyWidgetHandler = useCallback(() => {
setSelectedRowWidgetId(null);
handleToggleDashboardSlider(true);
logEvent('Dashboard Detail: Add new panel clicked', {
dashboardId: selectedDashboard?.uuid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,8 @@

.menu-content {
.section-1 {
.rename-btn {
.rename-btn,
.new-panel-btn {
display: flex;
align-items: center;
gap: 6px;
Expand All @@ -150,6 +151,10 @@
margin-inline-end: 0px;
}
}

.rename-btn {
padding-bottom: 10px;
}
}

.section-2 {
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/container/GridCardLayout/GridCardLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
isDashboardLocked,
dashboardQueryRangeCalled,
setDashboardQueryRangeCalled,
setSelectedRowWidgetId,
} = useDashboard();
const { data } = selectedDashboard || {};
const { pathname } = useLocation();
Expand Down Expand Up @@ -174,6 +175,7 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {

updateDashboardMutation.mutate(updatedDashboard, {
onSuccess: (updatedDashboard) => {
setSelectedRowWidgetId(null);
if (updatedDashboard.payload) {
if (updatedDashboard.payload.data.layout)
setLayouts(sortLayout(updatedDashboard.payload.data.layout));
Expand Down
38 changes: 37 additions & 1 deletion frontend/src/container/GridCardLayout/WidgetRow.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { Button, Popover } from 'antd';
import { EllipsisIcon, PenLine, X } from 'lucide-react';
import useComponentPermission from 'hooks/useComponentPermission';
import { EllipsisIcon, PenLine, Plus, X } from 'lucide-react';
import { useAppContext } from 'providers/App/App';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useState } from 'react';
import { Layout } from 'react-grid-layout';
import { ROLES, USER_ROLES } from 'types/roles';
import { ComponentTypes } from 'utils/permission';

interface WidgetRowHeaderProps {
rowWidgetProperties: {
Expand All @@ -27,6 +32,23 @@ export function WidgetRowHeader(props: WidgetRowHeaderProps): JSX.Element {
id,
} = props;
const [isRowSettingsOpen, setIsRowSettingsOpen] = useState<boolean>(false);

const {
handleToggleDashboardSlider,
selectedDashboard,
isDashboardLocked,
setSelectedRowWidgetId,
} = useDashboard();

const permissions: ComponentTypes[] = ['add_panel'];
const { user } = useAppContext();

const userRole: ROLES | null =
selectedDashboard?.created_by === user?.email
? (USER_ROLES.AUTHOR as ROLES)
: user.role;
const [addPanelPermission] = useComponentPermission(permissions, userRole);

return (
<Popover
open={isRowSettingsOpen}
Expand All @@ -52,6 +74,20 @@ export function WidgetRowHeader(props: WidgetRowHeaderProps): JSX.Element {
Rename
</Button>
</section>
<section className="section-1">
<Button
className="new-panel-btn"
type="text"
disabled={!editWidget && addPanelPermission && !isDashboardLocked}
icon={<Plus size={14} />}
onClick={(): void => {
setSelectedRowWidgetId(id);
handleToggleDashboardSlider(true);
}}
>
New Panel
</Button>
</section>
{!rowWidgetProperties.collapsed && (
<section className="section-2">
<Button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
listSortOrder,
setSelectedDashboard,
handleToggleDashboardSlider,
setSelectedRowWidgetId,
handleDashboardLockToggle,
} = useDashboard();

Expand Down Expand Up @@ -157,6 +158,7 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
const [addPanelPermission] = useComponentPermission(permissions, userRole);

const onEmptyWidgetHandler = useCallback(() => {
setSelectedRowWidgetId(null);
handleToggleDashboardSlider(true);
logEvent('Dashboard Detail: Add new panel clicked', {
dashboardId: selectedDashboard?.uuid,
Expand Down
128 changes: 127 additions & 1 deletion frontend/src/container/NewWidget/__test__/NewWidget.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// - Handling multiple rows correctly
// - Handling widgets with different heights

import { placeWidgetAtBottom } from '../utils';
import { placeWidgetAtBottom, placeWidgetBetweenRows } from '../utils';

describe('placeWidgetAtBottom', () => {
it('should place widget at (0,0) when layout is empty', () => {
Expand Down Expand Up @@ -90,3 +90,129 @@ describe('placeWidgetAtBottom', () => {
});
});
});

describe('placeWidgetBetweenRows', () => {
it('should return single widget layout when layout is empty', () => {
const result = placeWidgetBetweenRows('widget1', [], 'currentRow');
expect(result).toEqual([
{
i: 'widget1',
x: 0,
y: 0,
w: 6,
h: 6,
},
]);
});

it('should place widget at the end of the layout when no nextRowId is provided', () => {
const existingLayout = [
{ i: 'widget1', x: 0, y: 0, w: 6, h: 6 },
{ i: 'widget2', x: 6, y: 0, w: 6, h: 6 },
];

const result = placeWidgetBetweenRows('widget3', existingLayout, 'widget2');

expect(result).toEqual([
{ i: 'widget1', x: 0, y: 0, w: 6, h: 6 },
{ i: 'widget2', x: 6, y: 0, w: 6, h: 6 },
{ i: 'widget3', x: 0, y: 6, w: 6, h: 6 },
]);
});

it('should place widget between current and next row', () => {
const existingLayout = [
{
h: 1,
i: "'widget1'",
SagarRajput-7 marked this conversation as resolved.
Show resolved Hide resolved
SagarRajput-7 marked this conversation as resolved.
Show resolved Hide resolved
maxH: 1,
minH: 1,
minW: 12,
moved: false,
static: false,
w: 12,
x: 0,
y: 0,
},
{ i: 'widget2', x: 6, y: 0, w: 6, h: 6 },
{
h: 1,
i: 'widget3',
maxH: 1,
minH: 1,
minW: 12,
moved: false,
static: false,
w: 12,
x: 0,
y: 7,
},
];

const result = placeWidgetBetweenRows(
'widget4',
existingLayout,
'widget1',
'widget3',
);

expect(result).toEqual([
{
h: 1,
i: "'widget1'",
maxH: 1,
minH: 1,
minW: 12,
moved: false,
static: false,
w: 12,
x: 0,
y: 0,
},
{
h: 6,
i: 'widget2',
w: 6,
x: 6,
y: 0,
},
{
h: 6,
i: 'widget4',
w: 6,
x: 0,
y: 6,
},
{
h: 1,
i: 'widget3',
maxH: 1,
minH: 1,
minW: 12,
moved: false,
static: false,
w: 12,
x: 0,
y: 7,
},
]);
});

it('should respect custom widget dimensions', () => {
const existingLayout = [{ i: 'widget1', x: 0, y: 0, w: 12, h: 4 }];

const result = placeWidgetBetweenRows(
'widget2',
existingLayout,
'widget1',
null,
8,
3,
);

expect(result).toEqual([
{ i: 'widget1', x: 0, y: 0, w: 12, h: 4 },
{ i: 'widget2', x: 0, y: 4, w: 8, h: 3 },
]);
});
});
43 changes: 38 additions & 5 deletions frontend/src/container/NewWidget/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import logEvent from 'api/common/logEvent';
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
import { FeatureKeys } from 'constants/features';
import { QueryParams } from 'constants/query';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import {
initialQueriesMap,
PANEL_GROUP_TYPES,
PANEL_TYPES,
} from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import { DashboardShortcuts } from 'constants/shortcuts/DashboardShortcuts';
import { DEFAULT_BUCKET_COUNT } from 'container/PanelWrapper/constants';
Expand All @@ -20,7 +24,7 @@ import useUrlQuery from 'hooks/useUrlQuery';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import history from 'lib/history';
import { defaultTo, isUndefined } from 'lodash-es';
import { defaultTo, isEmpty, isUndefined } from 'lodash-es';
import { Check, X } from 'lucide-react';
import { DashboardWidgetPageParams } from 'pages/DashboardWidget';
import { useAppContext } from 'providers/App/App';
Expand Down Expand Up @@ -59,13 +63,16 @@ import {
getIsQueryModified,
handleQueryChange,
placeWidgetAtBottom,
placeWidgetBetweenRows,
} from './utils';

function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
const {
selectedDashboard,
setSelectedDashboard,
setToScrollWidgetId,
selectedRowWidgetId,
setSelectedRowWidgetId,
} = useDashboard();

const { t } = useTranslation(['dashboard']);
Expand Down Expand Up @@ -367,11 +374,33 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
const widgetId = query.get('widgetId') || '';
let updatedLayout = selectedDashboard.data.layout || [];

if (isNewDashboard) {
if (isNewDashboard && isEmpty(selectedRowWidgetId)) {
const newLayoutItem = placeWidgetAtBottom(widgetId, updatedLayout);
updatedLayout = [...updatedLayout, newLayoutItem];
}

if (isNewDashboard && selectedRowWidgetId) {
// Find the next row by looking through remaining layout items
const currentIndex = updatedLayout.findIndex(
(e) => e.i === selectedRowWidgetId,
);
const nextRowIndex = updatedLayout.findIndex(
(item, index) =>
index > currentIndex &&
widgets?.find((w) => w.id === item.i)?.panelTypes ===
PANEL_GROUP_TYPES.ROW,
);
const nextRowId = nextRowIndex !== -1 ? updatedLayout[nextRowIndex].i : null;

const newLayoutItem = placeWidgetBetweenRows(
widgetId,
updatedLayout,
selectedRowWidgetId,
nextRowId,
);
updatedLayout = newLayoutItem;
}

const dashboard: Dashboard = {
...selectedDashboard,
uuid: selectedDashboard.uuid,
Expand Down Expand Up @@ -437,6 +466,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {

updateDashboardMutation.mutateAsync(dashboard, {
onSuccess: () => {
setSelectedRowWidgetId(null);
setSelectedDashboard(dashboard);
setToScrollWidgetId(selectedWidget?.id || '');
history.push({
Expand All @@ -449,16 +479,19 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
selectedDashboard,
query,
isNewDashboard,
preWidgets,
selectedRowWidgetId,
afterWidgets,
selectedWidget,
selectedTime.enum,
graphType,
currentQuery,
afterWidgets,
preWidgets,
updateDashboardMutation,
handleError,
widgets,
setSelectedDashboard,
setToScrollWidgetId,
setSelectedRowWidgetId,
dashboardId,
]);

Expand Down
Loading
Loading