Page tree
Skip to end of metadata
Go to start of metadata

Wiki markup allows you to get creative and visualize more complex metrics in Structure columns, such as custom progress bars, bar charts and much more.

We've put together several advanced, customizable examples of wiki markup usage:

subtasks.MAP("""[${$.key}|${$.url}]""")

Create a borderless background behind the value

WITH addBackground(value, color) =
  """{panel:bgColor=$color|borderWidth=0px}$value{panel}""":
addBackground(summary, "#ADFF2F")

Customizable Progress Bar

In this simple example, we used Wiki Markup to create a customized progress bar. In the left column you can see the built-in progress column. In the right one, we've built a progress bar which is split into 10% sections.

We used the following formula to build the custom progress bar:

Simple progress bar
WITH simpleProgressBar(progress, maxProgress, stepCount) = (
  WITH _bars(count, color) = """{color:$color}${REPEAT("■", count)}{color}""":
  WITH doneBarsCount = FLOOR(progress / maxProgress * stepCount):
  _bars(doneBarsCount, "green") CONCAT _bars(stepCount - doneBarsCount, "gray")
):

simpleProgressBar(customProgress, 1, 10)

Starting with this, you can tailor the progress bar to your team's particular needs.

  • Colors can easily be configured by altering the "color" values - in this case, we used green and gray squares.
  • The progress calculation can be based on any percentage value. In the following example, we used an arbitrary percentage field and aggregated up the hierarchy. 
Simple progress bar
WITH simpleProgressBar(progress, maxProgress, stepCount) = (
  WITH _bars(count, color) = """{color:$color}${REPEAT("■", count)}{color}""":
  WITH doneBarsCount = FLOOR(progress / maxProgress * stepCount):
  _bars(doneBarsCount, "green") CONCAT _bars(stepCount - doneBarsCount, "gray")
):

simpleProgressBar(SUM { progressField }, SUM { 1 }, 10)

This can be especially useful if you want to display progress based on some complex fields, like a ScriptRunner scripted field, which is not supported by the standard formula column at the moment.

Customizable Status Bars

Wiki markup can also be used to create more complex progress calculations, based on multiple issue statuses.

In the following example, we created multiple custom status bars, tracking the following statuses:

  • To Do = Red
  • In Progress = Orange
  • Done = Green
  • All Other Statuses = Gray

As with our custom progress bar, these formulas can easily be modified to adjust status colors, include additional statuses or represent each status in a different format.

Multi-bar

We used the following code to build the Multi-bar Status Bar.

Multi-tiered progress bar
//stepCount - length of the bar chart in characters
WITH multiProgressBar(progressArray, maxProgress, colorsArray, colorForRemaining, stepCount) = (
  WITH _bars(count, color) = (IF count > 0: """{color:$color}${REPEAT("▮", count)}{color}""" ELSE ""):
  WITH barCounts = progressArray.MAP(FLOOR($ / maxProgress * stepCount)):
  progressArray.INDEXES()
    .MAP(_bars(barCounts.GET($), colorsArray.GET($)))
    .MERGE_ARRAYS(_bars(MAX(0, stepCount - barCounts.SUM()), colorForRemaining))
    .JOIN("", "", "")
):

WITH todo = COUNT#truthy { status = "To Do" }:
WITH inProgress = COUNT#truthy { status = "In Progress" }:
WITH done = COUNT#truthy { status = "Done" }:

multiProgressBar(
  ARRAY(todo, inProgress, done), COUNT { 1 },
  ARRAY("red", "orange", "green"), "gray",
  20
)

You can change the appearance of the status simply by altering the granularity (length of the bar sections) or a using a larger symbol as we did in the Multi-bar different character example.

While the ■ or ▮ symbols may lack solid feel, the █ symbol still creates a slight brick-layer effect.

Multi-bar with Image

In this example, we used a simple, monochrome images (a 1x1 pixel size is enough) to make the status bar appear more solid. If you decide to try this, we highly recommend using a locally-hosted image, rather than one taken from public sources, because some hosts may block multiple successive requests for an image.

Multi-tiered progress bar based on images
//Granularity - length of the bar chart in pixels
WITH multiProgressBarWithImage(progressArray, maxProgress, imagesArray, imageForRemaining, granularity) = (
  WITH bar(width, image) = (IF width > 0: """!$image|height=20,width=$width!""" ELSE ""):
  WITH barCounts = progressArray.MAP(FLOOR($ / maxProgress * granularity)):
  progressArray.INDEXES()
    .MAP(bar(barCounts.GET($), imagesArray.GET($)))
    .MERGE_ARRAYS(bar(MAX(0, granularity - barCounts.SUM()), imageForRemaining))
    .JOIN("", "", "")
):


WITH todo = COUNT#truthy {status = "to do"}:
WITH inProgress = COUNT#truthy {status = "in progress"}:
WITH done = COUNT#truthy {status = "done"}:

WITH link(name) = """https://www.example.com/images/$name.png""":

multiProgressBarWithImage(
  ARRAY(todo, inProgress, done), COUNT{1},
  ARRAY("Red", "Orange", "Green").MAP(link), link("Gray"),
  200
)

Multi-bar with Numbers

In this last example, the status bar displays an issue count for each status, when the bar width permits. This code could be easily customized to display either the actual number of issues or their percentage.

Progress bar with numbers
//Parameters: granularity - length of bar-chart in characters; bar - filler of the bar chart
WITH multiProgressBarWithNumbers(progressArray, maxProgress, colorsArray, colorForRemaining, granularity, bar) = (
  WITH bars(count, value, color) = (
    IF count <= 0:
      ""
    ELSE:
      WITH bars = (
        WITH charsForValue = LEN(value):
        IF count >= charsForValue + 2:
          WITH charsBeforeValue = FLOOR((count - charsForValue) / 2):
          REPEAT(bar, charsBeforeValue)
          CONCAT value
          CONCAT REPEAT(bar, count - charsBeforeValue - charsForValue)
        ELSE:
          REPEAT(bar, count)
      ):
	  """{color:$color}$bars{color}"""   
  ):
  WITH barCounts = progressArray.MAP(FLOOR($ / maxProgress * granularity)):
  progressArray.INDEXES()
    .MAP(bars(barCounts.GET($), progressArray.GET($), colorsArray.GET($)))
    .MERGE_ARRAYS(bars(MAX(0, granularity - barCounts.SUM()), MAX(0, maxProgress - progressArray.SUM()), colorForRemaining))
    .JOIN("", "", "")
):

WITH todo = COUNT#truthy {status = "to do"}:
WITH inProgress = COUNT#truthy {status = "in progress"}:
WITH done = COUNT#truthy {status = "done"}:

multiProgressBarWithNumbers(
  ARRAY(todo, inProgress, done), COUNT{1},
  ARRAY("red", "orange", "green"), "gray",
  20, "▮"
)

Simple Burn-down Chart

You can get even more creative and use wiki markup to build mini-charts – including this simple burn-down chart. In this example, our chart displays created issues in red and resolved issues in green, with each pair corresponding to one day in a week.

Due to space limitations, there is a height limit of 20 pixels imposed within the chart, but this is more than enough to create a simple, powerful visualization.

Burn-down chart
WITH dataArray = ARRAY(
  COUNT#truthy {DATE_SUBTRACT(NOW(),6,"days") <= created  and DATE_SUBTRACT(NOW(),5,"days") > created},
  COUNT#truthy {DATE_SUBTRACT(NOW(),6,"days") <= resolved and DATE_SUBTRACT(NOW(),5,"days") > resolved},
  COUNT#truthy {DATE_SUBTRACT(NOW(),5,"days") <= created  and DATE_SUBTRACT(NOW(),4,"days") > created},
  COUNT#truthy {DATE_SUBTRACT(NOW(),5,"days") <= resolved and DATE_SUBTRACT(NOW(),4,"days") > resolved},
  COUNT#truthy {DATE_SUBTRACT(NOW(),4,"days") <= created  and DATE_SUBTRACT(NOW(),3,"days") > created},
  COUNT#truthy {DATE_SUBTRACT(NOW(),4,"days") <= resolved and DATE_SUBTRACT(NOW(),3,"days") > resolved},
  COUNT#truthy {DATE_SUBTRACT(NOW(),3,"days") <= created  and DATE_SUBTRACT(NOW(),2,"days") > created},
  COUNT#truthy {DATE_SUBTRACT(NOW(),3,"days") <= resolved and DATE_SUBTRACT(NOW(),2,"days") > resolved},
  COUNT#truthy {DATE_SUBTRACT(NOW(),2,"days") <= created  and DATE_SUBTRACT(NOW(),1,"days") > created},
  COUNT#truthy {DATE_SUBTRACT(NOW(),2,"days") <= resolved and DATE_SUBTRACT(NOW(),1,"days") > resolved},
  COUNT#truthy {DATE_SUBTRACT(NOW(),1,"days") <= created  and DATE_SUBTRACT(NOW(),8,"hours") > created},
  COUNT#truthy {DATE_SUBTRACT(NOW(),1,"days") <= resolved and DATE_SUBTRACT(NOW(),8,"hours") > resolved},
  COUNT#truthy {DATE_SUBTRACT(NOW(),8,"hours") <= created},
  COUNT#truthy {DATE_SUBTRACT(NOW(),8,"hours") <= resolved}
):


//25 is maximum working height
WITH maxHeight = 25:
WITH maxValue = dataArray.MAX():
WITH getPicture(index) = (IF index.MOD(2) == 0: "https://www.example.com/images/Red.png" ELSE: "https://www.example.com/images/Green.png"):
WITH getHeight(index) = IF maxValue : FLOOR(dataArray.GET(index) / maxValue * maxHeight) ELSE : 0 :

IF itemtype != "issue":
dataArray
  .INDEXES()
  .MAP("""!${getPicture($)}|height=${getHeight($)},width=5!""")
  .JOIN("", "", "")

The criteria for issue inclusion can be easily customized to your team's needs. As mentioned above, we recommend hosting image files locally.