Skip to main content

Extending the Checklist API

Custom Item Definitions

The Checklist API provides a set of default checklist item definitions that are mapped from checklist item types according to the mapping defined by ChecklistItemTypeDefMap. If a specific checklist implementation needs to define additional or different properties for checklist items, then it can do so by creating its own set of custom item definitions and associated mapping.

First, define type aliases for each custom definition. Each type alias must satisfy the types defined in BaseChecklistItemTypeDefMap. Then, define a mapping from checklist item types to the custom item definitions. The mapping must satisfy BaseChecklistItemTypeDefMap.

import {
CoreChecklistItemDef, ChecklistActionableItemDef, ChecklistItemType, ChecklistNoteItemDef, ChecklistTitleItemDef
} from '@microsoft/msfs-sdk';

// Extend the default actionable item definition with an 'indent' property.
type CustomActionableItemDef = ChecklistActionableItemDef & {
indent: number;
};

// Extend the default note item definition with a 'textColor' property.
type CustomNoteItemDef = ChecklistNoteItemDef & {
textColor: string;
};

// Remove the 'height' property from the default spacer item definition.
type CustomSpacerItemDef = CoreChecklistItemDef<ChecklistItemType.Spacer>;

// Mapping from checklist item types to custom item definitions.
type CustomChecklistItemTypeDefMap = {
[ChecklistItemType.Actionable]: CustomActionableItemDef;
[ChecklistItemType.Branch]: ChecklistBranchItemDef;
[ChecklistItemType.Link]: ChecklistLinkItemDef;
[ChecklistItemType.Note]: CustomNoteItemDef;
[ChecklistItemType.Title]: ChecklistTitleItemDef;
[ChecklistItemType.Spacer]: CustomSpacerItemDef;
};

Once the custom item definitions and the custom mapping are declared, use the mapping type alias where appropriate to ensure the custom item definitions get used:

import {
ChecklistItemType, ChecklistSetDef, DefaultChecklistStateProvider
} from '@microsoft/msfs-sdk';

// Item definitions in setDef are now the custom ones defined in CustomChecklistItemTypeDefMap.
const setDef: ChecklistSetDef<CustomChecklistItemTypeDefMap> = ...

// We can also define a type alias to cut down on boilerplate.
type CustomChecklistSetDef<S = unknown, G = unknown, L = unknown, B = unknown>
= ChecklistSetDef<CustomChecklistItemTypeDefMap, S, G, L, B>;

const firstItemDef = setDef.groups[0].lists[0].items[0];
if (firstItemDef.type === ChecklistItemType.Actionable) {
// firstItemDef now has type CustomActionableItemDef, so we can access the 'indent' property on it.
console.log(`First item has indent of ${firstItemDef.indent}`);
}

// Explicitly passing CustomChecklistItemTypeDefMap to DefaultChecklistStateProvider's first type parameter will
// ensure the state object the latter provides uses the custom item definitions.
// Alternatively, we could have left out the explicit type parameter and allowed TypeScript to infer the correct type
// from setDef.
const provider = new DefaultChecklistStateProvider<CustomChecklistItemTypeDefMap>(1, bus, setDef);
const firstItem = provider.state.groups[0].lists[0].items[0];
if (firstItem.type === ChecklistItemType.Actionable) {
// firstItem now has type CustomActionableItemDef, so we can access the 'indent' property on it.
console.log(`First item has indent of ${firstItem.indent}`);
}

Extending ChecklistDOMParser

ChecklistDOMParser parses default item definitions. If you wish to parse custom item definitions from DOM, you must extend ChecklistDOMParser with a subclass.

Subclasses that parse custom item definitions should override one or more of the following methods:

  • parseActionableItemDef()
  • parseBranchItemDef()
  • parseLinkItemDef()
  • parseNoteItemDef()
  • parseTitleItemDef()
  • parseSpacerItemDef()

For example, the following class parses the custom item definitions used in the examples from the previous section.

import {
ChecklistDOMParser, ChecklistDOMParseOptionsToUse, ChecklistItemType, DefaultChecklistStateProvider
} from '@microsoft/msfs-sdk';

class CustomChecklistDOMParser extends ChecklistDOMParser<CustomChecklistItemTypeDefMap> {

protected override parseActionableItemDef(
element: Element,
groupName: string,
listName: string,
options: ChecklistDOMParseOptionsToUse<CustomChecklistItemTypeDefMap>
): CustomChecklistItemTypeDefMap[ChecklistItemType.Actionable] | undefined {
const baseItemDef = super.parseActionableItemDef(element, groupName, listName, options);
if (!baseItemDef) {
return undefined;
}

const indentAttr = element.getAttribute('indent');
const indent = Number(indentAttr ?? 0);
if (!Number.isInteger(indent) || indent < 0) {
options.onError(`CustomChecklistDOMParser: invalid indent ${indentAttr} for an actionable checklist item in list ${listName} (group ${groupName}).`);
return undefined;
}

return {
...baseItemDef,
indent
};
}

protected override parseNoteItemDef(
element: Element,
groupName: string,
listName: string,
options: ChecklistDOMParseOptionsToUse<CustomChecklistItemTypeDefMap>
): CustomChecklistItemTypeDefMap[ChecklistItemType.Note] | undefined {
const baseItemDef = super.parseNoteItemDef(element, groupName, listName, options);
if (!baseItemDef) {
return undefined;
}

const textColor = element.getAttribute('text-color');
if (!textColor) {
options.onError(`CustomChecklistDOMParser: missing text color for a note checklist item in list ${listName} (group ${groupName}).`);
return undefined;
}

return {
...baseItemDef,
textColor
};
}

// parseTitleItemDef() does not need to be overridden because the custom mapping
// still uses the default title item definition.

protected override parseSpacerItemDef(): CustomChecklistItemTypeDefMap[ChecklistItemType.Spacer] | undefined {
return { type: ChecklistItemType.Spacer };
}

}