diff --git a/src/plugins/content_management/public/components/card_container/card_list.tsx b/src/plugins/content_management/public/components/card_container/card_list.tsx
index 871c6451a8cb..37cf18adb048 100644
--- a/src/plugins/content_management/public/components/card_container/card_list.tsx
+++ b/src/plugins/content_management/public/components/card_container/card_list.tsx
@@ -22,6 +22,27 @@ interface Props {
}
const CardListInner = ({ embeddable, input, embeddableServices }: Props) => {
+ if (input.columns) {
+ const width = `${(1 / input.columns) * 100}%`;
+ const cards = Object.values(input.panels).map((panel) => {
+ const child = embeddable.getChild(panel.explicitInput.id);
+ return (
+
+
+
+ );
+ });
+ return (
+
+ {cards}
+
+ );
+ }
+
const cards = Object.values(input.panels).map((panel) => {
const child = embeddable.getChild(panel.explicitInput.id);
return (
@@ -31,10 +52,9 @@ const CardListInner = ({ embeddable, input, embeddableServices }: Props) => {
);
});
- // TODO: we should perhaps display the cards in multiple rows when the actual number of cards exceed the column size
return (
-
- {input.columns ? cards.slice(0, input.columns) : cards}
+
+ {cards}
);
};
diff --git a/src/plugins/content_management/public/components/card_container/types.ts b/src/plugins/content_management/public/components/card_container/types.ts
index 5c6ad129af66..8f4ea7855cab 100644
--- a/src/plugins/content_management/public/components/card_container/types.ts
+++ b/src/plugins/content_management/public/components/card_container/types.ts
@@ -13,7 +13,10 @@ export interface CardExplicitInput {
getFooter?: () => React.ReactElement;
}
-export type CardContainerInput = ContainerInput & { columns?: number };
+export type CardContainerInput = ContainerInput & {
+ columns?: number;
+ wrap?: boolean;
+};
/**
* The props which allow to be updated after card container was created
diff --git a/src/plugins/content_management/public/components/section_input.test.ts b/src/plugins/content_management/public/components/section_input.test.ts
index be471c8428a2..80fe5ce0863c 100644
--- a/src/plugins/content_management/public/components/section_input.test.ts
+++ b/src/plugins/content_management/public/components/section_input.test.ts
@@ -5,7 +5,7 @@
import { coreMock } from '../../../../core/public/mocks';
import { Content, Section } from '../services';
-import { createCardInput, createDashboardInput } from './section_input';
+import { DASHBOARD_PANEL_WIDTH, createCardInput, createDashboardInput } from './section_input';
test('it should create card section input', () => {
const section: Section = { id: 'section1', kind: 'card', order: 10 };
@@ -363,3 +363,119 @@ test('it should create section with a dynamic dashboard as content', async () =>
},
});
});
+
+test('it renders content with custom width and height', async () => {
+ const customWidth = 24;
+ const customHeight = 20;
+ const section: Section = { id: 'section1', kind: 'dashboard', order: 10 };
+ const staticViz: Content = {
+ id: 'content1',
+ kind: 'visualization',
+ order: 0,
+ width: customWidth,
+ height: customHeight,
+ input: {
+ kind: 'static',
+ id: 'viz-id-static',
+ },
+ };
+
+ const clientMock = coreMock.createStart().savedObjects.client;
+ const input = await createDashboardInput(section, [staticViz], {
+ savedObjectsClient: clientMock,
+ });
+
+ expect(input.panels).toEqual({
+ content1: {
+ explicitInput: {
+ disabledActions: ['togglePanel'],
+ id: 'content1',
+ savedObjectId: 'viz-id-static',
+ },
+ gridData: {
+ i: 'content1',
+ h: customHeight,
+ w: customWidth,
+ x: 0,
+ y: 0,
+ },
+ type: 'visualization',
+ },
+ });
+});
+
+test('it uses default width if custom content width is <= 0', async () => {
+ const customWidthShouldBeBiggerThan0 = 0;
+ const section: Section = { id: 'section1', kind: 'dashboard', order: 10 };
+ const staticViz: Content = {
+ id: 'content1',
+ kind: 'visualization',
+ order: 0,
+ width: customWidthShouldBeBiggerThan0,
+ input: {
+ kind: 'static',
+ id: 'viz-id-static',
+ },
+ };
+
+ const clientMock = coreMock.createStart().savedObjects.client;
+ const input = await createDashboardInput(section, [staticViz], {
+ savedObjectsClient: clientMock,
+ });
+
+ expect(input.panels).toEqual({
+ content1: {
+ explicitInput: {
+ disabledActions: ['togglePanel'],
+ id: 'content1',
+ savedObjectId: 'viz-id-static',
+ },
+ gridData: {
+ h: 15,
+ i: 'content1',
+ w: DASHBOARD_PANEL_WIDTH,
+ x: 0,
+ y: 0,
+ },
+ type: 'visualization',
+ },
+ });
+});
+
+test('it should use default width if custom content width > DASHBOARD_GRID_COLUMN_COUNT: 48', async () => {
+ const customWidthShouldNotBeBiggerThan0 = 49;
+ const section: Section = { id: 'section1', kind: 'dashboard', order: 10 };
+ const staticViz: Content = {
+ id: 'content1',
+ kind: 'visualization',
+ order: 0,
+ width: customWidthShouldNotBeBiggerThan0,
+ input: {
+ kind: 'static',
+ id: 'viz-id-static',
+ },
+ };
+
+ const clientMock = coreMock.createStart().savedObjects.client;
+ const input = await createDashboardInput(section, [staticViz], {
+ savedObjectsClient: clientMock,
+ });
+
+ expect(input.panels).toEqual({
+ content1: {
+ explicitInput: {
+ disabledActions: ['togglePanel'],
+ id: 'content1',
+ savedObjectId: 'viz-id-static',
+ },
+ gridData: {
+ h: 15,
+ i: 'content1',
+ w: DASHBOARD_PANEL_WIDTH,
+ x: 0,
+ y: 0,
+ },
+ type: 'visualization',
+ },
+ });
+});
diff --git a/src/plugins/content_management/public/components/section_input.ts b/src/plugins/content_management/public/components/section_input.ts
index 5a0b9ca370c6..7d49f8a17ef9 100644
--- a/src/plugins/content_management/public/components/section_input.ts
+++ b/src/plugins/content_management/public/components/section_input.ts
@@ -14,6 +14,8 @@ import { CARD_EMBEDDABLE } from './card_container/card_embeddable';
import { CardContainerInput } from './card_container/types';
const DASHBOARD_GRID_COLUMN_COUNT = 48;
+export const DASHBOARD_PANEL_WIDTH = 12;
+export const DASHBOARD_PANEL_HEIGHT = 15;
export const createCardInput = (
section: Section,
@@ -31,6 +33,7 @@ export const createCardInput = (
hidePanelTitles: true,
viewMode: ViewMode.VIEW,
columns: section.columns,
+ wrap: section.wrap,
panels,
...section.input,
};
@@ -71,12 +74,26 @@ export const createDashboardInput = async (
const panels: DashboardContainerInput['panels'] = {};
let x = 0;
let y = 0;
- const w = 12;
- const h = 15;
const counter = new BehaviorSubject(0);
contents.forEach(async (content) => {
counter.next(counter.value + 1);
+
+ let w = DASHBOARD_PANEL_WIDTH;
+ let h = DASHBOARD_PANEL_HEIGHT;
+
+ if ('width' in content && typeof content.width === 'number') {
+ if (content.width > 0 && content.width <= DASHBOARD_GRID_COLUMN_COUNT) {
+ w = content.width;
+ }
+ }
+
+ if ('height' in content && typeof content.height === 'number') {
+ if (content.height > 0) {
+ h = content.height;
+ }
+ }
+
try {
if (content.kind === 'dashboard') {
let dashboardId = '';
@@ -120,7 +137,13 @@ export const createDashboardInput = async (
return;
}
- const config: DashboardContainerInput['panels'][string] = {
+ // If current panel exceed the max dashboard container width, add the panel to the next row
+ if (x + w > DASHBOARD_GRID_COLUMN_COUNT) {
+ x = 0;
+ y = y + h;
+ }
+
+ const panelConfig: DashboardContainerInput['panels'][string] = {
gridData: {
w,
h,
@@ -135,28 +158,25 @@ export const createDashboardInput = async (
},
};
+ // The new x starts from the current panel x + current panel width
x = x + w;
- if (x >= DASHBOARD_GRID_COLUMN_COUNT) {
- x = 0;
- y = y + h;
- }
if (content.kind === 'visualization') {
- config.type = 'visualization';
+ panelConfig.type = 'visualization';
if (content.input.kind === 'dynamic') {
- config.explicitInput.savedObjectId = await content.input.get();
+ panelConfig.explicitInput.savedObjectId = await content.input.get();
}
if (content.input.kind === 'static') {
- config.explicitInput.savedObjectId = content.input.id;
+ panelConfig.explicitInput.savedObjectId = content.input.id;
}
}
if (content.kind === 'custom') {
- config.type = CUSTOM_CONTENT_EMBEDDABLE;
- config.explicitInput.render = content.render;
+ panelConfig.type = CUSTOM_CONTENT_EMBEDDABLE;
+ panelConfig.explicitInput.render = content.render;
}
- panels[content.id] = config;
+ panels[content.id] = panelConfig;
} catch (e) {
// eslint-disable-next-line
console.log(e);
diff --git a/src/plugins/content_management/public/services/content_management/types.ts b/src/plugins/content_management/public/services/content_management/types.ts
index 5b9b4662d37f..755e6f426a0a 100644
--- a/src/plugins/content_management/public/services/content_management/types.ts
+++ b/src/plugins/content_management/public/services/content_management/types.ts
@@ -35,8 +35,9 @@ export type Section =
id: string;
order: number;
title?: string;
- columns?: number;
input?: CardContainerExplicitInput;
+ columns?: number;
+ wrap?: boolean;
};
export type Content =
@@ -45,18 +46,24 @@ export type Content =
id: string;
order: number;
input: SavedObjectInput;
+ width?: number;
+ height?: number;
}
| {
kind: 'dashboard';
id: string;
order: number;
input: SavedObjectInput;
+ width?: number;
+ height?: number;
}
| {
kind: 'custom';
id: string;
order: number;
render: () => JSX.Element;
+ width?: number;
+ height?: number;
}
| {
kind: 'card';