diff options
author | Aijana Schumann <aijana.schumann@highstreet-technologies.com> | 2020-03-13 10:39:11 +0100 |
---|---|---|
committer | Aijana S <aijana.schumann@highstreet-technologies.com> | 2020-03-13 10:14:15 +0000 |
commit | ce78a6683ab8648ed90e944cd23ed7c01205fce6 (patch) | |
tree | 42bac21bac743bdf889639c732aa70b2f747eb29 /sdnr/wt/odlux | |
parent | f3bd99fbe11c443966fff14c6990367b36bdedc4 (diff) |
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
Diffstat (limited to 'sdnr/wt/odlux')
21 files changed, 319 insertions, 91 deletions
diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx index c153ed5a1..503133b0f 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 @@ const ConnectedElementTable = MaterialTable as MaterialTableCtorType<NetworkElem 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 ad7b247b0..96f6c8a6b 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 @@ const ConnectionStatusTable = MaterialTable as MaterialTableCtorType<NetworkElem type ConnectionStatusLogComponentProps = Connect<typeof mapProps, typeof mapDispatch>; +let initialSorted = false; + + class ConnectionStatusLogComponent extends React.Component<ConnectionStatusLogComponentProps> { render(): JSX.Element { return ( @@ -46,6 +49,15 @@ class ConnectionStatusLogComponent extends React.Component<ConnectionStatusLogCo </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 d50a81ed5..53e10481a 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 @@ type NetworkElementsListComponentState = { } 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 @@ export class NetworkElementsListComponent extends React.Component<NetworkElement }; 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 a96d3d635..a7feae923 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 @@ class ConnectApplicationComponent extends React.Component<ConnectApplicationComp 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 5993bb5c4..aa9027206 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 @@ const mapDispatch = (dispatcher: IDispatcher) => ({ 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 @@ class EventLogComponent extends React.Component<Connect<typeof mapProps, typeof } 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 000000000..198976796 --- /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 a5cf928fc..e03d2b560 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 { IActionHandler } from '../../../../framework/src/flux/action'; 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 @@ const actionHandlers = { 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 666667e40..2056976d9 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx @@ -62,8 +62,12 @@ const FaultApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteCom 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 @@ export function register() { 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 ed395d2e4..10721549c 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 { PanelId } from '../models/panelId'; 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 @@ const mapDisp = (dispatcher: IDispatcher) => ({ 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 @@ type FaultApplicationState = { 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 @@ class FaultApplicationComponent extends React.Component<FaultApplicationComponen 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 @@ class FaultApplicationComponent extends React.Component<FaultApplicationComponen }; 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 b4de26d50..eab44b4ca 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 @@ const mapProps = (state: IApplicationStoreState) => ({ 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 @@ class HelpApplicationComponent extends React.Component<HelpApplicationComponentP 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 350bac221..f087ed2e4 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 MaintenenceViewComponentProps = Connect<typeof mapProps, typeof mapDispatch type MaintenenceViewComponentState = { maintenenceEntryToEdit: MaintenenceEntry; maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode; -} +}; + +let initialSorted = false; class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentProps, MaintenenceViewComponentState> { @@ -148,8 +150,15 @@ class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentP } 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 d422a0c3b..f96223297 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 @@ class MediatorApplicationComponent extends React.Component<MediatorApplicationCo 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 aaade65db..c16906ad0 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 @@ type MediatorServerSelectionComponentState = { mediatorServerEditorMode: EditMediatorServerDialogMode } +let initialSorted = false; + class MediatorServerSelectionComponent extends React.Component<MediatorServerSelectionComponentProps, MediatorServerSelectionComponentState> { constructor(props: MediatorServerSelectionComponentProps) { @@ -116,7 +118,13 @@ class MediatorServerSelectionComponent extends React.Component<MediatorServerSel } 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 967ca2a7e..b7ea291eb 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 a80a5a58d..3dfbe0b91 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 @@ type MaterialTableComponentBaseProps<TData> = WithStyles<typeof styles> & { tableId?: string; title?: string; stickyHeader?: boolean; + defaultSortOrder?: 'asc' | 'desc'; + defaultSortColumn?: keyof TData; enableSelection?: boolean; disableSorting?: boolean; disableFilter?: boolean; @@ -164,8 +166,8 @@ class MaterialTableComponent<TData extends {} = {}> extends React.Component<Mate 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 74682cd95..07ffe2ff5 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 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState } } + 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 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState 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 @@ export function createExternal<TData>(callback: DataCallback<TData>, selectState (!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 98ee291d1..1bb49367c 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 @@ const styles = (theme: Theme) => createStyles({ } }); +export enum SearchMode { + OnKeyDown = 1, + OnEnter =2 +} + export type TreeItem<TData = { }> = { disabled?: boolean; icon?: React.ComponentType<SvgIconProps>; @@ -47,13 +52,19 @@ export type TreeItem<TData = { }> = { 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 @@ type TreeViewComponentBaseProps<TData = {}> = WithTheme & WithStyles<typeof styl style?: React.CSSProperties; itemHeight?: number; depthOffset?: number; + searchMode?: SearchMode; } type TreeViewComponentWithInternalStateProps<TData = { }> = TreeViewComponentBaseProps<TData> & { @@ -72,7 +84,17 @@ type TreeViewComponentWithInternalStateProps<TData = { }> = TreeViewComponentBas 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 TreeViewComponentWithExternalStateProps<TData = { }> = TreeViewComponentBas 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 @@ class TreeViewComponent<TData = { }> extends React.Component<TreeViewComponentPr 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 @@ class TreeViewComponent<TData = { }> extends React.Component<TreeViewComponentPr } 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 @@ class TreeViewComponent<TData = { }> extends React.Component<TreeViewComponentPr }, [] 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 @@ class TreeViewComponent<TData = { }> extends React.Component<TreeViewComponentPr } }; - 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 @@ class TreeViewComponent<TData = { }> extends React.Component<TreeViewComponentPr { // highlight search result matchIndex > -1 - ? (<span> + ? <ListItemText className={item.contentClass} primary={(<span> {text.substring(0, matchIndex)} <span style={{ @@ -198,8 +233,15 @@ class TreeViewComponent<TData = { }> extends React.Component<TreeViewComponentPr {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 @@ class TreeViewComponent<TData = { }> extends React.Component<TreeViewComponentPr }); }; + 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 @@ class TreeViewComponent<TData = { }> extends React.Component<TreeViewComponentPr 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 233c2fd61..620abd708 100644 --- a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx +++ b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx @@ -29,18 +29,22 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; 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 @@ const styles = (theme: Theme) => createStyles({ [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 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di <Drawer
variant="permanent"
className={
- classNames({
+ classNames(classes.drawer, {
[classes.drawerOpen]: isOpen,
[classes.drawerClose]: !isOpen
})
@@ -115,7 +136,7 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di {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 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di : 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 71cb7408d..240da266d 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 59a71512c..db0411793 100644 --- a/sdnr/wt/odlux/framework/src/views/about.tsx +++ b/sdnr/wt/odlux/framework/src/views/about.tsx @@ -64,15 +64,19 @@ class AboutComponent extends React.Component<any, AboutState> { 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 f2f6f66cc..521897554 100644 --- a/sdnr/wt/odlux/framework/src/views/frame.tsx +++ b/sdnr/wt/odlux/framework/src/views/frame.tsx @@ -114,5 +114,4 @@ class FrameComponent extends React.Component<FrameProps>{ }
export const Frame = withStyles(styles)(FrameComponent);
-
export default Frame;
|