Customizing task contents

This guide describes how to customize the contents of task cards on the TaskBoard.

Configuring task items

Cards are divided into three parts: header, body and footer. The contents of those can be somewhat customized by configuring which task items to show in which part. Each part has a corresponding config that accepts task items. The configs are:

  • headerItems - Defaults to use a text item (named text) displaying the name of a task.
  • bodyItems - Defaults to use a text item (named text) displaying the description of a task.
  • footerItems - Defaults to use a resource avatar item (named resourceAvatars), if resources are used in the app.

The following item types are available:

  • image - Displays an image
  • progress - Displays a progress bar
  • rating - Displays a star rating
  • resourceAvatars - Displays resource avatars or initials
  • separator - Displays a separator (<hr>)
  • tags - Displays tags, from a string, array of strings or array of objects
  • template - Displays content from a template
  • text - Displays text
  • todoList - Displays todo items with checkboxes

Given the following data:

{
    "id"          : 1,
    "name"        : "Write a guide",
    "title"       : "Customizing task contents",
    "description" : "On customizing task contents",
    "summary"     : "Guide that shows the different options",
    "prio"        : "high",
    "status"      : "doing",
    "percentDone" : 40
}

By default we get the following card rendition:

Default task contents
//<code-header>
fiddle.title = 'Default task contents';
//</code-header>
const taskBoard = new TaskBoard({
    appendTo : targetElement,

    features : {
        columnToolbars : false
    },

    // Columns to display
    columns : [
        'todo',
        'doing',
        'done'
    ],

    // Field used to pair a task to a column
    columnField : 'status',

    // Project using inline data
    project : {
        tasks : [
            {
                id          : 1,
                name        : 'Write a guide',
                title       : 'Customizing task contents',
                description : 'On customizing task contents',
                summary     : 'Guide that shows the different options',
                prio        : 'high',
                status      : 'doing'
            }
        ]
    }
});

Here we reconfigure and add some items:

const taskBoard = new TaskBoard({ 
    headerItems : {
        // Reconfigure the default text item
        text : { field : 'title' }
    },
  
    bodyItems : {
        // Reconfigure the default text item
        text        : { field : 'summary' },
        // Add a new progress item to the body
        // (no field is supplied => key is used as the field = percentDone)
        percentDone : { type : 'progress' } 
    },
  
    footerItems : {
        // Add a new text item to the footer
        prio : { type : 'text' }
    }
})

Which results in:

Task content fields
//<code-header>
fiddle.title = 'Task content fields';
//</code-header>
const taskBoard = new TaskBoard({
    appendTo : targetElement,

    headerItems : {
        text : { field : 'title' }
    },

    bodyItems : {
        text        : { field : 'summary' },
        percentDone : { type : 'progress' }
    },

    footerItems : {
        prio : { type : 'text' }
    },

    features : {
        columnToolbars : false
    },

    // Columns to display
    columns : [
        'todo',
        'doing',
        'done'
    ],

    // Field used to pair a task to a column
    columnField : 'status',

    // Project using inline data
    project : {
        tasks : [
            {
                id          : 1,
                name        : 'Write a guide',
                title       : 'Customizing task contents',
                description : 'On customizing task contents',
                summary     : 'Guide that shows the different options',
                prio        : 'high',
                status      : 'doing',
                percentDone : 40,
                eventColor  : 'blue'
            }
        ]
    }
});

Customizing card elements using a taskRenderer

TaskBoard can be configured with a taskRenderer, a function called for each task when its card is about to be drawn. In this function you can manipulate a DOM config object and by doing so affect what ends up in the DOM.

Configuring a simple taskRenderer:

const taskBoard = new TaskBoard({
    taskRenderer({ taskRecord, cardConfig }) {
        // Add an icon to the header of each card
        cardConfig.children.header.children.icon = {
            tag : 'i',
            class : 'fa fa-coffee',
            style : 'order : 200'
        }
    }
});

Result:

Task renderer
//<code-header>
fiddle.title = 'Task renderer';
//</code-header>
const taskBoard = new TaskBoard({
    appendTo : targetElement,

    taskRenderer({ taskRecord, cardConfig }) {
        // Add an icon to the header of each card
        cardConfig.children.header.children.icon = {
            tag   : 'i',
            class : 'fa fa-coffee',
            style : 'order : 200'
        };
    },

    features : {
        columnToolbars : false
    },

    // Columns to display
    columns : [
        'todo',
        'doing',
        'done'
    ],

    // Field used to pair a task to a column
    columnField : 'status',

    // Project using inline data
    project : {
        tasks : [
            {
                id          : 1,
                name        : 'Write a guide',
                description : 'On customizing task contents',
                prio        : 'high',
                status      : 'doing',
                eventColor  : 'yellow'
            },
            {
                id          : 2,
                name        : 'Make demos',
                description : 'For the guide',
                prio        : 'medium',
                status      : 'todo',
                eventColor  : 'blue'
            }
        ]
    }
});

As shown by the snippet above, a taskRenderer is called with a single argument with taskRecord and cardConfig properties. By manipulating cardConfig you affect the resulting DOM. It is a pretty complex object, which gives you access to the cards full markup.

The cardConfig object

This listing shows the relevant parts of the cardConfig object before the modification above (with comments inserted):

cardConfig = {
  // CSS classes applied to the cards outermost element (the card itself).
  // Uses object form to simplify adding conditional classes
  class : {
    'b-task-board-card' : true
  },
  // Child elements = the cards different parts: header, body, footer
  // Uses object form to simplify accessing a specific part of the card (children.body vs children[1])
  children : {
    // Card header  
    header : {
      class : 'b-task-board-card-header',
      children : {
        // Header has a single child by default, holding its text  
        text : {
          class : {
              'b-task-board-task-item' : 1,
              'b-task-board-text'      : 1
          },
          text  : 'Make demos'
        },
        // The modification in the taskRenderer snippet above inserts the icon here, ending up as:
        // icon : {
        //   tag   : 'i',
        //   class : 'fa fa-coffee',
        //   style : 'order : 200'
        // }
      }
    },
    // Card body
    body : {
      class    : 'b-task-board-card-body',
      children : {
        // Body has a single child by defauly, holding its text  
        text : {
          class : {
            'b-task-board-task-item' : 1,
            'b-task-board-text'      : 1
          },
          text  : 'For the guide'
        }
      }
    },
    // Card footer (empty by default, empty parts are removed before rendering to the DOM)
    footer : {
      class    : 'b-task-board-card-footer',
      children : {}
    }
  }
}

The cardConfig above (with the icon added) ends up as the following HTML (slightly redacted to save some space):

<div class="b-task-board-card">
  <header class="b-task-board-card-header">
    <div class="b-task-board-task-item b-task-board-text">Make demos</div>
    <i class="fa fa-coffee"></i>
  </header>
  <section class="b-task-board-card-body">
    <div class="b-task-board-task-item b-task-board-text">For the guide</div>
  </section>
</div>

It might seem complicated to use the DomConfig object form over a string template, but it is for good reasons:

  1. It is easier to manipulate an object than a string, allowing for easier customization
  2. It is faster to parse than a string

Customization examples

Some things you might want to do, and how to do it:

Add a CSS class to the card:

cardConfig.class.myCssClass = true;

Change the headers text:

cardConfig.children.header.children.text.text = 'Header text';

Add an image to the body:

cardConfig.children.body.children.image = {
    tag : 'img',
    src : 'myimage.png'
};