mirror of
https://github.com/a-nyx/maputnik-with-pmtiles.git
synced 2024-12-26 21:10:36 +01:00
Added more webdriver tests testing against a real browser.
This commit is contained in:
parent
6e86c60f89
commit
942b2240a7
47 changed files with 2244 additions and 637 deletions
11
.babelrc
11
.babelrc
|
@ -1,4 +1,13 @@
|
||||||
{
|
{
|
||||||
"presets": ["env", "react"],
|
"presets": ["env", "react"],
|
||||||
"plugins": ["transform-object-rest-spread", "transform-class-properties"]
|
"plugins": ["transform-object-rest-spread", "transform-class-properties"],
|
||||||
|
"env": {
|
||||||
|
"test": {
|
||||||
|
"plugins": [
|
||||||
|
["istanbul", {
|
||||||
|
exclude: ["node_modules/**", "test/**"]
|
||||||
|
}]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
27
.circleci/config.yml
Normal file
27
.circleci/config.yml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
version: 2
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
docker:
|
||||||
|
# specify the version you desire here
|
||||||
|
- image: node:8
|
||||||
|
- image: selenium/standalone-chrome:3.1.0
|
||||||
|
working_directory: ~/repo
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: "Create artifacts directory"
|
||||||
|
command: mkdir /tmp/artifacts
|
||||||
|
- restore_cache:
|
||||||
|
key: v1-dependencies-{{ checksum "package.json" }}
|
||||||
|
|
||||||
|
- run: npm install
|
||||||
|
|
||||||
|
- save_cache:
|
||||||
|
paths:
|
||||||
|
- node_modules
|
||||||
|
key: v1-dependencies-{{ checksum "package.json" }}
|
||||||
|
|
||||||
|
- run: mkdir -p /tmp/artifacts/logs
|
||||||
|
- run: DOCKER_HOST=localhost npm test
|
||||||
|
- run: npm run build
|
||||||
|
- run: ./node_modules/.bin/istanbul report --include /tmp/artifacts/coverage/coverage.json --dir /tmp/artifacts/coverage html lcov
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -30,3 +30,5 @@ node_modules
|
||||||
|
|
||||||
# Ignore build files
|
# Ignore build files
|
||||||
public
|
public
|
||||||
|
/errorShots
|
||||||
|
/old
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
machine:
|
|
||||||
node:
|
|
||||||
version: 6
|
|
||||||
test:
|
|
||||||
post:
|
|
||||||
- npm run build
|
|
|
@ -2,36 +2,36 @@ var webpack = require("webpack");
|
||||||
var WebpackDevServer = require("webpack-dev-server");
|
var WebpackDevServer = require("webpack-dev-server");
|
||||||
var webpackConfig = require("./webpack.production.config");
|
var webpackConfig = require("./webpack.production.config");
|
||||||
var testConfig = require("../test/config/specs");
|
var testConfig = require("../test/config/specs");
|
||||||
|
var artifacts = require("../test/artifacts");
|
||||||
|
var isDocker = require("is-docker");
|
||||||
|
|
||||||
|
|
||||||
var server;
|
var server;
|
||||||
|
var SCREENSHOT_PATH = artifacts.pathSync("screenshots");
|
||||||
|
|
||||||
exports.config = {
|
exports.config = {
|
||||||
specs: [
|
specs: [
|
||||||
'./test/specs/**/*.js'
|
'./test/functional/advanced.js'
|
||||||
],
|
],
|
||||||
exclude: [
|
exclude: [
|
||||||
],
|
],
|
||||||
maxInstances: 10,
|
maxInstances: 10,
|
||||||
capabilities: [{
|
capabilities: [{
|
||||||
maxInstances: 5,
|
maxInstances: 5,
|
||||||
browserName: 'firefox'
|
browserName: 'chrome'
|
||||||
}],
|
}],
|
||||||
sync: true,
|
sync: true,
|
||||||
logLevel: 'verbose',
|
logLevel: 'verbose',
|
||||||
coloredLogs: true,
|
coloredLogs: true,
|
||||||
bail: 0,
|
bail: 0,
|
||||||
screenshotPath: './errorShots/',
|
screenshotPath: SCREENSHOT_PATH,
|
||||||
|
host: (isDocker() ? process.env.DOCKER_HOST : "127.0.0.1"),
|
||||||
baseUrl: 'http://localhost',
|
baseUrl: 'http://localhost',
|
||||||
waitforTimeout: 10000,
|
waitforTimeout: 10000,
|
||||||
connectionRetryTimeout: 90000,
|
connectionRetryTimeout: 90000,
|
||||||
connectionRetryCount: 3,
|
connectionRetryCount: 3,
|
||||||
services: ['phantomjs'],
|
|
||||||
framework: 'mocha',
|
framework: 'mocha',
|
||||||
reporters: ['spec'],
|
reporters: ['spec'],
|
||||||
phantomjsOpts: {
|
|
||||||
webdriverLogfile: 'phantomjs.log'
|
|
||||||
},
|
|
||||||
mochaOpts: {
|
mochaOpts: {
|
||||||
ui: 'bdd',
|
ui: 'bdd',
|
||||||
// Because we don't know how long the initial build will take...
|
// Because we don't know how long the initial build will take...
|
||||||
|
@ -43,6 +43,6 @@ exports.config = {
|
||||||
server.listen(testConfig.port);
|
server.listen(testConfig.port);
|
||||||
},
|
},
|
||||||
onComplete: function(exitCode) {
|
onComplete: function(exitCode) {
|
||||||
server.close();
|
server.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
var webpack = require('webpack');
|
var webpack = require('webpack');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var loaders = require('./webpack.loaders');
|
var loaders = require('./webpack.loaders');
|
||||||
|
@ -7,14 +6,9 @@ var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||||
var WebpackCleanupPlugin = require('webpack-cleanup-plugin');
|
var WebpackCleanupPlugin = require('webpack-cleanup-plugin');
|
||||||
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||||
var CopyWebpackPlugin = require('copy-webpack-plugin');
|
var CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
|
var artifacts = require("../test/artifacts");
|
||||||
|
|
||||||
var OUTPATH;
|
var OUTPATH = artifacts.pathSync("/build");
|
||||||
if(process.env.CIRCLE_ARTIFACTS) {
|
|
||||||
OUTPATH = path.join(process.env.CIRCLE_ARTIFACTS, "build");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
OUTPATH = path.join(__dirname, '..', 'public');
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: {
|
entry: {
|
||||||
|
|
1177
package-lock.json
generated
1177
package-lock.json
generated
File diff suppressed because it is too large
Load diff
23
package.json
23
package.json
|
@ -39,14 +39,14 @@
|
||||||
"ol-mapbox-style": "^1.0.1",
|
"ol-mapbox-style": "^1.0.1",
|
||||||
"openlayers": "^4.4.2",
|
"openlayers": "^4.4.2",
|
||||||
"prop-types": "^15.6.0",
|
"prop-types": "^15.6.0",
|
||||||
"react": "16.0.0",
|
"react": "^15.6.2",
|
||||||
"react-addons-pure-render-mixin": "^15.6.2",
|
"react-addons-pure-render-mixin": "^15.6.2",
|
||||||
"react-autocomplete": "^1.7.2",
|
"react-autocomplete": "^1.7.2",
|
||||||
"react-codemirror2": "^3.0.7",
|
"react-codemirror2": "^3.0.7",
|
||||||
"react-collapse": "^4.0.3",
|
"react-collapse": "^4.0.3",
|
||||||
"react-color": "^2.13.8",
|
"react-color": "^2.13.8",
|
||||||
"react-copy-to-clipboard": "^5.0.1",
|
"react-copy-to-clipboard": "^5.0.1",
|
||||||
"react-dom": "16.0.0",
|
"react-dom": "^15.6.2",
|
||||||
"react-file-reader-input": "^1.1.4",
|
"react-file-reader-input": "^1.1.4",
|
||||||
"react-height": "^3.0.0",
|
"react-height": "^3.0.0",
|
||||||
"react-icon-base": "^2.1.1",
|
"react-icon-base": "^2.1.1",
|
||||||
|
@ -90,6 +90,7 @@
|
||||||
"babel-core": "^6.26.0",
|
"babel-core": "^6.26.0",
|
||||||
"babel-eslint": "^8.0.2",
|
"babel-eslint": "^8.0.2",
|
||||||
"babel-loader": "7.1.1",
|
"babel-loader": "7.1.1",
|
||||||
|
"babel-plugin-istanbul": "^4.1.5",
|
||||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||||
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
"babel-plugin-transform-decorators-legacy": "^1.3.4",
|
||||||
"babel-plugin-transform-flow-strip-types": "^6.22.0",
|
"babel-plugin-transform-flow-strip-types": "^6.22.0",
|
||||||
|
@ -97,34 +98,38 @@
|
||||||
"babel-plugin-transform-runtime": "^6.23.0",
|
"babel-plugin-transform-runtime": "^6.23.0",
|
||||||
"babel-preset-env": "^1.6.1",
|
"babel-preset-env": "^1.6.1",
|
||||||
"babel-preset-react": "^6.24.1",
|
"babel-preset-react": "^6.24.1",
|
||||||
|
"babel-register": "^6.26.0",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
"base64-loader": "^1.0.0",
|
"base64-loader": "^1.0.0",
|
||||||
"copy-webpack-plugin": "^4.2.0",
|
"copy-webpack-plugin": "^4.2.0",
|
||||||
|
"cors": "^2.8.4",
|
||||||
"css-loader": "^0.28.7",
|
"css-loader": "^0.28.7",
|
||||||
"eslint": "^4.10.0",
|
"eslint": "^4.10.0",
|
||||||
"eslint-plugin-react": "^7.4.0",
|
"eslint-plugin-react": "^7.4.0",
|
||||||
|
"express": "^4.16.2",
|
||||||
"extract-text-webpack-plugin": "^3.0.2",
|
"extract-text-webpack-plugin": "^3.0.2",
|
||||||
"file-loader": "^1.1.5",
|
"file-loader": "^1.1.5",
|
||||||
"html-webpack-plugin": "^2.30.1",
|
"html-webpack-plugin": "^2.30.1",
|
||||||
|
"is-docker": "^1.1.0",
|
||||||
|
"istanbul": "^0.4.5",
|
||||||
|
"istanbul-lib-coverage": "^1.1.1",
|
||||||
"json-loader": "^0.5.7",
|
"json-loader": "^0.5.7",
|
||||||
"karma": "^1.7.1",
|
"mkdirp": "^0.5.1",
|
||||||
"karma-chrome-launcher": "^2.2.0",
|
|
||||||
"karma-firefox-launcher": "^1.0.1",
|
|
||||||
"karma-mocha": "^1.3.0",
|
|
||||||
"karma-webpack": "^2.0.5",
|
|
||||||
"mocha": "^4.0.1",
|
"mocha": "^4.0.1",
|
||||||
"mocha-loader": "^1.1.1",
|
|
||||||
"node-sass": "^4.6.0",
|
"node-sass": "^4.6.0",
|
||||||
"react-hot-loader": "^3.1.1",
|
"react-hot-loader": "^3.1.1",
|
||||||
"sass-loader": "^6.0.6",
|
"sass-loader": "^6.0.6",
|
||||||
|
"selenium-standalone": "^6.11.0",
|
||||||
"style-loader": "^0.19.0",
|
"style-loader": "^0.19.0",
|
||||||
"stylelint": "^7.13.0",
|
"stylelint": "^7.13.0",
|
||||||
"stylelint-config-standard": "^15.0.1",
|
"stylelint-config-standard": "^15.0.1",
|
||||||
"transform-loader": "^0.2.4",
|
"transform-loader": "^0.2.4",
|
||||||
|
"uuid": "^3.1.0",
|
||||||
"wdio-mocha-framework": "^0.5.11",
|
"wdio-mocha-framework": "^0.5.11",
|
||||||
"wdio-phantomjs-service": "^0.2.2",
|
"wdio-phantomjs-service": "^0.2.2",
|
||||||
|
"wdio-selenium-standalone-service": "0.0.9",
|
||||||
"wdio-spec-reporter": "^0.1.2",
|
"wdio-spec-reporter": "^0.1.2",
|
||||||
"webdriverio": "^4.8.0",
|
"webdriverio": "^4.9.8",
|
||||||
"webpack": "^3.8.1",
|
"webpack": "^3.8.1",
|
||||||
"webpack-bundle-analyzer": "^2.9.0",
|
"webpack-bundle-analyzer": "^2.9.0",
|
||||||
"webpack-cleanup-plugin": "^0.5.1",
|
"webpack-cleanup-plugin": "^0.5.1",
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { RevisionStore } from '../libs/revisions'
|
||||||
import LayerWatcher from '../libs/layerwatcher'
|
import LayerWatcher from '../libs/layerwatcher'
|
||||||
import tokens from '../config/tokens.json'
|
import tokens from '../config/tokens.json'
|
||||||
import isEqual from 'lodash.isequal'
|
import isEqual from 'lodash.isequal'
|
||||||
|
import Debug from '../libs/debug'
|
||||||
|
|
||||||
function updateRootSpec(spec, fieldName, newValues) {
|
function updateRootSpec(spec, fieldName, newValues) {
|
||||||
return {
|
return {
|
||||||
|
@ -53,9 +54,19 @@ export default class App extends React.Component {
|
||||||
this.styleStore = new StyleStore()
|
this.styleStore = new StyleStore()
|
||||||
}
|
}
|
||||||
this.styleStore.latestStyle(mapStyle => this.onStyleChanged(mapStyle))
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
errors: [],
|
errors: [],
|
||||||
infos: [],
|
infos: [],
|
||||||
|
@ -70,22 +81,28 @@ export default class App extends React.Component {
|
||||||
this.layerWatcher = new LayerWatcher({
|
this.layerWatcher = new LayerWatcher({
|
||||||
onVectorLayersChange: v => this.setState({ vectorLayers: v })
|
onVectorLayersChange: v => this.setState({ vectorLayers: v })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.onKeyDown = this.onKeyDown.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown(e) {
|
||||||
|
console.log("??? keyCode ctrlKey="+e.ctrlKey+", keyCode="+e.keyCode)
|
||||||
|
// Control + Z
|
||||||
|
if(e.ctrlKey && e.keyCode === 90) {
|
||||||
|
this.onUndo(e);
|
||||||
|
}
|
||||||
|
else if(e.ctrlKey && e.keyCode === 89) {
|
||||||
|
this.onRedo(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.fetchSources();
|
this.fetchSources();
|
||||||
Mousetrap.bind(['ctrl+z'], this.onUndo.bind(this));
|
document.addEventListener("keydown", this.onKeyDown);
|
||||||
Mousetrap.bind(['ctrl+y'], this.onRedo.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
Mousetrap.unbind(['ctrl+z'], this.onUndo.bind(this));
|
document.removeEventListener("keydown", this.onKeyDown);
|
||||||
Mousetrap.unbind(['ctrl+y'], this.onRedo.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
onReset() {
|
|
||||||
this.styleStore.purge()
|
|
||||||
loadDefaultStyle(mapStyle => this.onStyleOpen(mapStyle))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
saveStyle(snapshotStyle) {
|
saveStyle(snapshotStyle) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import classnames from 'classnames'
|
||||||
|
|
||||||
class Button extends React.Component {
|
class Button extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
"data-wd-key": PropTypes.string,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
@ -14,6 +15,7 @@ class Button extends React.Component {
|
||||||
return <a
|
return <a
|
||||||
onClick={this.props.onClick}
|
onClick={this.props.onClick}
|
||||||
className={classnames("maputnik-button", this.props.className)}
|
className={classnames("maputnik-button", this.props.className)}
|
||||||
|
data-wd-key={this.props["data-wd-key"]}
|
||||||
style={this.props.style}>
|
style={this.props.style}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -63,6 +63,7 @@ class ToolbarAction extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return <a
|
return <a
|
||||||
className='maputnik-toolbar-action'
|
className='maputnik-toolbar-action'
|
||||||
|
data-wd-key={this.props.wdKey}
|
||||||
onClick={this.props.onClick}
|
onClick={this.props.onClick}
|
||||||
>
|
>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
@ -139,23 +140,23 @@ export default class Toolbar extends React.Component {
|
||||||
<h1>Maputnik</h1>
|
<h1>Maputnik</h1>
|
||||||
</ToolbarLink>
|
</ToolbarLink>
|
||||||
<div className="maputnik-toolbar__actions">
|
<div className="maputnik-toolbar__actions">
|
||||||
<ToolbarAction onClick={this.toggleModal.bind(this, 'open')}>
|
<ToolbarAction wdKey="nav:open" onClick={this.toggleModal.bind(this, 'open')}>
|
||||||
<OpenIcon />
|
<OpenIcon />
|
||||||
<IconText>Open</IconText>
|
<IconText>Open</IconText>
|
||||||
</ToolbarAction>
|
</ToolbarAction>
|
||||||
<ToolbarAction onClick={this.toggleModal.bind(this, 'export')}>
|
<ToolbarAction wdKey="nav:export" onClick={this.toggleModal.bind(this, 'export')}>
|
||||||
<MdFileDownload />
|
<MdFileDownload />
|
||||||
<IconText>Export</IconText>
|
<IconText>Export</IconText>
|
||||||
</ToolbarAction>
|
</ToolbarAction>
|
||||||
<ToolbarAction onClick={this.toggleModal.bind(this, 'sources')}>
|
<ToolbarAction wdKey="nav:sources" onClick={this.toggleModal.bind(this, 'sources')}>
|
||||||
<SourcesIcon />
|
<SourcesIcon />
|
||||||
<IconText>Sources</IconText>
|
<IconText>Sources</IconText>
|
||||||
</ToolbarAction>
|
</ToolbarAction>
|
||||||
<ToolbarAction onClick={this.toggleModal.bind(this, 'settings')}>
|
<ToolbarAction wdKey="nav:settings" onClick={this.toggleModal.bind(this, 'settings')}>
|
||||||
<SettingsIcon />
|
<SettingsIcon />
|
||||||
<IconText>Style Settings</IconText>
|
<IconText>Style Settings</IconText>
|
||||||
</ToolbarAction>
|
</ToolbarAction>
|
||||||
<ToolbarAction onClick={this.props.onInspectModeToggle}>
|
<ToolbarAction wdKey="nav:inspect" onClick={this.props.onInspectModeToggle}>
|
||||||
<InspectionIcon />
|
<InspectionIcon />
|
||||||
<IconText>
|
<IconText>
|
||||||
{ this.props.inspectModeEnabled && <span>Map Mode</span> }
|
{ this.props.inspectModeEnabled && <span>Map Mode</span> }
|
||||||
|
|
|
@ -131,8 +131,7 @@ export default class FunctionSpecProperty extends React.Component {
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
return <div className={propClass} data-wd-key={"spec-field:"+this.props.fieldName}>
|
||||||
return <div className={propClass}>
|
|
||||||
{specField}
|
{specField}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,70 +58,79 @@ export default class SpecField extends React.Component {
|
||||||
name: this.props.fieldName,
|
name: this.props.fieldName,
|
||||||
onChange: newValue => this.props.onChange(this.props.fieldName, newValue)
|
onChange: newValue => this.props.onChange(this.props.fieldName, newValue)
|
||||||
}
|
}
|
||||||
switch(this.props.fieldSpec.type) {
|
|
||||||
case 'number': return (
|
|
||||||
<NumberInput
|
|
||||||
{...commonProps}
|
|
||||||
min={this.props.fieldSpec.minimum}
|
|
||||||
max={this.props.fieldSpec.maximum}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'enum':
|
|
||||||
const options = Object.keys(this.props.fieldSpec.values).map(v => [v, capitalize(v)])
|
|
||||||
|
|
||||||
if(options.length <= 3 && optionsLabelLength(options) <= 20) {
|
function childNodes() {
|
||||||
return <MultiButtonInput
|
switch(this.props.fieldSpec.type) {
|
||||||
|
case 'number': return (
|
||||||
|
<NumberInput
|
||||||
{...commonProps}
|
{...commonProps}
|
||||||
options={options}
|
min={this.props.fieldSpec.minimum}
|
||||||
|
max={this.props.fieldSpec.maximum}
|
||||||
/>
|
/>
|
||||||
} else {
|
)
|
||||||
return <SelectInput
|
case 'enum':
|
||||||
{...commonProps}
|
const options = Object.keys(this.props.fieldSpec.values).map(v => [v, capitalize(v)])
|
||||||
options={options}
|
|
||||||
/>
|
if(options.length <= 3 && optionsLabelLength(options) <= 20) {
|
||||||
}
|
return <MultiButtonInput
|
||||||
case 'string':
|
|
||||||
if(iconProperties.indexOf(this.props.fieldName) >= 0) {
|
|
||||||
return <IconInput
|
|
||||||
{...commonProps}
|
|
||||||
icons={this.props.fieldSpec.values}
|
|
||||||
/>
|
|
||||||
} else {
|
|
||||||
return <StringInput
|
|
||||||
{...commonProps}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
case 'color': return (
|
|
||||||
<ColorField
|
|
||||||
{...commonProps}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'boolean': return (
|
|
||||||
<CheckboxInput
|
|
||||||
{...commonProps}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
case 'array':
|
|
||||||
if(this.props.fieldName === 'text-font') {
|
|
||||||
return <FontInput
|
|
||||||
{...commonProps}
|
|
||||||
fonts={this.props.fieldSpec.values}
|
|
||||||
/>
|
|
||||||
} else {
|
|
||||||
if (this.props.fieldSpec.length) {
|
|
||||||
return <ArrayInput
|
|
||||||
{...commonProps}
|
{...commonProps}
|
||||||
type={this.props.fieldSpec.value}
|
options={options}
|
||||||
length={this.props.fieldSpec.length}
|
|
||||||
/>
|
/>
|
||||||
} else {
|
} else {
|
||||||
return <DynamicArrayInput
|
return <SelectInput
|
||||||
{...commonProps}
|
{...commonProps}
|
||||||
type={this.props.fieldSpec.value}
|
options={options}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
}
|
case 'string':
|
||||||
default: return null
|
if(iconProperties.indexOf(this.props.fieldName) >= 0) {
|
||||||
|
return <IconInput
|
||||||
|
{...commonProps}
|
||||||
|
icons={this.props.fieldSpec.values}
|
||||||
|
/>
|
||||||
|
} else {
|
||||||
|
return <StringInput
|
||||||
|
{...commonProps}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
case 'color': return (
|
||||||
|
<ColorField
|
||||||
|
{...commonProps}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
case 'boolean': return (
|
||||||
|
<CheckboxInput
|
||||||
|
{...commonProps}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
case 'array':
|
||||||
|
if(this.props.fieldName === 'text-font') {
|
||||||
|
return <FontInput
|
||||||
|
{...commonProps}
|
||||||
|
fonts={this.props.fieldSpec.values}
|
||||||
|
/>
|
||||||
|
} else {
|
||||||
|
if (this.props.fieldSpec.length) {
|
||||||
|
return <ArrayInput
|
||||||
|
{...commonProps}
|
||||||
|
type={this.props.fieldSpec.value}
|
||||||
|
length={this.props.fieldSpec.length}
|
||||||
|
/>
|
||||||
|
} else {
|
||||||
|
return <DynamicArrayInput
|
||||||
|
{...commonProps}
|
||||||
|
type={this.props.fieldSpec.value}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-wd-key={"spec-field:"+this.props.fieldName}>
|
||||||
|
{childNodes.call(this)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ export default class CombiningFilterEditor extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="maputnik-filter-editor">
|
return <div className="maputnik-filter-editor">
|
||||||
<div className="maputnik-filter-editor-compound-select">
|
<div className="maputnik-filter-editor-compound-select" data-wd-key="layer-filter">
|
||||||
<DocLabel
|
<DocLabel
|
||||||
label={"Compound Filter"}
|
label={"Compound Filter"}
|
||||||
doc={styleSpec.latest.layer.filter.doc + " Combine multiple filters together by using a compound filter."}
|
doc={styleSpec.latest.layer.filter.doc + " Combine multiple filters together by using a compound filter."}
|
||||||
|
@ -103,6 +103,7 @@ export default class CombiningFilterEditor extends React.Component {
|
||||||
{editorBlocks}
|
{editorBlocks}
|
||||||
<div className="maputnik-filter-editor-add-wrapper">
|
<div className="maputnik-filter-editor-add-wrapper">
|
||||||
<Button
|
<Button
|
||||||
|
data-wd-key="layer-filter-button"
|
||||||
className="maputnik-add-filter"
|
className="maputnik-add-filter"
|
||||||
onClick={this.addFilterItem.bind(this)}>
|
onClick={this.addFilterItem.bind(this)}>
|
||||||
Add filter
|
Add filter
|
||||||
|
|
|
@ -23,7 +23,6 @@ class LayerIcon extends React.Component {
|
||||||
case 'line': return <LineIcon {...iconProps} />
|
case 'line': return <LineIcon {...iconProps} />
|
||||||
case 'symbol': return <SymbolIcon {...iconProps} />
|
case 'symbol': return <SymbolIcon {...iconProps} />
|
||||||
case 'circle': return <CircleIcon {...iconProps} />
|
case 'circle': return <CircleIcon {...iconProps} />
|
||||||
default: return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,8 +94,14 @@ class AutocompleteInput extends React.Component {
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
items={this.props.options}
|
items={this.props.options}
|
||||||
getItemValue={(item) => item[0]}
|
getItemValue={(item) => item[0]}
|
||||||
onSelect={v => this.props.onChange(v)}
|
onSelect={v => {
|
||||||
onChange={(e, v) => this.props.onChange(v)}
|
console.log("@@ onSelect", v)
|
||||||
|
this.props.onChange(v)
|
||||||
|
}}
|
||||||
|
onChange={(e, v) => {
|
||||||
|
console.log("@@ onChange", v)
|
||||||
|
this.props.onChange(v)
|
||||||
|
}}
|
||||||
renderItem={(item, isHighlighted) => (
|
renderItem={(item, isHighlighted) => (
|
||||||
<div
|
<div
|
||||||
key={item[0]}
|
key={item[0]}
|
||||||
|
|
|
@ -6,6 +6,7 @@ import DocLabel from '../fields/DocLabel'
|
||||||
/** Wrap a component with a label */
|
/** Wrap a component with a label */
|
||||||
class InputBlock extends React.Component {
|
class InputBlock extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
"data-wd-key": PropTypes.string,
|
||||||
label: PropTypes.oneOfType([
|
label: PropTypes.oneOfType([
|
||||||
PropTypes.string,
|
PropTypes.string,
|
||||||
PropTypes.element,
|
PropTypes.element,
|
||||||
|
@ -24,6 +25,7 @@ class InputBlock extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <div style={this.props.style}
|
return <div style={this.props.style}
|
||||||
|
data-wd-key={this.props["data-wd-key"]}
|
||||||
className={classnames({
|
className={classnames({
|
||||||
"maputnik-input-block": true,
|
"maputnik-input-block": true,
|
||||||
"maputnik-action-block": this.props.action
|
"maputnik-action-block": this.props.action
|
||||||
|
|
|
@ -4,6 +4,7 @@ import PropTypes from 'prop-types'
|
||||||
class SelectInput extends React.Component {
|
class SelectInput extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
value: PropTypes.string.isRequired,
|
value: PropTypes.string.isRequired,
|
||||||
|
"data-wd-key": PropTypes.string.isRequired,
|
||||||
options: PropTypes.array.isRequired,
|
options: PropTypes.array.isRequired,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
|
@ -18,6 +19,7 @@ class SelectInput extends React.Component {
|
||||||
|
|
||||||
return <select
|
return <select
|
||||||
className="maputnik-select"
|
className="maputnik-select"
|
||||||
|
data-wd-key={this.props["data-wd-key"]}
|
||||||
style={this.props.style}
|
style={this.props.style}
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
onChange={e => this.props.onChange(e.target.value)}
|
onChange={e => this.props.onChange(e.target.value)}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
|
||||||
|
|
||||||
class StringInput extends React.Component {
|
class StringInput extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
"data-wd-key": PropTypes.string,
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
default: PropTypes.string,
|
default: PropTypes.string,
|
||||||
|
@ -18,6 +19,7 @@ class StringInput extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps) {
|
componentWillReceiveProps(nextProps) {
|
||||||
|
console.log("@@ STRING componentWillReceiveProps", JSON.stringify(nextProps))
|
||||||
this.setState({ value: nextProps.value || '' })
|
this.setState({ value: nextProps.value || '' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,12 +42,19 @@ class StringInput extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return React.createElement(tag, {
|
return React.createElement(tag, {
|
||||||
|
"data-wd-key": this.props["data-wd-key"],
|
||||||
className: classes.join(" "),
|
className: classes.join(" "),
|
||||||
style: this.props.style,
|
style: this.props.style,
|
||||||
value: this.state.value,
|
value: this.state.value,
|
||||||
placeholder: this.props.default,
|
placeholder: this.props.default,
|
||||||
onChange: e => this.setState({ value: e.target.value }),
|
onChange: e => {
|
||||||
|
console.log("@@ STRING CHANGE", JSON.stringify(e.target.value));
|
||||||
|
this.setState({
|
||||||
|
value: e.target.value
|
||||||
|
})
|
||||||
|
},
|
||||||
onBlur: () => {
|
onBlur: () => {
|
||||||
|
console.log("@@ STRING BLUR", JSON.stringify(this.state.value), "props:", JSON.stringify(this.props.value));
|
||||||
if(this.state.value!==this.props.value) this.props.onChange(this.state.value)
|
if(this.state.value!==this.props.value) this.props.onChange(this.state.value)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,7 +11,11 @@ class MetadataBlock extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <InputBlock label={"Comments"} doc={"Comments for the current layer. This is non-standard and not in the spec."}>
|
return <InputBlock
|
||||||
|
label={"Comments"}
|
||||||
|
doc={"Comments for the current layer. This is non-standard and not in the spec."}
|
||||||
|
data-wd-key="layer-comment"
|
||||||
|
>
|
||||||
<StringInput
|
<StringInput
|
||||||
multi={true}
|
multi={true}
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types'
|
||||||
import {Controlled as CodeMirror} from 'react-codemirror2'
|
import {Controlled as CodeMirror} from 'react-codemirror2'
|
||||||
import InputBlock from '../inputs/InputBlock'
|
import InputBlock from '../inputs/InputBlock'
|
||||||
import StringInput from '../inputs/StringInput'
|
import StringInput from '../inputs/StringInput'
|
||||||
import SelectInput from '../inputs/SelectInput'
|
|
||||||
|
|
||||||
import 'codemirror/mode/javascript/javascript'
|
import 'codemirror/mode/javascript/javascript'
|
||||||
import 'codemirror/addon/lint/lint'
|
import 'codemirror/addon/lint/lint'
|
||||||
|
|
|
@ -19,11 +19,6 @@ import MultiButtonInput from '../inputs/MultiButtonInput'
|
||||||
import { changeType, changeProperty } from '../../libs/layer'
|
import { changeType, changeProperty } from '../../libs/layer'
|
||||||
import layout from '../../config/layout.json'
|
import layout from '../../config/layout.json'
|
||||||
|
|
||||||
class UnsupportedLayer extends React.Component {
|
|
||||||
render() {
|
|
||||||
return <div></div>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function layoutGroups(layerType) {
|
function layoutGroups(layerType) {
|
||||||
const layerGroup = {
|
const layerGroup = {
|
||||||
|
@ -121,6 +116,7 @@ export default class LayerEditor extends React.Component {
|
||||||
case 'layer': return <div>
|
case 'layer': return <div>
|
||||||
<LayerIdBlock
|
<LayerIdBlock
|
||||||
value={this.props.layer.id}
|
value={this.props.layer.id}
|
||||||
|
wdKey="layer-editor.layer-id"
|
||||||
onChange={newId => this.props.onLayerIdChange(this.props.layer.id, newId)}
|
onChange={newId => this.props.onLayerIdChange(this.props.layer.id, newId)}
|
||||||
/>
|
/>
|
||||||
<LayerTypeBlock
|
<LayerTypeBlock
|
||||||
|
@ -171,7 +167,6 @@ export default class LayerEditor extends React.Component {
|
||||||
layer={this.props.layer}
|
layer={this.props.layer}
|
||||||
onChange={this.props.onLayerChanged}
|
onChange={this.props.onLayerChanged}
|
||||||
/>
|
/>
|
||||||
default: return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +176,7 @@ export default class LayerEditor extends React.Component {
|
||||||
return !(layerType === 'background' && group.type === 'source')
|
return !(layerType === 'background' && group.type === 'source')
|
||||||
}).map(group => {
|
}).map(group => {
|
||||||
return <LayerEditorGroup
|
return <LayerEditorGroup
|
||||||
|
data-wd-key={group.title}
|
||||||
key={group.title}
|
key={group.title}
|
||||||
title={group.title}
|
title={group.title}
|
||||||
isActive={this.state.editorGroups[group.title]}
|
isActive={this.state.editorGroups[group.title]}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Collapser from './Collapser'
|
||||||
|
|
||||||
export default class LayerEditorGroup extends React.Component {
|
export default class LayerEditorGroup extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
"data-wd-key": PropTypes.string,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
isActive: PropTypes.bool.isRequired,
|
isActive: PropTypes.bool.isRequired,
|
||||||
children: PropTypes.element.isRequired,
|
children: PropTypes.element.isRequired,
|
||||||
|
@ -14,6 +15,7 @@ export default class LayerEditorGroup extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return <div>
|
return <div>
|
||||||
<div className="maputnik-layer-editor-group"
|
<div className="maputnik-layer-editor-group"
|
||||||
|
data-wd-key={"layer-editor-group:"+this.props["data-wd-key"]}
|
||||||
onClick={e => this.props.onActiveToggle(!this.props.isActive)}
|
onClick={e => this.props.onActiveToggle(!this.props.isActive)}
|
||||||
>
|
>
|
||||||
<span>{this.props.title}</span>
|
<span>{this.props.title}</span>
|
||||||
|
|
|
@ -8,11 +8,14 @@ import StringInput from '../inputs/StringInput'
|
||||||
class LayerIdBlock extends React.Component {
|
class LayerIdBlock extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
value: PropTypes.string.isRequired,
|
value: PropTypes.string.isRequired,
|
||||||
|
wdKey: PropTypes.string.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <InputBlock label={"ID"} doc={styleSpec.latest.layer.id.doc}>
|
return <InputBlock label={"ID"} doc={styleSpec.latest.layer.id.doc}
|
||||||
|
data-wd-key={this.props.wdKey}
|
||||||
|
>
|
||||||
<StringInput
|
<StringInput
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
onChange={this.props.onChange}
|
onChange={this.props.onChange}
|
||||||
|
|
|
@ -162,6 +162,7 @@ class LayerListContainer extends React.Component {
|
||||||
const groupPrefix = layerPrefix(layers[0].id)
|
const groupPrefix = layerPrefix(layers[0].id)
|
||||||
if(layers.length > 1) {
|
if(layers.length > 1) {
|
||||||
const grp = <LayerListGroup
|
const grp = <LayerListGroup
|
||||||
|
data-wd-key={[groupPrefix, idx].join('-')}
|
||||||
key={[groupPrefix, idx].join('-')}
|
key={[groupPrefix, idx].join('-')}
|
||||||
title={groupPrefix}
|
title={groupPrefix}
|
||||||
isActive={!this.isCollapsed(groupPrefix, idx)}
|
isActive={!this.isCollapsed(groupPrefix, idx)}
|
||||||
|
@ -217,6 +218,7 @@ class LayerListContainer extends React.Component {
|
||||||
<div className="maputnik-multibutton">
|
<div className="maputnik-multibutton">
|
||||||
<a
|
<a
|
||||||
onClick={this.toggleModal.bind(this, 'add')}
|
onClick={this.toggleModal.bind(this, 'add')}
|
||||||
|
data-wd-key="layer-list:add-layer"
|
||||||
className="maputnik-button maputnik-button-selected">
|
className="maputnik-button maputnik-button-selected">
|
||||||
Add Layer
|
Add Layer
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Collapser from './Collapser'
|
||||||
export default class LayerListGroup extends React.Component {
|
export default class LayerListGroup extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
|
"data-wd-key": PropTypes.string,
|
||||||
isActive: PropTypes.bool.isRequired,
|
isActive: PropTypes.bool.isRequired,
|
||||||
onActiveToggle: PropTypes.func.isRequired
|
onActiveToggle: PropTypes.func.isRequired
|
||||||
}
|
}
|
||||||
|
@ -12,6 +13,7 @@ export default class LayerListGroup extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
return <li className="maputnik-layer-list-group">
|
return <li className="maputnik-layer-list-group">
|
||||||
<div className="maputnik-layer-list-group-header"
|
<div className="maputnik-layer-list-group-header"
|
||||||
|
data-wd-key={"layer-list-group:"+this.props["data-wd-key"]}
|
||||||
onClick={e => this.props.onActiveToggle(!this.props.isActive)}
|
onClick={e => this.props.onActiveToggle(!this.props.isActive)}
|
||||||
>
|
>
|
||||||
<span className="maputnik-layer-list-group-title">{this.props.title}</span>
|
<span className="maputnik-layer-list-group-title">{this.props.title}</span>
|
||||||
|
|
|
@ -33,6 +33,7 @@ class IconAction extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
action: PropTypes.string.isRequired,
|
action: PropTypes.string.isRequired,
|
||||||
onClick: PropTypes.func.isRequired,
|
onClick: PropTypes.func.isRequired,
|
||||||
|
wdKey: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
renderIcon() {
|
renderIcon() {
|
||||||
|
@ -41,13 +42,13 @@ class IconAction extends React.Component {
|
||||||
case 'show': return <VisibilityIcon />
|
case 'show': return <VisibilityIcon />
|
||||||
case 'hide': return <VisibilityOffIcon />
|
case 'hide': return <VisibilityOffIcon />
|
||||||
case 'delete': return <DeleteIcon />
|
case 'delete': return <DeleteIcon />
|
||||||
default: return null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <a
|
return <a
|
||||||
className="maputnik-layer-list-icon-action"
|
className="maputnik-layer-list-icon-action"
|
||||||
|
data-wd-key={this.props.wdKey}
|
||||||
onClick={this.props.onClick}
|
onClick={this.props.onClick}
|
||||||
>
|
>
|
||||||
{this.renderIcon()}
|
{this.renderIcon()}
|
||||||
|
@ -92,6 +93,7 @@ class LayerListItem extends React.Component {
|
||||||
return <li
|
return <li
|
||||||
key={this.props.layerId}
|
key={this.props.layerId}
|
||||||
onClick={e => this.props.onLayerSelect(this.props.layerId)}
|
onClick={e => this.props.onLayerSelect(this.props.layerId)}
|
||||||
|
data-wd-key={"layer-list-item:"+this.props.layerId}
|
||||||
className={classnames({
|
className={classnames({
|
||||||
"maputnik-layer-list-item": true,
|
"maputnik-layer-list-item": true,
|
||||||
"maputnik-layer-list-item-selected": this.props.isSelected,
|
"maputnik-layer-list-item-selected": this.props.isSelected,
|
||||||
|
@ -101,14 +103,17 @@ class LayerListItem extends React.Component {
|
||||||
<span className="maputnik-layer-list-item-id">{this.props.layerId}</span>
|
<span className="maputnik-layer-list-item-id">{this.props.layerId}</span>
|
||||||
<span style={{flexGrow: 1}} />
|
<span style={{flexGrow: 1}} />
|
||||||
<IconAction
|
<IconAction
|
||||||
|
wdKey={"layer-list-item:"+this.props.layerId+":delete"}
|
||||||
action={'delete'}
|
action={'delete'}
|
||||||
onClick={e => this.props.onLayerDestroy(this.props.layerId)}
|
onClick={e => this.props.onLayerDestroy(this.props.layerId)}
|
||||||
/>
|
/>
|
||||||
<IconAction
|
<IconAction
|
||||||
|
wdKey={"layer-list-item:"+this.props.layerId+":copy"}
|
||||||
action={'copy'}
|
action={'copy'}
|
||||||
onClick={e => this.props.onLayerCopy(this.props.layerId)}
|
onClick={e => this.props.onLayerCopy(this.props.layerId)}
|
||||||
/>
|
/>
|
||||||
<IconAction
|
<IconAction
|
||||||
|
wdKey={"layer-list-item:"+this.props.layerId+":toggle-visibility"}
|
||||||
action={this.props.visibility === 'visible' ? 'hide' : 'show'}
|
action={this.props.visibility === 'visible' ? 'hide' : 'show'}
|
||||||
onClick={e => this.props.onLayerVisibilityToggle(this.props.layerId)}
|
onClick={e => this.props.onLayerVisibilityToggle(this.props.layerId)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -4,12 +4,12 @@ import PropTypes from 'prop-types'
|
||||||
import styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
import styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
||||||
import InputBlock from '../inputs/InputBlock'
|
import InputBlock from '../inputs/InputBlock'
|
||||||
import StringInput from '../inputs/StringInput'
|
import StringInput from '../inputs/StringInput'
|
||||||
import SelectInput from '../inputs/SelectInput'
|
|
||||||
import AutocompleteInput from '../inputs/AutocompleteInput'
|
import AutocompleteInput from '../inputs/AutocompleteInput'
|
||||||
|
|
||||||
class LayerSourceBlock extends React.Component {
|
class LayerSourceBlock extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
value: PropTypes.string,
|
value: PropTypes.string,
|
||||||
|
wdKey: PropTypes.string,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
sourceIds: PropTypes.array,
|
sourceIds: PropTypes.array,
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,9 @@ class LayerSourceBlock extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <InputBlock label={"Source"} doc={styleSpec.latest.layer.source.doc}>
|
return <InputBlock label={"Source"} doc={styleSpec.latest.layer.source.doc}
|
||||||
|
data-wd-key={this.props.wdKey}
|
||||||
|
>
|
||||||
<AutocompleteInput
|
<AutocompleteInput
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
onChange={this.props.onChange}
|
onChange={this.props.onChange}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types'
|
||||||
import styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
import styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
||||||
import InputBlock from '../inputs/InputBlock'
|
import InputBlock from '../inputs/InputBlock'
|
||||||
import StringInput from '../inputs/StringInput'
|
import StringInput from '../inputs/StringInput'
|
||||||
import SelectInput from '../inputs/SelectInput'
|
|
||||||
import AutocompleteInput from '../inputs/AutocompleteInput'
|
import AutocompleteInput from '../inputs/AutocompleteInput'
|
||||||
|
|
||||||
class LayerSourceLayer extends React.Component {
|
class LayerSourceLayer extends React.Component {
|
||||||
|
@ -22,7 +21,9 @@ class LayerSourceLayer extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <InputBlock label={"Source Layer"} doc={styleSpec.latest.layer['source-layer'].doc}>
|
return <InputBlock label={"Source Layer"} doc={styleSpec.latest.layer['source-layer'].doc}
|
||||||
|
data-wd-key="layer-source-layer"
|
||||||
|
>
|
||||||
<AutocompleteInput
|
<AutocompleteInput
|
||||||
keepMenuWithinWindowBounds={!!this.props.isFixed}
|
keepMenuWithinWindowBounds={!!this.props.isFixed}
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
|
|
|
@ -8,11 +8,14 @@ import SelectInput from '../inputs/SelectInput'
|
||||||
class LayerTypeBlock extends React.Component {
|
class LayerTypeBlock extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
value: PropTypes.string.isRequired,
|
value: PropTypes.string.isRequired,
|
||||||
|
wdKey: PropTypes.string.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <InputBlock label={"Type"} doc={styleSpec.latest.layer.type.doc}>
|
return <InputBlock label={"Type"} doc={styleSpec.latest.layer.type.doc}
|
||||||
|
data-wd-key={this.props.wdKey}
|
||||||
|
>
|
||||||
<SelectInput
|
<SelectInput
|
||||||
options={[
|
options={[
|
||||||
['background', 'Background'],
|
['background', 'Background'],
|
||||||
|
|
|
@ -12,7 +12,9 @@ class MaxZoomBlock extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <InputBlock label={"Max Zoom"} doc={styleSpec.latest.layer.maxzoom.doc}>
|
return <InputBlock label={"Max Zoom"} doc={styleSpec.latest.layer.maxzoom.doc}
|
||||||
|
data-wd-key="max-zoom"
|
||||||
|
>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
onChange={this.props.onChange}
|
onChange={this.props.onChange}
|
||||||
|
|
|
@ -12,7 +12,9 @@ class MinZoomBlock extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <InputBlock label={"Min Zoom"} doc={styleSpec.latest.layer.minzoom.doc}>
|
return <InputBlock label={"Min Zoom"} doc={styleSpec.latest.layer.minzoom.doc}
|
||||||
|
data-wd-key="min-zoom"
|
||||||
|
>
|
||||||
<NumberInput
|
<NumberInput
|
||||||
value={this.props.value}
|
value={this.props.value}
|
||||||
onChange={this.props.onChange}
|
onChange={this.props.onChange}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import PropTypes from 'prop-types'
|
||||||
import Button from '../Button'
|
import Button from '../Button'
|
||||||
import InputBlock from '../inputs/InputBlock'
|
import InputBlock from '../inputs/InputBlock'
|
||||||
import StringInput from '../inputs/StringInput'
|
import StringInput from '../inputs/StringInput'
|
||||||
import SelectInput from '../inputs/SelectInput'
|
|
||||||
import Modal from './Modal'
|
import Modal from './Modal'
|
||||||
|
|
||||||
import LayerTypeBlock from '../layers/LayerTypeBlock'
|
import LayerTypeBlock from '../layers/LayerTypeBlock'
|
||||||
|
@ -108,19 +107,26 @@ class AddModal extends React.Component {
|
||||||
isOpen={this.props.isOpen}
|
isOpen={this.props.isOpen}
|
||||||
onOpenToggle={this.props.onOpenToggle}
|
onOpenToggle={this.props.onOpenToggle}
|
||||||
title={'Add Layer'}
|
title={'Add Layer'}
|
||||||
|
data-wd-key="modal:add-layer"
|
||||||
>
|
>
|
||||||
<div className="maputnik-add-layer">
|
<div className="maputnik-add-layer">
|
||||||
<LayerIdBlock
|
<LayerIdBlock
|
||||||
value={this.state.id}
|
value={this.state.id}
|
||||||
onChange={v => this.setState({ id: v })}
|
wdKey="add-layer.layer-id"
|
||||||
|
onChange={v => {
|
||||||
|
console.log("@@ upper_id_change", JSON.stringify(v))
|
||||||
|
this.setState({ id: v })
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<LayerTypeBlock
|
<LayerTypeBlock
|
||||||
value={this.state.type}
|
value={this.state.type}
|
||||||
|
wdKey="add-layer.layer-type"
|
||||||
onChange={v => this.setState({ type: v })}
|
onChange={v => this.setState({ type: v })}
|
||||||
/>
|
/>
|
||||||
{this.state.type !== 'background' &&
|
{this.state.type !== 'background' &&
|
||||||
<LayerSourceBlock
|
<LayerSourceBlock
|
||||||
sourceIds={sources}
|
sourceIds={sources}
|
||||||
|
wdKey="add-layer.layer-source-block"
|
||||||
value={this.state.source}
|
value={this.state.source}
|
||||||
onChange={v => this.setState({ source: v })}
|
onChange={v => this.setState({ source: v })}
|
||||||
/>
|
/>
|
||||||
|
@ -133,7 +139,11 @@ class AddModal extends React.Component {
|
||||||
onChange={v => this.setState({ 'source-layer': v })}
|
onChange={v => this.setState({ 'source-layer': v })}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
<Button className="maputnik-add-layer-button" onClick={this.addLayer.bind(this)}>
|
<Button
|
||||||
|
className="maputnik-add-layer-button"
|
||||||
|
onClick={this.addLayer.bind(this)}
|
||||||
|
data-wd-key="add-layer"
|
||||||
|
>
|
||||||
Add Layer
|
Add Layer
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,7 +5,6 @@ import { saveAs } from 'file-saver'
|
||||||
import styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
import styleSpec from '@mapbox/mapbox-gl-style-spec/style-spec'
|
||||||
import InputBlock from '../inputs/InputBlock'
|
import InputBlock from '../inputs/InputBlock'
|
||||||
import StringInput from '../inputs/StringInput'
|
import StringInput from '../inputs/StringInput'
|
||||||
import SelectInput from '../inputs/SelectInput'
|
|
||||||
import CheckboxInput from '../inputs/CheckboxInput'
|
import CheckboxInput from '../inputs/CheckboxInput'
|
||||||
import Button from '../Button'
|
import Button from '../Button'
|
||||||
import Modal from './Modal'
|
import Modal from './Modal'
|
||||||
|
@ -215,6 +214,7 @@ class ExportModal extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <Modal
|
return <Modal
|
||||||
|
data-wd-key="export-modal"
|
||||||
isOpen={this.props.isOpen}
|
isOpen={this.props.isOpen}
|
||||||
onOpenToggle={this.props.onOpenToggle}
|
onOpenToggle={this.props.onOpenToggle}
|
||||||
title={'Export Style'}
|
title={'Export Style'}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Overlay from './Overlay'
|
||||||
|
|
||||||
class Modal extends React.Component {
|
class Modal extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
"data-wd-key": PropTypes.string,
|
||||||
isOpen: PropTypes.bool.isRequired,
|
isOpen: PropTypes.bool.isRequired,
|
||||||
title: PropTypes.string.isRequired,
|
title: PropTypes.string.isRequired,
|
||||||
onOpenToggle: PropTypes.func.isRequired,
|
onOpenToggle: PropTypes.func.isRequired,
|
||||||
|
@ -13,12 +14,15 @@ class Modal extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return <Overlay isOpen={this.props.isOpen}>
|
return <Overlay isOpen={this.props.isOpen}>
|
||||||
<div className="maputnik-modal">
|
<div className="maputnik-modal"
|
||||||
|
data-wd-key={this.props["data-wd-key"]}
|
||||||
|
>
|
||||||
<header className="maputnik-modal-header">
|
<header className="maputnik-modal-header">
|
||||||
<h1 className="maputnik-modal-header-title">{this.props.title}</h1>
|
<h1 className="maputnik-modal-header-title">{this.props.title}</h1>
|
||||||
<span className="maputnik-modal-header-space"></span>
|
<span className="maputnik-modal-header-space"></span>
|
||||||
<a className="maputnik-modal-header-toggle"
|
<a className="maputnik-modal-header-toggle"
|
||||||
onClick={() => this.props.onOpenToggle(false)}
|
onClick={() => this.props.onOpenToggle(false)}
|
||||||
|
data-wd-key={this.props["data-wd-key"]+".close-modal"}
|
||||||
>
|
>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -133,6 +133,7 @@ class OpenModal extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Modal
|
return <Modal
|
||||||
|
data-wd-key="open-modal"
|
||||||
isOpen={this.props.isOpen}
|
isOpen={this.props.isOpen}
|
||||||
onOpenToggle={() => this.onOpenToggle()}
|
onOpenToggle={() => this.onOpenToggle()}
|
||||||
title={'Open Style'}
|
title={'Open Style'}
|
||||||
|
@ -151,9 +152,9 @@ class OpenModal extends React.Component {
|
||||||
<p>
|
<p>
|
||||||
Load from a URL. Note that the URL must have <a href="https://enable-cors.org" target="_blank" rel="noopener noreferrer">CORS enabled</a>.
|
Load from a URL. Note that the URL must have <a href="https://enable-cors.org" target="_blank" rel="noopener noreferrer">CORS enabled</a>.
|
||||||
</p>
|
</p>
|
||||||
<input type="text" ref={(input) => this.styleUrlElement = input} className="maputnik-input" placeholder="Enter URL..."/>
|
<input data-wd-key="open-modal.url.input" type="text" ref={(input) => this.styleUrlElement = input} className="maputnik-input" placeholder="Enter URL..."/>
|
||||||
<div>
|
<div>
|
||||||
<Button className="maputnik-big-button" onClick={this.onOpenUrl.bind(this)}>Open URL</Button>
|
<Button data-wd-key="open-modal.url.button" className="maputnik-big-button" onClick={this.onOpenUrl.bind(this)}>Open URL</Button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ class SettingsModal extends React.Component {
|
||||||
const metadata = this.props.mapStyle.metadata || {}
|
const metadata = this.props.mapStyle.metadata || {}
|
||||||
const inputProps = { }
|
const inputProps = { }
|
||||||
return <Modal
|
return <Modal
|
||||||
|
data-wd-key="modal-settings"
|
||||||
isOpen={this.props.isOpen}
|
isOpen={this.props.isOpen}
|
||||||
onOpenToggle={this.props.onOpenToggle}
|
onOpenToggle={this.props.onOpenToggle}
|
||||||
title={'Style Settings'}
|
title={'Style Settings'}
|
||||||
|
@ -49,18 +50,21 @@ class SettingsModal extends React.Component {
|
||||||
<div style={{minWidth: 350}}>
|
<div style={{minWidth: 350}}>
|
||||||
<InputBlock label={"Name"} doc={styleSpec.latest.$root.name.doc}>
|
<InputBlock label={"Name"} doc={styleSpec.latest.$root.name.doc}>
|
||||||
<StringInput {...inputProps}
|
<StringInput {...inputProps}
|
||||||
|
data-wd-key="modal-settings.name"
|
||||||
value={this.props.mapStyle.name}
|
value={this.props.mapStyle.name}
|
||||||
onChange={this.changeStyleProperty.bind(this, "name")}
|
onChange={this.changeStyleProperty.bind(this, "name")}
|
||||||
/>
|
/>
|
||||||
</InputBlock>
|
</InputBlock>
|
||||||
<InputBlock label={"Owner"} doc={"Owner ID of the style. Used by Mapbox or future style APIs."}>
|
<InputBlock label={"Owner"} doc={"Owner ID of the style. Used by Mapbox or future style APIs."}>
|
||||||
<StringInput {...inputProps}
|
<StringInput {...inputProps}
|
||||||
|
data-wd-key="modal-settings.owner"
|
||||||
value={this.props.mapStyle.owner}
|
value={this.props.mapStyle.owner}
|
||||||
onChange={this.changeStyleProperty.bind(this, "owner")}
|
onChange={this.changeStyleProperty.bind(this, "owner")}
|
||||||
/>
|
/>
|
||||||
</InputBlock>
|
</InputBlock>
|
||||||
<InputBlock label={"Sprite URL"} doc={styleSpec.latest.$root.sprite.doc}>
|
<InputBlock label={"Sprite URL"} doc={styleSpec.latest.$root.sprite.doc}>
|
||||||
<StringInput {...inputProps}
|
<StringInput {...inputProps}
|
||||||
|
data-wd-key="modal-settings.sprite"
|
||||||
value={this.props.mapStyle.sprite}
|
value={this.props.mapStyle.sprite}
|
||||||
onChange={this.changeStyleProperty.bind(this, "sprite")}
|
onChange={this.changeStyleProperty.bind(this, "sprite")}
|
||||||
/>
|
/>
|
||||||
|
@ -68,6 +72,7 @@ class SettingsModal extends React.Component {
|
||||||
|
|
||||||
<InputBlock label={"Glyphs URL"} doc={styleSpec.latest.$root.glyphs.doc}>
|
<InputBlock label={"Glyphs URL"} doc={styleSpec.latest.$root.glyphs.doc}>
|
||||||
<StringInput {...inputProps}
|
<StringInput {...inputProps}
|
||||||
|
data-wd-key="modal-settings.glyphs"
|
||||||
value={this.props.mapStyle.glyphs}
|
value={this.props.mapStyle.glyphs}
|
||||||
onChange={this.changeStyleProperty.bind(this, "glyphs")}
|
onChange={this.changeStyleProperty.bind(this, "glyphs")}
|
||||||
/>
|
/>
|
||||||
|
@ -75,6 +80,7 @@ class SettingsModal extends React.Component {
|
||||||
|
|
||||||
<InputBlock label={"Mapbox Access Token"} doc={"Public access token for Mapbox services."}>
|
<InputBlock label={"Mapbox Access Token"} doc={"Public access token for Mapbox services."}>
|
||||||
<StringInput {...inputProps}
|
<StringInput {...inputProps}
|
||||||
|
data-wd-key="modal-settings.maputnik:mapbox_access_token"
|
||||||
value={metadata['maputnik:mapbox_access_token']}
|
value={metadata['maputnik:mapbox_access_token']}
|
||||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
|
onChange={this.changeMetadataProperty.bind(this, "maputnik:mapbox_access_token")}
|
||||||
/>
|
/>
|
||||||
|
@ -82,6 +88,7 @@ class SettingsModal extends React.Component {
|
||||||
|
|
||||||
<InputBlock label={"OpenMapTiles Access Token"} doc={"Public access token for the OpenMapTiles CDN."}>
|
<InputBlock label={"OpenMapTiles Access Token"} doc={"Public access token for the OpenMapTiles CDN."}>
|
||||||
<StringInput {...inputProps}
|
<StringInput {...inputProps}
|
||||||
|
data-wd-key="modal-settings.maputnik:openmaptiles_access_token"
|
||||||
value={metadata['maputnik:openmaptiles_access_token']}
|
value={metadata['maputnik:openmaptiles_access_token']}
|
||||||
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
|
onChange={this.changeMetadataProperty.bind(this, "maputnik:openmaptiles_access_token")}
|
||||||
/>
|
/>
|
||||||
|
@ -89,6 +96,7 @@ class SettingsModal extends React.Component {
|
||||||
|
|
||||||
<InputBlock label={"Style Renderer"} doc={"Choose the default Maputnik renderer for this style."}>
|
<InputBlock label={"Style Renderer"} doc={"Choose the default Maputnik renderer for this style."}>
|
||||||
<SelectInput {...inputProps}
|
<SelectInput {...inputProps}
|
||||||
|
data-wd-key="modal-settings.maputnik:renderer"
|
||||||
options={[
|
options={[
|
||||||
['mbgljs', 'MapboxGL JS'],
|
['mbgljs', 'MapboxGL JS'],
|
||||||
['ol3', 'Open Layers 3'],
|
['ol3', 'Open Layers 3'],
|
||||||
|
|
44
src/libs/debug.js
Normal file
44
src/libs/debug.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
import querystring from 'querystring'
|
||||||
|
|
||||||
|
|
||||||
|
const debugStore = {};
|
||||||
|
|
||||||
|
function enabled() {
|
||||||
|
const qs = querystring.parse(window.location.search.slice(1));
|
||||||
|
if(qs.hasOwnProperty("debug")) {
|
||||||
|
return !!qs.debug.match(/^(|1|true)$/);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function genErr() {
|
||||||
|
return new Error("Debug not enabled, enable by appending '?debug' to your query string");
|
||||||
|
}
|
||||||
|
|
||||||
|
function set(namespace, key, value) {
|
||||||
|
if(!enabled()) {
|
||||||
|
throw genErr();
|
||||||
|
}
|
||||||
|
debugStore[namespace] = debugStore[namespace] || {};
|
||||||
|
debugStore[namespace][key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(namespace, key) {
|
||||||
|
if(!enabled()) {
|
||||||
|
throw genErr();
|
||||||
|
}
|
||||||
|
if(debugStore.hasOwnProperty(namespace)) {
|
||||||
|
return debugStore[namespace][key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mod = {
|
||||||
|
enabled,
|
||||||
|
get,
|
||||||
|
set
|
||||||
|
}
|
||||||
|
|
||||||
|
window.debug = mod;
|
||||||
|
export default mod;
|
|
@ -36,3 +36,15 @@ $toolbar-offset: 0;
|
||||||
@import 'zoomproperty';
|
@import 'zoomproperty';
|
||||||
@import 'popup';
|
@import 'popup';
|
||||||
@import 'map';
|
@import 'map';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hacks for webdriverio isVisibleWithinViewport
|
||||||
|
*/
|
||||||
|
#app {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maputnik-layout {
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
|
39
test/artifacts.js
Normal file
39
test/artifacts.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
var path = require("path");
|
||||||
|
var mkdirp = require("mkdirp");
|
||||||
|
|
||||||
|
|
||||||
|
function genPath(subPath) {
|
||||||
|
subPath = subPath || ".";
|
||||||
|
var buildPath;
|
||||||
|
|
||||||
|
if(process.env.CIRCLE_ARTIFACTS) {
|
||||||
|
buildPath = path.join(process.env.CIRCLE_ARTIFACTS, subPath);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buildPath = path.join(__dirname, '..', 'build', subPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buildPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.path = function(subPath) {
|
||||||
|
var dirPath = genPath(subPath);
|
||||||
|
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
mkdirp(dirPath, function(err) {
|
||||||
|
if(err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resolve(dirPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.pathSync = function(subPath) {
|
||||||
|
var dirPath = genPath(subPath);
|
||||||
|
mkdirp.sync(dirPath);
|
||||||
|
return dirPath;
|
||||||
|
}
|
||||||
|
|
12
test/example-style.json
Normal file
12
test/example-style.json
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"id": "test-style",
|
||||||
|
"version": 8,
|
||||||
|
"name": "Test Style",
|
||||||
|
"metadata": {
|
||||||
|
"maputnik:renderer": "mbgljs"
|
||||||
|
},
|
||||||
|
"sources": {},
|
||||||
|
"glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"layers": []
|
||||||
|
}
|
1077
test/functional/advanced.js
Normal file
1077
test/functional/advanced.js
Normal file
File diff suppressed because it is too large
Load diff
93
test/geojson-server.js
Normal file
93
test/geojson-server.js
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
const cors = require("cors");
|
||||||
|
const express = require("express");
|
||||||
|
const fs = require("fs");
|
||||||
|
const sourceData = require("./sources");
|
||||||
|
|
||||||
|
|
||||||
|
var app = express();
|
||||||
|
|
||||||
|
app.use(cors());
|
||||||
|
|
||||||
|
|
||||||
|
function buildStyle(opts) {
|
||||||
|
opts = opts || {};
|
||||||
|
opts = Object.assign({
|
||||||
|
sources: {}
|
||||||
|
}, opts);
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": "test-style",
|
||||||
|
"version": 8,
|
||||||
|
"name": "Test Style",
|
||||||
|
"metadata": {
|
||||||
|
"maputnik:renderer": "mbgljs"
|
||||||
|
},
|
||||||
|
"sources": opts.sources,
|
||||||
|
"glyphs": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"sprites": "https://demo.tileserver.org/fonts/{fontstack}/{range}.pbf",
|
||||||
|
"layers": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildGeoJSONSource(data) {
|
||||||
|
return {
|
||||||
|
type: "vector",
|
||||||
|
data: data
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildResterSource(req, key) {
|
||||||
|
return {
|
||||||
|
"tileSize": 256,
|
||||||
|
"tiles": [
|
||||||
|
req.protocol + '://' + req.get('host') + "/" + key + "/{x}/{y}/{z}"
|
||||||
|
],
|
||||||
|
"type": "raster"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
app.get("/sources/raster/{x}/{y}/{z}", function(req, res) {
|
||||||
|
res.status(404).end();
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get("/styles/empty/:sources", function(req, res) {
|
||||||
|
var reqSources = req.params.sources.split(",");
|
||||||
|
|
||||||
|
var sources = {};
|
||||||
|
// reqSources.forEach(function(key) {
|
||||||
|
// sources[key] = buildGeoJSONSource(sourceData[key]);
|
||||||
|
// });
|
||||||
|
reqSources.forEach(function(key) {
|
||||||
|
var parts = key.split(":");
|
||||||
|
var type = parts[0];
|
||||||
|
var key = parts[1];
|
||||||
|
|
||||||
|
if(type === "geojson") {
|
||||||
|
sources[key] = buildGeoJSONSource(sourceData[key]);
|
||||||
|
}
|
||||||
|
else if(type === "raster") {
|
||||||
|
sources[key] = buildResterSource(req, key);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error("ERR: Invalid type: %s", type);
|
||||||
|
throw "Invalid type"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var json = buildStyle({
|
||||||
|
sources: sources
|
||||||
|
});
|
||||||
|
res.send(json);
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get("/example-style.json", function(req, res) {
|
||||||
|
res.json(
|
||||||
|
JSON.parse(
|
||||||
|
fs.readFileSync(__dirname+"/example-style.json").toString()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = app;
|
15
test/sources/example.json
Normal file
15
test/sources/example.json
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"type":"FeatureCollection",
|
||||||
|
"features":[
|
||||||
|
{
|
||||||
|
"type":"Feature",
|
||||||
|
"properties": {
|
||||||
|
"name": "Dinagat Islands"
|
||||||
|
},
|
||||||
|
"geometry":{
|
||||||
|
"type": "Point",
|
||||||
|
"coordinates": [125.6, 10.1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
3
test/sources/index.js
Normal file
3
test/sources/index.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
module.exports = {
|
||||||
|
example: require("./example")
|
||||||
|
};
|
|
@ -1,15 +0,0 @@
|
||||||
var assert = require('assert');
|
|
||||||
var config = require("../config/specs");
|
|
||||||
|
|
||||||
|
|
||||||
describe('maputnik', function() {
|
|
||||||
|
|
||||||
it('check logo exists', function () {
|
|
||||||
browser.url(config.baseUrl);
|
|
||||||
browser.waitForExist(".maputnik-toolbar-link");
|
|
||||||
|
|
||||||
var src = browser.getAttribute(".maputnik-toolbar-link img", "src");
|
|
||||||
assert.equal(src, config.baseUrl+'/img/logo-color.svg');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
6
test/wd-helper.js
Normal file
6
test/wd-helper.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
"$": function(key, selector) {
|
||||||
|
selector = selector || "";
|
||||||
|
return "*[data-wd-key='"+key+"'] "+selector;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue