Skip to main content

DHTMLX Kanban. Grouping two or more statuses into a single column example

When your workflow has more statuses than you want visible columns, you can group multiple statuses into a single column. This keeps the board compact while preserving fine-grained status tracking. Cards show their specific status, but live in a broader column.

Live example

const updateColumn = card => {
    for (let col in groupingRules) {
        if (groupingRules[col].includes(card.status)) {
            card.column = col;
            break;
        }
    }
};

const groupingRules = {
    open: ["todo", "unassigned"],
    inprogress: ["dev", "testing"],
    done: ["merged", "released"],
};

cards = cards.map(card => {
    updateColumn(card);
    return card;
});

const board = new kanban.Kanban("#root", {
    columns,
    cards,
    cardShape: {
        label: true,
        headerFields: [
            {
                key: "status",
                label: "Status",
            },
        ],
    },
    editorShape: [
        {
            key: "label",
            type: "text",
            label: "Title",
        },
        {
            key: "status",
            type: "select",
            label: "Status",
            values: [
                { id: "todo", label: "To do" },
                { id: "unassigned", label: "Unassigned" },
                { id: "dev", label: "In development" },
                { id: "testing", label: "Testing" },
                { id: "merged", label: "Merged" },
                { id: "released", label: "Released" },
            ],
        },
    ],
});

board.api.intercept("move-card", ev => {
    board.api.exec("update-card", {
        id: ev.id,
        card: { status: groupingRules[ev.columnId][0] },
    });
});

board.api.intercept("update-card", ev => {
    updateColumn(ev.card);
});
<div class="g-wrap" style="height: 100%;">
    <!-- Kanban container -->
    <div id="root" style="height: 100%;"></div>
</div>

<!-- dataset -->

<script>
const columns = [
		{
			label: "Open",
			id: "open",
		},
		{
			label: "In progress",
			id: "inprogress",
		},
		{
			label: "Done",
			id: "done",
		},
	];
    
    let cards = [
		{
			label: "Integration with Angular/React",
			status: "todo",
		},
		{
			label: "Archive the cards/boards ",
			status: "unassigned",
		},
		{
			label: "Searching and filtering",
			status: "dev",
		},
		{
			label: "Set the tasks priorities",
			status: "testing",
		},
		{
			label: "Custom icons",
			status: "merged",
		},
		{
			label: "Integration with Gantt",
			status: "released",
		},
		{
			label: "Drag and drop",
			status: "todo",
		},
		{
			label: "Adding images",
			status: "unassigned",
		},
		{
			label: "Create cards and lists from the UI and from code",
			status: "dev",
		},
		{
			label: "Draw swimlanes",
			status: "testing",
		},
		{
			label: "Progress bar",
			status: "released",
		},
	];
</script>

The code defines a groupingRules map that assigns statuses to columns (e.g., "todo" and "unassigned" both map to the "Open" column). Before initialization, cards are preprocessed to set their column based on their status. Two interceptors keep things in sync: intercept("move-card") assigns the first status of the target column when a card is dragged, and intercept("update-card") recalculates the column when a status changes in the editor. The headerFields card shape option displays the status badge directly on each card.

Solution overview

  1. Define a groupingRules object mapping column IDs to arrays of status values
  2. Pre-process cards to assign column based on their status field
  3. Use headerFields in cardShape to display the status on each card
  4. Add editorShape with a select field for the status, listing all possible values
  5. Intercept move-card to assign the first status of the target column
  6. Intercept update-card to recalculate the column when status changes

Key points

  • intercept, not on for pre-commit changes: api.intercept runs before the action is committed and can modify event data; api.on runs after. Use intercept when you need to transform data as part of the action, not react to it
  • First status is the default: When dragging a card to a new column, it gets groupingRules[columnId][0], the first status in that column's group
  • headerFields for visual feedback: the default card shape does not have the status of a card, because by default it is 1 status per column and the name of the status is on the column header
  • Custom editorShape required: The default editor doesn't have a status fields. You need to define it explicitly

API reference

  • cardShape: Configures card appearance including headerFields
  • editorShape: Defines custom editor fields
  • api.intercept: Intercepts and modifies events before they are applied

Additional resources