Hierarchy Control allows a user to explore, select (or perform adminstrative actions upon) items organised into a fixed-depth hierarchy.

Hierarchy Control presents a cascading list (or Miller Columns) view on a (JSON-defined) fixed-depth tree of items, in which each level of the tree (and thus column in the table) corresponds to a category / sub-category. It supports both (a) direct use as an "in page" component, and (b) [via HierarchyField] use in an editor dialog that populates a field with a value representing one or more items chosen from the hierarchy.

The control allows the user either:

  • to select one or more items (when in one of the two selection modes)
  • to perform actions on items in the hierarchy according to category-defined actions present in the configuration JSON e.g. add, edit, delete (when in admin mode)

In these modes, the purpose of the control is to select one or more items, and the control broadcasts changes in selection state by firing custom "itemSelectionChanged" events.

Fundamental assumptions / rules governing both (a) the interactive item selection behaviour and (b) the resolution of potential conflicts during updates to the component's "value" (which in this mode is a comma-separated list of item IDs) are as follows:

  • selection of a given hierarchy item is mutually exclusive with selection of any of that item's ancestor and/or descendant items

  • selection of any given hierarchy item is mutually exclusive with selection of the logical all-items-in-hierarchy" item (where defined via the optional allTopLevelItemsSelector property in the configuration).

    The assumed semantics underlying the above rules are (a) that inclusion of a hierarchy category item in a selection means "everything in that category", and (b) that the presence of the logical all-items-in-hierarchy item means "all items in the hierarchy" AT THE TIME THE SELECTION IS INTERPRETED (vs. "that were present in the hierarchy when the item was selected for inclusion"). For this reason that the control deliberately refrains from showing the (current) descendants of a selected category item as selected.

    The control's understanding of the actions that may be defined for a category / items within a category is limited to the fact that the "add" action for a category relates to the addition of an item to that level of the hierarchy, and therefore (unless that category is the root category) implicitly depends on the selection of a parent item (i.e. an item in the parent category).

    Specifically, the control has no knowledge of / makes no assumptions about:

  • the data that the wider system (outside of the component) may need to know about each item / what that data means

  • the validation rules might need to be applied (re creating, editing, deleting - or indeed performing any other action on an item)

    Accordingly, on user invocation of actions requiring that knowledge, the component merely dispatches a custom event with a detail object that (a) identifies the requested action and (b) provides the information necessary to support external handling of those actions as follows:

  • addItemToCategoryRequested

  • itemActionRequested

  • customAdminActionRequested

    It's up to the external system to attach handlers for these emitted event types that, where required, throw up a modal dialog for user input, validate / attempt the requested operation either (a) reporting any error back to the user, else (on successful completion of the requested operation) dispatch one of the following custom events back to the component so that it can update its internal state / UI accordingly:

  • itemAdded

  • itemEdited

  • itemDeleted

    The component's handling of each of the above incoming "action completion" events are as follows:

  • itemAdded: providing detail.itemDetails is valid, inserts this into the model (as a child of the item identified by detail.itemDetails.parentItem if specified, else as a root category item), the insert position determined so as to maintain a (presumed) ascending alphabetical child ordering by item.label within the relevant parent / root category.

  • itemEdited: providing the item identified by detail.itemDetails.ID exists, updates that item as per detail.itemDetails except for detail.itemDetails.parentItem (because this control does NOT currently support reparenting of existing items)

  • itemDeleted: providing the item identified by detail.itemId exists, recursively deletes that item and all of its descendants.