✨Discover storytelling in the AI age with Pixar's Matthew Luhn at Sanity Connect, May 8th—register now

Horizontal bar chart

By Jayne Mast

This is a schema we've used to create accessbile bar charts, with human-readable text for each bar.

horizontalBarChart.js

import React from "react";
import { RiBarChartHorizontalFill } from "react-icons/ri";

const scale = {
  name: "horizontalBarChart.scale",
  title: "Scale",
  type: "object",
  fields: [
    {
      name: "ticks",
      title: "Ticks",
      type: "array",
      of: [{ type: "number" }],
      description:
        "Each tick is a number on the scale. If you don't add ticks, the scale will not be shown.",
      validation: (Rule) => Rule.min(2),
    },
    {
      name: "prefix",
      title: "Prefix",
      type: "string",
      description: "A text that goes in front of each tick (for example, $)",
    },
    {
      name: "suffix",
      title: "Suffix",
      type: "string",
      description: "A text that goes after each tick (for example, %)",
    },
  ],
};

const legendItem = {
  name: "horizontalBarChart.legendItem",
  title: "Legend item",
  type: "object",
  fields: [
    {
      name: "label",
      title: "Label",
      type: "string",
      validation: (Rule) => Rule.required(),
    },
    {
      name: "color",
      title: "Color",
      type: "brandColor",
      validation: (Rule) => Rule.required(),
      description:
        "The color you set here would also be the color of one of the points (bars) in a series. For example, if this would be the second legend item, then the second point in a series would be this color. You can override the color of a point.",
    },
  ],
  preview: {
    select: {
      title: "label",
      color: "color",
    },
    prepare({ title, color }) {
      return {
        title,
        media: (
          <div
            style={{ width: "40px", height: "15px", backgroundColor: color }}
          ></div>
        ),
      };
    },
  },
};

const point = {
  name: "horizontalBarChart.point",
  title: "Point",
  type: "object",
  fields: [
    {
      name: "value",
      title: "Value",
      type: "number",
      validation: (Rule) => Rule.required(),
    },
    {
      name: "description",
      title: "Description",
      type: "string",
      description:
        "This is a tooltip that's shown when you hover over the point.",
    },
    {
      name: "color",
      title: "Color",
      type: "brandColor",
      description:
        "This would override the color set in the legend. Or if you don't have a legend, this would be the color of the point.",
    },
  ],
  preview: {
    select: {
      title: "value",
      subtitle: "description",
      color: "color",
    },
    prepare({ title, subtitle, color }) {
      return {
        title,
        subtitle,
        media: color && (
          <div
            style={{ width: "40px", height: "15px", backgroundColor: color }}
          ></div>
        ),
      };
    },
  },
};

const seriesItem = {
  name: "horizontalBarChart.seriesItem",
  title: "Series item",
  type: "object",
  fields: [
    {
      name: "label",
      title: "Label",
      type: "string",
      description: "The label for the set of columns.",
      validation: (Rule) => Rule.required(),
    },
    {
      name: "points",
      title: "Points",
      type: "array",
      of: [{ type: "horizontalBarChart.point" }],
      description: "Each point is one of the bars.",
      validation: (Rule) => Rule.required(),
    },
  ],
};

const chart = {
  name: "horizontalBarChart",
  type: "document",
  title: "Horizontal bar chart",
  icon: RiBarChartHorizontalFill,
  fields: [
    {
      name: "name",
      title: "Name",
      type: "string",
      description:
        "This is used so you can easily find it when adding to another document.",
      validation: (Rule) => Rule.required(),
    },
    {
      name: "scale",
      title: "Scale",
      type: "horizontalBarChart.scale",
      description:
        "The scale are the numbers that show up at the X-axis (the bottom).",
    },
    {
      name: "legend",
      title: "Legend",
      type: "array",
      of: [{ type: "horizontalBarChart.legendItem" }],
      description: "The legend shows what each color in the graph means.",
    },
    {
      name: "humanReadableText",
      title: "Human-readable text template",
      type: "text",
      rows: 3,
      validation: (Rule) => Rule.required(),
      description: (
        <>
          <p>
            This is a generated description of a single point (bar)This is a
            full description of the point. What would you tell someone who
            cannot see this point?
          </p>
          <p>
            You can use the following replacements:
            <br />
            {"- {legendLabel}"}
            <br />
            {"- {valueLabel}"}
            <br />
            {"- {value}"}
            <br />
            {"- {description}"}
          </p>
          <p>
            For example, "Among people who did{" "}
            <strong>{"{legendLabel}"}</strong>, <strong>{"{value}"}</strong>% (
            <strong>{"{description}"}</strong>) said they would use it for '
            <strong>{"{valueLabel}"}</strong>'" will become "Among people who
            did <strong>0-5 video calls</strong>, <strong>68</strong>% (
            <strong>21 respondents</strong>) said they would use it for '
            <strong>Follow-ups with patients on sick leave</strong>'".
          </p>
        </>
      ),
    },
    {
      name: "series",
      title: "Series",
      type: "array",
      of: [{ type: "horizontalBarChart.seriesItem" }],
      description:
        "The series is the set of data, each set of columns is a series.",
      validation: (Rule) => Rule.required(),
    },
  ],
};

export default { scale, chart, legendItem, seriesItem, point };

brandColor.js

// this uses sanity-plugin-color-list

export default {
  name: "brandColor",
  title: "Brand color",
  type: "colors",
  options: {
    list: [
      { title: "Primary (dark)", value: "#ff9f01" },
      { title: "Primary", value: "#ffbc00" },
      { title: "Primary (light)", value: "#ffefc0" },
      { title: "Secondary (dark)", value: "#00184c" },
      { title: "Secondary", value: "#123196" },
      { title: "Secondary (light)", value: "#a5d5ff" },
      { title: "Warning (dark)", value: "#f45540" },
      { title: "Warning", value: "#f97369" },
      { title: "Warning (light)", value: "#f5c6bc" },
      { title: "Accept (dark)", value: "#00ad94" },
      { title: "Accept", value: "#00d2bb" },
      { title: "Accept (light)", value: "#c6efe7" },
    ],
  },
};

With this you can create a bar chart. We've used this for our horizontal bar charts, hence the name, but you can use it for any charts you'd like.

For colors, you can select from a certain selection of colors that uses the sanity-plugin-color-list plugin.

There's also a field called humanReadableText where users can use variables to create a text for each bar. The implementor can use visually hidden fields to add this to their bar charts.

Contributor