n-ui
please speak to the Core UI team.
Server, build and client side bootstrapping for ft.com’s user-facing applications.
PLEASE DON’T USE THIS OUTSIDE OF USER-FACING FT.COM APPLICATIONS. If you need a good Express server with Handlebars, metrics etc available consider using n-internal-tool, n-express or copying what you need from n-ui
n-ui has three parts – a server, a client side “app shell” (JS, CSS & Handlebars layout), and a build. Expect things to break if you don’t use all 3.
n-ui is a wrapper around n-express which adds templating and asset-loading features.
npm install @financial-times/n-ui
const app = require('@financial-times/n-ui')(opts);
app.locals.nUiConfig = {
preset: 'complete', // 'discrete' will turn off ads & various popups
features: {
lazyLoadImages: true // turns individual features on/off. Check `/browser/bootstrap/js/component-initializer.js` for an up-to-date feature list
}
};
Where opts is an object supporting all n-express
’s options, but with many set to true
by default (see /server/index.js
for details). Additional options include:
partialsDirectory
String or array – path[s] to load partials from, this in addition to the standardviews/partials
that is set for every applayoutsDir
String – a path to load Handlebars views from defults tonode_modules/@financial-times/n-ui/layout
withJsonLd
Boolean – output jsonLD schema information in the page head
n-ui comes bundled with its own build tool – basically webpack
, haikro build
and a little bit of other stuff. To use it, add the following to your Makefile:
build:
nui build
build-production:
nui build --production
watch:
nui watch
It is possible to use a add custom webpack-style configuration file to your project. Simply add a n-ui-build.config.js
file in the root of your project.
This supports the following properties:
entry
- defines entry points for your assets as documented in the webpack entrypoints documentationplugins
- defines custom plugins as documented in the webpack plugins documentationpragma
- configures thepragma
used by babel-transform-react-jsx (defaults toReact.createElement
).
To define entry points for your assets, use a n-ui-build.config.js
file in the root of your project, which can export any object compatible with webpack.
n-ui takes care of loading polyfills etc, in order. n-ui exports 4 things you’ll want to use:
flags
– the feature/development/maintenance/MVT flags objectappInfo
– metadata about the app that's serving the pageallStylesLoaded
– a promise that resolves once all the lazy-loaded styles are in placeonAppInitialized
[required] – a function to call once the app js has successfully executed. This tells integration tests when the page is “complete” among other things
import
one or more of the above from n-ui in your application code, which no longer needs to be wrapped in a function.
e.g.
import { flags , allStylesLoaded, onAppInitialized } from 'n-ui';
if (flags.get('feature')) {
component.init();
}
allStylesLoaded
.then(() => {
lazyComponent.init();
onAppInitialized(); // it’s up to you to define when your app is “ready”
});
bower install n-ui
Nothing fancy going on here anymore 😄. No mixins (though the n-ui-foundations module has a few you will want to use), no tricky critical path CSS stuff.
@import "n-ui/main";
This will, when using the n-ui build tool, split n-ui’s styles into head-n-ui-core.css and n-ui-core.css files, and the server will inline/link to these appropriately.
You should be able to work in n-ui as if it’s an app – make watch
and make run
should work and serve a demo app on local.ft.com:5005
.
In local n-ui:
make install
make build-css-loader
npm link
bower link
In the app (e.g. next-article):
export NEXT_APP_SHELL=local
bower link n-ui
npm link @financial-times/n-ui
make build
- !! need to do this for each change in either n-ui or front facing app.make run
To test the cli locally, use ../n-ui/bin/nui.js build
or ../n-ui/bin/nui.js watch
from within a local copy of a user-facing app. To test the rebuild command first set the CIRCLECI_REBUILD_KEY
environment variable using export CIRCLECI_REBUILD_KEY={key}
(you can find the key in n-ui's continuous-integration folder in Vault). You can then use ./bin/nui.js rebuild next-search-page
within your local copy of n-ui (perhaps comment out the call to triggerMasterBuild
in the rebuild function when testing the ./bin/nui.js rebuild --all --serves user-page
command so you avoid actually causing all apps to rebuild ;)).
When you release an n-ui tag, 3 things happen:
- assets are built and deployed to s3, from where they are linked to/downloaded by apps
- the npm package is published
- during work hours (9am to 4pm), all user-facing apps are rebuilt to pick up the changes
Adds link headers to optimise requests for assets, defaulting to preload behaviour:
url
– absolute or relative path to the resourcemeta
– object defining additional properties to add to the headerrel
[default: 'preload'] - value of therel
propertyas
– value of theas
property e.g. 'stylesheet'
options
– additional options when creating the headerpriority
– a value of highest will add the link header before all previously added resources that do not specify this (should not normally used by apps – used internally to ensure n-ui’s resources are always loaded as quickly as possible)hashed
– if true the path to the asset will be resolved to the equivalent hashed asset path
If you pass withNavigation:true
in the init options, you will have navigation data available in res.locals.navigation
. This data comes from polling the navigation API. This data is used to populate the various menus and navigation items on the apps. The following data is available:
res.locals.navigation = {
lists: {
navbar_desktop: // data for the main nav in the header (only on large screens)
navbar_mobile: //data for the white strip that appears on the homepage and fastFT pages only on small screens
drawer: //data for the slide-out menu
footer: // data for the footer
}
};
See the MyFT page for a rendered copy of this config.
res.locals.navigation = {
// other settings here...
showSubNav: true,
// this populates the breadcrumb section at the left of the subnav
breadcrumb: [
{
id: 'my-ft',
label: 'My FT',
url: '/myft/following'
}
],
// this populates the current level of subnav
subsections: [
{
id: 'feed',
label: 'myFT Feed',
url: '/myft/following'
},
{
id: 'alerts',
label: 'Emails & Alerts',
url: '/myft/alerts'
}
],
// optionally, add a 'Sign out' link to the right of the subnav (default is off)
showSignOut: true
}
If you also pass withNavigationHierarchy: true
in the init options you get some additonal properties detailing the current page’s position in the hierarchy. This is only currently useful on stream pages. The following properties are added:
res.locals.navigation.currentItem // the current item
res.locals.navigation.children // an array of the direct descendant of the current page
res.locals.navigation.ancestors // an array of the parent items of the current page (top-level first)
The navigation model also controls the edition switching logic. The following properties are added:
res.locals.editions.current // the currently selected edition
res.locals.editions.others // and array of other possible editions
In same cases you might need to show only the FT logo in the header, and hide all other navigation. This pattern is used in several conversion apps.
{
nUi: {
header: {
variant: 'logo-only'
}
}
}
If your page will be linked to from the iOS app, and no outbound navigation from it is allowed, then the flag 'hideOutboundLinks' will be set to true for you. This will render the header logo without it being a link, and hide the page footer.
Similar nUi controls exist for footer. Current configuration allows footer theme changes and option disabling.
{
nUi: {
footer: {
themeLight: true,
legalOnly: true
}
}
}
- Our Handlebars engine loads partials from
bower_components
and has a number of additional helpers. It also points to n-layout to provide a vanilla and “wrapper” layout - Exposes everything in the app’s
./public
folder via./{{name-of-app}}
(only in non-production environments, please use next-assets or hashed-assets in production)