Exploring Predix UI seed
GE China Predix Bootcamp - Day 5
Shanghai foundry front-end team
Required Environment
- Devbox
- CloudFoundry CLI
- NodeJS
- Git
- Sublime Text (or any code editor)
what does seed app offer
- Polymer web app-shell scaffold
- Example Predix UI Components usages
- Express web server connect to Predix services and APIs
- Built-in support of Predix UAA
seed-app overview
BFF - Backend for front-end
- NodeJS
- bower/npm
- Express Web Server
bower vs npm
- non-blocking I/O
- Express middleware
- http-proxy middleware
Express Server
(request) blocking I/O
(request) non-blocking I/O
Middleware functions
- Execute any code
- Make changes to request/response
- End the request-response cycle
- Call the next middleware in stack
Express "hello world"
Express routes
HTTP Proxy Middleware
JSON-Server Middleware
Enable UAA in seed app
- Config UAA for authentication
- Secure Express routes
- Credentials in manifest file for Deploy
app.use('/login', {...});
app.use('/api', {...});
//Use this route to make the entire app secure. This forces login for any path in the entire app.
app.use('/', passport.authenticate('main', {
noredirect: false //Don't redirect a user to the authentication page, just show an error
}),
// other middleware
);
Authenticating All Routes
---
applications:
- name: predix-ui-seed
memory: 64M
buildpack: nodejs_buildpack
command: node server/app.js
path: dist
#services:
# - <your-name>-secure-uaa-instance
# - <your-name>-timeseries-instance
# - <your-name>-asset-instance
env:
node_env: cloud
uaa_service_label: predix-uaa
# Add these values for authentication in the cloud
#clientId: {Enter client ID, e.g. app-client-id, and place it here}
#base64ClientCredential: {Get clientID:clientSecret then base64 encode and place it here}
#windServiceURL: "{URL of the microservice <your-name>-winddata-timeseries-service}, e.g. https://your-name-winddata-timeseries-service.run.asw-usw02-pr.predix.io"
Config manifest
Px UI components
(used in seed app)
- px-app-nav
- px-context-browser
- px-simple-line-chart
- px-simple-horizontal-bar-chart
- px-vis-timeseries
- px-data-table
Time-series chart
- px-rangepicker
- px-vis-data-convertor
- chart features
time-series chart
two time-series format
UI dashboard
- view service hierarchy
- context-browser
- deck-selector
dashboard view hierarchy
dashboard elements
- View – Visual summary of the information displayed in a web application. It consists of one or more decks.
- Deck – Component that supplies simple one-dimensional layout control for cards and components arranged in a specified order.
- Card – Smallest functional unit of the page. You can create interactions between components within an individual card, as well as interactions with other cards based on user context.
- Component – User-interface element that allows you to add concrete features or access services.
dashboard & assets
Application Shell
- Web components
- HTML Import
- Polymer
HTML Import - linking
App Shell - structure
App Shell - bundling
layers of Polymer application
Polymer Web Component
<dom-module id="element-name">
<template>
<style>
/* CSS rules for your element */
</style>
<!-- local DOM for your element -->
<div>{{greeting}}</div> <!-- data bindings in local DOM -->
</template>
<script>
// element registration
Polymer({
is: "element-name",
// add properties and methods on the element's prototype
properties: {
// declare properties for the element's public API
greeting: {
type: String,
value: "Hello!"
}
}
});
</script>
</dom-module>
Predix CSS Framework
- Sass Lang
- Predix Design
- InuitCSS - BEM/ITCSS
SASS - Preprocessing CSS
Predix Design system
ITCSS - Inverted Triangle Cascading Style Sheet
- Objects – class-based selectors which define undecorated design patterns, for example media object known from OOCSS
- Components – specific UI components. This is where majority of our work takes place and our UI components are often composed of Objects and Components
- Trumps – utilities and helper classes with ability to override anything which goes before in the triangle, eg. hide helper class
- Settings – used with preprocessors and contain font, colors definitions, etc.
- Tools – globally used mixins and functions. It’s important not to output any CSS in the first 2 layers.
- Generic – reset and/or normalize styles, box-sizing definition, etc. This is the first layer which generates actual CSS.
- Elements – styling for bare HTML elements (like H1, A, etc.). These come with default styling from the browser so we can redefine them here.
Law of ITCSS
BEM style convention
example: starter-kit-design-demo
example: predix-button-design
Scoped Styling
- Shadow DOM & Scoped Styling
- Shared styles module
- Custom CSS Variables & Mix-ins
- Document-wide Styling
<dom-module id="my-element">
<template>
<style>
:host {
display: block;
border: 1px solid red;
}
#child-element {
background: yellow;
}
/* styling elements distributed to content (via ::content) requires */
/* selecting the parent of the <content> element for compatibility with */
/* shady DOM . This can be :host or a wrapper element. */
.content-wrapper ::content > .special {
background: orange;
}
</style>
<div id="child-element">In local DOM!</div>
<div class="content-wrapper"><content></content></div>
</template>
<script>
Polymer({
is: 'my-element'
});
</script>
</dom-module>
component isolated local styles
<dom-module id="my-toolbar">
<template>
<style>
:host {
padding: 4px;
background-color: gray;
}
.title {
color: var(--my-toolbar-title-color);
}
</style>
<span class="title">{{title}}</span>
</template>
<script>
Polymer({
is: 'my-toolbar',
properties: {
title: String
}
});
</script>
</dom-module>
cross-scope styling - variables
<!-- usage -->
<style>
my-toolbar {
--my-toolbar-theme: {
/* rules */
};
--my-toolbar-title-theme: {
/* rules */
};
}
</style>
<dom-module id="my-toolbar">
<template>
<style>
:host {
padding: 4px;
background-color: gray;
/* apply a mixin */
@apply(--my-toolbar-theme);
}
.title {
@apply(--my-toolbar-title-theme);
}
</style>
<span class="title">{{title}}</span>
</template>
</dom-module>
cross-scope styling - mixins
<!-- shared-styles.html -->
<dom-module id="shared-styles">
<template>
<style>
.red { color: red; }
</style>
</template>
</dom-module>
<!-- import the module -->
<link rel="import" href="../shared-styles/shared-styles.html">
<dom-module id="x-foo">
<template>
<!-- include the style module by name -->
<style include="shared-styles"></style>
<style>:host { display: block; }</style>
Hi
</template>
<script>Polymer({is: 'x-foo'});</script>
</dom-module>
shared style module
Data binding (MVVM)
- Declarative data-binding
- Observers v.s. Computed Property
- Control flow in data-binding
<dom-module id="user-view">
<template>
<div>[[name]]</div>
</template>
<script>
Polymer({
is: 'user-view',
properties: {
name: String
}
});
</script>
</dom-module>
<!-- usage -->
<user-view name="Samuel"></user-view>
<dom-module id="main-view">
<template>
<user-view first="{{user.first}}"
last="{{user.last}}"></user-view>
</template>
<script>
Polymer({
is: 'main-view',
properties: {
user: Object
}
});
</script>
</dom-module>
text-binding v.s. property binding
Polymer({
is: 'x-custom',
properties: {
disabled: {
type: Boolean,
observer: '_disabledChanged'
},
highlight: {
observer: '_highlightChanged'
}
},
_disabledChanged: function(newValue, oldValue) {
this.toggleClass('disabled', newValue);
this.highlight = true;
},
_highlightChanged: function() {
this.classList.add('highlight');
this.async(function() {
this.classList.remove('highlight');
}, 300);
}
});
Simple Property Observer
Polymer({
is: 'x-custom',
properties: {
preload: Boolean,
src: String,
size: String
},
observers: [
'updateImage(preload, src, size)'
],
updateImage: function(preload, src, size) {
// ... do work using dependent values
}
});
Complex Property Observer
<dom-module id="x-deep-observer">
<template>
<input value="{{user.name.first::input}}"
placeholder="First Name">
<input value="{{user.name.last::input}}"
placeholder="Last Name">
</template>
<script>
Polymer({
is: 'x-deep-observer',
properties: {
user: {
type: Object,
value: function() {
return {'name':{}};
}
}
},
observers: [
'userNameChanged(user.name.*)'
],
userNameChanged: function(changeRecord) {
console.log('path: ' + changeRecord.path);
console.log('value: ' + changeRecord.value);
},
});
</script>
</dom-module>
Deep Property Observer
<dom-module id="name-card">
<template>
<div>[[name.first]] [[name.last]]</div>
</template>
<script>Polymer({ is: 'name-card' });</script>
</dom-module>
Data flow within element itself
<dom-module id="user-profile">
<template>
…
<address-card
address="{{primaryAddress}}"></address-card>
</template>
…
</dom-module>
Data flow between elements
If <address-card> change it's "address", property "primaryAddress" is changed on <user-profile> as well.
routing & page-flow
- app-location & app-route
- px-view
- HTML5 history API
URL and View Routing
<!-- proxy to window.location -->
<app-location
route="{{_route}}">
</app-location>
<!-- match URL segment and convert to data -->
<app-route
route="[[_route]]"
pattern="/:page"
data="{{routeData}}">
</app-route>
<!-- select the view that matches route to display -->
<template is="dom-repeat" items="[[_routePages]]">
<px-view
active="[[_equals(routeData.page, item.page)]]"
element-href="[[item.elementHref]]">
</px-view>
</template>
Build the seed app
- build system
- gulp-sass
- gulp-vulcanizer
build system
gulp-sass task
gulp-vulcanizer
gulp.task('vulcanize', function() {
return gulp.src('app/elements/elements.html')
.pipe(vulcanize({
stripComments: true,
inlineScripts: true,
inlineCss: true
}))
.pipe(gulp.dest('dist/elements'));
});
Deploy the seed app
- CF Deploy
- Jenkins CI integration
---
applications:
- name: predix-ui-app
memory: 64M
buildpack: nodejs_buildpack
command: node server/app.js
#services:
# - <your-name>-secure-uaa-instance
# - <your-name>-timeseries-instance
# - <your-name>-asset-instance
env:
node_env: cloud
uaa_service_label : predix-uaa
# Add these values for authentication in the cloud
#clientId: {Enter client ID, e.g. app-client-id, and place it here}
#base64ClientCredential: dWFhLWNsaWVudC1pZDp1YWEtY2xpZW50LWlkLXNlY3JldA==
#windServiceURL: "{URL of the microservice <your-name>-winddata-timeseries-service}, e.g. https://your-name-winddata-timeseries-service.run.asw-usw02-pr.predix.io"
Nodejs buildpack
Jenkins CI build
Jenkins CI build log
Q & A
Exploring Predix Seed App
By Garry Yao
Exploring Predix Seed App
UI seed app structure and features explained
- 2,009