Nutritious Pumpkin Masses

    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

    Keywords

    none

    Install

    npm i nqm-app-framework

    DownloadsWeekly Downloads

    4

    Version

    0.3.23

    License

    ISC

    Unpacked Size

    306 kB

    Total Files

    44

    Last publish

    Collaborators

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