Fix odlux bugs

Fix help and about app not scrollable

Fix filter hiding and showing without user interaction and default sort  in all tables

Issue-ID: SDNC-1117
Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
Change-Id: I5c6ff86c73a3b222a8d9022125454788496f6399
diff --git a/sdnr/wt/data-provider/provider/pom.xml b/sdnr/wt/data-provider/provider/pom.xml
index fae95d2..dd2e03a 100644
--- a/sdnr/wt/data-provider/provider/pom.xml
+++ b/sdnr/wt/data-provider/provider/pom.xml
@@ -52,7 +52,7 @@
         <maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
         <buildtime>${maven.build.timestamp}</buildtime>
         <databaseport>49402</databaseport>
-        <odlux.buildno>46.9d666e7(20/02/28)</odlux.buildno>
+        <odlux.buildno>50.53aa73a(20/03/12)</odlux.buildno>
     </properties>
 
     <dependencies>
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx
index c153ed5..503133b 100644
--- a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx
+++ b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx
@@ -39,10 +39,17 @@
 
 type NetworkElementSelectorComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDispatch>;
 
+let initialSorted = false;
+
 class NetworkElementSelectorComponent extends React.Component<NetworkElementSelectorComponentProps> {
 
   componentDidMount() {
-    this.props.connectedNetworkElementsActions.onRefresh();
+
+    if (!initialSorted) {
+      initialSorted = true;
+      this.props.connectedNetworkElementsActions.onHandleRequestSort("node-id");
+    } else
+      this.props.connectedNetworkElementsActions.onRefresh();
   }
 
   render() {
diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx
index ad7b247..96f6c8a 100644
--- a/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx
+++ b/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx
@@ -35,6 +35,9 @@
 
 type ConnectionStatusLogComponentProps = Connect<typeof mapProps, typeof mapDispatch>;
 
+let initialSorted = false;
+
+
 class ConnectionStatusLogComponent extends React.Component<ConnectionStatusLogComponentProps> {
   render(): JSX.Element {
     return (
@@ -46,6 +49,15 @@
       </ConnectionStatusTable>
     );
   };
+
+  componentDidMount() {
+    if (!initialSorted) {
+      initialSorted = true;
+      this.props.connectionStatusLogActions.onHandleExplicitRequestSort("timestamp", "desc");
+    } else {
+      this.props.connectionStatusLogActions.onRefresh();
+    }
+  }
 }
 
 export const ConnectionStatusLog = connect(mapProps, mapDispatch)(ConnectionStatusLogComponent);
diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx
index d50a81e..53e1048 100644
--- a/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx
+++ b/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx
@@ -83,7 +83,7 @@
 }
 
 const emptyRequireNetworkElement: NetworkElementConnection = { id: "", nodeId: "", host: "", port: 0, status: "Disconnected", isRequired: false };
-
+let initialSorted = false;
 const NetworkElementTable = MaterialTable as MaterialTableCtorType<NetworkElementConnection>;
 
 export class NetworkElementsListComponent extends React.Component<NetworkElementsListComponentProps, NetworkElementsListComponentState> {
@@ -173,7 +173,12 @@
   };
 
   public componentDidMount() {
-    this.props.networkElementsActions.onRefresh();
+    if (!initialSorted) {
+      initialSorted = true;
+      this.props.networkElementsActions.onHandleRequestSort("node-id");
+    } else {
+      this.props.networkElementsActions.onRefresh();
+    }
   }
 
   private onOpenAddNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => {
diff --git a/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx b/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx
index a96d3d6..a7feae9 100644
--- a/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx
+++ b/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx
@@ -61,8 +61,8 @@
     if (this.props.panelId === null) { //don't change tabs, if one is selected already
       this.onTogglePanel("NetworkElements");
     }
-    this.props.networkElementsActions.onToggleFilter();
-    this.props.connectionStatusLogActions.onToggleFilter();
+    //this.props.networkElementsActions.onToggleFilter();
+    //this.props.connectionStatusLogActions.onToggleFilter();
   }
 
   public componentDidUpdate = async () => {
diff --git a/sdnr/wt/odlux/apps/eventLogApp/src/views/eventLog.tsx b/sdnr/wt/odlux/apps/eventLogApp/src/views/eventLog.tsx
index 5993bb5..aa90272 100644
--- a/sdnr/wt/odlux/apps/eventLogApp/src/views/eventLog.tsx
+++ b/sdnr/wt/odlux/apps/eventLogApp/src/views/eventLog.tsx
@@ -35,6 +35,8 @@
   eventLogActions: createEventLogActions(dispatcher.dispatch)
 });
 
+let initalSorted = false;
+
 class EventLogComponent extends React.Component<Connect<typeof mapProps, typeof mapDispatch>> {
   render() {
     return <EventLogTable stickyHeader title="Event Log" idProperty="_id" columns={[
@@ -50,8 +52,13 @@
   }
 
   componentDidMount() {
-    this.props.eventLogActions.onToggleFilter();
-    this.props.eventLogActions.onHandleRequestSort("node-id");
+
+    if (!initalSorted) {
+      initalSorted = true;
+      this.props.eventLogActions.onHandleExplicitRequestSort("timestamp", "desc");
+    } else {
+      this.props.eventLogActions.onRefresh();
+    }
   }
 }
 
diff --git a/sdnr/wt/odlux/apps/faultApp/src/actions/partialUpdatesAction.ts b/sdnr/wt/odlux/apps/faultApp/src/actions/partialUpdatesAction.ts
new file mode 100644
index 0000000..1989767
--- /dev/null
+++ b/sdnr/wt/odlux/apps/faultApp/src/actions/partialUpdatesAction.ts
@@ -0,0 +1,25 @@
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2020 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ * ============LICENSE_END==========================================================================
+ */
+
+import { Action } from "../../../../framework/src/flux/action";
+
+export class SetPartialUpdatesAction extends Action {
+    constructor(public isActive: boolean) {
+        super();
+    }
+}
\ No newline at end of file
diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts
index a5cf928..e03d2b5 100644
--- a/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts
+++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts
@@ -26,26 +26,31 @@
 import { IFaultNotifications, faultNotificationsHandler } from './notificationsHandler';
 import { ICurrentProblemsState, currentProblemsActionHandler } from './currentProblemsHandler';
 import { IAlarmLogEntriesState, alarmLogEntriesActionHandler } from './alarmLogEntriesHandler';
-import { SetPanelAction, RememberCurrentPanelAction } from '../actions/panelChangeActions';
+import { SetPanelAction } from '../actions/panelChangeActions';
 import { IFaultStatus, faultStatusHandler } from './faultStatusHandler';
 import { stuckAlarmHandler } from './clearStuckAlarmsHandler';
 import { PanelId } from 'models/panelId';
+import { SetPartialUpdatesAction } from '../actions/partialUpdatesAction';
 
 export interface IFaultAppStoreState {
   currentProblems: ICurrentProblemsState;
   faultNotifications: IFaultNotifications;
   alarmLogEntries: IAlarmLogEntriesState;
-  currentOpenPanel: ICurrentOpenPanelState;
+  currentOpenPanel: PanelId | null;
   faultStatus: IFaultStatus;
+  listenForPartialUpdates: boolean;
 }
 
-type ICurrentOpenPanelState = { openPanel: string | null, savedPanel: PanelId | null };
-const panelInitState = { openPanel: null, savedPanel: null };
-const currentOpenPanelHandler: IActionHandler<ICurrentOpenPanelState> = (state = panelInitState, action) => {
+const currentOpenPanelHandler: IActionHandler<PanelId | null> = (state = null, action) => {
   if (action instanceof SetPanelAction) {
-    state = { ...state, openPanel: action.panelId };
-  } else if (action instanceof RememberCurrentPanelAction) {
-    state = { ...state, savedPanel: action.panelId };
+    state = action.panelId;
+  }
+  return state;
+}
+
+const arePartialUpdatesActiveHandler: IActionHandler<boolean> = (state = false, action) => {
+  if (action instanceof SetPartialUpdatesAction) {
+    state = action.isActive;
   }
   return state;
 }
@@ -62,7 +67,8 @@
   alarmLogEntries: alarmLogEntriesActionHandler,
   currentOpenPanel: currentOpenPanelHandler,
   faultStatus: faultStatusHandler,
-  stuckAlarms: stuckAlarmHandler
+  stuckAlarms: stuckAlarmHandler,
+  listenForPartialUpdates: arePartialUpdatesActiveHandler
 };
 
 export const faultAppRootHandler = combineActionHandler<IFaultAppStoreState>(actionHandlers);
diff --git a/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx b/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx
index 666667e..2056976 100644
--- a/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx
+++ b/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx
@@ -62,8 +62,12 @@
       if (currentMountId) {
         props.setCurrentPanel("CurrentProblem");
         props.currentProblemsActions.onFilterChanged("nodeId", currentMountId);
-        props.currentProblemsProperties.showFilter; // || (props.currentProblemsActions.onToggleFilter());
-        props.currentProblemsActions.onRefresh();
+        if (!props.currentProblemsProperties.showFilter) {
+          props.currentProblemsActions.onToggleFilter(false);
+          props.currentProblemsActions.onRefresh();
+        }
+        else
+          props.currentProblemsActions.onRefresh();
       }
     });
   }
@@ -95,11 +99,13 @@
     if (fault && store) {
       store.dispatch(new AddFaultNotificationAction(fault));
 
-      //reload fault data if tab is open
-      if (store.state.fault.currentOpenPanel.openPanel === "AlarmLog") {
-        store.dispatch(alarmLogEntriesReloadAction);
-      } else if (store.state.fault.currentOpenPanel.openPanel === "CurrentProblem") {
-        store.dispatch(currentProblemsReloadAction);
+      // reload fault data if the view is displayed
+      if (store.state.fault.listenForPartialUpdates) {
+        if (store.state.fault.currentOpenPanel === "AlarmLog") {
+          store.dispatch(alarmLogEntriesReloadAction);
+        } else if (store.state.fault.currentOpenPanel === "CurrentProblem") {
+          store.dispatch(currentProblemsReloadAction);
+        }
       }
     }
   }));
diff --git a/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx b/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx
index ed395d2..1072154 100644
--- a/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx
+++ b/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx
@@ -33,14 +33,14 @@
 
 import { createCurrentProblemsProperties, createCurrentProblemsActions, currentProblemsReloadAction } from '../handlers/currentProblemsHandler';
 import { createAlarmLogEntriesProperties, createAlarmLogEntriesActions, alarmLogEntriesReloadAction } from '../handlers/alarmLogEntriesHandler';
-import { setPanelAction, RememberCurrentPanelAction } from '../actions/panelChangeActions';
+import { setPanelAction } from '../actions/panelChangeActions';
 import { Tooltip, IconButton, AppBar, Tabs, Tab } from '@material-ui/core';
 import RefreshIcon from '@material-ui/icons/Refresh';
 import ClearStuckAlarmsDialog, { ClearStuckAlarmsDialogMode } from '../components/clearStuckAlarmsDialog';
+import { SetPartialUpdatesAction } from '../actions/partialUpdatesAction';
 
 const mapProps = (state: IApplicationStoreState) => ({
-  panelId: state.fault.currentOpenPanel.openPanel,
-  savedPanel: state.fault.currentOpenPanel.savedPanel,
+  panelId: state.fault.currentOpenPanel,
   currentProblemsProperties: createCurrentProblemsProperties(state),
   faultNotifications: state.fault.faultNotifications,
   alarmLogEntriesProperties: createAlarmLogEntriesProperties(state)
@@ -54,7 +54,7 @@
   switchActivePanel: (panelId: PanelId) => {
     dispatcher.dispatch(setPanelAction(panelId));
   },
-  rememberCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new RememberCurrentPanelAction(panelId))
+  setPartialUpdates: (active: boolean) => dispatcher.dispatch(new SetPartialUpdatesAction(active))
 });
 
 type FaultApplicationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp>;
@@ -68,6 +68,8 @@
 const FaultTable = MaterialTable as MaterialTableCtorType<Fault>;
 const FaultAlarmNotificationTable = MaterialTable as MaterialTableCtorType<FaultAlarmNotification>;
 
+let currentProblemsInitalSorted = false;
+let alarmLogInitialSorted = false;
 
 class FaultApplicationComponent extends React.Component<FaultApplicationComponentProps, FaultApplicationState>{
 
@@ -97,10 +99,20 @@
     this.props.switchActivePanel(nextActivePanel);
     switch (nextActivePanel) {
       case 'CurrentProblem':
-        this.props.reloadCurrentProblems();
+        if (!currentProblemsInitalSorted) {
+          currentProblemsInitalSorted = true;
+          this.props.currentProblemsActions.onHandleExplicitRequestSort("timestamp", "desc");
+        } else {
+          this.props.reloadCurrentProblems();
+        }
         break;
       case 'AlarmLog':
-        this.props.reloadAlarmLogEntries();
+        if (!alarmLogInitialSorted) {
+          alarmLogInitialSorted = true;
+          this.props.alarmLogEntriesActions.onHandleExplicitRequestSort("timestamp", "desc");
+        } else {
+          this.props.reloadAlarmLogEntries();
+        }
         break;
       case 'AlarmNotifications':
       case null:
@@ -177,25 +189,16 @@
   };
 
   componentWillUnmount() {
-    if (this.props.panelId) {
-      this.props.rememberCurrentPanel(this.props.panelId as PanelId);
-      this.props.switchActivePanel(null);
-    }
+    this.props.setPartialUpdates(false);
   }
 
   public componentDidMount() {
-
-    if (this.props.panelId === null && this.props.savedPanel === null) { //set default tab if none is set
+    if (this.props.panelId === null) { //set default tab if none is set
       this.onToggleTabs("CurrentProblem");
-    } else // load saved tab if possible
-      if (this.props.panelId === null && this.props.savedPanel !== null) {
-        this.onToggleTabs(this.props.savedPanel);
-        this.props.rememberCurrentPanel(null);
-      }
-
-    this.props.alarmLogEntriesActions.onToggleFilter();
-    this.props.currentProblemsActions.onToggleFilter();
+    }
+    this.props.setPartialUpdates(true);
   }
+
   private renderIcon = (props: { rowData: Fault | FaultAlarmNotification }) => {
     return (
       <FontAwesomeIcon icon={faExclamationTriangle} />
diff --git a/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx b/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx
index b4de26d..eab44b4 100644
--- a/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx
+++ b/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx
@@ -32,6 +32,18 @@
   currentPath: state.help.currentPath
 });
 
+const containerStyle = {
+  overflow: "auto",
+  height: "100%",
+  width: "100%"
+};
+
+const styles = {
+  maxWidth: "960px",
+  margin: "1.5em auto",
+
+};
+
 type HelpApplicationComponentProps = Connect<typeof mapProps>;
 
 class HelpApplicationComponent extends React.Component<HelpApplicationComponentProps> {
@@ -58,8 +70,10 @@
 
   render(): JSX.Element {
     return this.props.content ? (
-      <Markdown text={this.props.content} markedOptions={{ renderer: this.renderer }} className="markdown-body"
-        style={{ maxWidth: "960px", margin: "1.5em auto" }} />
+      <div style={containerStyle}>
+        <Markdown text={this.props.content} markedOptions={{ renderer: this.renderer }} className="markdown-body"
+          style={styles} />
+      </div>
     ) : (<h2>Loading ...</h2>)
   }
 
diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx
index 350bac2..f087ed2 100644
--- a/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx
+++ b/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx
@@ -81,7 +81,9 @@
 type MaintenenceViewComponentState = {
   maintenenceEntryToEdit: MaintenenceEntry;
   maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode;
-}
+};
+
+let initialSorted = false;
 
 class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentProps, MaintenenceViewComponentState> {
 
@@ -148,8 +150,15 @@
   }
 
   public componentDidMount() {
-    this.props.maintenanceEntriesActions.onRefresh();
-    this.props.onLoadMaintenanceEntries();
+
+    if (!initialSorted) {
+      initialSorted = true;
+      this.props.maintenanceEntriesActions.onHandleRequestSort("node-id");
+    } else {
+      this.props.onLoadMaintenanceEntries();
+    }
+
+
   }
 
   private onOpenPlus1hEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => {
diff --git a/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx
index d422a0c..f962232 100644
--- a/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx
+++ b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx
@@ -161,7 +161,7 @@
 
           this.props.isReachable ?
 
-            <MediatorServerConfigurationsTable stickyHeader title={this.props.serverName || ''} customActionButtons={[addMediatorConfigAction]} idProperty={"Name"} rows={this.props.configurations} asynchronus columns={[
+            <MediatorServerConfigurationsTable defaultSortColumn={"Name"} defaultSortOrder="asc" stickyHeader title={this.props.serverName || ''} customActionButtons={[addMediatorConfigAction]} idProperty={"Name"} rows={this.props.configurations} asynchronus columns={[
               { property: "Name", title: "Mediator", type: ColumnType.text },
               { property: "Status", title: "Status", type: ColumnType.custom, customControl: ({ rowData }) => rowData.pid ? (<span>Running</span>) : (<span>Stopped</span>) },
               { property: "DeviceIp", title: "IP Adress", type: ColumnType.text },
diff --git a/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx
index aaade65..c16906a 100644
--- a/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx
+++ b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx
@@ -70,6 +70,8 @@
   mediatorServerEditorMode: EditMediatorServerDialogMode
 }
 
+let initialSorted = false;
+
 class MediatorServerSelectionComponent extends React.Component<MediatorServerSelectionComponentProps, MediatorServerSelectionComponentState> {
 
   constructor(props: MediatorServerSelectionComponentProps) {
@@ -116,7 +118,13 @@
   }
 
   public componentDidMount() {
-    this.props.mediatorServersActions.onToggleFilter();
+
+    if (!initialSorted) {
+      initialSorted = true;
+      this.props.mediatorServersActions.onHandleRequestSort("name");
+    } else {
+      this.props.mediatorServersActions.onRefresh();
+    }
   }
 
   private onSelectMediatorServer = (event: React.MouseEvent<HTMLElement>, server: MediatorServer) => {
diff --git a/sdnr/wt/odlux/framework/pom.xml b/sdnr/wt/odlux/framework/pom.xml
index f9b76a0..5c7f846 100644
--- a/sdnr/wt/odlux/framework/pom.xml
+++ b/sdnr/wt/odlux/framework/pom.xml
@@ -46,7 +46,7 @@
     <properties>
         <buildtime>${maven.build.timestamp}</buildtime>
         <distversion>ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version})</distversion>
-        <buildno>46.9d666e7(20/02/28)</buildno>
+        <buildno>50.53aa73a(20/03/12)</buildno>
         <odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version>
     </properties>
 
diff --git a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx
index a80a5a5..3dfbe0b 100644
--- a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx
@@ -111,6 +111,8 @@
   tableId?: string;
   title?: string;
   stickyHeader?: boolean;
+  defaultSortOrder?: 'asc' | 'desc';
+  defaultSortColumn?: keyof TData;
   enableSelection?: boolean;
   disableSorting?: boolean;
   disableFilter?: boolean;
@@ -164,8 +166,8 @@
       filter: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.filter || {} : {},
       showFilter: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.showFilter : false,
       loading: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.loading : false,
-      order: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.order : 'asc',
-      orderBy: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.orderBy : null,
+      order: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.order : this.props.defaultSortOrder || 'asc',
+      orderBy: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.orderBy : this.props.defaultSortColumn || null,
       selected: isMaterialTableComponentPropsWithRowsAndRequestData(this.props) ? this.props.selected : null,
       rows: isMaterialTableComponentPropsWithRows(this.props) && this.props.rows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) || [],
       total: isMaterialTableComponentPropsWithRows(this.props) && this.props.rows.length || 0,
diff --git a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts
index 74682cd..07ffe2f 100644
--- a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts
+++ b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts
@@ -49,6 +49,12 @@
     }
   }
 
+  class RequestExplicitSortAction extends TableAction {
+    constructor(public propertyName: string, public sortOrder: "asc" | "desc") {
+      super();
+    }
+  }
+
   class SetSelectedAction extends TableAction {
     constructor(public selected: TData[] | null) {
       super();
@@ -136,7 +142,15 @@
         orderBy: state.orderBy === action.orderBy && state.order === 'desc' ? null : action.orderBy,
         order: state.orderBy === action.orderBy && state.order === 'asc' ? 'desc' : 'asc',
       }
-    } else if (action instanceof SetShowFilterAction) {
+    } else if (action instanceof RequestExplicitSortAction) {
+      state = {
+        ...state,
+        loading: true,
+        orderBy: action.propertyName,
+        order: action.sortOrder
+      }
+    }
+    else if (action instanceof SetShowFilterAction) {
       state = {
         ...state,
         loading: true,
@@ -239,11 +253,18 @@
           (!skipRefresh) && dispatch(reloadAction);
         });
       },
-      onToggleFilter: () => {
+      onHandleExplicitRequestSort: (property: string, sortOrder: "asc" | "desc") => {
+        dispatch((dispatch: Dispatch) => {
+          dispatch(new RequestExplicitSortAction(property, sortOrder));
+          (!skipRefresh) && dispatch(reloadAction);
+        });
+      },
+      onToggleFilter: (refresh?: boolean) => {
         dispatch((dispatch: Dispatch, getAppState: () => IApplicationStoreState) => {
           const { showFilter } = selectState(getAppState());
           dispatch(new SetShowFilterAction(!showFilter));
-          (!skipRefresh) && dispatch(reloadAction);
+          if (!skipRefresh && (refresh === undefined || refresh))
+            dispatch(reloadAction);
         });
       },
       onFilterChanged: (property: string, filterTerm: string) => {
diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/treeView.tsx b/sdnr/wt/odlux/framework/src/components/material-ui/treeView.tsx
index 98ee291..1bb4936 100644
--- a/sdnr/wt/odlux/framework/src/components/material-ui/treeView.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-ui/treeView.tsx
@@ -37,6 +37,11 @@
   }
 });
 
+export enum SearchMode {
+  OnKeyDown = 1,
+  OnEnter =2
+}
+
 export type TreeItem<TData = { }> = {
   disabled?: boolean;
   icon?: React.ComponentType<SvgIconProps>;
@@ -47,13 +52,19 @@
   value?: TData;
 }
 
+export type ExternalTreeItem<TData = {}> = TreeItem<TData> & {
+  isMatch?: boolean;
+}
+
+
 type TreeViewComponentState<TData = { }> = {
   /** All indices of all expanded Items */
-  expandedItems: TreeItem<TData>[];
+  expandedItems: ExternalTreeItem<TData>[];
   /** The index of the active iten or undefined if no item is active. */
-  activeItem: undefined | TreeItem<TData>;
+  activeItem?: ExternalTreeItem<TData>;
   /** The search term or undefined if search is corrently not active. */
-  searchTerm: undefined | string;
+  searchTerm?: string;
+  searchTermValue?: string;
 }
 
 type TreeViewComponentBaseProps<TData = {}> = WithTheme & WithStyles<typeof styles> & {
@@ -65,6 +76,7 @@
   style?: React.CSSProperties;
   itemHeight?: number;
   depthOffset?: number;
+  searchMode?: SearchMode;
 }
 
 type TreeViewComponentWithInternalStateProps<TData = { }> = TreeViewComponentBaseProps<TData> & {
@@ -72,7 +84,17 @@
   onFolderClick?: (item: TreeItem<TData>) => void;
 }
 
-type TreeViewComponentWithExternalStateProps<TData = { }> = TreeViewComponentBaseProps<TData> & TreeViewComponentState<TData> & {
+type TreeViewComponentWithExternalSearchProps<TData = {}> = TreeViewComponentBaseProps<TData> & {
+  items: ExternalTreeItem<TData>[];
+  searchTerm: string;
+  onSearch: (searchTerm: string) => void;
+  onItemClick?: (item: TreeItem<TData>) => void;
+  onFolderClick?: (item: TreeItem<TData>) => void;
+}
+
+type TreeViewComponentWithExternalStateProps<TData = {}> = TreeViewComponentBaseProps<TData> & TreeViewComponentState<TData> & {
+  items: ExternalTreeItem<TData>[];
+  searchTerm: string;
   onSearch: (searchTerm: string) => void;
   onItemClick: (item: TreeItem<TData>) => void;
   onFolderClick: (item: TreeItem<TData>) => void;
@@ -80,14 +102,25 @@
 
 type TreeViewComponentProps<TData = { }> =
   | TreeViewComponentWithInternalStateProps<TData>
+  | TreeViewComponentWithExternalSearchProps<TData>
   | TreeViewComponentWithExternalStateProps<TData>;
 
+function isTreeViewComponentWithExternalSearchProps(props: TreeViewComponentProps): props is TreeViewComponentWithExternalSearchProps {
+  const propsWithExternalState = (props as TreeViewComponentWithExternalStateProps)
+  return (
+    propsWithExternalState.onSearch instanceof Function &&
+    propsWithExternalState.onFolderClick === undefined &&
+    propsWithExternalState.expandedItems === undefined &&
+    propsWithExternalState.searchTerm !== undefined
+  );
+}
+
 function isTreeViewComponentWithExternalStateProps(props: TreeViewComponentProps): props is TreeViewComponentWithExternalStateProps {
   const propsWithExternalState = (props as TreeViewComponentWithExternalStateProps)
   return (
-    propsWithExternalState.onSearch instanceof Function ||
-    propsWithExternalState.expandedItems !== undefined ||
-    propsWithExternalState.activeItem !== undefined ||
+    propsWithExternalState.onSearch instanceof Function &&
+    propsWithExternalState.onFolderClick instanceof Function &&
+    propsWithExternalState.expandedItems !== undefined &&
     propsWithExternalState.searchTerm !== undefined
   );
 }
@@ -103,19 +136,20 @@
     this.state = {
       expandedItems: [],
       activeItem: undefined,
-      searchTerm: undefined
+      searchTerm: undefined,
+      searchTermValue: undefined
     };
   }
 
   render(): JSX.Element {
     this.itemIndex = 0;
-    const { searchTerm } = this.state;
+    const { searchTerm , searchTermValue} = this.state;
     const { children, items, enableSearchBar } = this.props;
 
     return (
       <div className={this.props.className ? `${this.props.classes.root} ${this.props.className}` : this.props.classes.root} style={this.props.style}>
         {children}
-        {enableSearchBar && <TextField label={"Search"} fullWidth={true} className={ this.props.classes.search } value={searchTerm} onChange={this.onChangeSearchText} /> || null}
+        {enableSearchBar && <TextField label={"Search"} fullWidth={true} className={this.props.classes.search} value={searchTermValue} onKeyDown={this.onSearchKeyDown} onChange={this.onChangeSearchText} /> || null}
         <List>
           {this.renderItems(items, searchTerm && searchTerm.toLowerCase())}
         </List>
@@ -124,20 +158,21 @@
   }
 
   private itemIndex: number = 0;
-  private renderItems = (items: TreeItem<TData>[], searchTerm: string | undefined, depth: number = 1) => {
+  private renderItems = (items: TreeItem<TData>[], searchTerm: string | undefined, depth: number = 1, forceRender: boolean = true) => {
+
     return items.reduce((acc, item) => {
 
       const children = item.children; // this.props.childrenProperty && ((item as any)[this.props.childrenProperty] as TData[]);
-      const childrenJsx = children && this.renderItems(children, searchTerm, depth + 1);
+      const childrenJsx = children && this.renderItems(children, searchTerm, depth + 1, this.state.expandedItems.indexOf(item) > -1);
 
-      const expanded = searchTerm
+      const expanded = !isTreeViewComponentWithExternalStateProps(this.props) && searchTerm
         ? childrenJsx && childrenJsx.length > 0
         : !children
           ? false
           : this.state.expandedItems.indexOf(item) > -1;
       const isFolder = children !== undefined;
 
-      const itemJsx = this.renderItem(item, searchTerm, depth, isFolder, expanded || false);
+      const itemJsx = this.renderItem(item, searchTerm, depth, isFolder, expanded || false, forceRender);
       itemJsx && acc.push(itemJsx);
 
       if (isFolder && expanded && childrenJsx) {
@@ -147,7 +182,7 @@
 
     }, [] as JSX.Element[]);
   }
-  private renderItem = (item: TreeItem<TData>, searchTerm: string | undefined, depth: number, isFolder: boolean, expanded: boolean): JSX.Element | null => {
+  private renderItem = (item: ExternalTreeItem<TData>, searchTerm: string | undefined, depth: number, isFolder: boolean, expanded: boolean, forceRender: boolean): JSX.Element | null => {
     const styles = {
       item: {
         paddingLeft: (((this.props.depthOffset || 0) + depth) * this.props.theme.spacing(3)),
@@ -175,7 +210,7 @@
       }
     };
 
-    return ((searchTerm && (matchIndex > -1 || expanded) || !searchTerm)
+    return ((searchTerm && (matchIndex > -1 || expanded || (!isTreeViewComponentWithExternalStateProps(this.props) && item.isMatch || depth === 1)) || !searchTerm || forceRender)
       ? (
         <ListItem key={`tree-list-${this.itemIndex++}`} style={styles.item} onClick={handleClickCreator(false)} button >
 
@@ -186,7 +221,7 @@
 
           { // highlight search result
             matchIndex > -1
-              ? (<span>
+              ? <ListItemText className={item.contentClass} primary={(<span>
                 {text.substring(0, matchIndex)}
                 <span
                   style={{
@@ -198,8 +233,15 @@
                   {text.substring(matchIndex, matchIndex + searchTermLength)}
                 </span>
                 {text.substring(matchIndex + searchTermLength)}
-              </span>)
-              : (<ListItemText className={ item.contentClass } primary={text} />)
+              </span>)} />
+              : <ListItemText className={item.contentClass} primary={(
+                <span style={item.isMatch ? {
+                  display: 'inline-block',
+                  backgroundColor: 'rgba(255,235,59,0.5)',
+                  padding: '3px',
+                } : undefined}>
+                  {text} </span>
+              )} />
           }
 
           { // display the right icon, depending on the state
@@ -235,16 +277,39 @@
     });
   };
 
+  private onSearchKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
+    const enterMode = this.props.searchMode === SearchMode.OnEnter;
+
+    if (enterMode && event.keyCode === 13) {
+      event.preventDefault();
+      event.stopPropagation();
+
+      enterMode && this.setState({
+        searchTerm: this.state.searchTermValue
+      });
+
+      if (isTreeViewComponentWithExternalSearchProps(this.props) || isTreeViewComponentWithExternalStateProps(this.props)) {
+        this.props.onSearch(this.state.searchTermValue || "");
+      }
+    }
+  }
+
   private onChangeSearchText = (event: React.ChangeEvent<HTMLInputElement>) => {
     event.preventDefault();
     event.stopPropagation();
 
-    if (isTreeViewComponentWithExternalStateProps(this.props)) {
-      this.props.onSearch(event.target.value)
-    } else {
-      this.setState({
-        searchTerm: event.target.value
-      });
+    const keyDownMode = (!this.props.searchMode || this.props.searchMode === SearchMode.OnKeyDown);
+
+    this.setState(keyDownMode
+      ? {
+        searchTerm: event.target.value,
+        searchTermValue: event.target.value,
+      } as any : {
+        searchTermValue: event.target.value,
+      }) as any;
+
+    if ((isTreeViewComponentWithExternalSearchProps(this.props) || isTreeViewComponentWithExternalStateProps(this.props)) && keyDownMode) {
+      this.props.onSearch(event.target.value);
     }
   };
 
@@ -256,6 +321,11 @@
         activeItem: props.activeItem,
         searchTerm: props.searchTerm
       };
+    } else if (isTreeViewComponentWithExternalSearchProps(props)) {
+      return {
+        ...state,
+        searchTerm: props.searchTerm,
+      };
     }
     return state;
   }
diff --git a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
index 233c2fd..620abd7 100644
--- a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
+++ b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
@@ -29,18 +29,22 @@
 

 import ListItemLink from '../components/material-ui/listItemLink';

 

-import connect, { Connect, IDispatcher } from '../flux/connect';

+import connect, { Connect } from '../flux/connect';

 import { MenuAction } from '../actions/menuAction';

 import * as classNames from 'classnames';

 

+

 const drawerWidth = 240;

 

+const extraLinks = (window as any)._odluxExtraLinks as [string, string][];

+

 const styles = (theme: Theme) => createStyles({

   drawerPaper: {

     position: 'relative',

     width: drawerWidth,

   },

   toolbar: theme.mixins.toolbar as any,

+

   drawerOpen: {

     width: drawerWidth,

     transition: theme.transitions.create('width', {

@@ -58,7 +62,24 @@
     [theme.breakpoints.up('sm')]: {

       width: theme.spacing(9) + 1,

     },

-  }

+  },

+  drawer: {

+

+  },

+  menu: {

+    flex: "1 0 0%",

+  },

+  optLinks: {

+    borderTop: "2px solid #cfcfcf",

+    display: "flex",

+    flexDirection: "row",

+    flexWrap: "wrap",

+    justifyContent: "space-around"

+  },

+  link: {

+    margin: theme.spacing(1)+1,

+    fontSize: theme.typography.fontSize-2,

+  },

 });

 

 const tabletWidthBreakpoint = 768;

@@ -103,7 +124,7 @@
     <Drawer

       variant="permanent"

       className={

-        classNames({

+        classNames(classes.drawer, {

           [classes.drawerOpen]: isOpen,

           [classes.drawerClose]: !isOpen

         })

@@ -115,7 +136,7 @@
       {user && user.isValid && <>

         <div className={classes.toolbar} />

         { /* https://fiffty.github.io/react-treeview-mui/ */}

-        <List component="nav">

+        <List className={classes.menu} component="nav">

           <ListItemLink exact to="/" primary="Home" icon={<FontAwesomeIcon icon={faHome} />} />

           <Divider />

           {

@@ -141,6 +162,9 @@
             : null

           }

         </List>

+        {isOpen && extraLinks && <div className={classes.optLinks}>

+          {extraLinks.map(linkInfo => (<a className={classes.link} href={linkInfo[1]}>{linkInfo[0]}</a>))}

+        </div> || null}

       </> || null

       }

     </Drawer>)

diff --git a/sdnr/wt/odlux/framework/src/index.dev.html b/sdnr/wt/odlux/framework/src/index.dev.html
index 71cb740..240da26 100644
--- a/sdnr/wt/odlux/framework/src/index.dev.html
+++ b/sdnr/wt/odlux/framework/src/index.dev.html
@@ -13,6 +13,12 @@
   <div id="app"></div>
   <script type="text/javascript" src="./require.js"></script>
   <script type="text/javascript" src="./config.js"></script>
+  <script >
+    //  window._odluxExtraLinks = [
+    //    ["imprint","https://some-url-to-imprint"],
+    //    ["privacy declaration", "https://some-url-to-privacy-declaration"]
+    //  ]
+  </script>
   <script>
     // run the application
     require(["app" /*,"connectApp","inventoryApp","faultApp","helpApp"*/], function (app,connectApp,inventoryApp,faultApp,helpApp) {
diff --git a/sdnr/wt/odlux/framework/src/views/about.tsx b/sdnr/wt/odlux/framework/src/views/about.tsx
index 59a7151..db04117 100644
--- a/sdnr/wt/odlux/framework/src/views/about.tsx
+++ b/sdnr/wt/odlux/framework/src/views/about.tsx
@@ -64,15 +64,19 @@
 
     const className = "about-table"
     const style: React.CSSProperties = {};
+    const containerStyle = { overflow: "auto", paddingRight: "20px" }
 
     const html = (marked(this.state.content || 'loading', { renderer: markedOptions && markedOptions.renderer || defaultRenderer }));
 
     return (
-      <div
-        dangerouslySetInnerHTML={{ __html: html }}
-        className={className}
-        style={style}
-      />
+      <div style={containerStyle}>
+        <div
+          dangerouslySetInnerHTML={{ __html: html }}
+          className={className}
+          style={style}
+        />
+      </div>
+
 
     );
   }
diff --git a/sdnr/wt/odlux/framework/src/views/frame.tsx b/sdnr/wt/odlux/framework/src/views/frame.tsx
index f2f6f66..5218975 100644
--- a/sdnr/wt/odlux/framework/src/views/frame.tsx
+++ b/sdnr/wt/odlux/framework/src/views/frame.tsx
@@ -114,5 +114,4 @@
 }

 

 export const Frame = withStyles(styles)(FrameComponent);

-

 export default Frame;