mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-27 09:25:24 +01:00
Added more functional tests.
This commit is contained in:
parent
3d4cc34a08
commit
8d3ad6b1a1
19 changed files with 1085 additions and 91 deletions
832
src/components/App.jsx_json_modal
Normal file
832
src/components/App.jsx_json_modal
Normal file
|
@ -0,0 +1,832 @@
|
|||
import autoBind from 'react-autobind';
|
||||
import React from 'react'
|
||||
import cloneDeep from 'lodash.clonedeep'
|
||||
import clamp from 'lodash.clamp'
|
||||
import get from 'lodash.get'
|
||||
import {unset} from 'lodash'
|
||||
import {arrayMove} from 'react-sortable-hoc'
|
||||
import url from 'url'
|
||||
|
||||
import MapboxGlMap from './map/MapboxGlMap'
|
||||
import OpenLayersMap from './map/OpenLayersMap'
|
||||
import LayerList from './layers/LayerList'
|
||||
import LayerEditor from './layers/LayerEditor'
|
||||
import Toolbar from './Toolbar'
|
||||
import AppLayout from './AppLayout'
|
||||
import MessagePanel from './MessagePanel'
|
||||
|
||||
import SettingsModal from './modals/SettingsModal'
|
||||
import ExportModal from './modals/ExportModal'
|
||||
import SourcesModal from './modals/SourcesModal'
|
||||
import OpenModal from './modals/OpenModal'
|
||||
import ShortcutsModal from './modals/ShortcutsModal'
|
||||
import SurveyModal from './modals/SurveyModal'
|
||||
import DebugModal from './modals/DebugModal'
|
||||
import JSONEditorModal from './modals/JSONEditorModal'
|
||||
|
||||
import { downloadGlyphsMetadata, downloadSpriteMetadata } from '../libs/metadata'
|
||||
import {latest, validate} from '@mapbox/mapbox-gl-style-spec'
|
||||
import style from '../libs/style'
|
||||
import { initialStyleUrl, loadStyleUrl, removeStyleQuerystring } from '../libs/urlopen'
|
||||
import { undoMessages, redoMessages } from '../libs/diffmessage'
|
||||
import { StyleStore } from '../libs/stylestore'
|
||||
import { ApiStyleStore } from '../libs/apistore'
|
||||
import { RevisionStore } from '../libs/revisions'
|
||||
import LayerWatcher from '../libs/layerwatcher'
|
||||
import tokens from '../config/tokens.json'
|
||||
import isEqual from 'lodash.isequal'
|
||||
import Debug from '../libs/debug'
|
||||
import queryUtil from '../libs/query-util'
|
||||
|
||||
import MapboxGl from 'mapbox-gl'
|
||||
|
||||
|
||||
// Similar functionality as <https://github.com/mapbox/mapbox-gl-js/blob/7e30aadf5177486c2cfa14fe1790c60e217b5e56/src/util/mapbox.js>
|
||||
function normalizeSourceURL (url, apiToken="") {
|
||||
const matches = url.match(/^mapbox:\/\/(.*)/);
|
||||
if (matches) {
|
||||
// mapbox://mapbox.mapbox-streets-v7
|
||||
return `https://api.mapbox.com/v4/${matches[1]}.json?secure&access_token=${apiToken}`
|
||||
}
|
||||
else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
function setFetchAccessToken(url, mapStyle) {
|
||||
const matchesTilehosting = url.match(/\.tilehosting\.com/);
|
||||
const matchesMaptiler = url.match(/\.maptiler\.com/);
|
||||
const matchesThunderforest = url.match(/\.thunderforest\.com/);
|
||||
if (matchesTilehosting || matchesMaptiler) {
|
||||
const accessToken = style.getAccessToken("openmaptiles", mapStyle, {allowFallback: true})
|
||||
if (accessToken) {
|
||||
return url.replace('{key}', accessToken)
|
||||
}
|
||||
}
|
||||
else if (matchesThunderforest) {
|
||||
const accessToken = style.getAccessToken("thunderforest", mapStyle, {allowFallback: true})
|
||||
if (accessToken) {
|
||||
return url.replace('{key}', accessToken)
|
||||
}
|
||||
}
|
||||
else {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
function updateRootSpec(spec, fieldName, newValues) {
|
||||
return {
|
||||
...spec,
|
||||
$root: {
|
||||
...spec.$root,
|
||||
[fieldName]: {
|
||||
...spec.$root[fieldName],
|
||||
values: newValues
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default class App extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
autoBind(this);
|
||||
|
||||
this.revisionStore = new RevisionStore()
|
||||
const params = new URLSearchParams(window.location.search.substring(1))
|
||||
let port = params.get("localport")
|
||||
if (port == null && (window.location.port != 80 && window.location.port != 443)) {
|
||||
port = window.location.port
|
||||
}
|
||||
this.styleStore = new ApiStyleStore({
|
||||
onLocalStyleChange: mapStyle => this.onStyleChanged(mapStyle, {save: false}),
|
||||
port: port,
|
||||
host: params.get("localhost")
|
||||
})
|
||||
|
||||
|
||||
const shortcuts = [
|
||||
{
|
||||
key: "j",
|
||||
handler: () => {
|
||||
this.toggleModal("json_editor");
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "?",
|
||||
handler: () => {
|
||||
this.toggleModal("shortcuts");
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "o",
|
||||
handler: () => {
|
||||
this.toggleModal("open");
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "e",
|
||||
handler: () => {
|
||||
this.toggleModal("export");
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "d",
|
||||
handler: () => {
|
||||
this.toggleModal("sources");
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "s",
|
||||
handler: () => {
|
||||
this.toggleModal("settings");
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "i",
|
||||
handler: () => {
|
||||
this.setMapState(
|
||||
this.state.mapState === "map" ? "inspect" : "map"
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "m",
|
||||
handler: () => {
|
||||
document.querySelector(".mapboxgl-canvas").focus();
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "!",
|
||||
handler: () => {
|
||||
this.toggleModal("debug");
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
document.body.addEventListener("keyup", (e) => {
|
||||
if(e.key === "Escape") {
|
||||
e.target.blur();
|
||||
document.body.focus();
|
||||
}
|
||||
else if(this.state.isOpen.shortcuts || document.activeElement === document.body) {
|
||||
const shortcut = shortcuts.find((shortcut) => {
|
||||
return (shortcut.key === e.key)
|
||||
})
|
||||
|
||||
if(shortcut) {
|
||||
this.setModal("shortcuts", false);
|
||||
shortcut.handler(e);
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const styleUrl = initialStyleUrl()
|
||||
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')
|
||||
this.styleStore = new StyleStore()
|
||||
}
|
||||
this.styleStore.latestStyle(mapStyle => this.onStyleChanged(mapStyle))
|
||||
|
||||
if(Debug.enabled()) {
|
||||
Debug.set("maputnik", "styleStore", this.styleStore);
|
||||
Debug.set("maputnik", "revisionStore", this.revisionStore);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if(Debug.enabled()) {
|
||||
Debug.set("maputnik", "revisionStore", this.revisionStore);
|
||||
Debug.set("maputnik", "styleStore", this.styleStore);
|
||||
}
|
||||
|
||||
const queryObj = url.parse(window.location.href, true).query;
|
||||
|
||||
this.state = {
|
||||
errors: [],
|
||||
infos: [],
|
||||
mapStyle: style.emptyStyle,
|
||||
selectedLayerIndex: 0,
|
||||
sources: {},
|
||||
vectorLayers: {},
|
||||
mapState: "map",
|
||||
spec: latest,
|
||||
mapView: {
|
||||
zoom: 0,
|
||||
center: {
|
||||
lng: 0,
|
||||
lat: 0,
|
||||
},
|
||||
},
|
||||
isOpen: {
|
||||
settings: false,
|
||||
sources: false,
|
||||
open: false,
|
||||
shortcuts: false,
|
||||
export: false,
|
||||
// TODO: Disabled for now, this should be opened on the Nth visit to the editor
|
||||
survey: false,
|
||||
debug: false,
|
||||
},
|
||||
mapboxGlDebugOptions: {
|
||||
showTileBoundaries: false,
|
||||
showCollisionBoxes: false,
|
||||
showOverdrawInspector: false,
|
||||
},
|
||||
openlayersDebugOptions: {
|
||||
debugToolbox: false,
|
||||
},
|
||||
}
|
||||
|
||||
this.layerWatcher = new LayerWatcher({
|
||||
onVectorLayersChange: v => this.setState({ vectorLayers: v })
|
||||
})
|
||||
}
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
if(navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
|
||||
if(e.metaKey && e.shiftKey && e.keyCode === 90) {
|
||||
e.preventDefault();
|
||||
this.onRedo(e);
|
||||
}
|
||||
else if(e.metaKey && e.keyCode === 90) {
|
||||
e.preventDefault();
|
||||
this.onUndo(e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(e.ctrlKey && e.keyCode === 90) {
|
||||
e.preventDefault();
|
||||
this.onUndo(e);
|
||||
}
|
||||
else if(e.ctrlKey && e.keyCode === 89) {
|
||||
e.preventDefault();
|
||||
this.onRedo(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener("keydown", this.handleKeyPress);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("keydown", this.handleKeyPress);
|
||||
}
|
||||
|
||||
saveStyle(snapshotStyle) {
|
||||
this.styleStore.save(snapshotStyle)
|
||||
}
|
||||
|
||||
updateFonts(urlTemplate) {
|
||||
const metadata = this.state.mapStyle.metadata || {}
|
||||
const accessToken = metadata['maputnik:openmaptiles_access_token'] || tokens.openmaptiles
|
||||
|
||||
let glyphUrl = (typeof urlTemplate === 'string')? urlTemplate.replace('{key}', accessToken): urlTemplate;
|
||||
downloadGlyphsMetadata(glyphUrl, fonts => {
|
||||
this.setState({ spec: updateRootSpec(this.state.spec, 'glyphs', fonts)})
|
||||
})
|
||||
}
|
||||
|
||||
updateIcons(baseUrl) {
|
||||
downloadSpriteMetadata(baseUrl, icons => {
|
||||
this.setState({ spec: updateRootSpec(this.state.spec, 'sprite', icons)})
|
||||
})
|
||||
}
|
||||
|
||||
onChangeMetadataProperty = (property, value) => {
|
||||
// If we're changing renderer reset the map state.
|
||||
if (
|
||||
property === 'maputnik:renderer' &&
|
||||
value !== get(this.state.mapStyle, ['metadata', 'maputnik:renderer'], 'mbgljs')
|
||||
) {
|
||||
this.setState({
|
||||
mapState: 'map'
|
||||
});
|
||||
}
|
||||
|
||||
const changedStyle = {
|
||||
...this.state.mapStyle,
|
||||
metadata: {
|
||||
...this.state.mapStyle.metadata,
|
||||
[property]: value
|
||||
}
|
||||
}
|
||||
this.onStyleChanged(changedStyle)
|
||||
}
|
||||
|
||||
onStyleChanged = (newStyle, opts={}) => {
|
||||
opts = {
|
||||
save: true,
|
||||
addRevision: true,
|
||||
...opts,
|
||||
};
|
||||
|
||||
const errors = validate(newStyle, latest) || [];
|
||||
|
||||
// The validate function doesn't give us errors for duplicate error with
|
||||
// empty string for layer.id, manually deal with that here.
|
||||
const layerErrors = [];
|
||||
if (newStyle && newStyle.layers) {
|
||||
const foundLayers = new Map();
|
||||
newStyle.layers.forEach((layer, index) => {
|
||||
if (layer.id === "" && foundLayers.has(layer.id)) {
|
||||
const message = `Duplicate layer: [empty string]`;
|
||||
layerErrors.push({
|
||||
message,
|
||||
parsed: {
|
||||
type: "layer",
|
||||
data: {
|
||||
index,
|
||||
message,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
foundLayers.set(layer.id, true);
|
||||
});
|
||||
}
|
||||
|
||||
const mappedErrors = errors.map(error => {
|
||||
const layerMatch = error.message.match(/layers\[(\d+)\]\.(?:(\S+)\.)?(\S+): (.*)/);
|
||||
if (layerMatch) {
|
||||
const [matchStr, index, group, property, message] = layerMatch;
|
||||
const key = (group && property) ? [group, property].join(".") : property;
|
||||
return {
|
||||
message: error.message,
|
||||
parsed: {
|
||||
type: "layer",
|
||||
data: {
|
||||
index,
|
||||
key,
|
||||
message
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return {
|
||||
message: error.message,
|
||||
};
|
||||
}
|
||||
}).concat(layerErrors);
|
||||
|
||||
let dirtyMapStyle = undefined;
|
||||
if (errors.length > 0) {
|
||||
dirtyMapStyle = cloneDeep(newStyle);
|
||||
|
||||
errors.forEach(error => {
|
||||
const {message} = error;
|
||||
if (message) {
|
||||
try {
|
||||
const objPath = message.split(":")[0];
|
||||
// Errors can be deply nested for example 'layers[0].filter[1][1][0]' we only care upto the property 'layers[0].filter'
|
||||
const unsetPath = objPath.match(/^\S+?\[\d+\]\.[^\[]+/)[0];
|
||||
unset(dirtyMapStyle, unsetPath);
|
||||
}
|
||||
catch (err) {
|
||||
console.warn(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(newStyle.glyphs !== this.state.mapStyle.glyphs) {
|
||||
this.updateFonts(newStyle.glyphs)
|
||||
}
|
||||
if(newStyle.sprite !== this.state.mapStyle.sprite) {
|
||||
this.updateIcons(newStyle.sprite)
|
||||
}
|
||||
|
||||
if (opts.addRevision) {
|
||||
this.revisionStore.addRevision(newStyle);
|
||||
}
|
||||
if (opts.save) {
|
||||
this.saveStyle(newStyle);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
mapStyle: newStyle,
|
||||
dirtyMapStyle: dirtyMapStyle,
|
||||
errors: mappedErrors,
|
||||
})
|
||||
|
||||
this.fetchSources();
|
||||
}
|
||||
|
||||
onUndo = () => {
|
||||
const activeStyle = this.revisionStore.undo()
|
||||
|
||||
const messages = undoMessages(this.state.mapStyle, activeStyle)
|
||||
this.onStyleChanged(activeStyle, {addRevision: false});
|
||||
this.setState({
|
||||
infos: messages,
|
||||
})
|
||||
}
|
||||
|
||||
onRedo = () => {
|
||||
const activeStyle = this.revisionStore.redo()
|
||||
const messages = redoMessages(this.state.mapStyle, activeStyle)
|
||||
this.onStyleChanged(activeStyle, {addRevision: false});
|
||||
this.setState({
|
||||
infos: messages,
|
||||
})
|
||||
}
|
||||
|
||||
onMoveLayer = (move) => {
|
||||
let { oldIndex, newIndex } = move;
|
||||
let layers = this.state.mapStyle.layers;
|
||||
oldIndex = clamp(oldIndex, 0, layers.length-1);
|
||||
newIndex = clamp(newIndex, 0, layers.length-1);
|
||||
if(oldIndex === newIndex) return;
|
||||
|
||||
if (oldIndex === this.state.selectedLayerIndex) {
|
||||
this.setState({
|
||||
selectedLayerIndex: newIndex
|
||||
});
|
||||
}
|
||||
|
||||
layers = layers.slice(0);
|
||||
layers = arrayMove(layers, oldIndex, newIndex);
|
||||
this.onLayersChange(layers);
|
||||
}
|
||||
|
||||
onLayersChange = (changedLayers) => {
|
||||
const changedStyle = {
|
||||
...this.state.mapStyle,
|
||||
layers: changedLayers
|
||||
}
|
||||
this.onStyleChanged(changedStyle)
|
||||
}
|
||||
|
||||
onLayerDestroy = (index) => {
|
||||
let layers = this.state.mapStyle.layers;
|
||||
const remainingLayers = layers.slice(0);
|
||||
remainingLayers.splice(index, 1);
|
||||
this.onLayersChange(remainingLayers);
|
||||
}
|
||||
|
||||
onLayerCopy = (index) => {
|
||||
let layers = this.state.mapStyle.layers;
|
||||
const changedLayers = layers.slice(0)
|
||||
|
||||
const clonedLayer = cloneDeep(changedLayers[index])
|
||||
clonedLayer.id = clonedLayer.id + "-copy"
|
||||
changedLayers.splice(index, 0, clonedLayer)
|
||||
this.onLayersChange(changedLayers)
|
||||
}
|
||||
|
||||
onLayerVisibilityToggle = (index) => {
|
||||
let layers = this.state.mapStyle.layers;
|
||||
const changedLayers = layers.slice(0)
|
||||
|
||||
const layer = { ...changedLayers[index] }
|
||||
const changedLayout = 'layout' in layer ? {...layer.layout} : {}
|
||||
changedLayout.visibility = changedLayout.visibility === 'none' ? 'visible' : 'none'
|
||||
|
||||
layer.layout = changedLayout
|
||||
changedLayers[index] = layer
|
||||
this.onLayersChange(changedLayers)
|
||||
}
|
||||
|
||||
|
||||
onLayerIdChange = (index, oldId, newId) => {
|
||||
const changedLayers = this.state.mapStyle.layers.slice(0)
|
||||
changedLayers[index] = {
|
||||
...changedLayers[index],
|
||||
id: newId
|
||||
}
|
||||
|
||||
this.onLayersChange(changedLayers)
|
||||
}
|
||||
|
||||
onLayerChanged = (index, layer) => {
|
||||
const changedLayers = this.state.mapStyle.layers.slice(0)
|
||||
changedLayers[index] = layer
|
||||
|
||||
this.onLayersChange(changedLayers)
|
||||
}
|
||||
|
||||
setMapState = (newState) => {
|
||||
this.setState({
|
||||
mapState: newState
|
||||
})
|
||||
}
|
||||
|
||||
setDefaultValues = (styleObj) => {
|
||||
const metadata = styleObj.metadata || {}
|
||||
if(metadata['maputnik:renderer'] === undefined) {
|
||||
const changedStyle = {
|
||||
...styleObj,
|
||||
metadata: {
|
||||
...styleObj.metadata,
|
||||
'maputnik:renderer': 'mbgljs'
|
||||
}
|
||||
}
|
||||
return changedStyle
|
||||
} else {
|
||||
return styleObj
|
||||
}
|
||||
}
|
||||
|
||||
openStyle = (styleObj) => {
|
||||
styleObj = this.setDefaultValues(styleObj)
|
||||
this.onStyleChanged(styleObj)
|
||||
}
|
||||
|
||||
fetchSources() {
|
||||
const sourceList = {...this.state.sources};
|
||||
|
||||
for(let [key, val] of Object.entries(this.state.mapStyle.sources)) {
|
||||
if(sourceList.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sourceList[key] = {
|
||||
type: val.type,
|
||||
layers: []
|
||||
};
|
||||
|
||||
if(!this.state.sources.hasOwnProperty(key) && val.type === "vector" && val.hasOwnProperty("url")) {
|
||||
let url = val.url;
|
||||
try {
|
||||
url = normalizeSourceURL(url, MapboxGl.accessToken);
|
||||
} catch(err) {
|
||||
console.warn("Failed to normalizeSourceURL: ", err);
|
||||
}
|
||||
|
||||
try {
|
||||
url = setFetchAccessToken(url, this.state.mapStyle)
|
||||
} catch(err) {
|
||||
console.warn("Failed to setFetchAccessToken: ", err);
|
||||
}
|
||||
|
||||
fetch(url, {
|
||||
mode: 'cors',
|
||||
})
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((json) => {
|
||||
if(!json.hasOwnProperty("vector_layers")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create new objects before setState
|
||||
const sources = Object.assign({}, this.state.sources);
|
||||
|
||||
for(let layer of json.vector_layers) {
|
||||
sources[key].layers.push(layer.id)
|
||||
}
|
||||
|
||||
console.debug("Updating source: "+key);
|
||||
this.setState({
|
||||
sources: sources
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Failed to process sources for '%s'", url, err);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if(!isEqual(this.state.sources, sourceList)) {
|
||||
console.debug("Setting sources");
|
||||
this.setState({
|
||||
sources: sourceList
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
_getRenderer () {
|
||||
const metadata = this.state.mapStyle.metadata || {};
|
||||
return metadata['maputnik:renderer'] || 'mbgljs';
|
||||
}
|
||||
|
||||
onMapChange = (mapView) => {
|
||||
this.setState({
|
||||
mapView,
|
||||
});
|
||||
}
|
||||
|
||||
mapRenderer() {
|
||||
const {mapStyle, dirtyMapStyle} = this.state;
|
||||
const metadata = this.state.mapStyle.metadata || {};
|
||||
|
||||
const mapProps = {
|
||||
mapStyle: (dirtyMapStyle || mapStyle),
|
||||
replaceAccessTokens: (mapStyle) => {
|
||||
return style.replaceAccessTokens(mapStyle, {
|
||||
allowFallback: true
|
||||
});
|
||||
},
|
||||
onDataChange: (e) => {
|
||||
this.layerWatcher.analyzeMap(e.map)
|
||||
this.fetchSources();
|
||||
},
|
||||
}
|
||||
|
||||
const renderer = this._getRenderer();
|
||||
|
||||
let mapElement;
|
||||
|
||||
// Check if OL code has been loaded?
|
||||
if(renderer === 'ol') {
|
||||
mapElement = <OpenLayersMap
|
||||
{...mapProps}
|
||||
onChange={this.onMapChange}
|
||||
debugToolbox={this.state.openlayersDebugOptions.debugToolbox}
|
||||
onLayerSelect={this.onLayerSelect}
|
||||
/>
|
||||
} else {
|
||||
mapElement = <MapboxGlMap {...mapProps}
|
||||
onChange={this.onMapChange}
|
||||
options={this.state.mapboxGlDebugOptions}
|
||||
inspectModeEnabled={this.state.mapState === "inspect"}
|
||||
highlightedLayer={this.state.mapStyle.layers[this.state.selectedLayerIndex]}
|
||||
onLayerSelect={this.onLayerSelect} />
|
||||
}
|
||||
|
||||
let filterName;
|
||||
if(this.state.mapState.match(/^filter-/)) {
|
||||
filterName = this.state.mapState.replace(/^filter-/, "");
|
||||
}
|
||||
const elementStyle = {};
|
||||
if (filterName) {
|
||||
elementStyle.filter = `url('#${filterName}')`;
|
||||
};
|
||||
|
||||
return <div style={elementStyle} className="maputnik-map__container">
|
||||
{mapElement}
|
||||
</div>
|
||||
}
|
||||
|
||||
onLayerSelect = (index) => {
|
||||
this.setState({ selectedLayerIndex: index })
|
||||
}
|
||||
|
||||
setModal(modalName, value) {
|
||||
if(modalName === 'survey' && value === false) {
|
||||
localStorage.setItem('survey', '');
|
||||
}
|
||||
|
||||
this.setState({
|
||||
isOpen: {
|
||||
...this.state.isOpen,
|
||||
[modalName]: value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
toggleModal(modalName) {
|
||||
this.setModal(modalName, !this.state.isOpen[modalName]);
|
||||
}
|
||||
|
||||
onChangeOpenlayersDebug = (key, value) => {
|
||||
this.setState({
|
||||
openlayersDebugOptions: {
|
||||
...this.state.openlayersDebugOptions,
|
||||
[key]: value,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onChangeMaboxGlDebug = (key, value) => {
|
||||
this.setState({
|
||||
mapboxGlDebugOptions: {
|
||||
...this.state.mapboxGlDebugOptions,
|
||||
[key]: value,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const layers = this.state.mapStyle.layers || []
|
||||
const selectedLayer = layers.length > 0 ? layers[this.state.selectedLayerIndex] : null
|
||||
const metadata = this.state.mapStyle.metadata || {}
|
||||
|
||||
const toolbar = <Toolbar
|
||||
renderer={this._getRenderer()}
|
||||
mapState={this.state.mapState}
|
||||
mapStyle={this.state.mapStyle}
|
||||
inspectModeEnabled={this.state.mapState === "inspect"}
|
||||
sources={this.state.sources}
|
||||
onStyleChanged={this.onStyleChanged}
|
||||
onStyleOpen={this.onStyleChanged}
|
||||
onSetMapState={this.setMapState}
|
||||
onToggleModal={this.toggleModal.bind(this)}
|
||||
/>
|
||||
|
||||
const layerList = <LayerList
|
||||
onMoveLayer={this.onMoveLayer}
|
||||
onLayerDestroy={this.onLayerDestroy}
|
||||
onLayerCopy={this.onLayerCopy}
|
||||
onLayerVisibilityToggle={this.onLayerVisibilityToggle}
|
||||
onLayersChange={this.onLayersChange}
|
||||
onLayerSelect={this.onLayerSelect}
|
||||
selectedLayerIndex={this.state.selectedLayerIndex}
|
||||
layers={layers}
|
||||
sources={this.state.sources}
|
||||
errors={this.state.errors}
|
||||
/>
|
||||
|
||||
const layerEditor = selectedLayer ? <LayerEditor
|
||||
key={selectedLayer.id}
|
||||
layer={selectedLayer}
|
||||
layerIndex={this.state.selectedLayerIndex}
|
||||
isFirstLayer={this.state.selectedLayerIndex < 1}
|
||||
isLastLayer={this.state.selectedLayerIndex === this.state.mapStyle.layers.length-1}
|
||||
sources={this.state.sources}
|
||||
vectorLayers={this.state.vectorLayers}
|
||||
spec={this.state.spec}
|
||||
onMoveLayer={this.onMoveLayer}
|
||||
onLayerChanged={this.onLayerChanged}
|
||||
onLayerDestroy={this.onLayerDestroy}
|
||||
onLayerCopy={this.onLayerCopy}
|
||||
onLayerVisibilityToggle={this.onLayerVisibilityToggle}
|
||||
onLayerIdChange={this.onLayerIdChange}
|
||||
errors={this.state.errors}
|
||||
/> : null
|
||||
|
||||
const bottomPanel = (this.state.errors.length + this.state.infos.length) > 0 ? <MessagePanel
|
||||
currentLayer={selectedLayer}
|
||||
onLayerSelect={this.onLayerSelect}
|
||||
mapStyle={this.state.mapStyle}
|
||||
errors={this.state.errors}
|
||||
infos={this.state.infos}
|
||||
/> : null
|
||||
|
||||
|
||||
const modals = <div>
|
||||
<DebugModal
|
||||
renderer={this._getRenderer()}
|
||||
mapboxGlDebugOptions={this.state.mapboxGlDebugOptions}
|
||||
openlayersDebugOptions={this.state.openlayersDebugOptions}
|
||||
onChangeMaboxGlDebug={this.onChangeMaboxGlDebug}
|
||||
onChangeOpenlayersDebug={this.onChangeOpenlayersDebug}
|
||||
isOpen={this.state.isOpen.debug}
|
||||
onOpenToggle={this.toggleModal.bind(this, 'debug')}
|
||||
mapView={this.state.mapView}
|
||||
/>
|
||||
<ShortcutsModal
|
||||
ref={(el) => this.shortcutEl = el}
|
||||
isOpen={this.state.isOpen.shortcuts}
|
||||
onOpenToggle={this.toggleModal.bind(this, 'shortcuts')}
|
||||
/>
|
||||
<SettingsModal
|
||||
mapStyle={this.state.mapStyle}
|
||||
onStyleChanged={this.onStyleChanged}
|
||||
onChangeMetadataProperty={this.onChangeMetadataProperty}
|
||||
isOpen={this.state.isOpen.settings}
|
||||
onOpenToggle={this.toggleModal.bind(this, 'settings')}
|
||||
openlayersDebugOptions={this.state.openlayersDebugOptions}
|
||||
/>
|
||||
<JSONEditorModal
|
||||
title="JSON Editor"
|
||||
mapStyle={this.state.mapStyle}
|
||||
onStyleChanged={this.onStyleChanged}
|
||||
isOpen={this.state.isOpen.json_editor}
|
||||
onOpenToggle={this.toggleModal.bind(this, 'json_editor')}
|
||||
/>
|
||||
<ExportModal
|
||||
mapStyle={this.state.mapStyle}
|
||||
onStyleChanged={this.onStyleChanged}
|
||||
isOpen={this.state.isOpen.export}
|
||||
onOpenToggle={this.toggleModal.bind(this, 'export')}
|
||||
/>
|
||||
<OpenModal
|
||||
isOpen={this.state.isOpen.open}
|
||||
onStyleOpen={this.openStyle}
|
||||
onOpenToggle={this.toggleModal.bind(this, 'open')}
|
||||
/>
|
||||
<SourcesModal
|
||||
mapStyle={this.state.mapStyle}
|
||||
onStyleChanged={this.onStyleChanged}
|
||||
isOpen={this.state.isOpen.sources}
|
||||
onOpenToggle={this.toggleModal.bind(this, 'sources')}
|
||||
/>
|
||||
<SurveyModal
|
||||
isOpen={this.state.isOpen.survey}
|
||||
onOpenToggle={this.toggleModal.bind(this, 'survey')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
return <AppLayout
|
||||
toolbar={toolbar}
|
||||
layerList={layerList}
|
||||
layerEditor={layerEditor}
|
||||
map={this.mapRenderer()}
|
||||
bottom={bottomPanel}
|
||||
modals={modals}
|
||||
/>
|
||||
}
|
||||
}
|
|
@ -185,18 +185,21 @@ export default class Toolbar extends React.Component {
|
|||
>
|
||||
{/* Keyboard accessible quick links */}
|
||||
<button
|
||||
data-wd-key="root:skip:layer-list"
|
||||
className="maputnik-toolbar-skip"
|
||||
onClick={e => this.onSkip("layer-list")}
|
||||
>
|
||||
Layers list
|
||||
</button>
|
||||
<button
|
||||
data-wd-key="root:skip:layer-editor"
|
||||
className="maputnik-toolbar-skip"
|
||||
onClick={e => this.onSkip("layer-editor")}
|
||||
>
|
||||
Layer editor
|
||||
</button>
|
||||
<button
|
||||
data-wd-key="root:skip:map-view"
|
||||
className="maputnik-toolbar-skip"
|
||||
onClick={e => this.onSkip("map")}
|
||||
>
|
||||
|
|
|
@ -24,7 +24,7 @@ class DebugModal extends React.Component {
|
|||
const osmLat = Number.parseFloat(mapView.center.lat).toFixed(5);
|
||||
|
||||
return <Modal
|
||||
data-wd-key="debug-modal"
|
||||
data-wd-key="modal:debug"
|
||||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Debug'}
|
||||
|
|
|
@ -61,7 +61,7 @@ class ExportModal extends React.Component {
|
|||
|
||||
render() {
|
||||
return <Modal
|
||||
data-wd-key="export-modal"
|
||||
data-wd-key="modal:export"
|
||||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Export Style'}
|
||||
|
|
|
@ -20,7 +20,7 @@ class LoadingModal extends React.Component {
|
|||
|
||||
render() {
|
||||
return <Modal
|
||||
data-wd-key="loading-modal"
|
||||
data-wd-key="modal:loading"
|
||||
isOpen={this.props.isOpen}
|
||||
underlayClickExits={false}
|
||||
underlayProps={{
|
||||
|
|
|
@ -191,7 +191,7 @@ class OpenModal extends React.Component {
|
|||
return (
|
||||
<div>
|
||||
<Modal
|
||||
data-wd-key="open-modal"
|
||||
data-wd-key="modal:open"
|
||||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={() => this.onOpenToggle()}
|
||||
title={'Open Style'}
|
||||
|
@ -212,7 +212,7 @@ class OpenModal extends React.Component {
|
|||
</p>
|
||||
<form onSubmit={this.onSubmitUrl}>
|
||||
<UrlInput
|
||||
data-wd-key="open-modal.url.input"
|
||||
data-wd-key="modal:open.url.input"
|
||||
type="text"
|
||||
className="maputnik-input"
|
||||
default="Enter URL..."
|
||||
|
@ -222,7 +222,7 @@ class OpenModal extends React.Component {
|
|||
/>
|
||||
<div>
|
||||
<Button
|
||||
data-wd-key="open-modal.url.button"
|
||||
data-wd-key="modal:open.url.button"
|
||||
type="submit"
|
||||
className="maputnik-big-button"
|
||||
disabled={this.state.styleUrl.length < 1}
|
||||
|
|
|
@ -81,29 +81,29 @@ class SettingsModal extends React.Component {
|
|||
const transition = this.props.mapStyle.transition || {};
|
||||
|
||||
return <Modal
|
||||
data-wd-key="modal-settings"
|
||||
data-wd-key="modal:settings"
|
||||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Style Settings'}
|
||||
>
|
||||
<div className="modal-settings">
|
||||
<div className="modal:settings">
|
||||
<InputBlock label={"Name"} fieldSpec={latest.$root.name}>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.name"
|
||||
data-wd-key="modal:settings.name"
|
||||
value={this.props.mapStyle.name}
|
||||
onChange={this.changeStyleProperty.bind(this, "name")}
|
||||
/>
|
||||
</InputBlock>
|
||||
<InputBlock label={"Owner"} fieldSpec={{doc: "Owner ID of the style. Used by Mapbox or future style APIs."}}>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.owner"
|
||||
data-wd-key="modal:settings.owner"
|
||||
value={this.props.mapStyle.owner}
|
||||
onChange={this.changeStyleProperty.bind(this, "owner")}
|
||||
/>
|
||||
</InputBlock>
|
||||
<InputBlock label={"Sprite URL"} fieldSpec={latest.$root.sprite}>
|
||||
<UrlInput {...inputProps}
|
||||
data-wd-key="modal-settings.sprite"
|
||||
data-wd-key="modal:settings.sprite"
|
||||
value={this.props.mapStyle.sprite}
|
||||
onChange={this.changeStyleProperty.bind(this, "sprite")}
|
||||
/>
|
||||
|
@ -111,7 +111,7 @@ class SettingsModal extends React.Component {
|
|||
|
||||
<InputBlock label={"Glyphs URL"} fieldSpec={latest.$root.glyphs}>
|
||||
<UrlInput {...inputProps}
|
||||
data-wd-key="modal-settings.glyphs"
|
||||
data-wd-key="modal:settings.glyphs"
|
||||
value={this.props.mapStyle.glyphs}
|
||||
onChange={this.changeStyleProperty.bind(this, "glyphs")}
|
||||
/>
|
||||
|
@ -122,7 +122,7 @@ class SettingsModal extends React.Component {
|
|||
fieldSpec={fieldSpecAdditional.maputnik.mapbox_access_token}
|
||||
>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:mapbox_access_token"
|
||||
data-wd-key="modal:settings.maputnik:mapbox_access_token"
|
||||
value={metadata['maputnik:mapbox_access_token']}
|
||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
|
||||
/>
|
||||
|
@ -133,7 +133,7 @@ class SettingsModal extends React.Component {
|
|||
fieldSpec={fieldSpecAdditional.maputnik.maptiler_access_token}
|
||||
>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:openmaptiles_access_token"
|
||||
data-wd-key="modal:settings.maputnik:openmaptiles_access_token"
|
||||
value={metadata['maputnik:openmaptiles_access_token']}
|
||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
|
||||
/>
|
||||
|
@ -144,7 +144,7 @@ class SettingsModal extends React.Component {
|
|||
fieldSpec={fieldSpecAdditional.maputnik.thunderforest_access_token}
|
||||
>
|
||||
<StringInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:thunderforest_access_token"
|
||||
data-wd-key="modal:settings.maputnik:thunderforest_access_token"
|
||||
value={metadata['maputnik:thunderforest_access_token']}
|
||||
onChange={onChangeMetadataProperty.bind(this, "maputnik:thunderforest_access_token")}
|
||||
/>
|
||||
|
@ -250,7 +250,7 @@ class SettingsModal extends React.Component {
|
|||
fieldSpec={fieldSpecAdditional.maputnik.style_renderer}
|
||||
>
|
||||
<SelectInput {...inputProps}
|
||||
data-wd-key="modal-settings.maputnik:renderer"
|
||||
data-wd-key="modal:settings.maputnik:renderer"
|
||||
options={[
|
||||
['mbgljs', 'MapboxGL JS'],
|
||||
['ol', 'Open Layers (experimental)'],
|
||||
|
|
|
@ -100,7 +100,7 @@ class ShortcutsModal extends React.Component {
|
|||
|
||||
|
||||
return <Modal
|
||||
data-wd-key="shortcuts-modal"
|
||||
data-wd-key="modal:shortcuts"
|
||||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Shortcuts'}
|
||||
|
|
|
@ -286,6 +286,7 @@ class SourcesModal extends React.Component {
|
|||
|
||||
const inputProps = { }
|
||||
return <Modal
|
||||
data-wd-key="modal:sources"
|
||||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={this.props.onOpenToggle}
|
||||
title={'Sources'}
|
||||
|
|
|
@ -20,7 +20,7 @@ class SurveyModal extends React.Component {
|
|||
|
||||
render() {
|
||||
return <Modal
|
||||
data-wd-key="modal-survey"
|
||||
data-wd-key="modal:survey"
|
||||
isOpen={this.props.isOpen}
|
||||
onOpenToggle={this.props.onOpenToggle}
|
||||
title="Maputnik Survey"
|
||||
|
|
18
test/example-layer-style.json
Normal file
18
test/example-layer-style.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"id": "test-style",
|
||||
"version": 8,
|
||||
"name": "Test Style",
|
||||
"metadata": {
|
||||
"maputnik:renderer": "mbgljs"
|
||||
},
|
||||
"sources": {},
|
||||
"glyphs": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||
"sprites": "https://example.local/fonts/{fontstack}/{range}.pbf",
|
||||
"layers": [
|
||||
{
|
||||
"id": "background",
|
||||
"type": "background"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
3
test/functional/accessibility/index.js
Normal file
3
test/functional/accessibility/index.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
describe("accessibility", function () {
|
||||
require("./skip-links");
|
||||
})
|
51
test/functional/accessibility/skip-links.js
Normal file
51
test/functional/accessibility/skip-links.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
var assert = require("assert");
|
||||
var config = require("../../config/specs");
|
||||
var helper = require("../helper");
|
||||
var wd = require("../../wd-helper");
|
||||
|
||||
|
||||
describe("skip links", function() {
|
||||
beforeEach(function () {
|
||||
browser.url(config.baseUrl+"?debug&style="+helper.getGeoServerUrl("example-layer-style.json"));
|
||||
browser.acceptAlert();
|
||||
});
|
||||
|
||||
it("skip link to layer list", function() {
|
||||
const selector = wd.$("root:skip:layer-list")
|
||||
const elem = $(selector);
|
||||
assert(elem.isExisting());
|
||||
browser.keys(['Tab']);
|
||||
assert(elem.isFocused());
|
||||
elem.click();
|
||||
|
||||
const targetEl = $("#skip-target-layer-list");
|
||||
assert(targetEl.isFocused());
|
||||
});
|
||||
|
||||
it("skip link to layer editor", function() {
|
||||
const selector = wd.$("root:skip:layer-editor")
|
||||
const elem = $(selector);
|
||||
assert(elem.isExisting());
|
||||
browser.keys(['Tab']);
|
||||
browser.keys(['Tab']);
|
||||
assert(elem.isFocused());
|
||||
elem.click();
|
||||
|
||||
const targetEl = $("#skip-target-layer-editor");
|
||||
assert(targetEl.isFocused());
|
||||
});
|
||||
|
||||
it("skip link to map view", function() {
|
||||
const selector = wd.$("root:skip:map-view")
|
||||
const elem = $(selector);
|
||||
assert(elem.isExisting());
|
||||
browser.keys(['Tab']);
|
||||
browser.keys(['Tab']);
|
||||
browser.keys(['Tab']);
|
||||
assert(elem.isFocused());
|
||||
elem.click();
|
||||
|
||||
const targetEl = $(".mapboxgl-canvas");
|
||||
assert(targetEl.isFocused());
|
||||
});
|
||||
});
|
|
@ -3,7 +3,23 @@ var config = require("../../config/specs");
|
|||
var helper = require("../helper");
|
||||
|
||||
|
||||
describe.skip("history", function() {
|
||||
|
||||
describe("history", function() {
|
||||
let undoKeyCombo;
|
||||
let undoKeyComboReset;
|
||||
let redoKeyCombo;
|
||||
let redoKeyComboReset;
|
||||
|
||||
before(function() {
|
||||
const isMac = browser.execute(function() {
|
||||
return navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
||||
});
|
||||
undoKeyCombo = ['Meta', 'z'];
|
||||
undoKeyComboReset = ['Meta'];
|
||||
redoKeyCombo = isMac ? ['Meta', 'Shift', 'z'] : ['Meta', 'y'];
|
||||
redoKeyComboReset = isMac ? ['Meta', 'Shift'] : ['Meta'];
|
||||
});
|
||||
|
||||
/**
|
||||
* See <https://github.com/webdriverio/webdriverio/issues/1126>
|
||||
*/
|
||||
|
@ -13,7 +29,7 @@ describe.skip("history", function() {
|
|||
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
|
||||
"geojson:example"
|
||||
]));
|
||||
browser.alertAccept();
|
||||
browser.acceptAlert();
|
||||
|
||||
helper.modal.addLayer.open();
|
||||
|
||||
|
@ -51,9 +67,8 @@ describe.skip("history", function() {
|
|||
}
|
||||
]);
|
||||
|
||||
browser
|
||||
.keys(['Control', 'z'])
|
||||
.keys(['Control']);
|
||||
browser.keys(undoKeyCombo)
|
||||
browser.keys(undoKeyComboReset);
|
||||
styleObj = helper.getStyleStore(browser);
|
||||
assert.deepEqual(styleObj.layers, [
|
||||
{
|
||||
|
@ -62,16 +77,14 @@ describe.skip("history", function() {
|
|||
}
|
||||
]);
|
||||
|
||||
browser
|
||||
.keys(['Control', 'z'])
|
||||
.keys(['Control']);
|
||||
browser.keys(undoKeyCombo)
|
||||
browser.keys(undoKeyComboReset);
|
||||
styleObj = helper.getStyleStore(browser);
|
||||
assert.deepEqual(styleObj.layers, [
|
||||
]);
|
||||
|
||||
browser
|
||||
.keys(['Control', 'y'])
|
||||
.keys(['Control']);
|
||||
browser.keys(redoKeyCombo)
|
||||
browser.keys(redoKeyComboReset);
|
||||
styleObj = helper.getStyleStore(browser);
|
||||
assert.deepEqual(styleObj.layers, [
|
||||
{
|
||||
|
@ -80,9 +93,8 @@ describe.skip("history", function() {
|
|||
}
|
||||
]);
|
||||
|
||||
browser
|
||||
.keys(['Control', 'y'])
|
||||
.keys(['Control']);
|
||||
browser.keys(redoKeyCombo)
|
||||
browser.keys(redoKeyComboReset);
|
||||
styleObj = helper.getStyleStore(browser);
|
||||
assert.deepEqual(styleObj.layers, [
|
||||
{
|
||||
|
|
|
@ -37,6 +37,8 @@ describe('maputnik', function() {
|
|||
require("./map");
|
||||
require("./modals");
|
||||
require("./screenshots");
|
||||
require("./accessibility");
|
||||
require("./keyboard");
|
||||
// ------------------------
|
||||
|
||||
});
|
||||
|
|
58
test/functional/keyboard/index.js
Normal file
58
test/functional/keyboard/index.js
Normal file
|
@ -0,0 +1,58 @@
|
|||
var assert = require("assert");
|
||||
var config = require("../../config/specs");
|
||||
var helper = require("../helper");
|
||||
var wd = require("../../wd-helper");
|
||||
|
||||
|
||||
describe("keyboard", function() {
|
||||
describe("shortcuts", function() {
|
||||
it("ESC should unfocus", function() {
|
||||
const tmpTargetEl = $(wd.$("nav:inspect") + " select");
|
||||
tmpTargetEl.click();
|
||||
assert(tmpTargetEl.isFocused());
|
||||
|
||||
browser.keys(["Escape"]);
|
||||
assert($("body").isFocused());
|
||||
});
|
||||
|
||||
it("'?' should show shortcuts modal", function() {
|
||||
browser.keys(["?"]);
|
||||
assert($(wd.$("modal:shortcuts")).isDisplayed());
|
||||
});
|
||||
|
||||
it("'o' should show open modal", function() {
|
||||
browser.keys(["o"]);
|
||||
assert($(wd.$("modal:open")).isDisplayed());
|
||||
});
|
||||
|
||||
it("'e' should show export modal", function() {
|
||||
browser.keys(["e"]);
|
||||
assert($(wd.$("modal:export")).isDisplayed());
|
||||
});
|
||||
|
||||
it("'d' should show sources modal", function() {
|
||||
browser.keys(["d"]);
|
||||
assert($(wd.$("modal:sources")).isDisplayed());
|
||||
});
|
||||
|
||||
it("'s' should show settings modal", function() {
|
||||
browser.keys(["s"]);
|
||||
assert($(wd.$("modal:settings")).isDisplayed());
|
||||
});
|
||||
|
||||
it.skip("'i' should change map to inspect mode", function() {
|
||||
// browser.keys(["i"]);
|
||||
});
|
||||
|
||||
it("'m' should focus map", function() {
|
||||
browser.keys(["m"]);
|
||||
$(".mapboxgl-canvas").isFocused();
|
||||
});
|
||||
|
||||
it("'!' should show debug modal", function() {
|
||||
browser.keys(["!"]);
|
||||
assert($(wd.$("modal:debug")).isDisplayed());
|
||||
});
|
||||
});
|
||||
|
||||
});
|
|
@ -115,12 +115,6 @@ describe("layers", function() {
|
|||
})
|
||||
})
|
||||
|
||||
describe("grouped", function() {
|
||||
it("with underscore")
|
||||
it("no without underscore")
|
||||
it("double underscore only grouped once")
|
||||
})
|
||||
|
||||
describe("tooltips", function() {
|
||||
})
|
||||
|
||||
|
@ -130,12 +124,11 @@ describe("layers", function() {
|
|||
|
||||
describe('background', function () {
|
||||
|
||||
it.skip("add", function() {
|
||||
it("add", function() {
|
||||
var id = helper.modal.addLayer.fill({
|
||||
type: "background"
|
||||
})
|
||||
|
||||
browser.waitUntil(function() {
|
||||
var styleObj = helper.getStyleStore(browser);
|
||||
assert.deepEqual(styleObj.layers, [
|
||||
{
|
||||
|
@ -144,7 +137,6 @@ describe("layers", function() {
|
|||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("modify", function() {
|
||||
function createBackground() {
|
||||
|
@ -192,9 +184,6 @@ describe("layers", function() {
|
|||
]);
|
||||
});
|
||||
|
||||
// NOTE: This needs to be removed from the code
|
||||
it("type");
|
||||
|
||||
it("min-zoom", function() {
|
||||
var bgId = createBackground();
|
||||
|
||||
|
@ -340,7 +329,7 @@ describe("layers", function() {
|
|||
});
|
||||
|
||||
describe('fill', function () {
|
||||
it.skip("add", function() {
|
||||
it("add", function() {
|
||||
// browser.debug();
|
||||
|
||||
var id = helper.modal.addLayer.fill({
|
||||
|
@ -363,7 +352,7 @@ describe("layers", function() {
|
|||
});
|
||||
|
||||
describe('line', function () {
|
||||
it.skip("add", function() {
|
||||
it("add", function() {
|
||||
var id = helper.modal.addLayer.fill({
|
||||
type: "line",
|
||||
layer: "example"
|
||||
|
@ -386,7 +375,7 @@ describe("layers", function() {
|
|||
});
|
||||
|
||||
describe('symbol', function () {
|
||||
it.skip("add", function() {
|
||||
it("add", function() {
|
||||
var id = helper.modal.addLayer.fill({
|
||||
type: "symbol",
|
||||
layer: "example"
|
||||
|
@ -404,7 +393,7 @@ describe("layers", function() {
|
|||
});
|
||||
|
||||
describe('raster', function () {
|
||||
it.skip("add", function() {
|
||||
it("add", function() {
|
||||
var id = helper.modal.addLayer.fill({
|
||||
type: "raster",
|
||||
layer: "raster"
|
||||
|
@ -422,7 +411,7 @@ describe("layers", function() {
|
|||
});
|
||||
|
||||
describe('circle', function () {
|
||||
it.skip("add", function() {
|
||||
it("add", function() {
|
||||
var id = helper.modal.addLayer.fill({
|
||||
type: "circle",
|
||||
layer: "example"
|
||||
|
@ -441,7 +430,7 @@ describe("layers", function() {
|
|||
});
|
||||
|
||||
describe('fill extrusion', function () {
|
||||
it.skip("add", function() {
|
||||
it("add", function() {
|
||||
var id = helper.modal.addLayer.fill({
|
||||
type: "fill-extrusion",
|
||||
layer: "example"
|
||||
|
@ -459,12 +448,12 @@ describe("layers", function() {
|
|||
});
|
||||
|
||||
|
||||
describe.skip("groups", function() {
|
||||
describe("groups", function() {
|
||||
it("simple", function() {
|
||||
browser.url(config.baseUrl+"?debug&style="+getStyleUrl([
|
||||
browser.url(config.baseUrl+"?debug&style="+helper.getStyleUrl([
|
||||
"geojson:example"
|
||||
]));
|
||||
browser.alertAccept();
|
||||
browser.acceptAlert();
|
||||
|
||||
helper.modal.addLayer.open();
|
||||
var aId = helper.modal.addLayer.fill({
|
||||
|
@ -480,21 +469,22 @@ describe("layers", function() {
|
|||
|
||||
helper.modal.addLayer.open();
|
||||
var bId = helper.modal.addLayer.fill({
|
||||
id: "foo_baz",
|
||||
id: "foo_bar_baz",
|
||||
type: "background"
|
||||
})
|
||||
|
||||
browser.waitForExist(wd.$("layer-list-group:foo-0"));
|
||||
const groupEl = $(wd.$("layer-list-group:foo-0"));
|
||||
groupEl.isDisplayed();
|
||||
|
||||
assert.equal(browser.isVisibleWithinViewport(wd.$("layer-list-item:foo")), false);
|
||||
assert.equal(browser.isVisibleWithinViewport(wd.$("layer-list-item:foo_bar")), false);
|
||||
assert.equal(browser.isVisibleWithinViewport(wd.$("layer-list-item:foo_baz")), false);
|
||||
assert.equal($(wd.$("layer-list-item:foo")).isDisplayedInViewport(), true);
|
||||
assert.equal($(wd.$("layer-list-item:foo_bar")).isDisplayedInViewport(), false);
|
||||
assert.equal($(wd.$("layer-list-item:foo_bar_baz")).isDisplayedInViewport(), false);
|
||||
|
||||
browser.click(wd.$("layer-list-group:foo-0"));
|
||||
groupEl.click();
|
||||
|
||||
assert.equal(browser.isVisibleWithinViewport(wd.$("layer-list-item:foo")), true);
|
||||
assert.equal(browser.isVisibleWithinViewport(wd.$("layer-list-item:foo_bar")), true);
|
||||
assert.equal(browser.isVisibleWithinViewport(wd.$("layer-list-item:foo_baz")), true);
|
||||
assert.equal($(wd.$("layer-list-item:foo")).isDisplayedInViewport(), true);
|
||||
assert.equal($(wd.$("layer-list-item:foo_bar")).isDisplayedInViewport(), true);
|
||||
assert.equal($(wd.$("layer-list-item:foo_bar_baz")).isDisplayedInViewport(), true);
|
||||
})
|
||||
})
|
||||
});
|
||||
|
|
|
@ -41,7 +41,7 @@ describe("modals", function() {
|
|||
});
|
||||
|
||||
it("close", function() {
|
||||
closeModal("open-modal");
|
||||
closeModal("modal:open");
|
||||
});
|
||||
|
||||
// "chooseFile" command currently not available for wdio v5 https://github.com/webdriverio/webdriverio/pull/3632
|
||||
|
@ -57,9 +57,9 @@ describe("modals", function() {
|
|||
it("load from url", function() {
|
||||
var styleFileUrl = helper.getGeoServerUrl("example-style.json");
|
||||
|
||||
browser.setValueSafe(wd.$("open-modal.url.input"), styleFileUrl);
|
||||
browser.setValueSafe(wd.$("modal:open.url.input"), styleFileUrl);
|
||||
|
||||
const selector = $(wd.$("open-modal.url.button"));
|
||||
const selector = $(wd.$("modal:open.url.button"));
|
||||
selector.click();
|
||||
|
||||
// Allow the network request to happen
|
||||
|
@ -74,6 +74,24 @@ describe("modals", function() {
|
|||
it("gallery")
|
||||
})
|
||||
|
||||
describe("shortcuts", function() {
|
||||
it("open/close", function() {
|
||||
browser.url(config.baseUrl+"?debug");
|
||||
|
||||
const elem = $(".maputnik-toolbar-link");
|
||||
elem.waitForExist();
|
||||
browser.flushReactUpdates();
|
||||
|
||||
browser.keys(["?"]);
|
||||
|
||||
const modalEl = $(wd.$("modal:shortcuts"))
|
||||
assert(modalEl.isDisplayed());
|
||||
|
||||
closeModal("modal:shortcuts");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe("export", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
|
@ -89,14 +107,12 @@ describe("modals", function() {
|
|||
});
|
||||
|
||||
it("close", function() {
|
||||
closeModal("export-modal");
|
||||
closeModal("modal:export");
|
||||
});
|
||||
|
||||
// TODO: Work out how to download a file and check the contents
|
||||
it("download")
|
||||
|
||||
// TODO: Work out how to mock the end git points
|
||||
it("save to gist")
|
||||
})
|
||||
|
||||
describe("sources", function() {
|
||||
|
@ -131,8 +147,8 @@ describe("modals", function() {
|
|||
});
|
||||
|
||||
it("name", function() {
|
||||
browser.setValueSafe(wd.$("modal-settings.name"), "foobar")
|
||||
const elem = $(wd.$("modal-settings.owner"));
|
||||
browser.setValueSafe(wd.$("modal:settings.name"), "foobar")
|
||||
const elem = $(wd.$("modal:settings.owner"));
|
||||
elem.click();
|
||||
browser.flushReactUpdates();
|
||||
|
||||
|
@ -140,8 +156,8 @@ describe("modals", function() {
|
|||
assert.equal(styleObj.name, "foobar");
|
||||
})
|
||||
it("owner", function() {
|
||||
browser.setValueSafe(wd.$("modal-settings.owner"), "foobar")
|
||||
const elem = $(wd.$("modal-settings.name"));
|
||||
browser.setValueSafe(wd.$("modal:settings.owner"), "foobar")
|
||||
const elem = $(wd.$("modal:settings.name"));
|
||||
elem.click();
|
||||
browser.flushReactUpdates();
|
||||
|
||||
|
@ -149,8 +165,8 @@ describe("modals", function() {
|
|||
assert.equal(styleObj.owner, "foobar");
|
||||
})
|
||||
it("sprite url", function() {
|
||||
browser.setValueSafe(wd.$("modal-settings.sprite"), "http://example.com")
|
||||
const elem = $(wd.$("modal-settings.name"));
|
||||
browser.setValueSafe(wd.$("modal:settings.sprite"), "http://example.com")
|
||||
const elem = $(wd.$("modal:settings.name"));
|
||||
elem.click();
|
||||
browser.flushReactUpdates();
|
||||
|
||||
|
@ -159,8 +175,8 @@ describe("modals", function() {
|
|||
})
|
||||
it("glyphs url", function() {
|
||||
var glyphsUrl = "http://example.com/{fontstack}/{range}.pbf"
|
||||
browser.setValueSafe(wd.$("modal-settings.glyphs"), glyphsUrl)
|
||||
const elem = $(wd.$("modal-settings.name"));
|
||||
browser.setValueSafe(wd.$("modal:settings.glyphs"), glyphsUrl)
|
||||
const elem = $(wd.$("modal:settings.name"));
|
||||
elem.click();
|
||||
browser.flushReactUpdates();
|
||||
|
||||
|
@ -170,8 +186,8 @@ describe("modals", function() {
|
|||
|
||||
it("mapbox access token", function() {
|
||||
var apiKey = "testing123";
|
||||
browser.setValueSafe(wd.$("modal-settings.maputnik:mapbox_access_token"), apiKey);
|
||||
const elem = $(wd.$("modal-settings.name"));
|
||||
browser.setValueSafe(wd.$("modal:settings.maputnik:mapbox_access_token"), apiKey);
|
||||
const elem = $(wd.$("modal:settings.name"));
|
||||
elem.click();
|
||||
browser.flushReactUpdates();
|
||||
|
||||
|
@ -183,8 +199,8 @@ describe("modals", function() {
|
|||
|
||||
it("maptiler access token", function() {
|
||||
var apiKey = "testing123";
|
||||
browser.setValueSafe(wd.$("modal-settings.maputnik:openmaptiles_access_token"), apiKey);
|
||||
const elem = $(wd.$("modal-settings.name"));
|
||||
browser.setValueSafe(wd.$("modal:settings.maputnik:openmaptiles_access_token"), apiKey);
|
||||
const elem = $(wd.$("modal:settings.name"));
|
||||
elem.click();
|
||||
browser.flushReactUpdates();
|
||||
|
||||
|
@ -194,8 +210,8 @@ describe("modals", function() {
|
|||
|
||||
it("thunderforest access token", function() {
|
||||
var apiKey = "testing123";
|
||||
browser.setValueSafe(wd.$("modal-settings.maputnik:thunderforest_access_token"), apiKey);
|
||||
const elem = $(wd.$("modal-settings.name"));
|
||||
browser.setValueSafe(wd.$("modal:settings.maputnik:thunderforest_access_token"), apiKey);
|
||||
const elem = $(wd.$("modal:settings.name"));
|
||||
elem.click();
|
||||
browser.flushReactUpdates();
|
||||
|
||||
|
@ -204,9 +220,9 @@ describe("modals", function() {
|
|||
})
|
||||
|
||||
it("style renderer", function() {
|
||||
const selector = $(wd.$("modal-settings.maputnik:renderer"));
|
||||
const selector = $(wd.$("modal:settings.maputnik:renderer"));
|
||||
selector.selectByAttribute('value', "ol");
|
||||
const elem = $(wd.$("modal-settings.name"));
|
||||
const elem = $(wd.$("modal:settings.name"));
|
||||
elem.click();
|
||||
browser.flushReactUpdates();
|
||||
|
||||
|
|
|
@ -78,6 +78,14 @@ app.get("/styles/empty/:sources", function(req, res) {
|
|||
res.send(json);
|
||||
})
|
||||
|
||||
app.get("/example-layer-style.json", function(req, res) {
|
||||
res.json(
|
||||
JSON.parse(
|
||||
fs.readFileSync(__dirname+"/example-layer-style.json").toString()
|
||||
)
|
||||
);
|
||||
})
|
||||
|
||||
app.get("/example-style.json", function(req, res) {
|
||||
res.json(
|
||||
JSON.parse(
|
||||
|
|
Loading…
Reference in a new issue