nqm-app-framework

0.3.23 • Public • Published

nqm-app-framework

introduction

The nquiringminds application framework is a set of components, containers and functions that provide a consistent, scaleable and responsive infrastructure on which to quickly build bespoke applications against the nquiringminds Trusted Data Exchange (TDX).

Core features include:

  • opinionated mobile-first user interface design and layout that scales well across all devices
  • built-in integration with the TDX auth server, plus single sign-on integration with the nquiringminds toolbox
  • application state management with support for serialization and replay
  • built-in support for subscribing to TDX data publications
  • built-in support for the TDX REST API

technologies

You will need to familiarise yourself with the following technologies, utilised by this framework.

  • meteor - Meteor is a full-stack JavaScript platform for developing modern web and mobile applications. Meteor includes a key set of technologies for building connected-client reactive applications.
  • mantra - application architecture and workflow, provides built-in unit testing support, controlled load order, routing, context, consistent module structure.
  • redux - predictable application state management
  • react - library for building component-based user interfaces
  • react-router - client-side router for react apps
  • material-ui - a set of React components implementing Google's Material Design spec.
  • jss - high performance JS to CSS compiler

A working knowledge of mongoDB query syntax is desirable.

background reading

As well as familiarising yourself with the above technologies, you may find the following links useful background reading.

packaging

Use the following steps to publish a meteor application that utilises nqm-app-framework.

install meteor-build-client

sudo npm install -g meteor-build-client

create the html template

An HTML template needs to be created that specifies the React root DOM node, for example if you're using a 'react-root' div element, create a build-template.html file containing:

<!DOCTYPE html>
<html>
    <head>
        {{> css}}
        {{> head}}
    </head>
    <body>
      <div id="render-root"></div>
      {{> scripts}}
    </body>
</html>

build the app

cd /path/to/myApp
meteor-build-client ../myApp-client-only -s ./settings.json -t ../build-template.html

package the app

The application can then be run using a basic nodejs static file http server:

var express = require('express');
var app = express();

app.use("/", express.static(__dirname + "/myApp-client-only"));

app.listen(8081);

components

Layout

The main layout component, provides the central display area, an optional side bar on the left and a title bar. If supplied the sidebar will be responsive to changes in layout and screen size and can be toggled from the title bar navigation button.

properties

  • content - [functional React component] the main layout content
  • sideBarContent - [functional React component] optional content to appear in the left side bar
  • title - [string] the title, displayed in the app bar

example

import framework from "nqm-app-framework";
import Home from "../core/components/home";
import AppSideBar from "./components/app-side-bar";

const Layout = framework.ui.Layout;

export default function(injectDeps, context) {
  const {store} = context;
  const history = syncHistoryWithStore(browserHistory, store);

  const RouterCtx = () => (
    <Router history={history}>
      <Route path="/" component={Layout}>
        <IndexRoute title="lorem upsum" components={{content: Home, sideBarContent: AppSideBar}} />
      </Route>
    </Router>
  );

  ReactDOM.render(
    React.createElement(injectDeps(RouterCtx)),
    document.getElementById("render-root")
  );
}

ModalLayout

A simple modal layout component that provides a central display area and title bar. The title bar supports customisable save and close handlers. Use in conjection with ModalPageBase.

properties

  • content - [functional React component] the main layout content
  • title - [string] the title, displayed in the app bar

example

import framework from "nqm-app-framework";
import About from "../core/containers/about";

const ModalLayout = framework.ui.ModalLayout;

export default function(injectDeps, context) {
  const {store} = context;
  const history = syncHistoryWithStore(browserHistory, store);

  const RouterCtx = () => (
    <Router history={history}>
      <Route path="/modal" component={ModalLayout}>
        <IndexRoute components={{content: About}} />
      </Route>
    </Router>
  );

  ReactDOM.render(
    React.createElement(injectDeps(RouterCtx)),
    document.getElementById("render-root")
  );
}

SideBarContent

This is a container component for side bar panels. It has no properties and acts as a simple container for SideBarPanel components. The SideBar component expects one and only one SideBarContent child.

properties

none

example

import React from "react";
import framework from "nqm-app-framework";

// Import app-specific bar panels
import AppMenu from "../containers/app-menu";
import AppOptions from "../containers/app-options";

// Sidebar framework
const SideBarContent = framework.ui.SideBarContent;
const SideBarPanel = framework.ui.SideBarPanel;

const AppSideBar = () => {
  return (
    <SideBarContent>
      <SideBarPanel title="menu" value="menu" icon="apps">
        <AppMenu />
      </SideBarPanel>
      <SideBarPanel title="options" value="options" icon="settings">
        <AppOptions />
      </SideBarPanel>
    </SideBarContent>
  );
};

SideBarPanel

Defines a panel within the side bar. A tab button will be added to the side bar tabs, and clicking the button will activate the panel within the side bar. If the route property is specified, the panel will only be visible if the current route matches the property. See react router api for details of how the route matching works.

properties

  • title - [string] a short, descriptive title for the panel
  • value - [string] an identifier that uniquely identifies this panel
  • icon - [string] the name of the material-icons icon to use for the panel
  • route - [string] optional route path. The panel will only be visible if the current route matches

example

import React from "react";
import framework from "nqm-app-framework";

// Import app-specific bar panels
import AppMenu from "../containers/app-menu";
import AppOptions from "../containers/app-options";

// Sidebar framework
const SideBarContent = framework.ui.SideBarContent;
const SideBarPanel = framework.ui.SideBarPanel;

const AppSideBar = () => {
  return (
    <SideBarContent>
      <SideBarPanel title="menu" value="menu" icon="apps">
        <AppMenu />
      </SideBarPanel>
      <SideBarPanel title="resource options" value="resource" icon="settings" route="/resource">
        <AppOptions />
      </SideBarPanel>
    </SideBarContent>
  );
};

NotificationUi

Snackbar style notification area, similar to the notifactions section on the TDX. Supports fire and forget actions as well as longer running notification chains.

example

// Container
const depsMapper = (context, actions) => ({
  addNotification: actions.notification.addNotification,
  updateNotification: actions.notification.updateNotification,
});

// addNotification
// autoExpire is a boolean, if true then message will display for 4 seconds or, if specified, the duration in milliseconds then automatically be removed, otherwise it will remain until marked as finished with updateNotification
const id = addNotification("message", autoExpire, (duration));

// The returned id may be used to update an existing notification, finished is a boolean, if true the notification will be removed 4 seconds after the message is updated
updateNotification(id, "Progress", finished);

const id = addNotification(`Deleting resource ${resourceId}`);
tdxApi.deleteDatasetAsync(resourceId)
.then(() => {
  updateNotification(id, `Deleted resource ${resourceId}`, true);
})
.catch((err) => {
  updateNotification(id, `Failed to delete resource ${resourceId}: ${err.message}`, true);
});

data loaders

The framework encourages separating the data loading aspects of an app from it's UI components. Doing this involves 3 types of 'mapping' data from various data sources to properties of the component. These data sources are:

  • application context - application-wide context, such as access to the TDX connection manager and the application state store.
  • application state - the current state of the application, usually created as a result of user actions such as the active view or selected sort order etc. Application state is not usually persisted and should not be used to store 'domain data'.
  • domain data - the data required by the application, usually residing in the TDX, such as LSOA lists, region boundaries, sensor definitions and latest readings etc.

The idea is for each component to define the data it requires using React properties. The component does not concern itself with where that data comes from or how it is loaded or saved. This approach ensures components are self-contained and able to be shared across applications and/or domains if required. It also makes it possible to write tests for the component in a generic way.

Component containers are used to 'map' data from the various data sources described above to the properties defined by the component.

compose

This is a general purpose helper function that provides the 'glue' between the mapping function you provide and the component properties.

merge

This is a general-purpose helper that lets you map data to your component properties using more than one data source, by chaining together one or more compose invocations.

useDeps

Short for 'use dependencies', use this function to declare the aspects of application context your component container needs to access in order to fulfill the data source to property mappings.

trackerFactory

A factory function that creates a reactive wrapper around your data loading function. See the Meteor Tracker documentation for more information about Meteor reactivity.

reduxFactory

For example, if your component has a 'sortOrder' property you probably want to synchronise it with the currently selected sort order from application state.

application state

to do

Dependencies (1)

Dev Dependencies (0)

    Package Sidebar

    Install

    npm i nqm-app-framework

    Weekly Downloads

    33

    Version

    0.3.23

    License

    ISC

    Unpacked Size

    306 kB

    Total Files

    44

    Last publish

    Collaborators

    • jr7g19
    • ionut_nqm
    • nqminds-bot
    • nqminds-org
    • mereacre
    • antmcc
    • toby.ealden
    • aloisklink
    • cbrafter
    • ashleysetter