Persist User preference widths for MCTSplitPane

Fix to Issue #1646
Persist MCTSplitPane widths to local storage, thus when user reloads, the Panes maintain dimensions
Use persisted widths if available otherwise use default
Add tests for localStorage and fix failing tests
This commit is contained in:
Deep Tailor 2017-08-02 11:12:09 -07:00 committed by Deep Tailor
parent 5ac377ec6a
commit 449923feae
7 changed files with 89 additions and 37 deletions

2
.gitignore vendored
View File

@ -36,3 +36,5 @@ protractor/logs
# npm-debug log
npm-debug.log
package-lock.json

View File

@ -26,7 +26,7 @@
ng-controller="PaneController as modelPaneTree"
ng-class="modelPaneTree.visible() ? 'pane-tree-showing' : 'pane-tree-hidden'">
<mct-split-pane class='abs contents'
anchor='left'>
anchor='left' alias="leftSide">
<div class='split-pane-component treeview pane left'>
<div class="abs holder l-flex-col holder-treeview-elements">
<mct-representation key="'create-button'"
@ -60,7 +60,7 @@
ng-controller="InspectorPaneController as modelPaneInspect"
ng-class="modelPaneInspect.visible() ? 'pane-inspect-showing' : 'pane-inspect-hidden'">
<mct-split-pane class='l-object-and-inspector contents abs' anchor='right'>
<mct-split-pane class='l-object-and-inspector contents abs' anchor='right' alias="rightSide">
<div class='split-pane-component t-object pane primary-pane left'>
<mct-representation mct-object="navigatedObject"
key="navigatedObject.getCapability('status').get('editing') ? 'edit-object' : 'browse-object'"
@ -87,4 +87,3 @@
</div>
<mct-include key="'bottombar'"></mct-include>
</div>

View File

@ -374,7 +374,8 @@ define([
"depends": [
"$parse",
"$log",
"$interval"
"$interval",
"$window"
]
},
{

View File

@ -93,13 +93,21 @@ define(
* @memberof platform/commonUI/general
* @constructor
*/
function MCTSplitPane($parse, $log, $interval) {
function MCTSplitPane($parse, $log, $interval, $window) {
function controller($scope, $element, $attrs) {
var anchorKey = $attrs.anchor || DEFAULT_ANCHOR,
positionParsed = $parse($attrs.position),
anchor,
activeInterval,
positionParsed = $parse($attrs.position),
position; // Start undefined, until explicitly set
position,
splitterSize,
alias = $attrs.alias !== undefined ?
"mctSplitPane-" + $attrs.alias : undefined,
//convert string to number from localStorage
userWidthPreference = $window.localStorage.getItem(alias) === null ?
undefined : Number($window.localStorage.getItem(alias));
// Get relevant size (height or width) of DOM element
function getSize(domElement) {
@ -114,11 +122,11 @@ define(
var first = children.eq(anchor.reversed ? 2 : 0),
splitter = children.eq(1),
last = children.eq(anchor.reversed ? 0 : 2),
splitterSize = getSize(splitter[0]),
firstSize;
splitterSize = getSize(splitter[0]);
first.css(anchor.edge, "0px");
first.css(anchor.dimension, (position - splitterSize) + 'px');
first.css(anchor.dimension, (userWidthPreference || position) + 'px');
// Get actual size (to obey min-width etc.)
firstSize = getSize(first[0]);
@ -126,9 +134,8 @@ define(
splitter.css(anchor.edge, firstSize + 'px');
splitter.css(anchor.opposite, "auto");
last.css(anchor.edge, (firstSize + splitterSize) + 'px');
last.css(anchor.edge, firstSize + splitterSize + 'px');
last.css(anchor.opposite, "0px");
position = firstSize + splitterSize;
}
@ -169,6 +176,17 @@ define(
return position;
}
function setUserWidthPreference(value) {
userWidthPreference = value - splitterSize;
}
function persistToLocalStorage(value) {
if (alias) {
userWidthPreference = value - splitterSize;
$window.localStorage.setItem(alias, userWidthPreference);
}
}
// Dynamically apply a CSS class to elements when the user
// is actively resizing
function toggleClass(classToToggle) {
@ -196,18 +214,31 @@ define(
activeInterval = $interval(function () {
getSetPosition(getSetPosition());
}, POLLING_INTERVAL, 0, false);
// ...and stop polling when we're destroyed.
$scope.$on('$destroy', function () {
$interval.cancel(activeInterval);
});
// Interface exposed by controller, for mct-splitter to user
return {
position: getSetPosition,
toggleClass: toggleClass,
anchor: function () {
return anchor;
},
position: function (value) {
if (arguments.length > 0) {
setUserWidthPreference(value);
return getSetPosition(value);
} else {
return getSetPosition();
}
},
startResizing: function () {
toggleClass('resizing');
},
endResizing: function (finalPosition) {
persistToLocalStorage(finalPosition);
toggleClass('resizing');
}
};
}
@ -219,9 +250,7 @@ define(
controller: ['$scope', '$element', '$attrs', controller]
};
}
return MCTSplitPane;
}
);

View File

@ -37,15 +37,16 @@ define(
*/
function MCTSplitter() {
function link(scope, element, attrs, mctSplitPane) {
var initialPosition;
var initialPosition,
newPosition;
element.addClass("splitter");
scope.splitter = {
// Begin moving this splitter
startMove: function () {
mctSplitPane.startResizing();
initialPosition = mctSplitPane.position();
mctSplitPane.toggleClass('resizing');
},
// Handle user changes to splitter position
move: function (delta) {
@ -55,12 +56,13 @@ define(
(anchor.reversed ? -1 : 1);
// Update the position of this splitter
mctSplitPane.position(initialPosition + pixelDelta);
newPosition = initialPosition + pixelDelta;
mctSplitPane.position(newPosition);
},
// Grab the event when the user is done moving
// the splitter and pass it on
endMove: function () {
mctSplitPane.toggleClass('resizing');
mctSplitPane.endResizing(newPosition);
}
};
}
@ -83,4 +85,3 @@ define(
}
);

View File

@ -38,7 +38,8 @@ define(
mockLog,
mockInterval,
mockParsed,
mctSplitPane;
mctSplitPane,
mockWindow = {};
beforeEach(function () {
mockParse = jasmine.createSpy('$parse');
@ -48,13 +49,23 @@ define(
mockInterval.cancel = jasmine.createSpy('mockCancel');
mockParsed = jasmine.createSpy('parsed');
mockParsed.assign = jasmine.createSpy('assign');
mockParse.andReturn(mockParsed);
mockWindow.localStorage = {
store: {},
setItem: function (key, value) {
this.store[key] = value;
},
getItem: function (key) {
return this.store[key];
}
};
mctSplitPane = new MCTSplitPane(
mockParse,
mockLog,
mockInterval
mockInterval,
mockWindow
);
});
@ -85,7 +96,7 @@ define(
jasmine.createSpyObj('$scope', ['$apply', '$watch', '$on']);
mockElement =
jasmine.createSpyObj('element', JQLITE_METHODS);
testAttrs = {};
testAttrs = {alias: 'rightSide'};
mockChildren =
jasmine.createSpyObj('children', JQLITE_METHODS);
mockFirstPane =
@ -142,10 +153,14 @@ define(
});
});
it("allows classes to be toggled on contained elements", function () {
controller.toggleClass('resizing');
expect(mockChildren.toggleClass)
.toHaveBeenCalledWith('resizing');
it("applies resizing class to children when resizing", function () {
controller.startResizing();
expect(mockChildren.toggleClass).toHaveBeenCalledWith('resizing');
});
it("removes resizing class from children when resizing action ends", function () {
controller.endResizing(0);
expect(mockChildren.toggleClass).toHaveBeenCalledWith('resizing');
});
it("allows positions to be set", function () {
@ -198,6 +213,12 @@ define(
fireOn('$destroy');
expect(mockInterval.cancel).toHaveBeenCalled();
});
it("saves user preference to localStorage when user is done resizing", function () {
controller.endResizing(100);
expect(Number(mockWindow.localStorage.getItem('mctSplitPane-rightSide'))).toEqual(100 - mockSplitter[0].offsetWidth);
});
});
});

View File

@ -57,7 +57,7 @@ define(
testAttrs = {};
mockSplitPane = jasmine.createSpyObj(
'mctSplitPane',
['position', 'toggleClass', 'anchor']
['position', 'startResizing', 'endResizing', 'anchor']
);
mctSplitter.link(
@ -86,9 +86,9 @@ define(
mockScope.splitter.startMove();
});
it("adds a 'resizing' class", function () {
expect(mockSplitPane.toggleClass)
.toHaveBeenCalledWith('resizing');
it("tell's the splitter when it is resizing", function () {
expect(mockSplitPane.startResizing)
.toHaveBeenCalled();
});
it("repositions during drag", function () {
@ -97,11 +97,10 @@ define(
.toHaveBeenCalledWith(testPosition + 10);
});
it("removes the 'resizing' class when finished", function () {
mockSplitPane.toggleClass.reset();
it("tell's the splitter when it is done resizing", function () {
mockScope.splitter.move([10,0]);
mockScope.splitter.endMove();
expect(mockSplitPane.toggleClass)
.toHaveBeenCalledWith('resizing');
expect(mockSplitPane.endResizing).toHaveBeenCalledWith(testPosition + 10);
});
});