Dashboards followups 2 (#15107)
adding min height and widths on each chart this Pr address this followups - - https://github.com/twentyhq/core-team-issues/issues/1710#issuecomment-3405312108 - <img width="843" height="38" alt="Screenshot 2025-10-15 at 15 19 27" src="https://github.com/user-attachments/assets/f33da21f-a605-40f9-b7fa-02be9dff5908" /> - <img width="830" height="42" alt="Screenshot 2025-10-15 at 15 19 40" src="https://github.com/user-attachments/assets/bf6f3783-b5cc-44c0-94a2-029a489c852e" /> video QA before: https://github.com/user-attachments/assets/28262a7b-531f-4dcb-9047-c587fe3d8af6 after: https://github.com/user-attachments/assets/b0fdbc18-3a7e-4c85-98b6-5799d7207bb4
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
export const DEFAULT_WIDGET_SIZE = {
|
||||
default: { w: 4, h: 4 },
|
||||
minimum: { w: 2, h: 2 },
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
import { GraphType } from '~/generated-metadata/graphql';
|
||||
|
||||
type WidgetSizeConfig = {
|
||||
default: { w: number; h: number };
|
||||
minimum: { w: number; h: number };
|
||||
};
|
||||
|
||||
export const WIDGET_SIZES: Record<GraphType, WidgetSizeConfig> = {
|
||||
[GraphType.NUMBER]: {
|
||||
default: { w: 3, h: 2 },
|
||||
minimum: { w: 2, h: 2 },
|
||||
},
|
||||
[GraphType.GAUGE]: {
|
||||
default: { w: 3, h: 4 },
|
||||
minimum: { w: 3, h: 4 },
|
||||
},
|
||||
[GraphType.PIE]: {
|
||||
default: { w: 4, h: 4 },
|
||||
minimum: { w: 3, h: 4 },
|
||||
},
|
||||
[GraphType.VERTICAL_BAR]: {
|
||||
default: { w: 6, h: 6 },
|
||||
minimum: { w: 5, h: 5 },
|
||||
},
|
||||
[GraphType.HORIZONTAL_BAR]: {
|
||||
default: { w: 6, h: 6 },
|
||||
minimum: { w: 5, h: 5 },
|
||||
},
|
||||
[GraphType.LINE]: {
|
||||
default: { w: 6, h: 10 },
|
||||
minimum: { w: 5, h: 5 },
|
||||
},
|
||||
};
|
||||
+5
-5
@@ -5,13 +5,11 @@ import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLa
|
||||
import { type GraphWidgetFieldSelection } from '@/page-layout/types/GraphWidgetFieldSelection';
|
||||
import { addWidgetToTab } from '@/page-layout/utils/addWidgetToTab';
|
||||
import { createDefaultGraphWidget } from '@/page-layout/utils/createDefaultGraphWidget';
|
||||
import {
|
||||
getWidgetSize,
|
||||
getWidgetTitle,
|
||||
} from '@/page-layout/utils/getDefaultWidgetData';
|
||||
import { getDefaultWidgetPosition } from '@/page-layout/utils/getDefaultWidgetPosition';
|
||||
import { getTabListInstanceIdFromPageLayoutId } from '@/page-layout/utils/getTabListInstanceIdFromPageLayoutId';
|
||||
import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts';
|
||||
import { getWidgetSize } from '@/page-layout/utils/getWidgetSize';
|
||||
import { getWidgetTitle } from '@/page-layout/utils/getWidgetTitle';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
@@ -91,10 +89,12 @@ export const useCreatePageLayoutGraphWidget = (
|
||||
const title = getWidgetTitle(graphType, existingWidgetCount);
|
||||
const widgetId = uuidv4();
|
||||
|
||||
const defaultSize = getWidgetSize(graphType);
|
||||
const defaultSize = getWidgetSize(graphType, 'default');
|
||||
const minimumSize = getWidgetSize(graphType, 'minimum');
|
||||
const position = getDefaultWidgetPosition(
|
||||
pageLayoutDraggedArea,
|
||||
defaultSize,
|
||||
minimumSize,
|
||||
);
|
||||
|
||||
const newWidget = createDefaultGraphWidget({
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DEFAULT_WIDGET_SIZE } from '@/page-layout/constants/DefaultWidgetSize';
|
||||
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
|
||||
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
|
||||
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
|
||||
@@ -59,9 +60,11 @@ export const useCreatePageLayoutIframeWidget = (
|
||||
|
||||
const widgetId = uuidv4();
|
||||
const defaultSize = { w: 6, h: 6 };
|
||||
const minimumSize = DEFAULT_WIDGET_SIZE.minimum;
|
||||
const position = getDefaultWidgetPosition(
|
||||
pageLayoutDraggedArea,
|
||||
defaultSize,
|
||||
minimumSize,
|
||||
);
|
||||
|
||||
const newWidget = createDefaultIframeWidget(
|
||||
|
||||
+4
-4
@@ -76,12 +76,12 @@ describe('convertPageLayoutToTabLayouts', () => {
|
||||
expect(result).toEqual({
|
||||
'tab-1': {
|
||||
desktop: [
|
||||
{ i: 'widget-1', x: 0, y: 0, w: 2, h: 2 },
|
||||
{ i: 'widget-2', x: 0, y: 2, w: 2, h: 2 },
|
||||
{ i: 'widget-1', x: 0, y: 0, w: 2, h: 2, minW: 2, minH: 2 },
|
||||
{ i: 'widget-2', x: 0, y: 2, w: 2, h: 2, minW: 3, minH: 4 },
|
||||
],
|
||||
mobile: [
|
||||
{ i: 'widget-1', x: 0, y: 0, w: 1, h: 2 },
|
||||
{ i: 'widget-2', x: 0, y: 2, w: 1, h: 2 },
|
||||
{ i: 'widget-1', x: 0, y: 0, w: 1, h: 2, minW: 2, minH: 2 },
|
||||
{ i: 'widget-2', x: 0, y: 2, w: 1, h: 2, minW: 3, minH: 4 },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
+52
-5
@@ -1,19 +1,66 @@
|
||||
import { getDefaultWidgetPosition } from '../getDefaultWidgetPosition';
|
||||
|
||||
describe('getDefaultWidgetPosition', () => {
|
||||
it('should return dragged area when provided', () => {
|
||||
it('should return dragged area when it meets minimum size', () => {
|
||||
const draggedArea = { x: 2, y: 3, w: 4, h: 5 };
|
||||
const defaultSize = { w: 2, h: 2 };
|
||||
const minimumSize = { w: 3, h: 3 };
|
||||
|
||||
expect(getDefaultWidgetPosition(draggedArea, defaultSize)).toEqual(
|
||||
draggedArea,
|
||||
);
|
||||
expect(
|
||||
getDefaultWidgetPosition(draggedArea, defaultSize, minimumSize),
|
||||
).toEqual(draggedArea);
|
||||
});
|
||||
|
||||
it('should expand dragged area to meet minimum width', () => {
|
||||
const draggedArea = { x: 2, y: 3, w: 2, h: 5 };
|
||||
const defaultSize = { w: 4, h: 4 };
|
||||
const minimumSize = { w: 5, h: 3 };
|
||||
|
||||
expect(
|
||||
getDefaultWidgetPosition(draggedArea, defaultSize, minimumSize),
|
||||
).toEqual({
|
||||
x: 2,
|
||||
y: 3,
|
||||
w: 5, // expanded from 2 to meet minimum
|
||||
h: 5,
|
||||
});
|
||||
});
|
||||
|
||||
it('should expand dragged area to meet minimum height', () => {
|
||||
const draggedArea = { x: 1, y: 2, w: 6, h: 2 };
|
||||
const defaultSize = { w: 4, h: 4 };
|
||||
const minimumSize = { w: 3, h: 5 };
|
||||
|
||||
expect(
|
||||
getDefaultWidgetPosition(draggedArea, defaultSize, minimumSize),
|
||||
).toEqual({
|
||||
x: 1,
|
||||
y: 2,
|
||||
w: 6,
|
||||
h: 5, // expanded from 2 to meet minimum
|
||||
});
|
||||
});
|
||||
|
||||
it('should expand dragged area to meet both minimum dimensions', () => {
|
||||
const draggedArea = { x: 0, y: 0, w: 2, h: 2 };
|
||||
const defaultSize = { w: 6, h: 6 };
|
||||
const minimumSize = { w: 5, h: 5 };
|
||||
|
||||
expect(
|
||||
getDefaultWidgetPosition(draggedArea, defaultSize, minimumSize),
|
||||
).toEqual({
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 5, // expanded from 2 to meet minimum
|
||||
h: 5, // expanded from 2 to meet minimum
|
||||
});
|
||||
});
|
||||
|
||||
it('should return default position with size when no dragged area', () => {
|
||||
const defaultSize = { w: 3, h: 4 };
|
||||
const minimumSize = { w: 2, h: 2 };
|
||||
|
||||
expect(getDefaultWidgetPosition(null, defaultSize)).toEqual({
|
||||
expect(getDefaultWidgetPosition(null, defaultSize, minimumSize)).toEqual({
|
||||
x: 0,
|
||||
y: 0,
|
||||
w: 3,
|
||||
|
||||
+31
-7
@@ -1,5 +1,9 @@
|
||||
import { DEFAULT_WIDGET_SIZE } from '@/page-layout/constants/DefaultWidgetSize';
|
||||
import { type PageLayout } from '@/page-layout/types/PageLayout';
|
||||
import { type TabLayouts } from '@/page-layout/types/tab-layouts';
|
||||
import { getWidgetSize } from '@/page-layout/utils/getWidgetSize';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { WidgetType } from '~/generated/graphql';
|
||||
|
||||
export const convertPageLayoutToTabLayouts = (
|
||||
pageLayout: PageLayout,
|
||||
@@ -11,13 +15,33 @@ export const convertPageLayoutToTabLayouts = (
|
||||
const tabLayouts: TabLayouts = {};
|
||||
|
||||
pageLayout.tabs.forEach((tab) => {
|
||||
const layouts = tab.widgets.map((widget) => ({
|
||||
i: widget.id,
|
||||
x: widget.gridPosition.column,
|
||||
y: widget.gridPosition.row,
|
||||
w: widget.gridPosition.columnSpan,
|
||||
h: widget.gridPosition.rowSpan,
|
||||
}));
|
||||
const layouts = tab.widgets.map((widget) => {
|
||||
let minW = DEFAULT_WIDGET_SIZE.minimum.w;
|
||||
let minH = DEFAULT_WIDGET_SIZE.minimum.h;
|
||||
|
||||
if (widget.type === WidgetType.GRAPH && isDefined(widget.configuration)) {
|
||||
const graphType =
|
||||
'graphType' in widget.configuration
|
||||
? widget.configuration.graphType
|
||||
: undefined;
|
||||
|
||||
if (isDefined(graphType)) {
|
||||
const minimumSize = getWidgetSize(graphType, 'minimum');
|
||||
minW = minimumSize.w;
|
||||
minH = minimumSize.h;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
i: widget.id,
|
||||
x: widget.gridPosition.column,
|
||||
y: widget.gridPosition.row,
|
||||
w: widget.gridPosition.columnSpan,
|
||||
h: widget.gridPosition.rowSpan,
|
||||
minW,
|
||||
minH,
|
||||
};
|
||||
});
|
||||
|
||||
tabLayouts[tab.id] = {
|
||||
desktop: layouts,
|
||||
|
||||
@@ -128,34 +128,3 @@ export const getDefaultWidgetData = (graphType: GraphType) => {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
export const getWidgetTitle = (graphType: GraphType, index: number): string => {
|
||||
const baseNames: Record<GraphType, string> = {
|
||||
[GraphType.NUMBER]: 'Number',
|
||||
[GraphType.GAUGE]: 'Gauge',
|
||||
[GraphType.PIE]: 'Pie Chart',
|
||||
[GraphType.VERTICAL_BAR]: 'Vertical Bar Chart',
|
||||
[GraphType.HORIZONTAL_BAR]: 'Horizontal Bar Chart',
|
||||
[GraphType.LINE]: 'Line Chart',
|
||||
};
|
||||
|
||||
return `${baseNames[graphType] || 'Widget'} ${index + 1}`;
|
||||
};
|
||||
|
||||
export const getWidgetSize = (graphType: GraphType) => {
|
||||
switch (graphType) {
|
||||
case GraphType.NUMBER:
|
||||
return { w: 3, h: 2 };
|
||||
case GraphType.GAUGE:
|
||||
return { w: 3, h: 3 };
|
||||
case GraphType.PIE:
|
||||
return { w: 4, h: 4 };
|
||||
case GraphType.VERTICAL_BAR:
|
||||
case GraphType.HORIZONTAL_BAR:
|
||||
return { w: 6, h: 6 };
|
||||
case GraphType.LINE:
|
||||
return { w: 6, h: 10 };
|
||||
default:
|
||||
return { w: 4, h: 4 };
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,9 +3,15 @@ import { type GridBounds } from './calculateGridBoundsFromSelectedCells';
|
||||
export const getDefaultWidgetPosition = (
|
||||
draggedArea: GridBounds | null,
|
||||
defaultSize: { w: number; h: number },
|
||||
minimumSize: { w: number; h: number },
|
||||
): GridBounds => {
|
||||
if (draggedArea !== null) {
|
||||
return draggedArea;
|
||||
return {
|
||||
x: draggedArea.x,
|
||||
y: draggedArea.y,
|
||||
w: Math.max(draggedArea.w, minimumSize.w),
|
||||
h: Math.max(draggedArea.h, minimumSize.h),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { DEFAULT_WIDGET_SIZE } from '@/page-layout/constants/DefaultWidgetSize';
|
||||
import { WIDGET_SIZES } from '@/page-layout/constants/WidgetSizes';
|
||||
import { type GraphType } from '~/generated/graphql';
|
||||
|
||||
export const getWidgetSize = (
|
||||
graphType: GraphType,
|
||||
type: 'default' | 'minimum',
|
||||
): { w: number; h: number } => {
|
||||
const sizeConfig = WIDGET_SIZES[graphType] ?? DEFAULT_WIDGET_SIZE;
|
||||
return sizeConfig[type];
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
import { GraphType } from '~/generated/graphql';
|
||||
|
||||
export const getWidgetTitle = (graphType: GraphType, index: number): string => {
|
||||
const baseNames: Record<GraphType, string> = {
|
||||
[GraphType.NUMBER]: 'Number',
|
||||
[GraphType.GAUGE]: 'Gauge',
|
||||
[GraphType.PIE]: 'Pie Chart',
|
||||
[GraphType.VERTICAL_BAR]: 'Vertical Bar Chart',
|
||||
[GraphType.HORIZONTAL_BAR]: 'Horizontal Bar Chart',
|
||||
[GraphType.LINE]: 'Line Chart',
|
||||
};
|
||||
|
||||
return `${baseNames[graphType] || 'Widget'} ${index + 1}`;
|
||||
};
|
||||
@@ -51,6 +51,7 @@ export const WidgetHeader = ({
|
||||
variant="tertiary"
|
||||
size="small"
|
||||
disabled={isEmpty}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
)}
|
||||
<StyledTitle>{isEmpty ? 'Add Widget' : title}</StyledTitle>
|
||||
|
||||
Reference in New Issue
Block a user