Setting up a Table component for Unified Selection

Interaction between Table component and Unified Selection is synchronized one way - from Unified Selection to the Table. That means that whenever Unified Selection changes, the content for the Table is reloaded to represent what is selected.

Reference

The Reference section in Unified Selection page describes the core APIs used in Unified Selection workflows.

The @itwin/presentation-components package delivers usePresentationTableWithUnifiedSelection hook to make setting up Table components to work with Unified Selection easier. The hook uses the general usePresentationTable, but updates the UsePresentationTableProps.keys prop every time Unified Selection changes.

As stated in the documentation of usePresentationTableWithUnifiedSelection, it should be used within Unified Selection context, or, simply put, within UnifiedSelectionContextProvider.

Example

The below example shows how to create a very basic presentation rules driven Table component and hook it into Unified Selection. The latter part is achieved by using usePresentationTableWithUnifiedSelection to create the data for the component, as opposed to using the general usePresentationTable.

function MyTable(props: { imodel: IModelConnection }) {
  // the library provides a variation of `usePresentationTable` that updates table content based
  // on unified selection
  const { columns, rows, isLoading } = usePresentationTableWithUnifiedSelection({
    imodel: props.imodel,
    ruleset,
    pageSize: 10,
    columnMapper: mapTableColumns,
    rowMapper: mapTableRow,
  });

  // don't render anything if the table is loading
  if (isLoading) {
    return null;
  }

  // if we're not loading and still don't have any columns or the columns list is empty - there's nothing
  // to build the table from, which means we probably have nothing selected
  if (!columns || columns.length === 0) {
    return <>Select something to see properties</>;
  }

  // render a simple HTML table
  return (
    <table>
      <thead>
        <tr>
          {columns.map((col, i) => (
            <td key={i}>{col.label}</td>
          ))}
        </tr>
      </thead>
      <tbody>
        {rows.map((row, ri) => (
          <tr key={ri}>
            {columns.map((col, ci) => (
              <td key={ci}>
                <Cell record={row[col.id]} />
              </td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

// cell renderer that uses `PropertyValueRendererManager` to render property values
function Cell(props: { record: PropertyRecord | undefined }) {
  return <>{props.record ? PropertyValueRendererManager.defaultManager.render(props.record) : null}</>;
}

// a function that maps presentation type of column definition to something that table renderer knows how to render
const mapTableColumns = (columnDefinitions: TableColumnDefinition) => ({
  id: columnDefinitions.name,
  label: columnDefinitions.label,
});

// a function that maps presentation type of row definition to something that table renderer knows how to render
function mapTableRow(rowDefinition: TableRowDefinition) {
  const rowValues: { [cellKey: string]: PropertyRecord } = {};
  rowDefinition.cells.forEach((cell) => {
    rowValues[cell.key] = cell.record;
  });
  return rowValues;
}

The above component needs to be used within the Unified Selection context:

<UnifiedSelectionContextProvider imodel={imodel}>
  <MyTable imodel={imodel} />
</UnifiedSelectionContextProvider>

Last Updated: 07 April, 2023