Skip to content

Datagateway Dataview

Louise Davies edited this page Feb 20, 2024 · 3 revisions

Table UI code overview

As can be seen in src/App.tsx, the top level of the App various table components on different routes e.g. src/table/investigationTable.component.tsx. These are the configured tables, which are described below.

Configured tables

Configured tables essentially handle defining what columns are required for that specific table view, as well as specifying the details panel and actions that the table might need. They need to supply any cellContentRenderers (found in src/table/cellRenderers/cellContentRenderers), which handle converting raw data values to the values that the table will display (e.g. converting from an integer to a string with bytes calculated and units added, or rendering a link instead of plain text), and supply any filter components, which should be a function that takes a label (what the column displays as a label) and dataKey (the property name of the value found in the object returned from the API) and returns some JSX that creates a filter found in src/table/columnnFilters and supplies it with the Redux function to trigger filter changes.

Additionally, they are connected components and so need to be hooked up to the Redux store and so trigger rerenders upon changes to the sort and filter states, as well as pass down action dispatching functions to their child components that are not Redux aware.

They will generally be used as part of defining routes for react router, with route props supplying needed context such as the DatafileTable requiring a datasetId - these routes are defined in App.tsx.

They each use the generic src/table/table.component.tsx component, which is described below

Generic table

The generic table component is what handles taking the column definitions given to it and generating the columns required. It also handles the styling for the table. The table uses both the react-virtualized and material-ui libraries to create the table, with react-virtualized controlling the overall height and width of the table and columns and also handling DOM virtualization, whereas material-ui handles the UI side of things using it's TableCell component.

The table needs to hold references to the react-virtualized Table component as well as the current opened details panel - this is so that it can calculate the correct heights for the rows when rows are expanded/contracted. Otherwise, we just use a couple of Higher Order Components from react-virtualized that do things like defining sizes and handling lazy loading, and go through defining the columns we want.

There are effectively 3 "types" of column, there's the Expand column that displays the button that when clicked will show the details panel for a row. There are also Data columns, which use the column definitions passed via prop to generate a column. And finally, there's the Actions column which can be used to put things like buttons that act on a row of data. The Expand and Action columns are optional and only appear if the detailsPanel or actions props are defined.

Each column uses subcomponents:

  • headerRenderers: what the header cell will display
  • cellRenderers: what the table cells will display

Additionally, there are also other subcomponents:

  • columnFilters/filterComponents: Interactive UI that is defined in the column definition and put into the headerRenderer for Data columns
  • rowRenderers: supplied to the Table as a whole and dictate how a row should be displayed.

Header Renderers

Often these can be defined inline, as is done for the Expand and Action columns as they just need to display a TableCell and perhaps a label. However, for Data columns a header cell needs to have a label that when clicked changes the sort state for that column, as well as displaying the sort state. Additionally, column filters also need to be rendered in the cell. This is what dataHeader.component.tsx does - creates a TableSortLabel that when clicked calls the provided onSort method with the correct arguments and also renders a filterComponent if defined.

Cell Renderers

There are three types of cell renderers, one for each type of column: actionCell.component.tsx, dataCell.component.tsx and expandCell.component.tsx.

actionCell.component.tsx

This merely takes the actions prop that is provided to it and renders each action in a TableCell whilst providing the data for the row to the action.

dataCell.component.tsx

This uses the dataKey to find the relevant data item in the row data. If the column instead has a cellContentRenderer defined, it applies that to the row data and uses that value instead.

expandCell.component.tsx

This renders an ExpandMore icon if the row isn't open and a ExpandLess icon if it is. When these buttons are clicked the update the expandedIndex.

cellContentRenderers.tsx

Additionally there are "cell content renderers" that are supplied in column definitions that handle the conversion of a data value to what the relevant column needs to display (e.g. converting a number to bytes with units appended, rendering a link to another table etc.). These are just simple functions that are grouped together for convenience.

Column filters

These are UI components that are given a label and an onChange method, and should use these to create some sort of UI that the user can interact with, where the label can be used for accessibility or labelling and the onChange method is called to update the filter for the relevant column. These column filters are then defined in the tables using functions that take labels and dataKeys and return the column filter. See any of the specific tables for examples of invoking a column filter. e.g.

const textFilter = (label: string, dataKey: string): React.ReactElement => (
  <TextColumnFilter
    label={label}
    onChange={(value: string) => filterTable(dataKey, value ? value : null)}
  />
);

Row renderers

Most of the time you want to be using the default table row renderer, but in order to allow for expandable rows we hook into the row renderer cycle and add an extra div which displays the details panel below the default rendered row. We then tell the table to use our custom row renderer if the row index matches the expanded index.

Page Breadcrumbs

The PageBreadcrumbs component is placed above the table on all pages. It works as a custom utility to aid users of the interface to navigate the pages that have been selected by the user.

This is a custom component which displays a custom breadcrumb trail where specific pieces of information are fetched from the API as and when required.

Breadcrumb settings

The breadcrumb trail is created via the entities that are available in the current path. Entities i.e. "investigation", "proposal", "dataset" etc. and their related entity IDs are queried with the API to return with required information.

The default breadcrumb display entry is from the queried TITLE attribute in the return data from the API. This is NAME for investigation entities by default. The component provides an option to customise what fields should be returned from the API by specifying settings which override the default ones.

These settings need to be specified in the datagateway-dataview-settings.json file under the key of breadcrumbs. Inside the breadcrumbs array within the JSON file, objects entries which correspond to their entity names can be provided to override those queries in the breadcrumbs component. As an example, an matchEntity of "investigation" can be specified to override every instance the "investigation" entity is queried with the API (before generating the breadcrumb trail).

Within the these entity entries, there are specific properties that can be provided to specify what information to query from the API. These properties are:

  • matchEntity (mandatory):

    • Specify the entity to replace - this is what is in the URL so e.g. /browse/instrument
    • E.g. To configure the breadcrumb for "investigation":
       ...
       "breadcrumbs": [
       	{    
                           "matchEntity": "investigation"
       	}
       ]
       ... 
    • This is mandatory for an entity instance under breadcrumbs and will not render without it.
  • replaceEntityField (mandatory):

    • Specify the attribute from the API query to return i.e. title, name, visitId etc.
    • E.g. To display value of name in the breadcrumb trail for instance of "investigation":
       ...
               "breadcrumbs": [
       	{    
                           "matchEntity": "investigation",
                           "replaceEntityField": "name"
       	}
       ]
       ... 
    • This is mandatory for an entity instance under breadcrumbs and will not render without it.
  • replaceEntity (optional):

    • In the event, the API request needs to be sent under as a different entity.
    • E.g. the "proposal" entity may not have an API endpoint, but the same information can be provided by "investigation" entity, so we can request every instance of "proposal" in the path to query for "investigation". The replaceEntityField property must also be specified:
       ...
               "breadcrumbs": [
       	{    
                           "matchEntity": "proposal",
                           "replaceEntityField": "TITLE",
                   "replaceEntity": "investigation"
       	}
       ]
       ...
  • parentEntity (optional):

    • This property works as a conditional setting within the breadcrumb component and will only be in effect when the entity that is referenced by this property is present in the path of the page.
    • E.g. If you want instances of "investigation" in the breadcrumb trail to show the visitId data returned from the API. Using the previous example:
       ...
               "breadcrumbs": [
       	{    
                           "matchEntity": "proposal",
                           "replaceEntityField": "title",
                   "replaceEntity": "investigation"
       	},
                       {    
                           "matchEntity": "investigation",
                           "replaceEntityField": "visitId",
       	    "parentEntity": "proposal"
       	}
       ]
       ...
      • Explanation: Like in the previous example, any instance of "proposal" in the path will be queried as the "investigation" entity and will return the title data from the API. When an "investigation" is encountered in the path it will only query the API to fetch visitId if "proposal" is in the path as well, otherwise it will default to querying the name attribute.
      • The likelihood of having this setting in place is rare, but it is useful if you want to deliver multiple custom views across various tables in datagateway.
  • replaceEntityQueryField (optional):

    • In the event, the API request needs to be modified to do a comparison on a different field to normal (normal comparison is based on entity ID).
    • E.g. If you want instances of "proposal" in the breadcrumb trail to be matched based on the name they provide in the url:
       ...
               "breadcrumbs": [
       	{    
                           "matchEntity": "proposal",
                           "replaceEntityField": "title",
                   "replaceEntity": "investigation",
                           "replaceEntityQueryField": "name"
       	},
       ]
       ...

The default setting that is found for the breadcrumbs component in the datagateway-dataview-settings.example.json file is necessary for both the DLS views to work as well as for the ISIS data publications view to work.

DataGateway Homepage

The DataGateway homepage is stored in datagateway-common but inherited and displayed by datagateway-dataview. It encompasses a separate registered route as described in datagateway-dataview-settings-example.json and is entirely optional as a route. It can be found in datagateway-dataview-settings-example.json as

{
  "section": "Homepage",
  "link": "/datagateway",
  "displayName": "DataGateway",
  "order": 0
},

To set it as the SciGateway homepage, leave the homepage route as it is in datagateway-dataview-settings.json and add

"homepageUrl": "/datagateway"

to SciGateway's public/settings.json. Alternatively, to not use it, remove it as a registered route in datagateway-dataview-settings.json so that it doesn't appear in the navigation drawer on SciGateway. Make sure that the new routes array starts from 0 in the order field.

Clone this wiki locally