diff --git a/README.md b/README.md index 536b0802..073c1f39 100644 --- a/README.md +++ b/README.md @@ -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. - :link: Design your maps online at **** (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. @@ -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/). -We ensure building and developing Maputnik works with - -- Linux, OSX and Windows -- Node >4 +We ensure building and developing Maputnik works with the [current active LTS Node.js version and above](https://github.com/nodejs/Release#release-schedule). 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. -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 diff --git a/appveyor.yml b/appveyor.yml index 9d469f89..bfe10868 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,6 @@ image: Visual Studio 2017 environment: matrix: - nodejs_version: "8" - - nodejs_version: "9" - nodejs_version: "10" platform: - x86 diff --git a/src/components/App.jsx b/src/components/App.jsx index df8983ca..6fc5ce91 100644 --- a/src/components/App.jsx +++ b/src/components/App.jsx @@ -23,7 +23,7 @@ import SurveyModal from './modals/SurveyModal' import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata' import {latest, validate} from '@mapbox/mapbox-gl-style-spec' 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 { StyleStore } from '../libs/stylestore' import { ApiStyleStore } from '../libs/apistore' @@ -73,57 +73,48 @@ export default class App extends React.Component { onLocalStyleChange: mapStyle => this.onStyleChanged(mapStyle, false) }) - - const keyCodes = { - "esc": 27, - "?": 191, - "o": 79, - "e": 69, - "s": 83, - "d": 68, - "i": 73, - "m": 77, - } const shortcuts = [ { - keyCode: keyCodes["?"], + key: "?", handler: () => { this.toggleModal("shortcuts"); } }, { - keyCode: keyCodes["o"], + key: "o", handler: () => { this.toggleModal("open"); } }, { - keyCode: keyCodes["e"], + key: "e", handler: () => { this.toggleModal("export"); } }, { - keyCode: keyCodes["d"], + key: "d", handler: () => { this.toggleModal("sources"); } }, { - keyCode: keyCodes["s"], + key: "s", handler: () => { this.toggleModal("settings"); } }, { - keyCode: keyCodes["i"], + key: "i", handler: () => { - this.setMapState("inspect"); + this.setMapState( + this.state.mapState === "map" ? "inspect" : "map" + ); } }, { - keyCode: keyCodes["m"], + key: "m", handler: () => { document.querySelector(".mapboxgl-canvas").focus(); } @@ -131,26 +122,31 @@ export default class App extends React.Component { ] document.body.addEventListener("keyup", (e) => { - if(e.keyCode === keyCodes["esc"]) { + if(e.key === "Escape") { e.target.blur(); document.body.focus(); } - else if(document.activeElement === document.body) { + else if(this.state.isOpen.shortcuts || document.activeElement === document.body) { const shortcut = shortcuts.find((shortcut) => { - return (shortcut.keyCode === e.keyCode) + return (shortcut.key === e.key) }) if(shortcut) { + this.setModal("shortcuts", false); shortcut.handler(e); } } }) const styleUrl = initialStyleUrl() - if(styleUrl) { + if(styleUrl && window.confirm("Load style from URL: " + styleUrl + " and discard current changes?")) { this.styleStore = new StyleStore() loadStyleUrl(styleUrl, mapStyle => this.onStyleChanged(mapStyle)) + removeStyleQuerystring() } else { + if(styleUrl) { + removeStyleQuerystring() + } this.styleStore.init(err => { if(err) { console.log('Falling back to local storage for storing styles') @@ -191,7 +187,8 @@ export default class App extends React.Component { }, mapOptions: { 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 }) } - toggleModal(modalName) { + setModal(modalName, value) { + if(modalName === 'survey' && value === false) { + localStorage.setItem('survey', ''); + } + this.setState({ 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() { @@ -549,6 +550,7 @@ export default class App extends React.Component { const modals =
this.shortcutEl = el} isOpen={this.state.isOpen.shortcuts} onOpenToggle={this.toggleModal.bind(this, 'shortcuts')} /> diff --git a/src/components/inputs/AutocompleteInput.jsx b/src/components/inputs/AutocompleteInput.jsx index 70ac561f..8f4ed3e2 100644 --- a/src/components/inputs/AutocompleteInput.jsx +++ b/src/components/inputs/AutocompleteInput.jsx @@ -54,7 +54,8 @@ class AutocompleteInput extends React.Component { menuStyle={{ position: "fixed", overflow: "auto", - maxHeight: this.state.maxHeight + maxHeight: this.state.maxHeight, + zIndex: '998' }} wrapperProps={{ className: "maputnik-autocomplete", diff --git a/src/components/layers/LayerList.jsx b/src/components/layers/LayerList.jsx index 9f62856d..0b3af807 100644 --- a/src/components/layers/LayerList.jsx +++ b/src/components/layers/LayerList.jsx @@ -46,7 +46,7 @@ class LayerListContainer extends React.Component { areAllGroupsExpanded: false, isOpen: { add: false, - } + } } toggleModal(modalName) { @@ -66,12 +66,12 @@ class LayerListContainer extends React.Component { this.groupedLayers().forEach(layers => { const groupPrefix = layerPrefix(layers[0].id) const lookupKey = [groupPrefix, idx].join('-') - + if (layers.length > 1) { newGroups[lookupKey] = this.state.areAllGroupsExpanded } - + layers.forEach((layer) => { idx += 1 }) @@ -204,6 +204,7 @@ export default class LayerList extends React.Component { render() { return diff --git a/src/components/map/MapboxGlMap.jsx b/src/components/map/MapboxGlMap.jsx index 6236243b..dd4885f8 100644 --- a/src/components/map/MapboxGlMap.jsx +++ b/src/components/map/MapboxGlMap.jsx @@ -114,6 +114,7 @@ export default class MapboxGlMap extends React.Component { if (map) { map.showTileBoundaries = this.props.options.showTileBoundaries; 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.showCollisionBoxes = mapOpts.showCollisionBoxes; + map.showOverdrawInspector = mapOpts.showOverdrawInspector; const zoom = new ZoomControl; map.addControl(zoom, 'top-right'); diff --git a/src/libs/urlopen.js b/src/libs/urlopen.js index 20b7aa3b..885d3c3a 100644 --- a/src/libs/urlopen.js +++ b/src/libs/urlopen.js @@ -1,4 +1,5 @@ import url from 'url' +import querystring from 'querystring' import style from './style.js' 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) { fetch(url, { mode: 'cors', diff --git a/src/styles/_layer.scss b/src/styles/_layer.scss index e3b396b9..11c60fbc 100644 --- a/src/styles/_layer.scss +++ b/src/styles/_layer.scss @@ -206,6 +206,11 @@ .more-menu { position: relative; + svg { + width: 22px; + height: 22px; + } + &__menu { position: absolute; z-index: 9999; @@ -241,3 +246,9 @@ } } +// Clone of the element which is sorted +.sortableHelper { + font-family: $font-family; + z-index: 9999; + border: none; +} diff --git a/src/styles/_toolbar.scss b/src/styles/_toolbar.scss index 734c8ad0..e54c1e7e 100644 --- a/src/styles/_toolbar.scss +++ b/src/styles/_toolbar.scss @@ -100,10 +100,18 @@ background: inherit; border-width: 0; @extend .maputnik-toolbar-link; -} -.maputnik-toolbar-select select { - margin-left: 4px; + select { + // HACK: + color: $color-black !important; + margin-left: 4px; + border-width: 0; + + option { + // HACK: + color: $color-black !important; + } + } } .maputnik-icon-text { diff --git a/test/functional/history/index.js b/test/functional/history/index.js index e788e7e4..11cf8af9 100644 --- a/test/functional/history/index.js +++ b/test/functional/history/index.js @@ -13,6 +13,7 @@ describe.skip("history", function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); + browser.alertAccept(); helper.modal.addLayer.open(); diff --git a/test/functional/index.js b/test/functional/index.js index 3c9f5ec6..53e61958 100644 --- a/test/functional/index.js +++ b/test/functional/index.js @@ -11,6 +11,7 @@ describe('maputnik', function() { "geojson:example", "raster:raster" ])); + browser.alertAccept(); browser.execute(function() { localStorage.setItem("survey", true); }); diff --git a/test/functional/layers/index.js b/test/functional/layers/index.js index 012b6729..048a7951 100644 --- a/test/functional/layers/index.js +++ b/test/functional/layers/index.js @@ -11,6 +11,7 @@ describe("layers", function() { "geojson:example", "raster:raster" ])); + browser.alertAccept(); browser.waitForExist(".maputnik-toolbar-link"); browser.flushReactUpdates(); @@ -449,6 +450,7 @@ describe("layers", function() { browser.url(config.baseUrl+"?debug&style="+getStyleUrl([ "geojson:example" ])); + browser.alertAccept(); helper.modal.addLayer.open(); var aId = helper.modal.addLayer.fill({ diff --git a/test/functional/map/index.js b/test/functional/map/index.js index 2df53934..816a30c7 100644 --- a/test/functional/map/index.js +++ b/test/functional/map/index.js @@ -9,6 +9,7 @@ describe("map", function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])+"#"+zoomLevel+"/41.3805/2.1635"); + browser.alertAccept(); browser.waitUntil(function () { return ( @@ -22,6 +23,7 @@ describe("map", function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])+"#"+zoomLevel+"/41.3805/2.1635"); + browser.alertAccept(); browser.click(".mapboxgl-ctrl-zoom-in") browser.waitUntil(function () { diff --git a/test/functional/modals/index.js b/test/functional/modals/index.js index 0403b220..3726e97b 100644 --- a/test/functional/modals/index.js +++ b/test/functional/modals/index.js @@ -99,6 +99,7 @@ describe("modals", function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); + browser.alertAccept(); 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"; browser.setValueSafe(wd.$("modal-settings.maputnik:openmaptiles_access_token"), apiKey); browser.click(wd.$("modal-settings.name")) @@ -171,14 +172,24 @@ describe("modals", function() { assert.equal(styleObj.metadata["maputnik:openmaptiles_access_token"], apiKey); }) - it.skip("style renderer", function() { - var selector = wd.$("modal-settings.maputnik:renderer"); - browser.selectByValue(selector, "ol3"); + it("thunderforest access token", function() { + var apiKey = "testing123"; + browser.setValueSafe(wd.$("modal-settings.maputnik:thunderforest_access_token"), apiKey); browser.click(wd.$("modal-settings.name")) browser.flushReactUpdates(); 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"); }) }) diff --git a/test/functional/screenshots/index.js b/test/functional/screenshots/index.js index 093802ea..ef976c4b 100644 --- a/test/functional/screenshots/index.js +++ b/test/functional/screenshots/index.js @@ -18,6 +18,7 @@ describe('screenshots', function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); + browser.alertAccept(); browser.waitForExist(".maputnik-toolbar-link"); browser.flushReactUpdates(); @@ -28,6 +29,7 @@ describe('screenshots', function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); + browser.alertAccept(); browser.waitForExist(".maputnik-toolbar-link"); browser.flushReactUpdates(); @@ -41,6 +43,7 @@ describe('screenshots', function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); + browser.alertAccept(); browser.waitForExist(".maputnik-toolbar-link"); browser.flushReactUpdates(); @@ -54,6 +57,7 @@ describe('screenshots', function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); + browser.alertAccept(); browser.waitForExist(".maputnik-toolbar-link"); browser.flushReactUpdates(); @@ -67,6 +71,7 @@ describe('screenshots', function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); + browser.alertAccept(); browser.waitForExist(".maputnik-toolbar-link"); browser.flushReactUpdates(); @@ -80,6 +85,7 @@ describe('screenshots', function() { browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([ "geojson:example" ])); + browser.alertAccept(); browser.waitForExist(".maputnik-toolbar-link"); browser.flushReactUpdates();