React components shared across teselagen's apps

TeselaGen-React-Components

React components shared across teselagen's apps.

Using:

yarn add teselagen-react-components

Add peer-dependencies:

yarn add @blueprintjs/core @blueprintjs/datetime react-addons-css-transition-group react-redux react-select redux 

Enhancers:

withDialog

withDialog()(YourComponent) wraps YourComponent in a blueprint Dialog!

First hook up dialog to redux (only need to do this once):

//rootReducer.js
import {tg_modalState} from 'teselagen-react-components'

export default combineReducers({
  tg_modalState,
})

Use the component

const DialogComp = withDialog({...bpDialogPropsHere})(MyComponent)
render() {
  return <DialogComp 
  dialogName={string} //optionally pass a UNIQUE dialog name to be used
  dialogProps={object} //optionally pass additional runtime blueprint dialog props here
  //all other props passed directly to wrapped component
  > 
  <Trigger/>
</DialogComp>
}

In RARE cases where you need to open the dialog programatically, make sure the dialog component is on the page (just don't pass a child component and nothing will appear to be added to the DOM), and call dispatch like:

dispatch({
  type: "TG_SHOW_MODAL",
  name: dialogName //you'll need to pass a unique dialogName prop to the compoennt
  props:  // pass props to the wrapped component here :)
})

withDelete, withUpsert, withQuery

import {withDelete, withUpsert, withQuery} from "teselagen-react-components";
import jobWorkflowRunsQuery from '../graphql/queries/jobWorkflowRunsQuery';
import workQueueItemFragment from '../graphql/fragments/workQueueItemFragment';

export default compose(
	//withUpsert takes a fragment/string as its first argument and an options object as its second param.
	//in the case below it will pass a prop to the wrapped component called upsertWorkQueueItem
	//upsertWorkQueueItem() can be passed an array or single object and will perform a create or an update based on 
	//if it detects an id
  withUpsert(workQueueItemFragment,
		{extraMutateArgs: {
			refetchQueries: [ { query: jobWorkflowRunsQuery } ]
			//any additional options are spread onto the usual apollo mutation enhancer 
	}}),
	//withDelete takes a fragment/string as its first argument and an options object as its second param.
	//in the case below it will pass a prop to the wrapped component called deleteWorkQueueItem
	//deleteWorkQueueItem() can be passed an array of ids or a single id and will delete those items with the given id
  withDelete(workQueueItemFragment, { 
    mutationName: "deleteWorkQueueItems",
		//any additional options are spread onto the usual apollo mutation enhancer 
  })
	//withQuery takes only a fragment as its first argument and an options object as its second param.
	//in the case below it will pass several props to the wrapped component: 
	//data  --- the usual apollo query data object 
	//workQueueItemsQuery  ---  the usual apollo query data object just on a unique name
	//workQueueItem/s  --- the actual workQueueItem record or the workQueueItems records array  
	//workQueueItemsCount --- the count of the records coming back if isPlural:true
	withQuery(workQueueItemFragment, {
		//isPlural: boolean whether or not to search for just one item or multiple
    //any additional options are spread onto the usual apollo query enhancer 
		options: props => {
      const id = parseInt(get(props, "match.params.id"), 10);
      return {
        variables: {
          id
        }
      };
    }
  }),
)(AddToWorkQueueDialog);

They can also be used as functions but you must pass in the apolloClient:

withQuery(userFragment, {
    asFunction: true,
    isPlural: true,
    client: apolloClient
  })({
    filter: {
      email: newEmail
    }
  })

getApolloMethods (query, upsert, delete)

//pass an apollo client here:
const {upsert, query, delete} = getApolloMethods(client);

query

const resultArray = await query(fragment, options)
//examples: 
const aliquot = await query(aliquotFragment, {variables: {id: 2}})
const aliquots = await query(aliquotFragment, {isPlural: true, variables: {filter: {name: 'aliquotX'}}})

upsert

const resultArray = await upsert(modelNameOrFragment [, options], modelOrModels)
//examples: 
const [aliquot] = await upsert('aliquot', {name: 'aliquot1'})
const aliquots = await upsert('aliquot', [{name: 'aliquot1'}, {name: 'aliquot2'}])

delete

await delete(modelNameOrFragment, [, options], modelOrModels)
//examples: 
await delete('aliquot', 1)
await delete('aliquot', [1,2,6,1616])

Data Table

Use the component with withTableParams() enhancer or by itself (locally connected)
DataTable props

import {DataTable} from "teselagen-react-components";
<DataTable {DataTableProps here} /> 

Simple DataTable Demo Src Code

withTableParams (enhancer)

Use withTableParams in conjunction with withQuery

withTableParams({
  formName: String; // - required unique identifier for the table
  schema: Boolean; //  - The data table schema
  urlConnected: Boolean; //  - default: false - whether the table should connect to/update the URL
  withSelectedEntities: Boolean; //  - whether or not to pass the selected entities
  defaults: defaults; // - tableParam defaults such as pageSize, filter, etc
})
withQuery() //the usual withQuery stuff here

withTableParams returns a prop called tableParams which you can spread like:

<DataTable {...tableParams}/> //this provides, entities, schema, handlers etc

Form Components

import {
	InputField,
	SelectField,
	DateInputField,
	CheckboxField,
	TextareaField,
	EditableTextField,
	ReactSelectField,
	NumericInputField,
	RadioGroupField,
	FileUploadField
} from 'teselagen-react-components'

<InputField
  name={"fieldName"}
  label="fieldLabel"
  placeholder="Enter text..."
  inputClassName="className(s) for input"
/>

Development:

Prerequisites

Node.js >= v4 must be installed.

Installation

  • Running npm install in the components's root directory will install everything you need for development.

Demo Development Server

  • npm start will run a development server with the component's demo app at http://localhost:3000 with hot module reloading. You can check the /demo folder for the source code.

Developing linked to another folder: aka lims/hde

//link everything up:

//LIMS EXAMPLE: 
cd lims/node_modules/react        //this is so we don't have 2 copies of react being used on the front-end (react will throw errors if so)
yarn link 
cd teselagen-react-components
yarn link
yarn link react
cd lims
yarn link teselagen-react-components

//HDE EXAMPLE: 
cd hde/client/node_modules/react     //this is so we don't have 2 copies of react being used on the front-end (react will throw errors if so)
yarn link 
cd teselagen-react-components
yarn link
yarn link react
cd hde/client
yarn link teselagen-react-components

//ALWAYS:
//start the auto rebuild:
cd teselagen-react-components
yarn build-watch

Running Tests

  • npm test will run the tests once.

  • npm run test:coverage will run the tests and produce a coverage report in coverage/.

  • npm run test:watch will run the tests on every change.

Releasing

  • npm whoami you should be teselagen-admin
  • npm login
    teselagen-admin//ask @tnrich or @tgreen or @tgadam for password//devops@teselagen.com
  • git pull
  • npm version patch|minor|major
  • npm publish
  • git push

Adding custom svg icons

See the demo page for live examples!

open src/customIcons.js and add a new exported svg with a name of xxxxIcon (you can find the svgs from iconmonstr or flaticon or wherever)

be sure to add it to the demo/src/examples/CustomIcons.js page to TEST THAT IT WORKS and so that people know it exists!

GitHub