1"use strict";(self.webpackChunkopenremote_docs=self.webpackChunkopenremote_docs||[]).push([["112998"],{943413(e,n,s){s.r(n),s.d(n,{metadata:()=>o,default:()=>p,frontMatter:()=>t,contentTitle:()=>a,toc:()=>c,assets:()=>d});var o=JSON.parse('{"id":"developer-guide/working-on-ui-and-apps","title":"Working on UI and apps","description":"Overview","source":"@site/docs/developer-guide/110-working-on-ui-and-apps.md","sourceDirName":"developer-guide","slug":"/developer-guide/working-on-ui-and-apps","permalink":"/docs/next/developer-guide/working-on-ui-and-apps","draft":false,"unlisted":false,"editUrl":"https://github.com/openremote/documentation/edit/main/docs/developer-guide/110-working-on-ui-and-apps.md","tags":[],"version":"current","sidebarPosition":110,"frontMatter":{},"sidebar":"docsSidebar","previous":{"title":"System Administration","permalink":"/docs/next/developer-guide/system-administration"},"next":{"title":"Adding Widgets on Insights","permalink":"/docs/next/developer-guide/adding-widgets-on-insights"}}'),r=s(474848),i=s(28453);let t={},a="Working on UI and apps",d={},c=[{value:"Overview",id:"overview",level:2},{value:"Working on an app (e.g. Manager UI)",id:"working-on-an-app-eg-manager-ui",level:2},{value:"Webpack dev server environment variables",id:"webpack-dev-server-environment-variables",level:3},{value:"Consuming UI components",id:"consuming-ui-components",level:2},{value:"UI development",id:"ui-development",level:2},{value:"UI Components & Apps (<code>/ui</code>)",id:"ui-components--apps-ui",level:3},{value:"Running Manager UI against remote instance",id:"running-manager-ui-against-remote-instance",level:3},{value:"Components",id:"components",level:3},{value:"Maps",id:"maps",level:3},{value:"Apps",id:"apps",level:3},{value:"Demos",id:"demos",level:3}];function l(e){let n={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"working-on-ui-and-apps",children:"Working on UI and apps"})}),"\n",(0,r.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,r.jsxs)(n.p,{children:["Front end applications are ",(0,r.jsx)(n.a,{href:"https://www.webcomponents.org/",children:"webcomponent"})," based using the ",(0,r.jsx)(n.a,{href:"https://lit.dev/",children:"lit"})," library and ",(0,r.jsx)(n.a,{href:"https://material.io/components?platform=web",children:"Material Design"})," for styling. We use a combination of Polymer LIT, Material Design and our own OpenRemote elements. The UI components are ",(0,r.jsx)(n.a,{href:"https://www.npmjs.com/org/openremote",children:"published on NPM"}),". The applications themselves are composed of our re-usable modular UI components which can be found in the code base in the ",(0,r.jsx)(n.a,{href:"https://github.com/openremote/openremote/tree/master/ui/component",children:"ui/component"})," folder, these are also published to ",(0,r.jsx)(n.a,{href:"https://www.npmjs.com/org/openremote",children:"NPM"}),"."]}),"\n",(0,r.jsx)(n.h2,{id:"working-on-an-app-eg-manager-ui",children:"Working on an app (e.g. Manager UI)"}),"\n",(0,r.jsxs)(n.p,{children:["To work on an app for example the ",(0,r.jsx)(n.code,{children:"Manager UI"})," :"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsx)(n.p,{children:"From the main directory, start the required containers, use dev-ui.yml for example:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-shell",children:"docker-compose -f profile/dev-ui.yml up -d\n"})}),"\n",(0,r.jsxs)(n.p,{children:["By default, this makes the manager container serve the apps present in the latest docker image on port 8080 e.g. ",(0,r.jsx)(n.code,{children:"http://localhost:8080/manager/"})," . While these apps are not necessary for this case, the included manager container webservice is."]}),"\n"]}),"\n",(0,r.jsxs)(n.li,{children:["\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.code,{children:"cd"})," into the app directory e.g. ",(0,r.jsx)(n.code,{children:"ui/app/manager"})," and run the following ",(0,r.jsx)(n.code,{children:"npm"})," script:"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-shell",children:"npm run serve\n"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["Compiles the typescript model from java code and then starts webpack dev server and serves the web app which can then be accessed at ",(0,r.jsx)(n.code,{children:"http://localhost:9000/manager/"})," (",(0,r.jsxs)(n.strong,{children:["NOTE: trailing ",(0,r.jsx)(n.code,{children:"/"})," is required"]}),")"]}),"\n",(0,r.jsx)(n.h3,{id:"webpack-dev-server-environment-variables",children:"Webpack dev server environment variables"}),"\n",(0,r.jsxs)(n.p,{children:["The following environment variables can be set when running ",(0,r.jsx)(n.code,{children:"npm run serve"})," using the syntax ",(0,r.jsx)(n.code,{children:'npm run serve "--" --env ENV_NAME=ENV_VALUE'}),":"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["managerUrl - By default webpack dev server expects the manager to be available at ",(0,r.jsx)(n.code,{children:"http://localhost:8080"})," but this can be configured for example when running the manager Docker image (e.g. ",(0,r.jsx)(n.code,{children:'npm run serve "--" --env managerUrl=https://localhost'}),")"]}),"\n",(0,r.jsxs)(n.li,{children:["keycloakUrl - By default Keycloak expects to be available at ",(0,r.jsx)(n.code,{children:"managerUrl/auth"})," but this can be configured for example when running the manager Docker image (e.g. ",(0,r.jsx)(n.code,{children:'npm run serve "--" --env keycloakUrl=https://keycloak/auth'}),")"]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"consuming-ui-components",children:"Consuming UI components"}),"\n",(0,r.jsx)(n.p,{children:"Components are published as ES6 modules (for use in bundlers like webpack) and are also pre-bundled for direct consumption."}),"\n",(0,r.jsx)(n.h2,{id:"ui-development",children:"UI development"}),"\n",(0,r.jsx)(n.p,{children:"Doing development on the UI means working on any of:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Front end applications"}),"\n",(0,r.jsx)(n.li,{children:"Front end components and their demos, shared between applications"}),"\n",(0,r.jsx)(n.li,{children:"Keycloak theme(s) used in authentication screens"}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["You will need the standard tool chain (see ",(0,r.jsx)(n.a,{href:"/docs/next/developer-guide/preparing-the-environment",children:"Preparing the environment"})," to be able to build and run the components and apps. Working on the web applications and/or components will also generally require backend services to interact with, you can either:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Run a Manager instance in an IDE (refer to ",(0,r.jsx)(n.a,{href:"/docs/next/developer-guide/setting-up-an-ide",children:"Setting up an IDE"}),")"]}),"\n",(0,r.jsxs)(n.li,{children:["Deploy the Manager using a Docker container (refer to ",(0,r.jsx)(n.a,{href:"/docs/next/developer-guide/docker-compose-profiles#ui-development-dev-uiyml",children:"UI Development profile"}),")"]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["If you want to create a new ",(0,r.jsx)(n.code,{children:"component"})," or ",(0,r.jsx)(n.code,{children:"app"})," then simply copy an existing one as a template (when creating a ",(0,r.jsx)(n.code,{children:"component"}
1)," then you may need to create a corresponding ",(0,r.jsx)(n.code,{children:"demo"})," which acts as a development harness that can be served by webpack dev server - one demo may act as harness for multiple components)."]}),"\n",(0,r.jsxs)(n.h3,{id:"ui-components--apps-ui",children:["UI Components & Apps (",(0,r.jsx)(n.code,{children:"/ui"}),")"]}),"\n",(0,r.jsxs)(n.p,{children:["All UI components and apps are located in the ",(0,r.jsx)(n.code,{children:"ui"})," directory; here you can find the standard OpenRemote web UI components and apps using a monorepo architecture. The code is divided into categories by directory:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://github.com/openremote/openremote/tree/master/ui/component",children:(0,r.jsx)(n.code,{children:"component"})})," - Base OpenRemote JS modules and web components (built using Polymer) these are written as ES6 modules"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://github.com/openremote/openremote/tree/master/ui/app",children:(0,r.jsx)(n.code,{children:"app"})})," - Built-in OpenRemote web applications (applications can be built with whatever frameworks/libraries are desired)"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.a,{href:"https://github.com/openremote/openremote/tree/master/ui/demo",children:(0,r.jsx)(n.code,{children:"demo"})})," - Demos of each web component (provides a development harness for developers working on the components)"]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["Typescript is used to provide static typing with the OpenRemote model available in the ",(0,r.jsx)(n.code,{children:"@openremote/model"})," component package; the components are published to ",(0,r.jsx)(n.code,{children:"npm"})," under the ",(0,r.jsx)(n.code,{children:"@openremote"})," scope; see the README in each component for information about each specific component."]}),"\n",(0,r.jsxs)(n.p,{children:["Yarn workspaces are used to symlink the OpenRemote packages into a root ",(0,r.jsx)(n.code,{children:"node_modules"})," directory (refer to the Yarn ",(0,r.jsx)(n.a,{href:"https://yarnpkg.com/",children:"documentation"})," for more details). Yarn acts as an alternative to ",(0,r.jsx)(n.code,{children:"npm"})," so please use it."]}),"\n",(0,r.jsxs)(n.p,{children:["NPM scripts are used for build and development purposes (the main build tool for the entire code base is ",(0,r.jsx)(n.code,{children:"gradle"})," so there are ",(0,r.jsx)(n.code,{children:"gradle"})," tasks that launch NPM scripts - these ",(0,r.jsx)(n.code,{children:"gradle"})," tasks have the prefix ",(0,r.jsx)(n.code,{children:"npm"})," followed by the NPM script name e.g. ",(0,r.jsx)(n.code,{children:"npmBuild"}),")."]}),"\n",(0,r.jsxs)(n.p,{children:["See ",(0,r.jsx)(n.a,{href:"/docs/next/developer-guide/code-formatting#ui-linting-and-formatting-with-yarn",children:"UI linting and formatting"})," for the Yarn, ESLint, Prettier, and Spotless commands used by OpenRemote."]}),"\n",(0,r.jsx)(n.p,{children:"The following standard NPM scripts are used throughout the components and apps for consistency:"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"clean"})," - Cleans up any build artefacts (typically uses the ",(0,r.jsx)(n.a,{href:"https://www.npmjs.com/package/shx",children:(0,r.jsx)(n.code,{children:"shx"})})," package)"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"build"})," - Build the component/app ready for publishing/deployment"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"test"})," - Run any automated tests"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"modelWatch"})," - Starts a ",(0,r.jsx)(n.code,{children:"gradle"})," task to watch the ",(0,r.jsx)(n.code,{children:"java"})," model code and to run the ",(0,r.jsx)(n.code,{children:"typescript"})," generator when changes are detected, the generated ",(0,r.jsx)(n.code,{children:"model"})," and ",(0,r.jsx)(n.code,{children:"restclient"})," typescript code is then transpiled to ",(0,r.jsx)(n.code,{children:"javascript"})]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"modelBuild"})," - Similar to ",(0,r.jsx)(n.code,{children:"modelWatch"})," but just does a onetime build rather than watching for changes"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.code,{children:"serve"})," - Starts ",(0,r.jsx)(n.code,{children:"webpack dev server"}
1)," typically on ",(0,r.jsx)(n.code,{children:"http://localhost:9000/[app|demo]/"})]}),"\n"]}),"\n",(0,r.jsxs)(n.p,{children:["The above script names should be used in ",(0,r.jsx)(n.code,{children:"package.json"})," files and then appropriate ",(0,r.jsx)(n.code,{children:"build.gradle"})," files should be added, see existing components, apps, demos for examples. Typically only the ",(0,r.jsx)(n.code,{children:"apps"})," need to be built by ",(0,r.jsx)(n.code,{children:"gradle"})," tasks as typescript should follow project references and compile any dependent components."]}),"\n",(0,r.jsx)(n.h3,{id:"running-manager-ui-against-remote-instance",children:"Running Manager UI against remote instance"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Add ",(0,r.jsx)(n.code,{children:"https://localhost/*"})," to Valid redirect URIs of openremote client in master realm of remote instance keycloak"]}),"\n",(0,r.jsxs)(n.li,{children:["Ensure remote instance manager container has ",(0,r.jsx)(n.code,{children:"localhost"})," listed in ",(0,r.jsx)(n.code,{children:"OR_ADDITIONAL_HOSTNAMES"})," (note this variable is comma separated list of hostnames)"]}),"\n",(0,r.jsxs)(n.li,{children:["Run the ",(0,r.jsx)(n.code,{children:"dev-proxy.yml"})," compose profile locally"]}),"\n",(0,r.jsxs)(n.li,{children:["Access shell in proxy container: ",(0,r.jsx)(n.code,{children:"docker exec -it <PROXY_CONTAINER_NAME> ash"})]}),"\n",(0,r.jsxs)(n.li,{children:["Add the following snippets to the proxy config (Press i for insert mode, when finished press Esc then : then wq): ",(0,r.jsx)(n.code,{children:"vi /etc/haproxy/haproxy.cfg"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Above the ",(0,r.jsx)(n.code,{children:"use_backend manager_backend"})," line"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:" acl webpack path_beg /manager\n use_backend webpack_backend if webpack\n"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Above the ",(0,r.jsx)(n.code,{children:"backend manager_backend"})," line:"]}),"\n"]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{children:"backend webpack_backend\n server webpack host.docker.internal:9000\n"})}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"Ctrl/Cmd-D to exit the proxy container shell"}),"\n",(0,r.jsxs)(n.li,{children:["Start the webpack dev server in the ",(0,r.jsx)(n.code,{children:"ui/app/manager"}),": ",(0,r.jsx)(n.code,{children:'npm run serve "--" --env managerUrl=https://demo.openremote.app'})]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"components",children:"Components"}),"\n",(0,r.jsxs)(n.p,{children:["Components can be developed and tested in isolation (with dependencies on other components and/or public npm modules as required). Some components have no visuals and provide standard OpenRemote functionality e.g. ",(0,r.jsx)(n.code,{children:"@openremote/core"}),", whilst others provide visuals that allow interaction with the Manager backend."]}),"\n",(0,r.jsx)(n.h3,{id:"maps",children:"Maps"}),"\n",(0,r.jsxs)(n.p,{children:["If you are working on a map component then please refer to the ",(0,r.jsx)(n.a,{href:"/docs/next/developer-guide/working-on-maps",children:"working on maps"}),"."]}),"\n",(0,r.jsx)(n.h3,{id:"apps",children:"Apps"}),"\n",(0,r.jsxs)(n.p,{children:["Apps bring together components and/or public NPM modules and can be written using any framework/library etc that is\ncompatible with web components (see ",(0,r.jsx)(n.a,{href:"https://custom-elements-everywhere.com/",children:"here"}),")."]}),"\n",(0,r.jsx)(n.h3,{id:"demos",children:"Demos"}),"\n",(0,r.jsx)(n.p,{children:"These are apps for development purposes Generally a 1-1 mapping between components and demos; they provide a simple harness for the components that can be used during development and optionally can be deployed to offer component demos."})]})}function p(e={}){let{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(l,{...e})}):l(e)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.