Merge remote-tracking branch 'upstream/master' into feature/layer-list-ui-handle

Conflicts:
	src/styles/_layer.scss
This commit is contained in:
orangemug 2018-11-02 17:57:42 +00:00
commit b712e7f184
15 changed files with 110 additions and 49 deletions

View file

@ -18,7 +18,6 @@ A free and open visual editor for the [Mapbox GL styles](https://www.mapbox.com/
targeted at developers and map designers. targeted at developers and map designers.
- :link: Design your maps online at **<https://maputnik.github.io/editor/>** (all in local storage) - :link: Design your maps online at **<https://maputnik.github.io/editor/>** (all in local storage)
- :link: Use the [Maputnik CLI](https://github.com/maputnik/editor/wiki/Maputnik-CLI) for local style development
Mapbox has built one of the best and most amazing OSS ecosystems. A key component to ensure its longevity and independance is an OSS map designer. Mapbox has built one of the best and most amazing OSS ecosystems. A key component to ensure its longevity and independance is an OSS map designer.
@ -40,10 +39,7 @@ The documentation can be found in the [Wiki](https://github.com/maputnik/editor/
Maputnik is written in ES6 and is using [React](https://github.com/facebook/react) and [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/api/). Maputnik is written in ES6 and is using [React](https://github.com/facebook/react) and [Mapbox GL JS](https://www.mapbox.com/mapbox-gl-js/api/).
We ensure building and developing Maputnik works with We ensure building and developing Maputnik works with the [current active LTS Node.js version and above](https://github.com/nodejs/Release#release-schedule).
- Linux, OSX and Windows
- Node >4
Install the deps, start the dev server and open the web browser on `http://localhost:8888/`. Install the deps, start the dev server and open the web browser on `http://localhost:8888/`.
@ -79,7 +75,7 @@ For testing we use [webdriverio](http://webdriver.io) and [selenium-standalone](
[selenium-standalone](https://github.com/vvo/selenium-standalone) starts a server that will launch browsers on your local machine. We use chrome so you **must** have chrome installed on your machine. [selenium-standalone](https://github.com/vvo/selenium-standalone) starts a server that will launch browsers on your local machine. We use chrome so you **must** have chrome installed on your machine.
Now open and terminal and run the following. This will install the drivers on your local machine Now open a terminal and run the following. This will install the drivers on your local machine
``` ```
./node_modules/.bin/selenium-standalone install ./node_modules/.bin/selenium-standalone install

View file

@ -2,7 +2,6 @@ image: Visual Studio 2017
environment: environment:
matrix: matrix:
- nodejs_version: "8" - nodejs_version: "8"
- nodejs_version: "9"
- nodejs_version: "10" - nodejs_version: "10"
platform: platform:
- x86 - x86

View file

@ -23,7 +23,7 @@ import SurveyModal from './modals/SurveyModal'
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata' import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
import {latest, validate} from '@mapbox/mapbox-gl-style-spec' import {latest, validate} from '@mapbox/mapbox-gl-style-spec'
import style from '../libs/style' import style from '../libs/style'
import { initialStyleUrl, loadStyleUrl } from '../libs/urlopen' import { initialStyleUrl, loadStyleUrl, removeStyleQuerystring } from '../libs/urlopen'
import { undoMessages, redoMessages } from '../libs/diffmessage' import { undoMessages, redoMessages } from '../libs/diffmessage'
import { StyleStore } from '../libs/stylestore' import { StyleStore } from '../libs/stylestore'
import { ApiStyleStore } from '../libs/apistore' import { ApiStyleStore } from '../libs/apistore'
@ -74,56 +74,47 @@ export default class App extends React.Component {
}) })
const keyCodes = {
"esc": 27,
"?": 191,
"o": 79,
"e": 69,
"s": 83,
"d": 68,
"i": 73,
"m": 77,
}
const shortcuts = [ const shortcuts = [
{ {
keyCode: keyCodes["?"], key: "?",
handler: () => { handler: () => {
this.toggleModal("shortcuts"); this.toggleModal("shortcuts");
} }
}, },
{ {
keyCode: keyCodes["o"], key: "o",
handler: () => { handler: () => {
this.toggleModal("open"); this.toggleModal("open");
} }
}, },
{ {
keyCode: keyCodes["e"], key: "e",
handler: () => { handler: () => {
this.toggleModal("export"); this.toggleModal("export");
} }
}, },
{ {
keyCode: keyCodes["d"], key: "d",
handler: () => { handler: () => {
this.toggleModal("sources"); this.toggleModal("sources");
} }
}, },
{ {
keyCode: keyCodes["s"], key: "s",
handler: () => { handler: () => {
this.toggleModal("settings"); this.toggleModal("settings");
} }
}, },
{ {
keyCode: keyCodes["i"], key: "i",
handler: () => { handler: () => {
this.setMapState("inspect"); this.setMapState(
this.state.mapState === "map" ? "inspect" : "map"
);
} }
}, },
{ {
keyCode: keyCodes["m"], key: "m",
handler: () => { handler: () => {
document.querySelector(".mapboxgl-canvas").focus(); document.querySelector(".mapboxgl-canvas").focus();
} }
@ -131,26 +122,31 @@ export default class App extends React.Component {
] ]
document.body.addEventListener("keyup", (e) => { document.body.addEventListener("keyup", (e) => {
if(e.keyCode === keyCodes["esc"]) { if(e.key === "Escape") {
e.target.blur(); e.target.blur();
document.body.focus(); document.body.focus();
} }
else if(document.activeElement === document.body) { else if(this.state.isOpen.shortcuts || document.activeElement === document.body) {
const shortcut = shortcuts.find((shortcut) => { const shortcut = shortcuts.find((shortcut) => {
return (shortcut.keyCode === e.keyCode) return (shortcut.key === e.key)
}) })
if(shortcut) { if(shortcut) {
this.setModal("shortcuts", false);
shortcut.handler(e); shortcut.handler(e);
} }
} }
}) })
const styleUrl = initialStyleUrl() const styleUrl = initialStyleUrl()
if(styleUrl) { if(styleUrl && window.confirm("Load style from URL: " + styleUrl + " and discard current changes?")) {
this.styleStore = new StyleStore() this.styleStore = new StyleStore()
loadStyleUrl(styleUrl, mapStyle => this.onStyleChanged(mapStyle)) loadStyleUrl(styleUrl, mapStyle => this.onStyleChanged(mapStyle))
removeStyleQuerystring()
} else { } else {
if(styleUrl) {
removeStyleQuerystring()
}
this.styleStore.init(err => { this.styleStore.init(err => {
if(err) { if(err) {
console.log('Falling back to local storage for storing styles') console.log('Falling back to local storage for storing styles')
@ -191,7 +187,8 @@ export default class App extends React.Component {
}, },
mapOptions: { mapOptions: {
showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries"), showTileBoundaries: queryUtil.asBool(queryObj, "show-tile-boundaries"),
showCollisionBoxes: queryUtil.asBool(queryObj, "show-collision-boxes") showCollisionBoxes: queryUtil.asBool(queryObj, "show-collision-boxes"),
showOverdrawInspector: queryUtil.asBool(queryObj, "show-overdraw-inspector")
}, },
} }
@ -484,17 +481,21 @@ export default class App extends React.Component {
this.setState({ selectedLayerIndex: idx }) this.setState({ selectedLayerIndex: idx })
} }
toggleModal(modalName) { setModal(modalName, value) {
if(modalName === 'survey' && value === false) {
localStorage.setItem('survey', '');
}
this.setState({ this.setState({
isOpen: { isOpen: {
...this.state.isOpen, ...this.state.isOpen,
[modalName]: !this.state.isOpen[modalName] [modalName]: value
} }
}) })
if(modalName === 'survey') {
localStorage.setItem('survey', '');
} }
toggleModal(modalName) {
this.setModal(modalName, !this.state.isOpen[modalName]);
} }
render() { render() {
@ -549,6 +550,7 @@ export default class App extends React.Component {
const modals = <div> const modals = <div>
<ShortcutsModal <ShortcutsModal
ref={(el) => this.shortcutEl = el}
isOpen={this.state.isOpen.shortcuts} isOpen={this.state.isOpen.shortcuts}
onOpenToggle={this.toggleModal.bind(this, 'shortcuts')} onOpenToggle={this.toggleModal.bind(this, 'shortcuts')}
/> />

View file

@ -54,7 +54,8 @@ class AutocompleteInput extends React.Component {
menuStyle={{ menuStyle={{
position: "fixed", position: "fixed",
overflow: "auto", overflow: "auto",
maxHeight: this.state.maxHeight maxHeight: this.state.maxHeight,
zIndex: '998'
}} }}
wrapperProps={{ wrapperProps={{
className: "maputnik-autocomplete", className: "maputnik-autocomplete",

View file

@ -204,6 +204,7 @@ export default class LayerList extends React.Component {
render() { render() {
return <LayerListContainerSortable return <LayerListContainerSortable
{...this.props} {...this.props}
helperClass='sortableHelper'
onSortEnd={this.props.onMoveLayer.bind(this)} onSortEnd={this.props.onMoveLayer.bind(this)}
useDragHandle={true} useDragHandle={true}
/> />

View file

@ -114,6 +114,7 @@ export default class MapboxGlMap extends React.Component {
if (map) { if (map) {
map.showTileBoundaries = this.props.options.showTileBoundaries; map.showTileBoundaries = this.props.options.showTileBoundaries;
map.showCollisionBoxes = this.props.options.showCollisionBoxes; map.showCollisionBoxes = this.props.options.showCollisionBoxes;
map.showOverdrawInspector = this.props.options.showOverdrawInspector;
} }
} }
@ -131,6 +132,7 @@ export default class MapboxGlMap extends React.Component {
map.showTileBoundaries = mapOpts.showTileBoundaries; map.showTileBoundaries = mapOpts.showTileBoundaries;
map.showCollisionBoxes = mapOpts.showCollisionBoxes; map.showCollisionBoxes = mapOpts.showCollisionBoxes;
map.showOverdrawInspector = mapOpts.showOverdrawInspector;
const zoom = new ZoomControl; const zoom = new ZoomControl;
map.addControl(zoom, 'top-right'); map.addControl(zoom, 'top-right');

View file

@ -1,4 +1,5 @@
import url from 'url' import url from 'url'
import querystring from 'querystring'
import style from './style.js' import style from './style.js'
export function initialStyleUrl() { export function initialStyleUrl() {
@ -24,6 +25,23 @@ export function loadStyleUrl(styleUrl, cb) {
}) })
} }
export function removeStyleQuerystring() {
const initialUrl = url.parse(window.location.href, true)
let qs = querystring.parse(window.location.search.slice(1))
delete qs["style"]
if(Object.getOwnPropertyNames(qs).length === 0) {
qs = ""
} else {
qs = "?" + querystring.stringify(qs)
}
let newUrlHash = initialUrl.hash
if(newUrlHash === null) {
newUrlHash = ""
}
const newUrl = initialUrl.protocol + "//" + initialUrl.host + initialUrl.pathname + qs + newUrlHash
window.history.replaceState({}, document.title, newUrl)
}
export function loadJSON(url, defaultValue, cb) { export function loadJSON(url, defaultValue, cb) {
fetch(url, { fetch(url, {
mode: 'cors', mode: 'cors',

View file

@ -206,6 +206,11 @@
.more-menu { .more-menu {
position: relative; position: relative;
svg {
width: 22px;
height: 22px;
}
&__menu { &__menu {
position: absolute; position: absolute;
z-index: 9999; z-index: 9999;
@ -241,3 +246,9 @@
} }
} }
// Clone of the element which is sorted
.sortableHelper {
font-family: $font-family;
z-index: 9999;
border: none;
}

View file

@ -100,10 +100,18 @@
background: inherit; background: inherit;
border-width: 0; border-width: 0;
@extend .maputnik-toolbar-link; @extend .maputnik-toolbar-link;
}
.maputnik-toolbar-select select { select {
// HACK: <https://github.com/maputnik/editor/pull/392#issuecomment-427595172>
color: $color-black !important;
margin-left: 4px; margin-left: 4px;
border-width: 0;
option {
// HACK: <https://github.com/maputnik/editor/pull/392#issuecomment-427595172>
color: $color-black !important;
}
}
} }
.maputnik-icon-text { .maputnik-icon-text {

View file

@ -13,6 +13,7 @@ describe.skip("history", function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example" "geojson:example"
])); ]));
browser.alertAccept();
helper.modal.addLayer.open(); helper.modal.addLayer.open();

View file

@ -11,6 +11,7 @@ describe('maputnik', function() {
"geojson:example", "geojson:example",
"raster:raster" "raster:raster"
])); ]));
browser.alertAccept();
browser.execute(function() { browser.execute(function() {
localStorage.setItem("survey", true); localStorage.setItem("survey", true);
}); });

View file

@ -11,6 +11,7 @@ describe("layers", function() {
"geojson:example", "geojson:example",
"raster:raster" "raster:raster"
])); ]));
browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link"); browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates(); browser.flushReactUpdates();
@ -449,6 +450,7 @@ describe("layers", function() {
browser.url(config.baseUrl+"?debug&style="+getStyleUrl([ browser.url(config.baseUrl+"?debug&style="+getStyleUrl([
"geojson:example" "geojson:example"
])); ]));
browser.alertAccept();
helper.modal.addLayer.open(); helper.modal.addLayer.open();
var aId = helper.modal.addLayer.fill({ var aId = helper.modal.addLayer.fill({

View file

@ -9,6 +9,7 @@ describe("map", function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example" "geojson:example"
])+"#"+zoomLevel+"/41.3805/2.1635"); ])+"#"+zoomLevel+"/41.3805/2.1635");
browser.alertAccept();
browser.waitUntil(function () { browser.waitUntil(function () {
return ( return (
@ -22,6 +23,7 @@ describe("map", function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example" "geojson:example"
])+"#"+zoomLevel+"/41.3805/2.1635"); ])+"#"+zoomLevel+"/41.3805/2.1635");
browser.alertAccept();
browser.click(".mapboxgl-ctrl-zoom-in") browser.click(".mapboxgl-ctrl-zoom-in")
browser.waitUntil(function () { browser.waitUntil(function () {

View file

@ -99,6 +99,7 @@ describe("modals", function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example" "geojson:example"
])); ]));
browser.alertAccept();
browser.selectByValue(wd.$("nav:inspect", "select"), "inspect"); browser.selectByValue(wd.$("nav:inspect", "select"), "inspect");
}) })
@ -161,7 +162,7 @@ describe("modals", function() {
}) })
}) })
it("open map tiles access token", function() { it("maptiler access token", function() {
var apiKey = "testing123"; var apiKey = "testing123";
browser.setValueSafe(wd.$("modal-settings.maputnik:openmaptiles_access_token"), apiKey); browser.setValueSafe(wd.$("modal-settings.maputnik:openmaptiles_access_token"), apiKey);
browser.click(wd.$("modal-settings.name")) browser.click(wd.$("modal-settings.name"))
@ -171,14 +172,24 @@ describe("modals", function() {
assert.equal(styleObj.metadata["maputnik:openmaptiles_access_token"], apiKey); assert.equal(styleObj.metadata["maputnik:openmaptiles_access_token"], apiKey);
}) })
it.skip("style renderer", function() { it("thunderforest access token", function() {
var selector = wd.$("modal-settings.maputnik:renderer"); var apiKey = "testing123";
browser.selectByValue(selector, "ol3"); browser.setValueSafe(wd.$("modal-settings.maputnik:thunderforest_access_token"), apiKey);
browser.click(wd.$("modal-settings.name")) browser.click(wd.$("modal-settings.name"))
browser.flushReactUpdates(); browser.flushReactUpdates();
var styleObj = helper.getStyleStore(browser); var styleObj = helper.getStyleStore(browser);
assert.equal(styleObj.metadata["maputnik:renderer"], "ol3"); assert.equal(styleObj.metadata["maputnik:thunderforest_access_token"], apiKey);
})
it("style renderer", function() {
var selector = wd.$("modal-settings.maputnik:renderer");
browser.selectByValue(selector, "ol");
browser.click(wd.$("modal-settings.name"))
browser.flushReactUpdates();
var styleObj = helper.getStyleStore(browser);
assert.equal(styleObj.metadata["maputnik:renderer"], "ol");
}) })
}) })

View file

@ -18,6 +18,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example" "geojson:example"
])); ]));
browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link"); browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates(); browser.flushReactUpdates();
@ -28,6 +29,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example" "geojson:example"
])); ]));
browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link"); browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates(); browser.flushReactUpdates();
@ -41,6 +43,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example" "geojson:example"
])); ]));
browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link"); browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates(); browser.flushReactUpdates();
@ -54,6 +57,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example" "geojson:example"
])); ]));
browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link"); browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates(); browser.flushReactUpdates();
@ -67,6 +71,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example" "geojson:example"
])); ]));
browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link"); browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates(); browser.flushReactUpdates();
@ -80,6 +85,7 @@ describe('screenshots', function() {
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
"geojson:example" "geojson:example"
])); ]));
browser.alertAccept();
browser.waitForExist(".maputnik-toolbar-link"); browser.waitForExist(".maputnik-toolbar-link");
browser.flushReactUpdates(); browser.flushReactUpdates();