mirror of
https://github.com/visgl/react-map-gl.git
synced 2025-12-08 20:16:02 +00:00
Avoid mutating style objects during normalization (#926)
This commit is contained in:
parent
d1cb92dffd
commit
f5011fe40d
2
.nycrc
2
.nycrc
@ -1,6 +1,6 @@
|
||||
{
|
||||
"extends": "node_modules/ocular-dev-tools/templates/.nycrc",
|
||||
"include": [
|
||||
"dist/es5/**/*.js"
|
||||
"src/**/*.js"
|
||||
]
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@
|
||||
"flow-bin": "^0.98.1",
|
||||
"immutable": "^3.8.2",
|
||||
"jsdom": "^15.0.0",
|
||||
"ocular-dev-tools": "0.0.26",
|
||||
"ocular-dev-tools": "0.0.29",
|
||||
"pre-commit": "^1.2.2",
|
||||
"react": "^16.3.0",
|
||||
"react-dom": "^16.3.0",
|
||||
|
||||
@ -6,6 +6,8 @@ type MapboxStyle =
|
||||
layers: Array<any>
|
||||
};
|
||||
|
||||
const refProps = ['type', 'source', 'source-layer', 'minzoom', 'maxzoom', 'filter', 'layout'];
|
||||
|
||||
// Prepare a map style object for diffing
|
||||
// If immutable - convert to plain object
|
||||
// Work around some issues in the styles that would fail Mapbox's diffing
|
||||
@ -19,46 +21,37 @@ export function normalizeStyle(style: ?MapboxStyle): ?MapboxStyle {
|
||||
if (style.toJS) {
|
||||
style = style.toJS();
|
||||
}
|
||||
const layerIndex = style.layers.reduce(
|
||||
(accum, current) => Object.assign(accum, {[current.id]: current}),
|
||||
{}
|
||||
);
|
||||
const layerIndex = {};
|
||||
|
||||
style.layers = style.layers.map(layer => {
|
||||
layer = Object.assign({}, layer);
|
||||
|
||||
// Breaks style diffing :(
|
||||
delete layer.interactive;
|
||||
for (const layer of style.layers) {
|
||||
layerIndex[layer.id] = layer;
|
||||
}
|
||||
|
||||
const layers = style.layers.map(layer => {
|
||||
const layerRef = layerIndex[layer.ref];
|
||||
let normalizedLayer = null;
|
||||
|
||||
if ('interactive' in layer) {
|
||||
normalizedLayer = {...layer};
|
||||
// Breaks style diffing :(
|
||||
delete normalizedLayer.interactive;
|
||||
}
|
||||
|
||||
// Style diffing doesn't work with refs so expand them out manually before diffing.
|
||||
if (layerRef) {
|
||||
delete layer.ref;
|
||||
if (layerRef.type !== undefined) {
|
||||
layer.type = layerRef.type;
|
||||
}
|
||||
if (layerRef.source !== undefined) {
|
||||
layer.source = layerRef.source;
|
||||
}
|
||||
if (layerRef['source-layer'] !== undefined) {
|
||||
layer['source-layer'] = layerRef['source-layer'];
|
||||
}
|
||||
if (layerRef.minzoom !== undefined) {
|
||||
layer.minzoom = layerRef.minzoom;
|
||||
}
|
||||
if (layerRef.maxzoom !== undefined) {
|
||||
layer.maxzoom = layerRef.maxzoom;
|
||||
}
|
||||
if (layerRef.filter !== undefined) {
|
||||
layer.filter = layerRef.filter;
|
||||
}
|
||||
if (layerRef.layout !== undefined) {
|
||||
layer.layout = layer.layout || {};
|
||||
layer.layout = Object.assign({}, layer.layout, layerRef.layout);
|
||||
normalizedLayer = normalizedLayer || {...layer};
|
||||
delete normalizedLayer.ref;
|
||||
// https://github.com/mapbox/mapbox-gl-js/blob/master/src/style-spec/deref.js
|
||||
for (const propName of refProps) {
|
||||
if (propName in layerRef) {
|
||||
normalizedLayer[propName] = layerRef[propName];
|
||||
}
|
||||
}
|
||||
}
|
||||
return layer;
|
||||
|
||||
return normalizedLayer || layer;
|
||||
});
|
||||
|
||||
return style;
|
||||
// Do not mutate the style object provided by the user
|
||||
return {...style, layers};
|
||||
}
|
||||
|
||||
@ -5,3 +5,4 @@ import './dynamic-position.spec';
|
||||
import './transition-manager.spec';
|
||||
import './debounce.spec';
|
||||
import './deep-equal.spec';
|
||||
import './style-utils.spec';
|
||||
|
||||
182
test/src/utils/style-utils.spec.js
Normal file
182
test/src/utils/style-utils.spec.js
Normal file
@ -0,0 +1,182 @@
|
||||
import test from 'tape-catch';
|
||||
import {fromJS} from 'immutable';
|
||||
|
||||
import {normalizeStyle} from 'react-map-gl/utils/style-utils';
|
||||
|
||||
const testStyle = {
|
||||
version: 8,
|
||||
name: 'Test',
|
||||
sources: {
|
||||
mapbox: {
|
||||
url: 'mapbox://mapbox.mapbox-streets-v7',
|
||||
type: 'vector'
|
||||
}
|
||||
},
|
||||
sprite: 'mapbox://sprites/mapbox/basic-v8',
|
||||
glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
|
||||
layers: [
|
||||
{
|
||||
id: 'background',
|
||||
type: 'background',
|
||||
paint: {
|
||||
'background-color': '#dedede'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'park',
|
||||
type: 'fill',
|
||||
source: 'mapbox',
|
||||
'source-layer': 'landuse_overlay',
|
||||
filter: ['==', 'class', 'park'],
|
||||
paint: {
|
||||
'fill-color': '#d2edae',
|
||||
'fill-opacity': 0.75
|
||||
},
|
||||
interactive: true
|
||||
},
|
||||
{
|
||||
id: 'road',
|
||||
source: 'mapbox',
|
||||
'source-layer': 'road',
|
||||
layout: {
|
||||
'line-cap': 'butt',
|
||||
'line-join': 'miter'
|
||||
},
|
||||
filter: ['all', ['==', '$type', 'LineString']],
|
||||
type: 'line',
|
||||
paint: {
|
||||
'line-color': '#efefef',
|
||||
'line-width': {
|
||||
base: 1.55,
|
||||
stops: [[4, 0.25], [20, 30]]
|
||||
}
|
||||
},
|
||||
minzoom: 5,
|
||||
maxzoom: 20,
|
||||
interactive: true
|
||||
},
|
||||
{
|
||||
id: 'park-2',
|
||||
ref: 'park',
|
||||
paint: {
|
||||
'fill-color': '#00f080',
|
||||
'fill-opacity': 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'road-outline',
|
||||
ref: 'road',
|
||||
minzoom: 10,
|
||||
maxzoom: 12,
|
||||
paint: {
|
||||
'line-color': '#efefef',
|
||||
'line-width': {
|
||||
base: 2,
|
||||
stops: [[4, 0.5], [20, 40]]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const expectedStyle = {
|
||||
version: 8,
|
||||
name: 'Test',
|
||||
sources: {
|
||||
mapbox: {
|
||||
url: 'mapbox://mapbox.mapbox-streets-v7',
|
||||
type: 'vector'
|
||||
}
|
||||
},
|
||||
sprite: 'mapbox://sprites/mapbox/basic-v8',
|
||||
glyphs: 'mapbox://fonts/mapbox/{fontstack}/{range}.pbf',
|
||||
layers: [
|
||||
{
|
||||
id: 'background',
|
||||
type: 'background',
|
||||
paint: {
|
||||
'background-color': '#dedede'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'park',
|
||||
type: 'fill',
|
||||
source: 'mapbox',
|
||||
'source-layer': 'landuse_overlay',
|
||||
filter: ['==', 'class', 'park'],
|
||||
paint: {
|
||||
'fill-color': '#d2edae',
|
||||
'fill-opacity': 0.75
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'road',
|
||||
source: 'mapbox',
|
||||
'source-layer': 'road',
|
||||
layout: {
|
||||
'line-cap': 'butt',
|
||||
'line-join': 'miter'
|
||||
},
|
||||
filter: ['all', ['==', '$type', 'LineString']],
|
||||
type: 'line',
|
||||
paint: {
|
||||
'line-color': '#efefef',
|
||||
'line-width': {
|
||||
base: 1.55,
|
||||
stops: [[4, 0.25], [20, 30]]
|
||||
}
|
||||
},
|
||||
minzoom: 5,
|
||||
maxzoom: 20
|
||||
},
|
||||
{
|
||||
id: 'park-2',
|
||||
type: 'fill',
|
||||
source: 'mapbox',
|
||||
'source-layer': 'landuse_overlay',
|
||||
filter: ['==', 'class', 'park'],
|
||||
paint: {
|
||||
'fill-color': '#00f080',
|
||||
'fill-opacity': 0.5
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'road-outline',
|
||||
source: 'mapbox',
|
||||
'source-layer': 'road',
|
||||
layout: {
|
||||
'line-cap': 'butt',
|
||||
'line-join': 'miter'
|
||||
},
|
||||
filter: ['all', ['==', '$type', 'LineString']],
|
||||
type: 'line',
|
||||
minzoom: 5,
|
||||
maxzoom: 20,
|
||||
paint: {
|
||||
'line-color': '#efefef',
|
||||
'line-width': {
|
||||
base: 2,
|
||||
stops: [[4, 0.5], [20, 40]]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
test('normalizeStyle', t => {
|
||||
t.is(normalizeStyle(null), null, 'Handles null');
|
||||
t.is(
|
||||
normalizeStyle('mapbox://styles/mapbox/light-v9'),
|
||||
'mapbox://styles/mapbox/light-v9',
|
||||
'Handles url string'
|
||||
);
|
||||
|
||||
let result = normalizeStyle(testStyle);
|
||||
t.notEqual(result, testStyle, 'style is not mutated');
|
||||
t.deepEqual(result, expectedStyle, 'plain object style is normalized');
|
||||
|
||||
result = normalizeStyle(fromJS(testStyle));
|
||||
t.deepEqual(result, expectedStyle, 'immutable style is normalized');
|
||||
|
||||
t.end();
|
||||
});
|
||||
11
yarn.lock
11
yarn.lock
@ -7069,10 +7069,10 @@ octokit-pagination-methods@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4"
|
||||
integrity sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==
|
||||
|
||||
ocular-dev-tools@0.0.26:
|
||||
version "0.0.26"
|
||||
resolved "https://registry.yarnpkg.com/ocular-dev-tools/-/ocular-dev-tools-0.0.26.tgz#6e40920dc3c19276f876f723a5b3e41154b1cc4c"
|
||||
integrity sha512-BqX46H/+Ux7TN/eyMcZKrs30UZfGQHrDF+BihHDxk6XiY5MhVzIhBdOgj8V3uQHnDOd719bVm/l4FXHHT8mxrw==
|
||||
ocular-dev-tools@0.0.29:
|
||||
version "0.0.29"
|
||||
resolved "https://registry.yarnpkg.com/ocular-dev-tools/-/ocular-dev-tools-0.0.29.tgz#7492858c6582ae1772a82725ade6d3918e8cb1ce"
|
||||
integrity sha512-fis0mt4nL/6xUdEbAkihw0yNN2lONgcxM0W3mE+a79xAy+syEW/adZqeagdCtXfACspb3CX5F5R5ULbJituzWA==
|
||||
dependencies:
|
||||
"@babel/cli" "^7.0.0"
|
||||
"@babel/core" "^7.0.0"
|
||||
@ -7084,8 +7084,11 @@ ocular-dev-tools@0.0.26:
|
||||
eslint "^4.13.1"
|
||||
eslint-config-prettier "^2.9.0"
|
||||
eslint-config-uber-es2015 "^3.0.0"
|
||||
handlebars "^4.1.2"
|
||||
html-webpack-plugin "^3.2.0"
|
||||
lerna "^3.14.1"
|
||||
lodash "^4.17.13"
|
||||
lodash.template "^4.5.0"
|
||||
module-alias "^2.0.0"
|
||||
nyc "^13.3.0"
|
||||
prettier "1.14.3"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user