Merge pull request #986 from prymitive/window-resize

fix(ui): better handling of window resize events
This commit is contained in:
Łukasz Mierzwa
2019-10-02 19:56:25 +01:00
committed by GitHub
4 changed files with 96 additions and 97 deletions

View File

@@ -19,7 +19,7 @@ const GetColumnsCount = (canvasWidth, baseWidth) =>
.map(gs => gs.columns)
.pop();
const GetGridElementWidth = (canvasWidth, baseWidth) =>
Math.floor(canvasWidth / GetColumnsCount(canvasWidth, baseWidth));
const GetGridElementWidth = (innerWidth, outerWidth, baseWidth) =>
Math.floor(innerWidth / GetColumnsCount(outerWidth, baseWidth));
export { GridSizesConfig, GetColumnsCount, GetGridElementWidth };

View File

@@ -39,61 +39,38 @@ const AlertGrid = observer(
// and group size
this.viewport = observable(
{
width: document.body.clientWidth,
widthAdjust: 0,
widthHistory: [],
update(width, height) {
if (
this.widthHistory.length === 4 &&
this.widthHistory[0] !== this.widthHistory[1] &&
this.widthHistory[0] !== this.widthHistory[3]
) {
const uniqueWidths = this.widthHistory.reduce((uniques, w) => {
const count = uniques[w] || 0;
uniques[w] = count + 1;
return uniques;
}, {});
if (
this.widthHistory.includes(width) &&
Object.keys(uniqueWidths).length === 2 &&
Object.values(uniqueWidths)[0] ===
Object.values(uniqueWidths)[1]
) {
this.widthAdjust = Math.min(this.widthAdjust + 20, 200);
} else {
this.widthAdjust = 0;
}
}
this.width = width;
this.widthHistory.unshift(width);
this.widthHistory = this.widthHistory.slice(0, 4);
canvasWidth: document.body.clientWidth,
windowWidth: window.innerWidth,
updateWidths(canvasWidth, windowWidth) {
this.canvasWidth = canvasWidth;
this.windowWidth = windowWidth;
},
get gridSizesConfig() {
return GridSizesConfig(
this.width,
props.settingsStore.gridConfig.config.groupWidth +
this.widthAdjust
this.windowWidth,
props.settingsStore.gridConfig.config.groupWidth
);
},
get groupWidth() {
return GetGridElementWidth(
this.width,
props.settingsStore.gridConfig.config.groupWidth +
this.widthAdjust
this.canvasWidth,
this.windowWidth,
props.settingsStore.gridConfig.config.groupWidth
);
}
},
{
update: action.bound,
updateWidths: action.bound,
gridSizesConfig: computed,
groupWidth: computed
}
);
}
handleResize = debounce(() => {
this.viewport.updateWidths(document.body.clientWidth, window.innerWidth);
}, 100);
// store reference to generated masonry component so we can call it
// to repack the grid after any component was re-rendered, which could
// alter its size breaking grid layout
@@ -153,6 +130,12 @@ const AlertGrid = observer(
// wait up to 30s, run no-op function on timeout
font.load(null, 30000).then(this.masonryRepack, () => {});
}
window.addEventListener("resize", this.handleResize);
}
componentWillUnmount() {
window.removeEventListener("resize", this.handleResize);
}
render() {
@@ -163,13 +146,9 @@ const AlertGrid = observer(
<ReactResizeDetector
handleWidth
handleHeight
onResize={debounce(this.viewport.update, 100)}
onResize={debounce(this.handleResize, 100)}
/>
<MasonryInfiniteScroller
key={
settingsStore.gridConfig.config.groupWidth +
this.viewport.widthAdjust
}
ref={this.storeMasonryRef}
position={false}
pack={true}

View File

@@ -75,10 +75,10 @@ const MockGroupList = (count, alertPerGroup) => {
alertStore.data.groups = groups;
};
const VerifyColumnCount = (innerWidth, columns) => {
const VerifyColumnCount = (innerWidth, outerWidth, columns) => {
MockGroupList(60, 5);
const tree = ShallowAlertGrid();
tree.instance().viewport.update(innerWidth, 500);
tree.instance().viewport.updateWidths(innerWidth, outerWidth);
expect(
tree
.find("AlertGroup")
@@ -167,22 +167,22 @@ describe("<AlertGrid />", () => {
// known breakpoints calculated from GridSize logic
[
{ breakpoint: 400, columns: 1 },
{ breakpoint: 800, columns: 2 },
{ breakpoint: 1200, columns: 3 },
{ breakpoint: 1600, columns: 4 },
{ breakpoint: 2000, columns: 5 },
{ breakpoint: 2400, columns: 6 },
{ breakpoint: 3000, columns: 7 },
{ breakpoint: 3400, columns: 8 },
{ breakpoint: 3800, columns: 9 },
{ breakpoint: 4200, columns: 10 }
{ canvas: 400, columns: 1 },
{ canvas: 800, columns: 2 },
{ canvas: 1200, columns: 3 },
{ canvas: 1600, columns: 4 },
{ canvas: 2000, columns: 5 },
{ canvas: 2400, columns: 6 },
{ canvas: 2800, columns: 7 },
{ canvas: 3200, columns: 8 },
{ canvas: 3600, columns: 9 },
{ canvas: 4000, columns: 10 }
].map(t =>
it(`renders ${t.columns} column(s) on ${t.breakpoint} breakpoint`, () => {
it(`renders ${t.columns} column(s) on ${t.canvas} breakpoint`, () => {
settingsStore.gridConfig.config.groupWidth = 400;
VerifyColumnCount(t.canvas - 1, Math.max(1, t.columns - 1));
VerifyColumnCount(t.canvas, t.columns);
VerifyColumnCount(t.canvas + 1, t.columns);
VerifyColumnCount(t.canvas - 1, t.canvas - 1, Math.max(1, t.columns - 1));
VerifyColumnCount(t.canvas, t.canvas, t.columns);
VerifyColumnCount(t.canvas + 1, t.canvas + 1, t.columns);
})
);
@@ -202,7 +202,7 @@ describe("<AlertGrid />", () => {
].map(t =>
it(`renders ${t.columns} column(s) with ${t.canvas} resolution`, () => {
settingsStore.gridConfig.config.groupWidth = 400;
VerifyColumnCount(t.canvas, t.columns);
VerifyColumnCount(t.canvas, t.canvas, t.columns);
})
);
@@ -211,7 +211,7 @@ describe("<AlertGrid />", () => {
let lastColumns = 1;
for (let i = 100; i <= 4096; i++) {
const expectedColumns = Math.max(Math.floor(i / minWidth), 1);
const columns = Math.floor(i / GetGridElementWidth(i, minWidth));
const columns = Math.floor(i / GetGridElementWidth(i, i, minWidth));
expect({
resolution: i,
@@ -234,7 +234,7 @@ describe("<AlertGrid />", () => {
MockGroupList(60, 5);
const tree = ShallowAlertGrid();
// set initial width
tree.instance().viewport.update(1980, 500);
tree.instance().viewport.updateWidths(1980, 1980);
expect(
tree
.find("AlertGroup")
@@ -243,7 +243,7 @@ describe("<AlertGrid />", () => {
).toBe(1980 / 4);
// then resize and verify if column count was changed
tree.instance().viewport.update(1000, 500);
tree.instance().viewport.updateWidths(1000, 1000);
expect(
tree
.find("AlertGroup")
@@ -252,14 +252,50 @@ describe("<AlertGrid />", () => {
).toBe(1000 / 2);
});
it("scrollbar render doesn't resize alert groups", () => {
settingsStore.gridConfig.config.groupWidth = 400;
MockGroupList(60, 5);
const tree = ShallowAlertGrid();
// set initial width
tree.instance().viewport.updateWidths(1600, 1600);
expect(
tree
.find("AlertGroup")
.at(0)
.props().style.width
).toBe(400);
// then resize and verify if column count was changed
tree.instance().viewport.updateWidths(1584, 1600);
expect(
tree
.find("AlertGroup")
.at(0)
.props().style.width
).toBe(396);
});
it("window resize event calls updateWidths", () => {
MockGroupList(60, 5);
const tree = ShallowAlertGrid();
const instance = tree.instance();
const updateWidthsSpy = jest.spyOn(instance.viewport, "updateWidths");
global.dispatchEvent(new Event("resize"));
expect(updateWidthsSpy).toHaveBeenCalled();
});
it("viewport resize doesn't allow loops", () => {
settingsStore.gridConfig.config.groupWidth = 410;
settingsStore.gridConfig.config.groupWidth = 400;
const tree = ShallowAlertGrid();
let results = [];
for (var index = 0; index < 14; index++) {
MockGroupList(60, 5);
tree.instance().viewport.update(index % 2 === 0 ? 800 : 830, 500);
tree
.instance()
.viewport.updateWidths(index % 2 === 0 ? 1600 : 1584, 1600);
results.push(
tree
.find("AlertGroup")
@@ -268,37 +304,22 @@ describe("<AlertGrid />", () => {
);
}
// first 4 results will switch beween 1 and 2 columns, but after than it
// should stabilise and return same result as the grid width
expect(results).toStrictEqual([
800,
415,
800,
415,
800,
830,
800,
830,
800,
830,
800,
830,
800,
830
400,
396,
400,
396,
400,
396,
400,
396,
400,
396,
400,
396,
400,
396
]);
// so now let's call it without any loop
results = [];
for (let width of [840, 820, 450, 450, 1200]) {
tree.instance().viewport.update(width, 500);
results.push(
tree
.find("AlertGroup")
.at(0)
.props().style.width
);
}
expect(results).toStrictEqual([420, 410, 450, 450, 600]);
});
it("doesn't crash on unmount", () => {

View File

@@ -73,7 +73,6 @@ const MockGroup = (groupName, alertCount, active, suppressed, unprocessed) => {
};
storiesOf("Grid", module)
.addDecorator(storyFn => <div className="p-2">{storyFn()}</div>)
.add("UpstreamError", () => {
return <UpstreamError name="am1" message="Something failed" />;
})