Skip to content

Commit

Permalink
Merge pull request #5614 from gooddata/ine-lx-668-2
Browse files Browse the repository at this point in the history
fix: nested layout min width limit on Lg screen
  • Loading branch information
ivan-nejezchleb authored Nov 27, 2024
2 parents 37f907e + 3fb03e4 commit 6c1f589
Show file tree
Hide file tree
Showing 25 changed files with 417 additions and 169 deletions.
28 changes: 25 additions & 3 deletions libs/sdk-ui-dashboard/api/sdk-ui-dashboard.api.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface IDashboardLayoutItemFacade<TWidget> {
index(): ILayoutItemPath;
size(): IDashboardLayoutSizeByScreenSize;
sizeForScreen(screen: ScreenSize): IDashboardLayoutSize | undefined;
sizeForScreenWithFallback(screen: ScreenSize): IDashboardLayoutSize;
widget(): TWidget | undefined;
section(): IDashboardLayoutSectionFacade<TWidget>;
ref(): ObjRef | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
import { IDashboardLayoutItemFacade, IDashboardLayoutSectionFacade } from "./interfaces.js";
import { ILayoutItemPath } from "../../../../types.js";
import { areLayoutPathsEqual, updateItemIndex } from "../../../layout/coordinates.js";
import { determineWidthForScreen } from "../../../layout/sizing.js";

/**
* @alpha
Expand Down Expand Up @@ -102,6 +103,13 @@ export class DashboardLayoutItemFacade<TWidget> implements IDashboardLayoutItemF
return this.size()[screen];
}

public sizeForScreenWithFallback(screen: ScreenSize): IDashboardLayoutSize {
return {
gridWidth: determineWidthForScreen(screen, this.size()),
gridHeight: this.sizeForScreen(screen)?.gridHeight,
};
}

public hasSizeForScreen(screen: ScreenSize): boolean {
return !isNil(this.sizeForScreen(screen));
}
Expand Down
147 changes: 145 additions & 2 deletions libs/sdk-ui-dashboard/src/_staging/layout/sizing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
IDashboardWidget,
ScreenSize,
IDashboardLayoutItem,
IDashboardLayoutSizeByScreenSize,
} from "@gooddata/sdk-model";
import {
fluidLayoutDescriptor,
Expand All @@ -47,6 +48,9 @@ import {
} from "./constants.js";
import { ExtendedDashboardWidget } from "../../model/types/layoutTypes.js";

import { DASHBOARD_LAYOUT_GRID_COLUMNS_COUNT } from "../dashboard/flexibleLayout/config.js";
import { invariant } from "ts-invariant";

/**
* @internal
*/
Expand Down Expand Up @@ -257,7 +261,7 @@ function getCurrentWidth(
item: IDashboardLayoutItem<IDashboardWidget>,
screen: ScreenSize,
): number | undefined {
return item.size[screen]?.gridWidth;
return determineWidthForScreen(screen, item.size);
}

/**
Expand All @@ -266,7 +270,7 @@ function getCurrentWidth(
export function getMinWidth(
widget: IDashboardWidget,
insightMap: ObjRefMap<IInsight>,
screen: ScreenSize = "xl",
screen: ScreenSize,
): number {
let widgetContent: MeasurableWidgetContent | undefined;
if (isKpiWidget(widget)) {
Expand Down Expand Up @@ -352,3 +356,142 @@ export const getDashboardLayoutItemHeight = (size: IDashboardLayoutSize): number

export const getDashboardLayoutItemHeightForGrid = (gridHeight: number): number =>
gridHeight * GRID_ROW_HEIGHT_IN_PX;

export const determineSizeForScreen = (
screen: ScreenSize,
layoutItemSize?: IDashboardLayoutSizeByScreenSize,
): IDashboardLayoutSize => {
return {
...(layoutItemSize ? layoutItemSize[screen] ?? {} : {}),
gridWidth: determineWidthForScreen(screen, layoutItemSize),
};
};

export const determineWidthForScreen = (
screen: ScreenSize,
layoutItemSize?: IDashboardLayoutSizeByScreenSize,
) => {
// Determine if element has size set in metadata object for the current screen size
const providedSizeForScreen = layoutItemSize?.[screen];
// Use the provided size for the screen if it is known, otherwise determine the size for the current
// screen if we at least know xl size from metadata object, otherwise expect the element to be root
// element with that spans the full size.
const itemSize =
providedSizeForScreen ??
implicitLayoutItemSizeFromXlSize(
layoutItemSize?.xl ?? {
gridWidth: DASHBOARD_LAYOUT_GRID_COLUMNS_COUNT,
},
)[screen];
// Expect element to be full size if we could not get the size for the current screen from the value above.
return itemSize?.gridWidth ?? DASHBOARD_LAYOUT_GRID_COLUMNS_COUNT;
};

/**
* Derive dashboard layout size for all screens from dashboard layout size defined for xl screen.
* We have only xl size saved in metadata, this will create additional screen sizes based on xl.
*
* @param xlSize - dashboard layout size for xl screen
*/
export function implicitLayoutItemSizeFromXlSize(
xlSize: IDashboardLayoutSize,
): IDashboardLayoutSizeByScreenSize {
const xlWidth = xlSize.gridWidth;
const xlHeight = xlSize.gridHeight;
const ratio = xlSize.heightAsRatio;

switch (xlWidth) {
case 0:
return dashboardLayoutItemSizeForAllScreens(0, 0, 0, 0, 0, 0, 0);
case 1:
return dashboardLayoutItemSizeForAllScreens(ratio, xlHeight, xlWidth, xlWidth, 2, 6, 12);
case 2:
return dashboardLayoutItemSizeForAllScreens(ratio, xlHeight, xlWidth, xlWidth, 4, 6, 12);
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
return dashboardLayoutItemSizeForAllScreens(ratio, xlHeight, xlWidth, xlWidth, 6, 12, 12);
case 10:
return dashboardLayoutItemSizeForAllScreens(ratio, xlHeight, xlWidth, xlWidth, 12, 12, 12);
case 11:
return dashboardLayoutItemSizeForAllScreens(ratio, xlHeight, xlWidth, xlWidth, 12, 12, 12);
case 12:
return dashboardLayoutItemSizeForAllScreens(ratio, xlHeight, xlWidth, xlWidth, 12, 12, 12);
default:
invariant(false, `Unsupported xlWidth: ${xlWidth}`);
}
}

/**
* Create dashboard layout item size for all screens,
* with identical height, defined as ratio,
* but different width, defined as grid items count.
*
* @param heightAsRatio - height as ratio to the width, defined in percents
* @param gridHeight - height as number of grid rows
* @param xl - width as grid items count for xl screen
* @param lg - width as grid items count for lg screen
* @param md - width as grid items count for md screen
* @param sm - width as grid items count for sm screen
* @param xs - width as grid items count for xs screen
*/
function dashboardLayoutItemSizeForAllScreens(
heightAsRatio: number | undefined,
gridHeight: number | undefined,
xl: number,
lg: number,
md: number,
sm: number,
xs: number,
): IDashboardLayoutSizeByScreenSize {
if (gridHeight) {
return {
xl: {
gridWidth: xl,
gridHeight,
},
lg: {
gridWidth: lg,
gridHeight,
},
md: {
gridWidth: md,
gridHeight,
},
sm: {
gridWidth: sm,
gridHeight,
},
xs: {
gridWidth: xs,
gridHeight,
},
};
}
return {
xl: {
gridWidth: xl,
heightAsRatio,
},
lg: {
gridWidth: lg,
heightAsRatio,
},
md: {
gridWidth: md,
heightAsRatio,
},
sm: {
gridWidth: sm,
heightAsRatio,
},
xs: {
gridWidth: xs,
heightAsRatio,
},
};
}
2 changes: 2 additions & 0 deletions libs/sdk-ui-dashboard/src/model/commandHandlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ import {
reloadFilterViewsHandler,
} from "./filterContext/filterViewHandler.js";
import { changeInsightWidgetIgnoreCrossFilteringHandler } from "./widgets/changeInsightWidgetIgnoreCrossFilteringHandler.js";
import { setScreenSizeHandler } from "./layout/setScreenSizeHandler.js";

function* notImplementedCommand(ctx: DashboardContext, cmd: IDashboardCommand): SagaIterator<void> {
yield dispatchDashboardEvent(commandRejected(ctx, cmd.correlationId));
Expand Down Expand Up @@ -152,6 +153,7 @@ export const DefaultCommandHandlers: {
"GDC.DASH/CMD.FLUID_LAYOUT.REMOVE_ITEM_BY_WIDGET_REF": removeSectionItemByWidgetRefHandler,
"GDC.DASH/CMD.FLUID_LAYOUT.REPLACE_ITEM": replaceSectionItemHandler,
"GDC.DASH/CMD.FLUID_LAYOUT.UNDO": undoLayoutChangesHandler,
"GDC.DASH/CMD.FLUID_LAYOUT.SET_SCREEN_SIZE": setScreenSizeHandler,
"GDC.DASH/CMD.KPI_WIDGET.CHANGE_HEADER": changeKpiWidgetHeaderHandler,
"GDC.DASH/CMD.KPI_WIDGET.CHANGE_MEASURE": changeKpiWidgetMeasureHandler,
"GDC.DASH/CMD.KPI_WIDGET.CHANGE_FILTER_SETTINGS": changeKpiWidgetFilterSettingsHandler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import { put, select } from "redux-saga/effects";
import { ResizeWidth } from "../../commands/layout.js";
import { invalidArgumentsProvided } from "../../events/general.js";

import { getMinWidth } from "../../../_staging/layout/sizing.js";
import { determineWidthForScreen, getMinWidth } from "../../../_staging/layout/sizing.js";
import { selectInsightsMap } from "../../store/insights/insightsSelectors.js";
import { layoutActions } from "../../store/layout/index.js";
import { selectLayout } from "../../store/layout/layoutSelectors.js";
import { selectLayout, selectScreen } from "../../store/layout/layoutSelectors.js";
import { DashboardContext } from "../../types/commonTypes.js";
import { validateItemExists, validateSectionExists } from "./validation/layoutValidation.js";
import {
Expand Down Expand Up @@ -85,9 +85,10 @@ export function* resizeWidthHandler(

const layout = yield select(selectLayout);
const insightsMap = yield select(selectInsightsMap);
const screen = yield select(selectScreen);

validateLayoutIndexes(ctx, layout, cmd);
validateWidth(ctx, layout, insightsMap, cmd);
validateWidth(ctx, layout, insightsMap, cmd, screen);

const layoutPath = itemPath === undefined ? [{ sectionIndex, itemIndex }] : itemPath;

Expand All @@ -113,6 +114,7 @@ function validateWidth(
layout: ReturnType<typeof selectLayout>,
insightsMap: ReturnType<typeof selectInsightsMap>,
cmd: ResizeWidth,
screen: ReturnType<typeof selectScreen> = "xl",
) {
const {
payload: { itemPath, sectionIndex, itemIndex, width },
Expand All @@ -123,11 +125,13 @@ function validateWidth(
? (layout.sections[sectionIndex].items[itemIndex].widget as IWidget)
: (findItem(layout, itemPath).widget as IWidget);

const minLimit = getMinWidth(widget, insightsMap);
const minLimit = getMinWidth(widget, insightsMap, screen);
const parent =
itemPath !== undefined && itemPath.slice(0, -1).length > 0 && findItem(layout, itemPath.slice(0, -1));

const maxLimit = parent ? parent.size.xl.gridWidth : DASHBOARD_LAYOUT_GRID_COLUMNS_COUNT;
const maxLimit = parent
? determineWidthForScreen(screen, parent.size)
: DASHBOARD_LAYOUT_GRID_COLUMNS_COUNT;

const validWidth = width >= minLimit && width <= maxLimit;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// (C) 2021-2024 GoodData Corporation
import { SagaIterator } from "redux-saga";
import { put } from "redux-saga/effects";

import { DashboardContext } from "../../types/commonTypes.js";
import { SetScreenSize } from "../../commands/index.js";

import { layoutActions } from "../../store/layout/index.js";
import { ScreenSizeChanged, screenSizeChanged } from "../../events/layout.js";

export function* setScreenSizeHandler(
ctx: DashboardContext,
cmd: SetScreenSize,
): SagaIterator<ScreenSizeChanged> {
const { screenSize } = cmd.payload;

yield put(layoutActions.setScreen({ screen: screenSize }));

return screenSizeChanged(ctx, screenSize, cmd.correlationId);
}
1 change: 1 addition & 0 deletions libs/sdk-ui-dashboard/src/model/commands/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export type DashboardCommandType =
| "GDC.DASH/CMD.FLUID_LAYOUT.UNDO"
| "GDC.DASH/CMD.FLUID_LAYOUT.RESIZE_HEIGHT"
| "GDC.DASH/CMD.FLUID_LAYOUT.RESIZE_WIDTH"
| "GDC.DASH/CMD.FLUID_LAYOUT.SET_SCREEN_SIZE"
| "GDC.DASH/CMD.KPI_WIDGET.CHANGE_HEADER"
| "GDC.DASH/CMD.KPI_WIDGET.CHANGE_MEASURE"
| "GDC.DASH/CMD.KPI_WIDGET.CHANGE_FILTER_SETTINGS"
Expand Down
8 changes: 7 additions & 1 deletion libs/sdk-ui-dashboard/src/model/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ import {
ResizeHeight,
ResizeWidth,
RemoveSectionItemByWidgetRef,
SetScreenSize,
} from "./layout.js";
import { CreateAlert, SaveAlert } from "./alerts.js";
import {
Expand Down Expand Up @@ -257,6 +258,8 @@ export type {
ResizeHeightPayload,
ResizeWidth,
ResizeWidthPayload,
SetScreenSize,
SetScreenSizePayload,
} from "./layout.js";
export {
addLayoutSection,
Expand Down Expand Up @@ -291,6 +294,7 @@ export {
resizeNestedLayoutItemsHeight,
resizeWidth,
resizeNestedLayoutItemWidth,
setScreenSize,
} from "./layout.js";

export type { CreateAlert, CreateAlertPayload, SaveAlert, SaveAlertPayload } from "./alerts.js";
Expand Down Expand Up @@ -581,4 +585,6 @@ export type DashboardCommands =
| DeleteFilterView
| ApplyFilterView
| SetFilterViewAsDefault
| ReloadFilterViews;
| ReloadFilterViews
//internal
| SetScreenSize;
44 changes: 43 additions & 1 deletion libs/sdk-ui-dashboard/src/model/commands/layout.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// (C) 2021-2024 GoodData Corporation

import { IDashboardLayoutSectionHeader, ObjRef } from "@gooddata/sdk-model";
import { IDashboardLayoutSectionHeader, ObjRef, ScreenSize } from "@gooddata/sdk-model";
import { DashboardItemDefinition, RelativeIndex, StashedDashboardItemsId } from "../types/layoutTypes.js";
import { ILayoutItemPath, ILayoutSectionPath } from "../../types.js";

Expand Down Expand Up @@ -1877,3 +1877,45 @@ export function resizeNestedLayoutItemWidth(
},
};
}

/////

/**
* Payload of the {@link SetScreenSize} command.
* @internal
*/
export interface SetScreenSizePayload {
screenSize: ScreenSize;
}

/**
* @internal
*/
export interface SetScreenSize extends IDashboardCommand {
readonly type: "GDC.DASH/CMD.FLUID_LAYOUT.SET_SCREEN_SIZE";
readonly payload: SetScreenSizePayload;
}

/**
* Creates the SetScreenSize command.
*
* @remarks
* This command sets new screen size for the dashboard layout.
* Do not use this command directly, it is used internally by the dashboard layouting engine.
*
* @param screenSize - new screen size to set
* @param correlationId - specify correlation id to use for this command. this will be included in all
* events that will be emitted during the command processing
*
* @internal
*
*/
export function setScreenSize(screenSize: ScreenSize, correlationId?: string): SetScreenSize {
return {
type: "GDC.DASH/CMD.FLUID_LAYOUT.SET_SCREEN_SIZE",
correlationId,
payload: {
screenSize,
},
};
}
1 change: 1 addition & 0 deletions libs/sdk-ui-dashboard/src/model/events/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export type DashboardEventType =
| "GDC.DASH/EVT.FLUID_LAYOUT.ITEM_MOVED_TO_NEW_SECTION"
| "GDC.DASH/EVT.FLUID_LAYOUT.ITEM_REMOVED"
| "GDC.DASH/EVT.FLUID_LAYOUT.LAYOUT_CHANGED"
| "GDC.DASH/EVT.FLUID_LAYOUT.SCREEN_SIZE_CHANGED"
| "GDC.DASH/EVT.KPI_WIDGET.HEADER_CHANGED"
| "GDC.DASH/EVT.KPI_WIDGET.DESCRIPTION_CHANGED"
| "GDC.DASH/EVT.KPI_WIDGET.CONFIGURATION_CHANGED"
Expand Down
Loading

0 comments on commit 6c1f589

Please sign in to comment.