UI Developer's Guide - tapis-project/tapis-ui GitHub Wiki

UI Developer's Guide

tapis-ui is the component library that represents TAPIS API results and objects, such as systems listings and app details. These components are used to compose tapis-app, which is the React client for TAPIS. This guide describes the architecture for developing UI elements.

tapis-ui

tapis-ui components provide UI representations of the TAPIS objects and collections of objects, such as <FileListing>. Components that are part of tapis-ui should be reusable across multiple contexts. Components that are only applicable to specific UI contexts will appear in tapis-app. For example, the <SystemsNav> is a navigational component used for router navigation purposes, whereas a potential <SystemsDropdown> would belong to tapis-ui, as a selection UI for a systems.

tapis-ui components should limit concerns to providing representations of TAPIS API calls as UI building blocks, to be re-used in potentially multiple areas in the downstream tapis-app consumer of these components.

Component Wrappers

tapis-ui components use tapis-hooks to make API requests. tapis-hooks manages queries to TAPIS. These hooks, in addition to providing query results, also manage the loading and error states for queries. Due to the need to for a standardized way of displaying loading and error states, there are a set of Wrapper components. These provide reusable code for displaying a loading spinner or error message.

QueryWrapper

The QueryWrapper component is used to wrap any component that retrieves data from TAPIS. For example, the AppsNav component requires retrieval of a list of apps, which results in both loading states and possible error states. The children of the QueryWrapper will not be displayed if either isLoading is true or error is non-null.

SubmitWrapper

The SubmitWrapper component is used to wrap submit buttons and display loading, error and success messages associated with mutation hooks. For example, the JobLauncher component has a submit button. The SubmitWrapper around the button displays a loading spinner while loading, an error message if there are any errors, and a success message if loading is false and the error object is null. By wrapping the Button component, it allows the parent component to control the logic for button properties, such as whether or not the button is disabled

Navbar and NavItem

The Navbar and NavItem components provide resuable code for making navigation sidebars for TAPIS listings. (Separate UI components for other listing use cases such as Dropdowns can be provided elsewhere.) An example use case is AppsNav. This component is a Sidebar style navigation component where each item is a TAPIS application. The NavItem component provides a standard navigation UI component for displaying each TAPIS app in this component.

tapis-app

tapis-app is the React client for TAPIS. It uses the tapis-ui building blocks to provide an interface that has DOM based route navigation. This project is in early stages. Layout decisions may change as the project grows to support different types of interactions. Currently, tapis-app mostly provides a "read-only" interface for the Systems, Apps, Files and Jobs interfaces. As tapis-app gains write capabilities and new UI elements, the organization and layout are subject to change.

Module Organization

The structure of tapis-app modules follows the navigational page hierarchy. Each page, at a minimum, must have a _Layout component that describes the visual organization of screen elements. It composes tapis-ui components, and if additional custom components are required that are specific to that page, they are stored in the _components folder. Finally, if the page provides routed navigation to subpages, it must have a _Router component. Child pages are stored in the root of the parent page folder.

This organizational structure allows for nested page trees, while maintaining a consistent separation of concerns between routing, layout and components for future UI development.

Top Level Organization

The tapis-ui application's sidebar interface provides access to top-level TAPIS services. Each service is presented as a page off of the main application. Individual service pages are strongly encouraged to follow layout consistency set by other services. For nesting beyond the second level, breadcrumbs are strongly encouraged. If the existing layouts do not suffice to represent a service's capabilities, proposals can be made to alter the standard Layout patterns. For example, as support for Files service navigation and upload/write capabilities is expanded the Layout should be revisited to support navigation breadcrumbs and toolbars.

<PageLayout> Component

Page components should use the <PageLayout> wrapper component to provide a header, sidebar for navigation within a service and a right side detail pane for rendering the result of navigation.

<Layout...> Wrappers

Page Layouts should use <Layout...> wrapper components. These provide reusable layout styling, for use with the <PageLayout> component. The separation of concerns between <Layout...> wrappers and <PageLayout> is that <PageLayout> provides locations for UI roles and <Layout...> wrappers provide consistent styles within those roles. An example of this can be seen in the <Layout> component for the Apps service.

<Router> Components

In order to reduce the amount of custom state management in tapis-app sections and tapis-ui navigation state selections, tapis-app pages have been refactored to utilize built in react-router-dom componentry. The Navbar and NavItem components already use react-router-dom to highlight the current route as well as control navigation through the client. <Router> components should return an instance of the <Switch> component from react-router-dom. The switch is responsible for matching the router's path to the appropriate component to display.

In the case of the Apps <Router>, the useRouteMatch hook extracts the current browser path. This is then used to pattern match and render <Route> components that match the appId and appVersion specified by the route. This eliminates the need for storing custom state within the navigation component, and provides users the ability to use the browser's URL for a specific application to return to that application in a later user session.

⚠️ **GitHub.com Fallback** ⚠️