Giter VIP home page Giter VIP logo

cidc-ui's Introduction

Environment Branch Status Maintainability Test Coverage
production production Continuous Integration
staging master Continuous Integration Maintainability Test Coverage

CIDC UI

Installation:

This repo uses nvm for node version management. Configure your node version:

nvm use

Next, install the dependencies:

npm install

Run

To run a local development server:

npm start

Build:

To create a new deployment bundle, run npm run build

Test:

To run unit tests, run: npm run test. This should generate code coverage files and an lcov.info file that is compatible with most code-coverage highlighting plugins.

Deploy

The CIDC leverages Google Cloud Storage's static site-hosting capabilities for serving the Portal UI. It's recommended that you rely on the GitHub Actions workflow for deployment to staging and production. However, here's how to deploy manually, should you need to. Set IS_PROD=true if you are deploying to production, then run:

## 1. BUILD
# Create an optimized production build with environment-specific configuration
if $IS_PROD; then cat .env.prod > .env; else cat .env.staging > .env; fi
npm run build

## 2. DEPLOY
# Figure out the UI bucket to deploy to
if $IS_PROD; then export BUCKET='gs://cidc-ui-prod'; else export BUCKET='gs://cidc-ui-staging'; fi
# Copy the build to the GCS UI bucket
gsutil -m -h 'Cache-Control:no-cache,max-age=0' cp -r build/* $BUCKET
# Make all objects in the GCS UI bucket public
gsutil iam ch allUsers:objectViewer $BUCKET

UI FAQs

How is the code in the UI repo structured?

This repo’s codebase was initialized using Create React App, and its top-level structure is pretty typical of apps created this way:

  • public/ contains static assets used to build the CIDC portal site, the most important of which is probably index.html (the HTML page that the React app gets loaded into - this is what gets sent to the browser initially on page load, before React starts up and begins rendering additional content to the page). The public/static/cg subdirectory contains code adapted (copy-and-pasted, with the exception of these lines) from the clustergrammer repo that is used in our clustergrammer-in-react implementation.
  • src/ contains the majority of the app’s code, including all React components. The index.tsx file contains the function call that attaches our React app to the static HTML code in the public/index.html file (to this div in particular). The App.tsx file contains the App component, which is the root node in the app’s React tree - that is, only components that are descendants of the App component will be included in the UI. Stepping through this App component is a good way to get a sense of how all the UI code is connected (I’ll do this in more detail below).
  • test/ contains helper methods used in tests, kept separately from the rest of the code because they should only be called by tests. The tests themselves, however, appear in the same directory as the file they’re testing, usually in a file called, e.g., “MyComponent.test.tsx”.
  • .env and .env.<prod|staging> contain environment variables used to configure the app. Environment variables are loaded from .env by default, so the appropriate staging/prod configuration must be copied into .env at build time. If you create a .env.local file, the app will load config from that file instead of .env, but the .env.local file won’t be tracked by git (since it’s gitignored).
  • package.json is the npm analog to a python setup.py file. It contains a dependency list (with separate dev dependencies) and some scripts that you’ll find yourself running in the course of UI development.
  • tsconfig.json configures the typescript compiler. This file is based pretty closely off of defaults that create-react-app gives you out of the box, and it’s pretty rare you would ever need to update it.

The src/ directory is where you’ll spend most of your time, so It's broken it down further here:

  • @types/ contains typescript module stubs for packages the CIDC uses that don’t have typescript type definitions available (many packages implemented in javascript provide typescript types, but some don’t). These files contain a one-line module declaration that tells the typescript compiler to essentially do no type checking for the associated package.
  • api/ contains generic client functions for interacting with the API. The key part of this code is the buildRequester factory function. This function takes as its argument an HTTP method (GET, POST, etc.) and returns another function called requester that makes requests using that HTTP method. This approach allows us to use the same requesting logic for our swr fetcher as we do for requests that don’t fetch data. The function handles logic related to building request headers, issuing requests to the API, handling etag conflicts, and retrying failed requests. It’s used to build the four helper functions that are used for issuing requests to the API throughout the UI code: apiFetch, apiCreate, apiUpdate, and apiDelete.
  • components/ contains, unsurprisingly, most of the React component definitions for the UI. Generally, each subdirectory corresponds to a page in the UI - for example, profile/, home/, transfer-data/, etc.. There are some exceptions, though: generic/ contains “generic” components that are used throughout the UI, like the InfoTooltip component; visualizations/ contains data visualization components; identity/ contains a collection of components related to authentication and authorization.
  • model/ contains typescript definitions generally corresponding to response types returned by the API.
  • util/ contains miscellaneous helper functions.
  • App.tsx contains the root component in the app’s React tree.
  • index.css contains global styles applied to the app via this import.
  • index.tsx renders the React app into the DOM.
  • react-app-env.d.ts is autogenerated by create-react-app (I’m not sure why).
  • rootStyles.tsx configures the global Material UI theme and creates some page layout default style classes.
  • setupTests.ts runs before any tests are executed. It’s analogous to pytest’s conftest.py.

What’s going on in App.tsx?

First, we lazily import code for the different pages in the UI. Using lazy imports tells React to only load javascript related to a certain component into the browser when that component is rendered. It’s not critical to understand this, since it’s just a minor performance optimization, but here are the relevant docs if you want to learn more.

Next, we define the root App component. This component ties together all the different parts of the UI. I’ll go through these different parts:

  • Router is a react-router component that provides context related to navigation (e.g., what page the user is currently on, what page they were on previously, etc.) to all child components. It’s required that we wrap our app in this component for all routing related functionality to work correctly.
  • SWRConfig provides global configuration to the swr library, which we rely on for smart data fetching throughout the UI code (for example, here’s how it’s used to load user account info).
  • QueryParamProvider sets up the library we use for reading and updating URL query params, called use-query-params (here’s an example usage).
  • CIDCThemeProvider provides the global Material UI theme defaults defined in rootStyles.tsx to all child components.
  • ErrorGuard is a custom component that is used for displaying “fatal” errors, like a user's account being disabled.
  • InfoProvider provides global info loaded from the API’s “/info” resource to all child components.
  • IdentityProvider provides user authentication/authorization/account info to all child components.
  • Header defines the UI page header (the tabs and everything above them).
  • This mysterious looking code is required for lazy component imports to work.
  • Switch is a react-router component that handles toggling between a set of different Route components. This block of code lays out all of the top-level pages that appear in the UI, so you’ll need to add code here if you’re adding a new page to the UI. See the Switch docs and Route docs for more info.
    • To see one interesting example of how a route component works, let’s look at the FileDetailsPage route. The path prop contains a parameter, fileId, which is accessible in the render method of the FileDetailsPage component like so (it’s used to load data from the API for the relevant file). In addition to URL path params, react-router passes other useful props to route components.

Anything that appears in the UI is somehow a descendant of the App component. If you can’t find where the code corresponding to something in the UI lives (and React Developer Tools doesn’t do the trick), then starting at the App component and making your way down the component hierarchy should get you to your answer.

How do I set up the development environment?

  1. Install nvm
  2. In cidc-ui directory
    1. nvm use
    2. npm install
  3. Install a browser extension that blocks CORS such as CORS Everywhere if using firefox or launch chrome without security if using chrome
  4. To run tests, npm test
  5. To run a development server, npm start 3. To point to prod, create top-level .env.local 4. Uncomment prod config, comment out staging config

How do I write tests for things in the UI?

We use jest and React Testing Library for testing the components that make up our UI. There are so many excellent resources online that help with learning them (between the docs, blog posts, talks).

How do I add a page to the portal?

  1. Create a new React component that will hold the contents for this page. By convention (not requirement), you should do this inside of a new subdirectory inside the src/components/ directory, in a file and component named like “<SomeName>Page”. Generally, the page’s body should be styled using the centeredPage style class defined in rootStyles.tsx (accessible via the useRootStyles hook). Add a test file and some initial tests, too - even just making sure the component renders without runtime errors!
  2. Add a route component corresponding to this new page to the Switch component in App.tsx. If the path string for this page matches a substring of any other route paths (e.g., “/path” and “/path-1”), make sure that the substring appears before the longer path in the Switch component’s children - otherwise, you won’t be able to navigate to the substring path.
  3. Add some interaction that makes it possible for the user to get to the new page. This could be anything from adding a new tab in the Header component, a button that sends the user to the page when it’s clicked, or a redirect based on an API response.
  4. (Maybe) add a smoke test to App.test.tsx to make sure the new page is reachable.

How does the authentication flow work?

Whereas the API’s authentication flow is centered around validating clients’ identity tokens, the UI’s auth flow is centered around obtaining valid identity tokens from Auth0 to use when authenticating with the API. Once a valid id token is obtained, the UI can then perform basic authorization checks after loading a user’s account information.

The authentication flow is handled by the IdentityProvider component (which wraps children of the App component here). The IdentityProvider’s implementation contains two other context providers which handle key logic related to authentication and user identity/authorization:

So, in this way, the IdentityProvider component checks if a user is logged in and loads their account information before allowing the rest of the app to render.

How does the trial/file browser tab work?

Since the trial/file browser is a critical part of the CIDC portal and since the way all its subcomponents string together is a bit complicated, we are stepping through it at a high level here.

BrowseDataPage is the container for both the trial and file browsers, and it handles layout and logic for switching between the two. By default, it displays the trial browser on initial render. It renders the browsers inside of a component called FilterProvider, which provides context about which facets the user has selected.

FilterProvider implements logic for loading available facets from the API and for updating which facets the user has selected. The facet update logic related to nested facets is kind of hard to understand at first glance, but the bulk of the complexity comes from handling the case where a parent facet (e.g. WES) is selected and we need to determine whether all of its subfacets should be selected or deselected.

Filters renders the filter facet checkboxes. It relies on data and functions for updating filters loaded from the FilterProvider's context and uses FilterCheckboxGroup for facet layout. The BrowseDataPage renders the Filters component alongside both the file and trial browsers.

Both the FileTable and TrialTable components change what data they load from the API based on the current state of the filter facets. FileTable does so here and TrialTable does so here.

How does the current clustergrammer-in-react implementation work?

Since clustergrammer doesn’t natively provide a component for use in React apps, we had to hack one ourselves by injecting the clustergrammer js code into an iframe. Here’s how the custom Clustergrammer component works:

  1. It loads the clustergrammer HTML as a string from the file stored here.
  2. It defines a function to be executed from within the clustergrammer iframe (this function can operate on the iframe’s window and document objects). This function passes the clustergrammer network data provided to the Clustergrammer component props to a function called DrawClustergram, which is defined in the clustergrammer HTML document and does the actual rendering of the clustergrammer visualization. This clustergrammer function will be re-run every time the window size changes.
  3. It renders the iframe with the clustergrammer HTML injected into the iframe document.

Note: this solution may become entirely moot if the frontend switches over to clustergrammer2. Since clustergrammer2 is packaged as an ipython widget instead of a plain HTML file, the solution may look more like finding a way to render an ipython widget initialized with data inside a React component.

cidc-ui's People

Contributors

jacoblurye avatar scvannost avatar ersinciftci avatar lemccarthy avatar roshni-b avatar curlup avatar jamesprovencher avatar dependabot[bot] avatar crouchcd avatar yongzhi2 avatar jim-bo avatar github-actions[bot] avatar mtmcduffie avatar

Watchers

 avatar  avatar James Cloos avatar Priti Kumari avatar Tony Fu avatar  avatar  avatar Jason L Weirather avatar Jeremy Pumphrey avatar

Forkers

curlup crouchcd

cidc-ui's Issues

Better behavior on `accounts_info` errors

When the portal UI gets an error back from the UI on the accounts_info endpoint, the user is prompted to re-register even if they're already registered in the system.

Instead of directing the user to register, we should display some sort of error message.

File browser only displays <=25 file records

We should:

  • implement a "load more" option via Eve pagination
  • implement more robust faceted search that actually builds queries to the API. Currently, faceted search just searches among the 25 first records loaded.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.