ONOS-4733: Simplified Settings Table view (removed redundant data)
- Now shows just simple class name for component
- Removed default value column
- Values that are *not* the same as the default are highlighted (emboldened)
- Table rows now selectable and show details panel
- panel shows component fully qualified class name
- panel currently read-only
- in future, user will be able to change the property value from here
Change-Id: I01a2af727dcfad82c6b7d2378701a5cb3e24e43a
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/SettingsViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/SettingsViewMessageHandler.java
index e83dad7..3ca6e88 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/SettingsViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/SettingsViewMessageHandler.java
@@ -36,14 +36,17 @@
private static final String SETTINGS = "settings";
private static final String COMPONENT = "component";
+ private static final String FQ_COMPONENT = "fqComponent";
private static final String ID = "id";
private static final String TYPE = "type";
private static final String VALUE = "value";
private static final String DEFAULT = "defValue";
private static final String DESC = "desc";
+ private static final char DOT = '.';
+
private static final String[] COL_IDS = {
- COMPONENT, ID, TYPE, VALUE, DEFAULT, DESC
+ COMPONENT, FQ_COMPONENT, ID, TYPE, VALUE, DEFAULT, DESC
};
@Override
@@ -90,13 +93,25 @@
}
}
- private void populateRow(TableModel.Row row, String component, ConfigProperty prop) {
- row.cell(COMPONENT, component)
+ private void populateRow(TableModel.Row row, String fqComp,
+ ConfigProperty prop) {
+ row.cell(COMPONENT, simpleName(fqComp))
+ .cell(FQ_COMPONENT, fqComp)
.cell(ID, prop.name())
- .cell(TYPE, prop.type().toString().toLowerCase())
+ .cell(TYPE, typeName(prop))
.cell(VALUE, prop.value())
.cell(DEFAULT, prop.defaultValue())
.cell(DESC, prop.description());
}
+
+ // return just simple class name: "a.b.c.MyClass" -> "MyClass"
+ private String simpleName(String component) {
+ int lastDot = component.lastIndexOf(DOT);
+ return lastDot < 0 ? component : component.substring(lastDot + 1);
+ }
+
+ private String typeName(ConfigProperty prop) {
+ return prop.type().toString().toLowerCase();
+ }
}
}
diff --git a/web/gui/src/main/webapp/app/view/app/app.js b/web/gui/src/main/webapp/app/view/app/app.js
index 048a78c..83ab06e 100644
--- a/web/gui/src/main/webapp/app/view/app/app.js
+++ b/web/gui/src/main/webapp/app/view/app/app.js
@@ -470,7 +470,7 @@
});
ks.gestureNotes([
['click', 'Select a row to show application details'],
- ['scroll down', 'See more application']
+ ['scroll down', 'See more applications']
]);
// if the panelData changes
diff --git a/web/gui/src/main/webapp/app/view/settings/settings.css b/web/gui/src/main/webapp/app/view/settings/settings.css
index ba3ca3d..4ddf491 100644
--- a/web/gui/src/main/webapp/app/view/settings/settings.css
+++ b/web/gui/src/main/webapp/app/view/settings/settings.css
@@ -25,3 +25,41 @@
#ov-settings div.ctrl-btns {
width: 45px;
}
+
+#ov-settings div.summary-list td.notdef {
+ color: #0071bd;
+ font-weight: bold;
+}
+
+#settings-details-panel .container {
+ padding: 0 30px;
+ overflow-y: scroll;
+}
+
+#settings-details-panel .close-btn {
+ position: absolute;
+ right: 26px;
+ top: 26px;
+ cursor: pointer;
+}
+
+#settings-details-panel .top .settings-title {
+ width: 90%;
+ height: 62px;
+ font-size: 20pt;
+ font-weight: lighter;
+ overflow: hidden;
+ white-space: nowrap;
+ padding: 0 12px 0 2px;
+ margin-top: 19px;
+ margin-bottom: 5px;
+}
+
+#settings-details-panel td.label {
+ font-weight: bold;
+ text-align: right;
+ font-size: 12pt;
+ padding-right: 6px;
+ vertical-align: top;
+ width: 120px;
+}
diff --git a/web/gui/src/main/webapp/app/view/settings/settings.html b/web/gui/src/main/webapp/app/view/settings/settings.html
index fef2ffe..7903977 100644
--- a/web/gui/src/main/webapp/app/view/settings/settings.html
+++ b/web/gui/src/main/webapp/app/view/settings/settings.html
@@ -14,11 +14,10 @@
<div class="table-header" onos-sortable-header>
<table>
<tr>
- <td colId="component" sortable col-width="300px">Component </td>
- <td colId="id" sortable col-width="240px">Property </td>
+ <td colId="component" sortable col-width="260px">Component </td>
+ <td colId="id" sortable col-width="260px">Property </td>
<td colId="type" sortable col-width="100px">Type </td>
<td colId="value" sortable col-width="100px">Value </td>
- <td colId="defValue" sortable col-width="100px">Default </td>
<td colId="desc">Description </td>
</tr>
</table>
@@ -33,17 +32,21 @@
</tr>
<tr ng-repeat="prop in tableData track by $index"
+ ng-click="selectCallback($event, prop)"
+ ng-class="{selected: prop.id === selId}"
ng-repeat-complete row-id="{{prop.id}}">
<td>{{prop.component}}</td>
<td>{{prop.id}}</td>
<td>{{prop.type}}</td>
- <td>{{prop.value}}</td>
- <td>{{prop.defValue}}</td>
+ <td ng-class="{notdef: prop.value !== prop.defValue}">
+ {{prop.value}}
+ </td>
<td>{{prop.desc}}</td>
</tr>
</table>
</div>
</div>
+ <settings-details-panel></settings-details-panel>
</div>
diff --git a/web/gui/src/main/webapp/app/view/settings/settings.js b/web/gui/src/main/webapp/app/view/settings/settings.js
index 00c102e..3197ff0 100644
--- a/web/gui/src/main/webapp/app/view/settings/settings.js
+++ b/web/gui/src/main/webapp/app/view/settings/settings.js
@@ -21,16 +21,215 @@
(function () {
'use strict';
- angular.module('ovSettings', [])
- .controller('OvSettingsCtrl',
- ['$log', '$scope', 'TableBuilderService',
+ // injected refs
+ var $log, $scope, wss, fs, ks, ps, is;
- function ($log, $scope, tbs) {
+ // internal state
+ var detailsPanel,
+ panelData,
+ top,
+ pStartY,
+ pHeight,
+ wSize = false;
+
+ // constants
+ var pName = 'settings-details-panel',
+ panelWidth = 540,
+ topPdg = 60,
+ propOrder = ['fqComponent', 'id', 'type', 'value', 'defValue', 'desc'],
+ friendlyProps = [
+ 'Component', 'Property', 'Type', 'Value', 'Default Value',
+ 'Description'
+ ];
+
+ function createDetailsPanel() {
+ detailsPanel = ps.createPanel(pName, {
+ width: wSize.width,
+ margin: 0,
+ hideMargin: 0
+ });
+
+ detailsPanel.el().style({
+ position: 'absolute',
+ top: pStartY + 'px'
+ });
+
+ detailsPanel.hide();
+ }
+
+ function closePanel() {
+ if (detailsPanel.isVisible()) {
+ $scope.selId = null;
+ panelData = null;
+ detailsPanel.hide();
+ return true;
+ }
+ return false;
+ }
+
+ function addCloseBtn(div) {
+ is.loadEmbeddedIcon(div, 'close', 26);
+ div.on('click', closePanel);
+ }
+
+ function setUpPanel() {
+ var container, closeBtn, div;
+
+ detailsPanel.empty();
+ detailsPanel.width(panelWidth);
+
+ container = detailsPanel.append('div').classed('container', true);
+
+ top = container.append('div').classed('top', true);
+ closeBtn = top.append('div').classed('close-btn', true);
+ addCloseBtn(closeBtn);
+
+ div = top.append('div').classed('top-content', true);
+
+ function ndiv(cls, addTable) {
+ var d = div.append('div').classed(cls, true);
+ if (addTable) {
+ d.append('table');
+ }
+ }
+
+ ndiv('settings-title');
+ ndiv('settings-props', true);
+ }
+
+ function addProp(tbody, index, value) {
+ var tr = tbody.append('tr');
+
+ function addCell(cls, txt) {
+ tr.append('td').attr('class', cls).html(txt);
+ }
+
+ addCell('label', friendlyProps[index] + ':');
+ addCell('value', value);
+ }
+
+ function populateTop(details) {
+ var propsBody = top.select('.settings-props').append('tbody');
+
+ top.select('.settings-title').text(details.id);
+
+ propOrder.forEach(function (prop, i) {
+ addProp(propsBody, i, details[prop]);
+ });
+ }
+
+ function populateDetails() {
+ setUpPanel();
+ populateTop(panelData);
+ detailsPanel.height(pHeight);
+ }
+
+ angular.module('ovSettings', [])
+ .controller('OvSettingsCtrl',
+ ['$log', '$scope',
+ 'WebSocketService', 'FnService', 'KeyService', 'PanelService',
+ 'IconService', 'TableBuilderService',
+
+ function (_$log_, _$scope_, _wss_, _fs_, _ks_, _ps_, _is_, tbs) {
+ $log = _$log_;
+ $scope = _$scope_;
+ wss = _wss_;
+ fs = _fs_;
+ ks = _ks_;
+ ps = _ps_;
+ is = _is_;
+ $scope.panelData = {};
+
+ function selCb($event, row) {
+ if ($scope.selId) {
+ panelData = row;
+ populateDetails();
+ detailsPanel.show();
+ } else {
+ panelData = null;
+ detailsPanel.hide();
+ }
+ }
+
tbs.buildTable({
scope: $scope,
- tag: 'setting'
+ tag: 'setting',
+ selCb: selCb
+ });
+
+ ks.keyBindings({
+ esc: [$scope.selectCallback, 'Deselect property'],
+ _helpFormat: ['esc']
+ });
+ ks.gestureNotes([
+ ['click row', 'Select / deselect settings property'],
+ ['scroll down', 'See more settings']
+ ]);
+
+ $scope.$on('$destroy', function () {
+ ks.unbindKeys();
});
$log.log('OvSettingsCtrl has been created');
- }]);
+ }])
+
+ .directive('settingsDetailsPanel',
+ ['$rootScope', '$window', '$timeout', 'KeyService',
+ function ($rootScope, $window, $timeout, ks) {
+ return function (scope) {
+ var unbindWatch;
+
+ function heightCalc() {
+ pStartY = fs.noPxStyle(d3.select('.tabular-header'), 'height')
+ + topPdg;
+ wSize = fs.windowSize(pStartY);
+ pHeight = wSize.height;
+ }
+
+ function initPanel() {
+ heightCalc();
+ createDetailsPanel();
+ $log.debug('start to initialize panel!');
+ }
+
+ // Safari has a bug where it renders the fixed-layout table wrong
+ // if you ask for the window's size too early
+ if (scope.onos.browser === 'safari') {
+ $timeout(initPanel);
+ } else {
+ initPanel();
+ }
+ // create key bindings to handle panel
+ ks.keyBindings({
+ esc: [closePanel, 'Close the details panel'],
+ _helpFormat: ['esc']
+ });
+ ks.gestureNotes([
+ ['click', 'Select a row to show property details'],
+ ['scroll down', 'See more properties']
+ ]);
+
+ // if the window size changes
+ unbindWatch = $rootScope.$watchCollection(
+ function () {
+ return {
+ h: $window.innerHeight,
+ w: $window.innerWidth
+ };
+ }, function () {
+ if (panelData) {
+ heightCalc();
+ populateDetails();
+ }
+ }
+ );
+
+ scope.$on('$destroy', function () {
+ panelData = null;
+ unbindWatch();
+ ks.unbindKeys();
+ ps.destroyPanel(pName);
+ });
+ };
+ }]);
}());