[adhoc-filters] Adding adhoc-filters to all viz types (#5206)

This commit is contained in:
John Bodley 2018-06-18 15:43:18 -07:00 committed by GitHub
parent 1fc4ee0d3c
commit d483ed121c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1012 additions and 980 deletions

0
babel-node Normal file
View File

743
package-lock.json generated Normal file
View File

@ -0,0 +1,743 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"ansi-styles": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
},
"babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
"requires": {
"chalk": "^1.1.3",
"esutils": "^2.0.2",
"js-tokens": "^3.0.2"
}
},
"babel-helper-builder-binary-assignment-operator-visitor": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz",
"integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=",
"dev": true,
"requires": {
"babel-helper-explode-assignable-expression": "^6.24.1",
"babel-runtime": "^6.22.0",
"babel-types": "^6.24.1"
}
},
"babel-helper-call-delegate": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
"integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=",
"requires": {
"babel-helper-hoist-variables": "^6.24.1",
"babel-runtime": "^6.22.0",
"babel-traverse": "^6.24.1",
"babel-types": "^6.24.1"
}
},
"babel-helper-define-map": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz",
"integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=",
"requires": {
"babel-helper-function-name": "^6.24.1",
"babel-runtime": "^6.26.0",
"babel-types": "^6.26.0",
"lodash": "^4.17.4"
}
},
"babel-helper-explode-assignable-expression": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz",
"integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=",
"dev": true,
"requires": {
"babel-runtime": "^6.22.0",
"babel-traverse": "^6.24.1",
"babel-types": "^6.24.1"
}
},
"babel-helper-function-name": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
"integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
"requires": {
"babel-helper-get-function-arity": "^6.24.1",
"babel-runtime": "^6.22.0",
"babel-template": "^6.24.1",
"babel-traverse": "^6.24.1",
"babel-types": "^6.24.1"
}
},
"babel-helper-get-function-arity": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
"integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
"requires": {
"babel-runtime": "^6.22.0",
"babel-types": "^6.24.1"
}
},
"babel-helper-hoist-variables": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
"integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=",
"requires": {
"babel-runtime": "^6.22.0",
"babel-types": "^6.24.1"
}
},
"babel-helper-optimise-call-expression": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
"integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=",
"requires": {
"babel-runtime": "^6.22.0",
"babel-types": "^6.24.1"
}
},
"babel-helper-regex": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz",
"integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=",
"requires": {
"babel-runtime": "^6.26.0",
"babel-types": "^6.26.0",
"lodash": "^4.17.4"
}
},
"babel-helper-remap-async-to-generator": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz",
"integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=",
"dev": true,
"requires": {
"babel-helper-function-name": "^6.24.1",
"babel-runtime": "^6.22.0",
"babel-template": "^6.24.1",
"babel-traverse": "^6.24.1",
"babel-types": "^6.24.1"
}
},
"babel-helper-replace-supers": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
"integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=",
"requires": {
"babel-helper-optimise-call-expression": "^6.24.1",
"babel-messages": "^6.23.0",
"babel-runtime": "^6.22.0",
"babel-template": "^6.24.1",
"babel-traverse": "^6.24.1",
"babel-types": "^6.24.1"
}
},
"babel-messages": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
"integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
"requires": {
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-check-es2015-constants": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
"integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
"requires": {
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-dynamic-import-node": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-1.2.0.tgz",
"integrity": "sha512-yeDwKaLgGdTpXL7RgGt5r6T4LmnTza/hUn5Ul8uZSGGMtEjYo13Nxai7SQaGCTEzUtg9Zq9qJn0EjEr7SeSlTQ==",
"requires": {
"babel-plugin-syntax-dynamic-import": "^6.18.0"
}
},
"babel-plugin-syntax-async-functions": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz",
"integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=",
"dev": true
},
"babel-plugin-syntax-dynamic-import": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
"integrity": "sha1-jWomIpyDdFqZgqRBBRVyyqF5sdo="
},
"babel-plugin-syntax-exponentiation-operator": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz",
"integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=",
"dev": true
},
"babel-plugin-syntax-trailing-function-commas": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
"integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=",
"dev": true
},
"babel-plugin-transform-async-to-generator": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz",
"integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=",
"dev": true,
"requires": {
"babel-helper-remap-async-to-generator": "^6.24.1",
"babel-plugin-syntax-async-functions": "^6.8.0",
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-arrow-functions": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
"integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=",
"requires": {
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-block-scoped-functions": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
"integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=",
"requires": {
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-block-scoping": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz",
"integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=",
"requires": {
"babel-runtime": "^6.26.0",
"babel-template": "^6.26.0",
"babel-traverse": "^6.26.0",
"babel-types": "^6.26.0",
"lodash": "^4.17.4"
}
},
"babel-plugin-transform-es2015-classes": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
"integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=",
"requires": {
"babel-helper-define-map": "^6.24.1",
"babel-helper-function-name": "^6.24.1",
"babel-helper-optimise-call-expression": "^6.24.1",
"babel-helper-replace-supers": "^6.24.1",
"babel-messages": "^6.23.0",
"babel-runtime": "^6.22.0",
"babel-template": "^6.24.1",
"babel-traverse": "^6.24.1",
"babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-computed-properties": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
"integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=",
"requires": {
"babel-runtime": "^6.22.0",
"babel-template": "^6.24.1"
}
},
"babel-plugin-transform-es2015-destructuring": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
"integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=",
"requires": {
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-duplicate-keys": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz",
"integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=",
"requires": {
"babel-runtime": "^6.22.0",
"babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-for-of": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
"integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=",
"requires": {
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-function-name": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
"integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=",
"requires": {
"babel-helper-function-name": "^6.24.1",
"babel-runtime": "^6.22.0",
"babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-literals": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
"integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=",
"requires": {
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-modules-amd": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz",
"integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=",
"requires": {
"babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
"babel-runtime": "^6.22.0",
"babel-template": "^6.24.1"
}
},
"babel-plugin-transform-es2015-modules-commonjs": {
"version": "6.26.2",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz",
"integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==",
"requires": {
"babel-plugin-transform-strict-mode": "^6.24.1",
"babel-runtime": "^6.26.0",
"babel-template": "^6.26.0",
"babel-types": "^6.26.0"
}
},
"babel-plugin-transform-es2015-modules-systemjs": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz",
"integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=",
"requires": {
"babel-helper-hoist-variables": "^6.24.1",
"babel-runtime": "^6.22.0",
"babel-template": "^6.24.1"
}
},
"babel-plugin-transform-es2015-modules-umd": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz",
"integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=",
"requires": {
"babel-plugin-transform-es2015-modules-amd": "^6.24.1",
"babel-runtime": "^6.22.0",
"babel-template": "^6.24.1"
}
},
"babel-plugin-transform-es2015-object-super": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
"integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=",
"requires": {
"babel-helper-replace-supers": "^6.24.1",
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-parameters": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
"integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=",
"requires": {
"babel-helper-call-delegate": "^6.24.1",
"babel-helper-get-function-arity": "^6.24.1",
"babel-runtime": "^6.22.0",
"babel-template": "^6.24.1",
"babel-traverse": "^6.24.1",
"babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-shorthand-properties": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
"integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=",
"requires": {
"babel-runtime": "^6.22.0",
"babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-spread": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
"integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=",
"requires": {
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-sticky-regex": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
"integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=",
"requires": {
"babel-helper-regex": "^6.24.1",
"babel-runtime": "^6.22.0",
"babel-types": "^6.24.1"
}
},
"babel-plugin-transform-es2015-template-literals": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
"integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=",
"requires": {
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-typeof-symbol": {
"version": "6.23.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz",
"integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=",
"requires": {
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-es2015-unicode-regex": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
"integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=",
"requires": {
"babel-helper-regex": "^6.24.1",
"babel-runtime": "^6.22.0",
"regexpu-core": "^2.0.0"
}
},
"babel-plugin-transform-exponentiation-operator": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz",
"integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=",
"dev": true,
"requires": {
"babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1",
"babel-plugin-syntax-exponentiation-operator": "^6.8.0",
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-transform-regenerator": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz",
"integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=",
"requires": {
"regenerator-transform": "^0.10.0"
}
},
"babel-plugin-transform-strict-mode": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
"integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
"requires": {
"babel-runtime": "^6.22.0",
"babel-types": "^6.24.1"
}
},
"babel-preset-env": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz",
"integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==",
"dev": true,
"requires": {
"babel-plugin-check-es2015-constants": "^6.22.0",
"babel-plugin-syntax-trailing-function-commas": "^6.22.0",
"babel-plugin-transform-async-to-generator": "^6.22.0",
"babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
"babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0",
"babel-plugin-transform-es2015-block-scoping": "^6.23.0",
"babel-plugin-transform-es2015-classes": "^6.23.0",
"babel-plugin-transform-es2015-computed-properties": "^6.22.0",
"babel-plugin-transform-es2015-destructuring": "^6.23.0",
"babel-plugin-transform-es2015-duplicate-keys": "^6.22.0",
"babel-plugin-transform-es2015-for-of": "^6.23.0",
"babel-plugin-transform-es2015-function-name": "^6.22.0",
"babel-plugin-transform-es2015-literals": "^6.22.0",
"babel-plugin-transform-es2015-modules-amd": "^6.22.0",
"babel-plugin-transform-es2015-modules-commonjs": "^6.23.0",
"babel-plugin-transform-es2015-modules-systemjs": "^6.23.0",
"babel-plugin-transform-es2015-modules-umd": "^6.23.0",
"babel-plugin-transform-es2015-object-super": "^6.22.0",
"babel-plugin-transform-es2015-parameters": "^6.23.0",
"babel-plugin-transform-es2015-shorthand-properties": "^6.22.0",
"babel-plugin-transform-es2015-spread": "^6.22.0",
"babel-plugin-transform-es2015-sticky-regex": "^6.22.0",
"babel-plugin-transform-es2015-template-literals": "^6.22.0",
"babel-plugin-transform-es2015-typeof-symbol": "^6.23.0",
"babel-plugin-transform-es2015-unicode-regex": "^6.22.0",
"babel-plugin-transform-exponentiation-operator": "^6.22.0",
"babel-plugin-transform-regenerator": "^6.22.0",
"browserslist": "^3.2.6",
"invariant": "^2.2.2",
"semver": "^5.3.0"
}
},
"babel-preset-es2015": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz",
"integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=",
"requires": {
"babel-plugin-check-es2015-constants": "^6.22.0",
"babel-plugin-transform-es2015-arrow-functions": "^6.22.0",
"babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0",
"babel-plugin-transform-es2015-block-scoping": "^6.24.1",
"babel-plugin-transform-es2015-classes": "^6.24.1",
"babel-plugin-transform-es2015-computed-properties": "^6.24.1",
"babel-plugin-transform-es2015-destructuring": "^6.22.0",
"babel-plugin-transform-es2015-duplicate-keys": "^6.24.1",
"babel-plugin-transform-es2015-for-of": "^6.22.0",
"babel-plugin-transform-es2015-function-name": "^6.24.1",
"babel-plugin-transform-es2015-literals": "^6.22.0",
"babel-plugin-transform-es2015-modules-amd": "^6.24.1",
"babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
"babel-plugin-transform-es2015-modules-systemjs": "^6.24.1",
"babel-plugin-transform-es2015-modules-umd": "^6.24.1",
"babel-plugin-transform-es2015-object-super": "^6.24.1",
"babel-plugin-transform-es2015-parameters": "^6.24.1",
"babel-plugin-transform-es2015-shorthand-properties": "^6.24.1",
"babel-plugin-transform-es2015-spread": "^6.22.0",
"babel-plugin-transform-es2015-sticky-regex": "^6.24.1",
"babel-plugin-transform-es2015-template-literals": "^6.22.0",
"babel-plugin-transform-es2015-typeof-symbol": "^6.22.0",
"babel-plugin-transform-es2015-unicode-regex": "^6.24.1",
"babel-plugin-transform-regenerator": "^6.24.1"
}
},
"babel-runtime": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
"requires": {
"core-js": "^2.4.0",
"regenerator-runtime": "^0.11.0"
}
},
"babel-template": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
"integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
"requires": {
"babel-runtime": "^6.26.0",
"babel-traverse": "^6.26.0",
"babel-types": "^6.26.0",
"babylon": "^6.18.0",
"lodash": "^4.17.4"
}
},
"babel-traverse": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
"integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
"requires": {
"babel-code-frame": "^6.26.0",
"babel-messages": "^6.23.0",
"babel-runtime": "^6.26.0",
"babel-types": "^6.26.0",
"babylon": "^6.18.0",
"debug": "^2.6.8",
"globals": "^9.18.0",
"invariant": "^2.2.2",
"lodash": "^4.17.4"
}
},
"babel-types": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
"integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
"requires": {
"babel-runtime": "^6.26.0",
"esutils": "^2.0.2",
"lodash": "^4.17.4",
"to-fast-properties": "^1.0.3"
}
},
"babylon": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
"integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
},
"browserslist": {
"version": "3.2.8",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz",
"integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30000844",
"electron-to-chromium": "^1.3.47"
}
},
"caniuse-lite": {
"version": "1.0.30000856",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000856.tgz",
"integrity": "sha512-x3mYcApHMQemyaHuH/RyqtKCGIYTgEA63fdi+VBvDz8xUSmRiVWTLeyKcoGQCGG6UPR9/+4qG4OKrTa6aSQRKg==",
"dev": true
},
"chalk": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
"has-ansi": "^2.0.0",
"strip-ansi": "^3.0.0",
"supports-color": "^2.0.0"
}
},
"core-js": {
"version": "2.5.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw=="
},
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"electron-to-chromium": {
"version": "1.3.48",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.48.tgz",
"integrity": "sha1-07DYWTgUBE4JLs4hCPw6ya6kuQA=",
"dev": true
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
},
"globals": {
"version": "9.18.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
},
"has-ansi": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"requires": {
"loose-envify": "^1.0.0"
}
},
"js-tokens": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
},
"jsesc": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0="
},
"lodash": {
"version": "4.17.10",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
},
"loose-envify": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
"integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
"requires": {
"js-tokens": "^3.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"private": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
"integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg=="
},
"regenerate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
"integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg=="
},
"regenerator-runtime": {
"version": "0.11.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
},
"regenerator-transform": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz",
"integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==",
"requires": {
"babel-runtime": "^6.18.0",
"babel-types": "^6.19.0",
"private": "^0.1.6"
}
},
"regexpu-core": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz",
"integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=",
"requires": {
"regenerate": "^1.2.1",
"regjsgen": "^0.2.0",
"regjsparser": "^0.1.4"
}
},
"regjsgen": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
"integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc="
},
"regjsparser": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
"integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
"requires": {
"jsesc": "~0.5.0"
}
},
"semver": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
"dev": true
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
},
"supports-color": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
},
"to-fast-properties": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
"integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc="
}
}
}

View File

@ -1683,18 +1683,6 @@
"renderTrigger": true,
"default": ""
},
"where": {
"type": "TextControl",
"label": "Custom WHERE clause",
"default": "",
"description": "The text in this box gets included in your query's WHERE clause, as an AND to other criteria. You can include complex expression, parenthesis and anything else supported by the backend it is directed towards."
},
"having": {
"type": "TextControl",
"label": "Custom HAVING clause",
"default": "",
"description": "The text in this box gets included in your query's HAVING clause, as an AND to other criteria. You can include complex expression, parenthesis and anything else supported by the backend it is directed towards."
},
"compare_lag": {
"type": "TextControl",
"label": "Comparison Period Lag",
@ -2628,12 +2616,6 @@
"default": "",
"description": "Labels for the marker lines"
},
"filters": {
"type": "FilterControl",
"label": "",
"default": [],
"description": ""
},
"annotation_layers": {
"type": "AnnotationLayerControl",
"label": "",
@ -2641,12 +2623,6 @@
"description": "Annotation Layers",
"renderTrigger": true
},
"having_filters": {
"type": "FilterControl",
"label": "",
"default": [],
"description": ""
},
"slice_id": {
"type": "HiddenControl",
"label": "Slice ID",

View File

@ -11,7 +11,15 @@ import TooltipWrapper from '../../../src/components/TooltipWrapper';
const defaultProps = {
origFormData: {
filters: [{ col: 'a', op: '==', val: 'hello' }],
adhoc_filters: [
{
clause: 'WHERE',
comparator: 'hello',
expressionType: 'SIMPLE',
operator: '==',
subject: 'a',
},
],
y_axis_bounds: [10, 20],
column_collection: [{ 1: 'a', b: ['6', 'g'] }],
bool: false,
@ -21,7 +29,15 @@ const defaultProps = {
ever: { a: 'b', c: 'd' },
},
currentFormData: {
filters: [{ col: 'b', op: 'in', val: ['hello', 'my', 'name'] }],
adhoc_filters: [
{
clause: 'WHERE',
comparator: ['hello', 'my', 'name'],
expressionType: 'SIMPLE',
operator: 'in',
subject: 'b',
},
],
y_axis_bounds: [15, 16],
column_collection: [{ 1: 'a', b: [9, '15'], t: 'gggg' }],
bool: true,
@ -33,9 +49,25 @@ const defaultProps = {
};
const expectedDiffs = {
filters: {
before: [{ col: 'a', op: '==', val: 'hello' }],
after: [{ col: 'b', op: 'in', val: ['hello', 'my', 'name'] }],
adhoc_filters: {
before: [
{
clause: 'WHERE',
comparator: 'hello',
expressionType: 'SIMPLE',
operator: '==',
subject: 'a',
},
],
after: [
{
clause: 'WHERE',
comparator: ['hello', 'my', 'name'],
expressionType: 'SIMPLE',
operator: 'in',
subject: 'b',
},
],
},
y_axis_bounds: {
before: [10, 20],
@ -211,25 +243,49 @@ describe('AlteredSliceTag', () => {
});
it('returns "[]" for empty filters', () => {
expect(wrapper.instance().formatValue([], 'filters')).to.equal('[]');
expect(wrapper.instance().formatValue([], 'adhoc_filters')).to.equal('[]');
});
it('correctly formats filters with array values', () => {
const filters = [
{ col: 'a', op: 'in', val: ['1', 'g', '7', 'ho'] },
{ col: 'b', op: 'not in', val: ['hu', 'ho', 'ha'] },
{
clause: 'WHERE',
comparator: ['1', 'g', '7', 'ho'],
expressionType: 'SIMPLE',
operator: 'in',
subject: 'a',
},
{
clause: 'WHERE',
comparator: ['hu', 'ho', 'ha'],
expressionType: 'SIMPLE',
operator: 'not in',
subject: 'b',
},
];
const expected = 'a in [1, g, 7, ho], b not in [hu, ho, ha]';
expect(wrapper.instance().formatValue(filters, 'filters')).to.equal(expected);
expect(wrapper.instance().formatValue(filters, 'adhoc_filters')).to.equal(expected);
});
it('correctly formats filters with string values', () => {
const filters = [
{ col: 'a', op: '==', val: 'gucci' },
{ col: 'b', op: 'LIKE', val: 'moshi moshi' },
{
clause: 'WHERE',
comparator: 'gucci',
expressionType: 'SIMPLE',
operator: '==',
subject: 'a',
},
{
clause: 'WHERE',
comparator: 'moshi moshi',
expressionType: 'SIMPLE',
operator: 'LIKE',
subject: 'b',
},
];
const expected = 'a == gucci, b LIKE moshi moshi';
expect(wrapper.instance().formatValue(filters, 'filters')).to.equal(expected);
expect(wrapper.instance().formatValue(filters, 'adhoc_filters')).to.equal(expected);
});
});
});

View File

@ -33,18 +33,9 @@ const columns = [
{ type: 'DOUBLE', column_name: 'value' },
];
const legacyFilter = { col: 'value', op: '>', val: '5' };
const legacyHavingFilter = { col: 'SUM(value)', op: '>', val: '10' };
const whereFilterText = 'target in (\'alpha\')';
const havingFilterText = 'SUM(value) < 20';
const formData = {
filters: [legacyFilter],
having: havingFilterText,
having_filters: [legacyHavingFilter],
metric: undefined,
metrics: [sumValueAdhocMetric, savedMetric.saved_metric_name],
where: whereFilterText,
};
function setup(overrides) {
@ -68,49 +59,6 @@ describe('AdhocFilterControl', () => {
expect(wrapper.find(OnPasteSelect)).to.have.lengthOf(1);
});
it('will translate legacy filters into adhoc filters if no adhoc filters are present', () => {
const { wrapper } = setup({ value: undefined });
expect(wrapper.state('values')).to.have.lengthOf(4);
expect(wrapper.state('values')[0].equals((
new AdhocFilter({
expressionType: EXPRESSION_TYPES.SIMPLE,
subject: 'value',
operator: '>',
comparator: '5',
clause: CLAUSES.WHERE,
})
))).to.be.true;
expect(wrapper.state('values')[1].equals((
new AdhocFilter({
expressionType: EXPRESSION_TYPES.SIMPLE,
subject: 'SUM(value)',
operator: '>',
comparator: '10',
clause: CLAUSES.HAVING,
})
))).to.be.true;
expect(wrapper.state('values')[2].equals((
new AdhocFilter({
expressionType: EXPRESSION_TYPES.SQL,
sqlExpression: 'target in (\'alpha\')',
clause: CLAUSES.WHERE,
})
))).to.be.true;
expect(wrapper.state('values')[3].equals((
new AdhocFilter({
expressionType: EXPRESSION_TYPES.SQL,
sqlExpression: 'SUM(value) < 20',
clause: CLAUSES.HAVING,
})
))).to.be.true;
});
it('will ignore legacy filters if adhoc filters are present', () => {
const { wrapper } = setup();
expect(wrapper.state('values')).to.have.lengthOf(1);
expect(wrapper.state('values')[0]).to.equal(simpleAdhocFilter);
});
it('handles saved metrics being selected to filter on', () => {
const { wrapper, onChange } = setup({ value: [] });
const select = wrapper.find(OnPasteSelect);

View File

@ -26,6 +26,6 @@ describe('ControlPanelsContainer', () => {
});
it('renders ControlPanelSections', () => {
expect(wrapper.find(ControlPanelSection)).to.have.lengthOf(7);
expect(wrapper.find(ControlPanelSection)).to.have.lengthOf(6);
});
});

View File

@ -1,248 +0,0 @@
/* eslint-disable no-unused-expressions */
import React from 'react';
import { Button } from 'react-bootstrap';
import sinon from 'sinon';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import FilterControl from '../../../../src/explore/components/controls/FilterControl';
import Filter from '../../../../src/explore/components/controls/Filter';
const $ = window.$ = require('jquery');
const defaultProps = {
name: 'not_having_filters',
onChange: sinon.spy(),
value: [
{
col: 'col1',
op: 'in',
val: ['a', 'b', 'd'],
},
{
col: 'col2',
op: '==',
val: 'Z',
},
],
datasource: {
id: 1,
type: 'qtable',
filter_select: true,
filterable_cols: [['col1', 'col2']],
metrics_combo: [
['m1', 'v1'],
['m2', 'v2'],
],
},
};
describe('FilterControl', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<FilterControl {...defaultProps} />);
wrapper.setState({
filters: [
{
valuesLoading: false,
valueChoices: ['a', 'b', 'c', 'd', 'e', 'f'],
},
{
valuesLoading: false,
valueChoices: ['X', 'Y', 'Z'],
},
// Need a duplicate since onChange calls are not changing props
{
valuesLoading: false,
valueChoices: ['X', 'Y', 'Z'],
},
],
});
});
it('renders Filters', () => {
expect(
React.isValidElement(<FilterControl {...defaultProps} />),
).to.equal(true);
});
it('renders one button and two filters', () => {
expect(wrapper.find(Filter)).to.have.lengthOf(2);
expect(wrapper.find(Button)).to.have.lengthOf(1);
});
it('adds filter when clicking Add Filter', () => {
const addButton = wrapper.find('#add-button');
expect(addButton).to.have.lengthOf(1);
addButton.simulate('click');
expect(defaultProps.onChange).to.have.property('callCount', 1);
expect(defaultProps.onChange.getCall(0).args[0]).to.deep.equal([
{
col: 'col1',
op: 'in',
val: ['a', 'b', 'd'],
},
{
col: 'col2',
op: '==',
val: 'Z',
},
{
col: 'col1',
op: 'in',
val: [],
},
]);
});
it('removes a the second filter when its delete button is clicked', () => {
expect(wrapper.find(Filter)).to.have.lengthOf(2);
wrapper.instance().removeFilter(1);
expect(defaultProps.onChange).to.have.property('callCount', 2);
expect(defaultProps.onChange.getCall(1).args[0]).to.deep.equal([
{
col: 'col1',
op: 'in',
val: ['a', 'b', 'd'],
},
]);
});
before(() => {
sinon.stub($, 'ajax');
});
after(() => {
$.ajax.restore();
});
it('makes a GET request to retrieve value choices', () => {
wrapper.instance().fetchFilterValues(0, 'col1');
expect($.ajax.getCall(0).args[0].type).to.deep.equal('GET');
expect($.ajax.getCall(0).args[0].url).to.deep.equal('/superset/filter/qtable/1/col1/');
});
it('changes filter values when one is removed', () => {
wrapper.instance().changeFilter(0, 'val', ['a', 'b']);
expect(defaultProps.onChange).to.have.property('callCount', 3);
expect(defaultProps.onChange.getCall(2).args[0]).to.deep.equal([
{
col: 'col1',
op: 'in',
val: ['a', 'b'],
},
{
col: 'col2',
op: '==',
val: 'Z',
},
]);
});
it('changes filter values when one is added', () => {
wrapper.instance().changeFilter(0, 'val', ['a', 'b', 'd', 'e']);
expect(defaultProps.onChange).to.have.property('callCount', 4);
expect(defaultProps.onChange.getCall(3).args[0]).to.deep.equal([
{
col: 'col1',
op: 'in',
val: ['a', 'b', 'd', 'e'],
},
{
col: 'col2',
op: '==',
val: 'Z',
},
]);
});
it('changes op and transforms values', () => {
wrapper.instance().changeFilter(0, ['val', 'op'], ['a', '==']);
wrapper.instance().changeFilter(1, ['val', 'op'], [['Z'], 'in']);
expect(defaultProps.onChange).to.have.property('callCount', 6);
expect(defaultProps.onChange.getCall(4).args[0]).to.deep.equal([
{
col: 'col1',
op: '==',
val: 'a',
},
{
col: 'col2',
op: '==',
val: 'Z',
},
]);
expect(defaultProps.onChange.getCall(5).args[0]).to.deep.equal([
{
col: 'col1',
op: 'in',
val: ['a', 'b', 'd'],
},
{
col: 'col2',
op: 'in',
val: ['Z'],
},
]);
});
it('changes column and clears invalid values', () => {
wrapper.instance().changeFilter(0, 'col', 'col2');
expect(defaultProps.onChange).to.have.property('callCount', 7);
expect(defaultProps.onChange.getCall(6).args[0]).to.deep.equal([
{
col: 'col2',
op: 'in',
val: [],
},
{
col: 'col2',
op: '==',
val: 'Z',
},
]);
wrapper.instance().changeFilter(1, 'col', 'col1');
expect(defaultProps.onChange).to.have.property('callCount', 8);
expect(defaultProps.onChange.getCall(7).args[0]).to.deep.equal([
{
col: 'col1',
op: 'in',
val: ['a', 'b', 'd'],
},
{
col: 'col1',
op: '==',
val: '',
},
]);
});
it('tracks an active filter select ajax request', () => {
const spyReq = sinon.spy();
$.ajax.reset();
$.ajax.onFirstCall().returns(spyReq);
wrapper.instance().fetchFilterValues(0, 'col1');
expect(wrapper.state().activeRequest).to.equal(spyReq);
// Sets active to null after success
$.ajax.getCall(0).args[0].success(['opt1', 'opt2', null, '']);
expect(wrapper.state().filters[0].valuesLoading).to.equal(false);
expect(wrapper.state().filters[0].valueChoices).to.deep.equal(['opt1', 'opt2', null, '']);
expect(wrapper.state().activeRequest).to.equal(null);
});
it('cancels active request if another is submitted', () => {
const spyReq = sinon.spy();
spyReq.abort = sinon.spy();
$.ajax.reset();
$.ajax.onFirstCall().returns(spyReq);
wrapper.instance().fetchFilterValues(0, 'col1');
expect(wrapper.state().activeRequest).to.equal(spyReq);
const spyReq1 = sinon.spy();
$.ajax.onSecondCall().returns(spyReq1);
wrapper.instance().fetchFilterValues(1, 'col2');
expect(spyReq.abort.called).to.equal(true);
expect(wrapper.state().activeRequest).to.equal(spyReq1);
});
});

View File

@ -1,115 +0,0 @@
/* eslint-disable no-unused-expressions */
import React from 'react';
import Select from 'react-select';
import { Button } from 'react-bootstrap';
import sinon from 'sinon';
import { expect } from 'chai';
import { describe, it, beforeEach } from 'mocha';
import { shallow } from 'enzyme';
import Filter from '../../../../src/explore/components/controls/Filter';
import SelectControl from '../../../../src/explore/components/controls/SelectControl';
const defaultProps = {
changeFilter: sinon.spy(),
removeFilter: () => {},
filter: {
col: null,
op: 'in',
value: ['val'],
},
datasource: {
id: 1,
type: 'qtable',
filter_select: false,
filterable_cols: ['col1', 'col2'],
metrics_combo: [
['m1', 'v1'],
['m2', 'v2'],
],
},
};
describe('Filter', () => {
let wrapper;
beforeEach(() => {
wrapper = shallow(<Filter {...defaultProps} />);
});
it('renders Filters', () => {
expect(
React.isValidElement(<Filter {...defaultProps} />),
).to.equal(true);
});
it('renders two selects, one button and one input', () => {
expect(wrapper.find(Select)).to.have.lengthOf(2);
expect(wrapper.find(Button)).to.have.lengthOf(1);
expect(wrapper.find(SelectControl)).to.have.lengthOf(1);
expect(wrapper.find('#select-op').prop('options')).to.have.lengthOf(10);
});
it('renders five op choices for table datasource', () => {
const props = Object.assign({}, defaultProps);
props.datasource = {
id: 1,
type: 'druid',
filter_select: false,
filterable_cols: ['country_name'],
};
const druidWrapper = shallow(<Filter {...props} />);
expect(druidWrapper.find('#select-op').prop('options')).to.have.lengthOf(11);
});
it('renders six op choices for having filter', () => {
const props = Object.assign({}, defaultProps);
props.having = true;
const havingWrapper = shallow(<Filter {...props} />);
expect(havingWrapper.find('#select-op').prop('options')).to.have.lengthOf(6);
});
it('calls changeFilter when select is changed', () => {
const selectCol = wrapper.find('#select-col');
selectCol.simulate('change', { value: 'col' });
const selectOp = wrapper.find('#select-op');
selectOp.simulate('change', { value: 'in' });
const selectVal = wrapper.find(SelectControl);
selectVal.simulate('change', { value: 'x' });
expect(defaultProps.changeFilter).to.have.property('callCount', 3);
});
it('renders input for regex filters', () => {
const props = Object.assign({}, defaultProps);
props.filter = {
col: null,
op: 'regex',
value: 'val',
};
const regexWrapper = shallow(<Filter {...props} />);
expect(regexWrapper.find('input')).to.have.lengthOf(1);
});
it('renders `input` for text filters', () => {
const props = Object.assign({}, defaultProps);
['>=', '>', '<=', '<'].forEach((op) => {
props.filter = {
col: 'col1',
op,
value: 'val',
};
wrapper = shallow(<Filter {...props} />);
expect(wrapper.find('input')).to.have.lengthOf(1);
});
});
it('replaces null filter values with empty string in `input`', () => {
const props = Object.assign({}, defaultProps);
props.filter = {
col: 'col1',
op: '>=',
value: null,
};
wrapper = shallow(<Filter {...props} />);
expect(wrapper.find('input').props().value).to.equal('');
});
});

View File

@ -42,6 +42,10 @@ export default class AlteredSliceTag extends React.Component {
if (!ofd[fdKey] && !cfd[fdKey]) {
continue;
}
// Ignore obsolete legacy filters
if (['filters', 'having', 'having_filters', 'where'].includes(fdKey)) {
continue;
}
if (!isEqual(ofd[fdKey], cfd[fdKey])) {
diffs[fdKey] = { before: ofd[fdKey], after: cfd[fdKey] };
}
@ -56,13 +60,15 @@ export default class AlteredSliceTag extends React.Component {
return 'N/A';
} else if (value === null) {
return 'null';
} else if (controls[key] && controls[key].type === 'FilterControl') {
} else if (controls[key] && controls[key].type === 'AdhocFilterControl') {
if (!value.length) {
return '[]';
}
return value.map((v) => {
const filterVal = v.val && v.val.constructor === Array ? `[${v.val.join(', ')}]` : v.val;
return `${v.col} ${v.op} ${filterVal}`;
const filterVal = v.comparator && v.comparator.constructor === Array ?
`[${v.comparator.join(', ')}]` :
v.comparator;
return `${v.subject} ${v.operator} ${filterVal}`;
}).join(', ');
} else if (controls[key] && controls[key].type === 'BoundsControl') {
return `Min: ${value[0]}, Max: ${value[1]}`;

View File

@ -16,12 +16,6 @@ import OnPasteSelect from '../../../components/OnPasteSelect';
import AdhocFilterOption from '../AdhocFilterOption';
import FilterDefinitionOption from '../FilterDefinitionOption';
const legacyFilterShape = PropTypes.shape({
col: PropTypes.string,
op: PropTypes.string,
val: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
});
const propTypes = {
name: PropTypes.string,
onChange: PropTypes.func,
@ -30,12 +24,8 @@ const propTypes = {
columns: PropTypes.arrayOf(columnType),
savedMetrics: PropTypes.arrayOf(savedMetricType),
formData: PropTypes.shape({
filters: PropTypes.arrayOf(legacyFilterShape),
having: PropTypes.string,
having_filters: PropTypes.arrayOf(legacyFilterShape),
metric: PropTypes.oneOfType([PropTypes.string, adhocMetricType]),
metrics: PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, adhocMetricType])),
where: PropTypes.string,
}),
};
@ -55,13 +45,15 @@ export default class AdhocFilterControl extends React.Component {
constructor(props) {
super(props);
this.coerceAdhocFilters = this.coerceAdhocFilters.bind(this);
this.optionsForSelect = this.optionsForSelect.bind(this);
this.onFilterEdit = this.onFilterEdit.bind(this);
this.onChange = this.onChange.bind(this);
this.getMetricExpression = this.getMetricExpression.bind(this);
const filters = this.coerceAdhocFilters(this.props.value, this.props.formData);
const filters = (this.props.value || []).map(filter => (
isDictionaryForAdhocFilter(filter) ? new AdhocFilter(filter) : filter
));
this.optionRenderer = VirtualizedRendererWrap(option => (
<FilterDefinitionOption option={option} />
));
@ -87,7 +79,11 @@ export default class AdhocFilterControl extends React.Component {
this.setState({ options: this.optionsForSelect(nextProps) });
}
if (this.props.value !== nextProps.value) {
this.setState({ values: this.coerceAdhocFilters(nextProps.value, nextProps.formData) });
this.setState({
values: (nextProps.value || []).map(
filter => (isDictionaryForAdhocFilter(filter) ? new AdhocFilter(filter) : filter
)),
});
}
}
@ -147,62 +143,6 @@ export default class AdhocFilterControl extends React.Component {
)).expression;
}
coerceAdhocFilters(propsValues, formData) {
// this converts filters from the four legacy filter controls into adhoc filters in the case
// someone loads an old slice in the explore view
if (propsValues) {
return propsValues.map(filter => (
isDictionaryForAdhocFilter(filter) ? new AdhocFilter(filter) : filter
));
}
return [
...(formData.filters || []).map(filter => (
new AdhocFilter({
subject: filter.col,
operator: filter.op,
comparator: filter.val,
clause: CLAUSES.WHERE,
expressionType: EXPRESSION_TYPES.SIMPLE,
filterOptionName: this.generateConvertedFilterOptionName(),
})
)),
...(formData.having_filters || []).map(filter => (
new AdhocFilter({
subject: filter.col,
operator: filter.op,
comparator: filter.val,
clause: CLAUSES.HAVING,
expressionType: EXPRESSION_TYPES.SIMPLE,
filterOptionName: this.generateConvertedFilterOptionName(),
})
)),
...[
formData.where ?
new AdhocFilter({
sqlExpression: formData.where,
clause: CLAUSES.WHERE,
expressionType: EXPRESSION_TYPES.SQL,
filterOptionName: this.generateConvertedFilterOptionName(),
}) :
null,
],
...[
formData.having ?
new AdhocFilter({
sqlExpression: formData.having,
clause: CLAUSES.HAVING,
expressionType: EXPRESSION_TYPES.SQL,
filterOptionName: this.generateConvertedFilterOptionName(),
}) :
null,
],
].filter(option => option);
}
generateConvertedFilterOptionName() {
return `form_filter_${Math.random().toString(36).substring(2, 15)}_${Math.random().toString(36).substring(2, 15)}`;
}
optionsForSelect(props) {
const options = [
...props.columns,

View File

@ -1,187 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import Select from 'react-select';
import { Button, Row, Col } from 'react-bootstrap';
import { t } from '../../../locales';
import SelectControl from './SelectControl';
const operatorsArr = [
{ val: 'in', type: 'array', useSelect: true, multi: true },
{ val: 'not in', type: 'array', useSelect: true, multi: true },
{ val: '==', type: 'string', useSelect: true, multi: false, havingOnly: true },
{ val: '!=', type: 'string', useSelect: true, multi: false, havingOnly: true },
{ val: '>=', type: 'string', havingOnly: true },
{ val: '<=', type: 'string', havingOnly: true },
{ val: '>', type: 'string', havingOnly: true },
{ val: '<', type: 'string', havingOnly: true },
{ val: 'regex', type: 'string', datasourceTypes: ['druid'] },
{ val: 'LIKE', type: 'string', datasourceTypes: ['table'] },
{ val: 'IS NULL', type: null },
{ val: 'IS NOT NULL', type: null },
];
const operators = {};
operatorsArr.forEach((op) => {
operators[op.val] = op;
});
const propTypes = {
changeFilter: PropTypes.func,
removeFilter: PropTypes.func,
filter: PropTypes.object.isRequired,
datasource: PropTypes.object,
having: PropTypes.bool,
valuesLoading: PropTypes.bool,
valueChoices: PropTypes.array,
};
const defaultProps = {
changeFilter: () => {},
removeFilter: () => {},
datasource: null,
having: false,
valuesLoading: false,
valueChoices: [],
};
export default class Filter extends React.Component {
switchFilterValue(prevOp, nextOp) {
if (operators[prevOp].type !== operators[nextOp].type) {
// Switch from array to string or vice versa
const val = this.props.filter.val;
let newVal;
if (operators[nextOp].type === 'string') {
if (!val || !val.length) {
newVal = '';
} else {
newVal = val[0];
}
} else if (operators[nextOp].type === 'array') {
if (!val || !val.length) {
newVal = [];
} else {
newVal = [val];
}
}
this.props.changeFilter(['val', 'op'], [newVal, nextOp]);
} else {
// No value type change
this.props.changeFilter('op', nextOp);
}
}
changeText(event) {
this.props.changeFilter('val', event.target.value);
}
changeSelect(value) {
this.props.changeFilter('val', value);
}
changeColumn(event) {
this.props.changeFilter('col', event.value);
}
changeOp(event) {
this.switchFilterValue(this.props.filter.op, event.value);
}
removeFilter(filter) {
this.props.removeFilter(filter);
}
renderFilterFormControl(filter) {
const operator = operators[filter.op];
if (operator.type === null) {
// IS NULL or IS NOT NULL
return null;
}
if (operator.useSelect && !this.props.having) {
// TODO should use a simple Select, not a control here...
return (
<SelectControl
multi={operator.multi}
freeForm
name="filter-value"
value={filter.val}
isLoading={this.props.valuesLoading}
choices={this.props.valueChoices}
onChange={this.changeSelect.bind(this)}
showHeader={false}
/>
);
}
return (
<input
type="text"
onChange={this.changeText.bind(this)}
value={filter.val || ''}
className="form-control input-sm"
placeholder={t('Filter value')}
/>
);
}
render() {
const datasource = this.props.datasource;
const filter = this.props.filter;
const opsChoices = operatorsArr
.filter((o) => {
if (this.props.having) {
return !!o.havingOnly;
}
return (!o.datasourceTypes || o.datasourceTypes.indexOf(datasource.type) >= 0);
})
.map(o => ({ value: o.val, label: o.val }));
let colChoices;
if (datasource) {
if (this.props.having) {
colChoices = datasource.metrics_combo.map(c => ({ value: c[0], label: c[1] }));
} else {
colChoices = datasource.filterable_cols.map(c => ({ value: c[0], label: c[1] }));
}
}
return (
<div>
<Row className="space-1">
<Col md={12}>
<Select
id="select-col"
placeholder={this.props.having ? t('Select metric') : t('Select column')}
clearable={false}
options={colChoices}
value={filter.col}
onChange={this.changeColumn.bind(this)}
/>
</Col>
</Row>
<Row className="space-1">
<Col md={3}>
<Select
id="select-op"
placeholder={t('Select operator')}
options={opsChoices}
clearable={false}
value={filter.op}
onChange={this.changeOp.bind(this)}
/>
</Col>
<Col md={7}>
{this.renderFilterFormControl(filter)}
</Col>
<Col md={2}>
<Button
id="remove-button"
bsSize="small"
onClick={this.removeFilter.bind(this)}
>
<i className="fa fa-minus" />
</Button>
</Col>
</Row>
</div>
);
}
}
Filter.propTypes = propTypes;
Filter.defaultProps = defaultProps;

View File

@ -1,155 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button, Row, Col } from 'react-bootstrap';
import Filter from './Filter';
import { t } from '../../../locales';
const $ = window.$ = require('jquery');
const propTypes = {
name: PropTypes.string,
onChange: PropTypes.func,
value: PropTypes.array,
datasource: PropTypes.object,
};
const defaultProps = {
onChange: () => {},
value: [],
};
export default class FilterControl extends React.Component {
constructor(props) {
super(props);
const initialFilters = props.value.map(() => ({
valuesLoading: false,
valueChoices: [],
}));
this.state = {
filters: initialFilters,
activeRequest: null,
};
}
componentDidMount() {
this.state.filters.forEach((filter, index) => this.fetchFilterValues(index));
}
fetchFilterValues(index, column) {
const datasource = this.props.datasource;
const col = column || this.props.value[index].col;
const having = this.props.name === 'having_filters';
if (col && this.props.datasource && this.props.datasource.filter_select && !having) {
this.setState((prevState) => {
const newStateFilters = Object.assign([], prevState.filters);
newStateFilters[index].valuesLoading = true;
return { filters: newStateFilters };
});
// if there is an outstanding request to fetch values, cancel it.
if (this.state.activeRequest) {
this.state.activeRequest.abort();
}
this.setState({
activeRequest: $.ajax({
type: 'GET',
url: `/superset/filter/${datasource.type}/${datasource.id}/${col}/`,
success: (data) => {
this.setState((prevState) => {
const newStateFilters = Object.assign([], prevState.filters);
newStateFilters[index] = { valuesLoading: false, valueChoices: data };
return { filters: newStateFilters, activeRequest: null };
});
},
}),
});
}
}
addFilter() {
const newFilters = Object.assign([], this.props.value);
const col = this.props.datasource && this.props.datasource.filterable_cols.length > 0 ?
this.props.datasource.filterable_cols[0][0] :
null;
newFilters.push({
col,
op: 'in',
val: this.props.datasource.filter_select ? [] : '',
});
this.props.onChange(newFilters);
const nextIndex = this.state.filters.length;
this.setState((prevState) => {
const newStateFilters = Object.assign([], prevState.filters);
newStateFilters.push({ valuesLoading: false, valueChoices: [] });
return { filters: newStateFilters };
});
this.fetchFilterValues(nextIndex, col);
}
changeFilter(index, control, value) {
const newFilters = Object.assign([], this.props.value);
const modifiedFilter = Object.assign({}, newFilters[index]);
if (typeof control === 'string') {
modifiedFilter[control] = value;
} else {
control.forEach((c, i) => {
modifiedFilter[c] = value[i];
});
}
// Clear selected values and refresh upon column change
if (control === 'col') {
if (modifiedFilter.val.constructor === Array) {
modifiedFilter.val = [];
} else if (typeof modifiedFilter.val === 'string') {
modifiedFilter.val = '';
}
this.fetchFilterValues(index, value);
}
newFilters.splice(index, 1, modifiedFilter);
this.props.onChange(newFilters);
}
removeFilter(index) {
this.props.onChange(this.props.value.filter((f, i) => i !== index));
this.setState((prevState) => {
const newStateFilters = Object.assign([], prevState.filters);
newStateFilters.splice(index, 1);
return { filters: newStateFilters };
});
}
render() {
const filters = this.props.value.map((filter, i) => (
<div key={i}>
<Filter
having={this.props.name === 'having_filters'}
filter={filter}
datasource={this.props.datasource}
removeFilter={this.removeFilter.bind(this, i)}
changeFilter={this.changeFilter.bind(this, i)}
valuesLoading={this.state.filters[i].valuesLoading}
valueChoices={this.state.filters[i].valueChoices}
/>
</div>
));
return (
<div>
{filters}
<Row className="space-2">
<Col md={2}>
<Button
id="add-button"
bsSize="sm"
onClick={this.addFilter.bind(this)}
>
<i className="fa fa-plus" /> &nbsp; {t('Add Filter')}
</Button>
</Col>
</Row>
</div>
);
}
}
FilterControl.propTypes = propTypes;
FilterControl.defaultProps = defaultProps;

View File

@ -6,7 +6,6 @@ import ColorPickerControl from './ColorPickerControl';
import ColorSchemeControl from './ColorSchemeControl';
import DatasourceControl from './DatasourceControl';
import DateFilterControl from './DateFilterControl';
import FilterControl from './FilterControl';
import FixedOrMetricControl from './FixedOrMetricControl';
import HiddenControl from './HiddenControl';
import SelectAsyncControl from './SelectAsyncControl';
@ -29,7 +28,6 @@ const controlMap = {
ColorSchemeControl,
DatasourceControl,
DateFilterControl,
FilterControl,
FixedOrMetricControl,
HiddenControl,
SelectAsyncControl,

View File

@ -1132,34 +1132,6 @@ export const controls = {
default: '',
},
where: {
type: 'TextAreaControl',
label: t('Custom WHERE clause'),
default: '',
language: 'sql',
minLines: 2,
maxLines: 10,
offerEditInModal: false,
description: t('The text in this box gets included in your query\'s WHERE ' +
'clause, as an AND to other criteria. You can include ' +
'complex expression, parenthesis and anything else ' +
'supported by the backend it is directed towards.'),
},
having: {
type: 'TextAreaControl',
label: t('Custom HAVING clause'),
default: '',
language: 'sql',
minLines: 2,
maxLines: 10,
offerEditInModal: false,
description: t('The text in this box gets included in your query\'s HAVING ' +
'clause, as an AND to other criteria. You can include ' +
'complex expression, parenthesis and anything else ' +
'supported by the backend it is directed towards.'),
},
compare_lag: {
type: 'TextControl',
label: t('Comparison Period Lag'),
@ -1822,16 +1794,6 @@ export const controls = {
description: t('Labels for the marker lines'),
},
filters: {
type: 'FilterControl',
label: '',
default: [],
description: '',
mapStateToProps: state => ({
datasource: state.datasource,
}),
},
annotation_layers: {
type: 'AnnotationLayerControl',
label: '',
@ -1854,18 +1816,6 @@ export const controls = {
provideFormDataToProps: true,
},
having_filters: {
type: 'FilterControl',
label: '',
default: [],
description: '',
mapStateToProps: state => ({
choices: (state.datasource) ? state.datasource.metrics_combo
.concat(state.datasource.filterable_cols) : [],
datasource: state.datasource,
}),
},
slice_id: {
type: 'HiddenControl',
label: t('Chart ID'),

View File

@ -68,14 +68,6 @@ export function getControlsState(state, form_data) {
delete formData[k];
}
}
// Removing invalid filters that point to a now inexisting column
if (control.type === 'FilterControl' && control.choices) {
if (!formData[k]) {
formData[k] = [];
}
const choiceValues = control.choices.map(c => c[0]);
formData[k] = formData[k].filter(flt => choiceValues.indexOf(flt.col) >= 0);
}
if (typeof control.default === 'function') {
control.default = control.default(control);

View File

@ -40,14 +40,6 @@ export const sections = {
['since', 'until'],
],
},
sqlClause: {
label: t('SQL'),
controlSetRows: [
['where'],
['having'],
],
description: t('This section exposes ways to include snippets of SQL in your query'),
},
annotations: {
label: t('Annotations and Layers'),
expanded: true,
@ -80,20 +72,6 @@ export const sections = {
],
},
],
filters: [
{
label: t('Filters'),
expanded: true,
controlSetRows: [['filters']],
},
{
label: t('Result Filters'),
expanded: true,
description: t('The filters to apply after post-aggregation.' +
'Leave the value control empty to filter empty strings or nulls'),
controlSetRows: [['having_filters']],
},
],
};
const timeGrainSqlaAnimationOverrides = {
@ -263,6 +241,13 @@ export const visTypes = {
['line_charts_2', 'y_axis_2_format'],
],
},
{
label: t('Query'),
expanded: true,
controlSetRows: [
['adhoc_filters'],
],
},
sections.annotations,
],
controlOverrides: {
@ -279,8 +264,6 @@ export const visTypes = {
},
},
sectionOverrides: {
sqlClause: [],
filters: [[]],
datasourceAndVizType: {
label: t('Chart Type'),
controlSetRows: [
@ -310,7 +293,9 @@ export const visTypes = {
label: t('Query'),
expanded: true,
controlSetRows: [
['metric', 'freq'],
['metric'],
['adhoc_filters'],
['freq'],
],
},
{
@ -373,6 +358,13 @@ export const visTypes = {
['metric_2', 'y_axis_2_format'],
],
},
{
label: t('Query'),
expanded: true,
controlSetRows: [
['adhoc_filters'],
],
},
sections.annotations,
],
controlOverrides: {
@ -489,6 +481,13 @@ export const visTypes = {
['deck_slices', null],
],
},
{
label: t('Query'),
expanded: true,
controlSetRows: [
['adhoc_filters'],
],
},
],
},
@ -502,6 +501,7 @@ export const visTypes = {
controlSetRows: [
['spatial', 'size'],
['row_limit', null],
['adhoc_filters'],
],
},
{
@ -540,6 +540,7 @@ export const visTypes = {
controlSetRows: [
['spatial', 'size'],
['row_limit', null],
['adhoc_filters'],
],
},
{
@ -579,6 +580,7 @@ export const visTypes = {
controlSetRows: [
['line_column', 'line_type'],
['row_limit', null],
['adhoc_filters'],
],
},
{
@ -612,6 +614,7 @@ export const visTypes = {
controlSetRows: [
['spatial', 'size'],
['row_limit', null],
['adhoc_filters'],
],
},
{
@ -656,6 +659,7 @@ export const visTypes = {
expanded: true,
controlSetRows: [
['geojson', 'row_limit'],
['adhoc_filters'],
],
},
{
@ -696,6 +700,7 @@ export const visTypes = {
controlSetRows: [
['line_column', 'line_type'],
['row_limit', null],
['adhoc_filters'],
],
},
{
@ -736,6 +741,7 @@ export const visTypes = {
controlSetRows: [
['start_spatial', 'end_spatial'],
['row_limit', null],
['adhoc_filters'],
],
},
{
@ -784,6 +790,7 @@ export const visTypes = {
expanded: true,
controlSetRows: [
['spatial', 'row_limit'],
['adhoc_filters'],
],
},
{
@ -901,6 +908,13 @@ export const visTypes = {
['row_limit', null],
],
},
{
label: t('Query'),
expanded: true,
controlSetRows: [
['adhoc_filters'],
],
},
{
label: t('Options'),
expanded: true,
@ -929,7 +943,9 @@ export const visTypes = {
label: t('Query'),
expanded: true,
controlSetRows: [
['groupby', 'metrics'],
['metrics'],
['adhoc_filters'],
['groupby'],
['limit'],
['column_collection'],
['url'],
@ -969,8 +985,10 @@ export const visTypes = {
label: t('Query'),
expanded: true,
controlSetRows: [
['groupby', 'columns'],
['metrics'],
['adhoc_filters'],
['groupby'],
['columns'],
['row_limit', null],
],
},
@ -1017,7 +1035,9 @@ export const visTypes = {
label: t('Query'),
expanded: true,
controlSetRows: [
['series', 'metric'],
['metric'],
['adhoc_filters'],
['series'],
['row_limit', null],
],
},
@ -1040,6 +1060,7 @@ export const visTypes = {
expanded: true,
controlSetRows: [
['metrics'],
['adhoc_filters'],
['groupby'],
],
},
@ -1070,6 +1091,7 @@ export const visTypes = {
controlSetRows: [
['domain_granularity', 'subdomain_granularity'],
['metrics'],
['adhoc_filters'],
],
},
{
@ -1106,7 +1128,9 @@ export const visTypes = {
expanded: true,
controlSetRows: [
['metrics'],
['groupby', 'limit'],
['adhoc_filters'],
['groupby'],
['limit'],
],
},
{
@ -1128,8 +1152,11 @@ export const visTypes = {
expanded: true,
controlSetRows: [
['series', 'entity'],
['x', 'y'],
['size', 'max_bubble_size'],
['x'],
['y'],
['adhoc_filters'],
['size'],
['max_bubble_size'],
['limit', null],
],
},
@ -1179,6 +1206,7 @@ export const visTypes = {
expanded: true,
controlSetRows: [
['metric'],
['adhoc_filters'],
],
},
{
@ -1256,6 +1284,7 @@ export const visTypes = {
expanded: true,
controlSetRows: [
['all_columns_x'],
['adhoc_filters'],
['row_limit'],
['groupby'],
],
@ -1298,7 +1327,9 @@ export const visTypes = {
expanded: true,
controlSetRows: [
['groupby'],
['metric', 'secondary_metric'],
['metric'],
['secondary_metric'],
['adhoc_filters'],
['row_limit'],
],
},
@ -1338,6 +1369,7 @@ export const visTypes = {
controlSetRows: [
['groupby'],
['metric'],
['adhoc_filters'],
['row_limit'],
],
},
@ -1366,6 +1398,7 @@ export const visTypes = {
controlSetRows: [
['groupby'],
['metric'],
['adhoc_filters'],
['row_limit'],
],
},
@ -1391,8 +1424,11 @@ export const visTypes = {
label: t('Query'),
expanded: true,
controlSetRows: [
['groupby', 'columns'],
['metric', 'row_limit'],
['groupby'],
['columns'],
['metric'],
['adhoc_filters'],
['row_limit'],
],
},
{
@ -1432,6 +1468,7 @@ export const visTypes = {
controlSetRows: [
['entity'],
['metric'],
['adhoc_filters'],
],
},
{
@ -1467,6 +1504,7 @@ export const visTypes = {
['entity'],
['country_fieldtype'],
['metric'],
['adhoc_filters'],
],
},
{
@ -1503,6 +1541,7 @@ export const visTypes = {
controlSetRows: [
['groupby'],
['metric'],
['adhoc_filters'],
['date_filter', 'instant_filtering'],
['show_sqla_time_granularity', 'show_sqla_time_column'],
['show_druid_time_granularity', 'show_druid_time_origin'],
@ -1544,6 +1583,7 @@ export const visTypes = {
['series'],
['metrics'],
['secondary_metric'],
['adhoc_filters'],
['limit'],
],
},
@ -1564,7 +1604,9 @@ export const visTypes = {
expanded: true,
controlSetRows: [
['all_columns_x', 'all_columns_y'],
['metric', 'row_limit'],
['metric'],
['adhoc_filters'],
['row_limit'],
],
},
{
@ -1627,6 +1669,7 @@ export const visTypes = {
['all_columns_x', 'all_columns_y'],
['clustering_radius'],
['row_limit'],
['adhoc_filters'],
['groupby'],
],
},
@ -1701,6 +1744,13 @@ export const visTypes = {
['min_leaf_node_event_count'],
],
},
{
label: t('Query'),
expanded: true,
controlSetRows: [
['adhoc_filters'],
],
},
{
label: t('Additional meta data'),
controlSetRows: [
@ -1799,12 +1849,6 @@ export const visTypes = {
export default visTypes;
function adhocFilterEnabled(viz) {
return viz.controlPanelSections.find((
section => section.controlSetRows.find(row => row.find(control => control === 'adhoc_filters'))
));
}
export function sectionsToRender(vizType, datasourceType) {
const viz = visTypes[vizType];
@ -1826,7 +1870,5 @@ export function sectionsToRender(vizType, datasourceType) {
sectionsCopy.datasourceAndVizType,
datasourceType === 'table' ? sectionsCopy.sqlaTimeSeries : sectionsCopy.druidTimeSeries,
viz.controlPanelSections,
!adhocFilterEnabled(viz) && (datasourceType === 'table' ? sectionsCopy.sqlClause : []),
!adhocFilterEnabled(viz) && (datasourceType === 'table' ? sectionsCopy.filters[0] : sectionsCopy.filters),
).filter(section => section);
}

View File

@ -0,0 +1,97 @@
"""adhoc filters
Revision ID: bddc498dd179
Revises: afb7730f6a9c
Create Date: 2018-06-13 14:54:47.086507
"""
# revision identifiers, used by Alembic.
revision = 'bddc498dd179'
down_revision = '80a67c5192fa'
from collections import defaultdict
import json
import uuid
from alembic import op
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, Text
from superset import db
from superset import utils
Base = declarative_base()
class Slice(Base):
__tablename__ = 'slices'
id = Column(Integer, primary_key=True)
params = Column(Text)
def upgrade():
bind = op.get_bind()
session = db.Session(bind=bind)
mapping = {'having': 'having_filters', 'where': 'filters'}
for slc in session.query(Slice).all():
try:
params = json.loads(slc.params)
if not 'adhoc_filters' in params:
params['adhoc_filters'] = []
for clause, filters in mapping.items():
if clause in params and params[clause] != '':
params['adhoc_filters'].append({
'clause': clause.upper(),
'expressionType': 'SQL',
'filterOptionName': str(uuid.uuid4()),
'sqlExpression': params[clause],
})
if filters in params:
for filt in params[filters]:
params['adhoc_filters'].append({
'clause': clause.upper(),
'comparator': filt['val'],
'expressionType': 'SIMPLE',
'filterOptionName': str(uuid.uuid4()),
'operator': filt['op'],
'subject': filt['col'],
})
for key in ('filters', 'having', 'having_filters', 'where'):
if key in params:
del params[key]
slc.params = json.dumps(params, sort_keys=True)
except Exception:
pass
session.commit()
session.close()
def downgrade():
bind = op.get_bind()
session = db.Session(bind=bind)
for slc in session.query(Slice).all():
try:
params = json.loads(slc.params)
utils.split_adhoc_filters_into_base_filters(params)
if 'adhoc_filters' in params:
del params['adhoc_filters']
slc.params = json.dumps(params, sort_keys=True)
except Exception:
pass
session.commit()
session.close()

View File

@ -1193,7 +1193,6 @@ msgstr ""
msgid "Select operator"
msgstr ""
#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:138
#: superset/templates/appbuilder/general/widgets/search.html:6
msgid "Add Filter"
msgstr ""

View File

@ -3815,7 +3815,6 @@ msgstr ""
msgid "Select operator"
msgstr ""
#: superset/assets/src/explore/components/controls/FilterControl.jsx:145
#: superset/templates/appbuilder/general/widgets/search.html:6
msgid "Add Filter"
msgstr ""

View File

@ -1250,7 +1250,6 @@ msgstr "Selecciona la columna"
msgid "Select operator"
msgstr "Selecciona el operador"
#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:138
#: superset/templates/appbuilder/general/widgets/search.html:6
msgid "Add Filter"
msgstr "Añadir Filtro"

View File

@ -3819,7 +3819,6 @@ msgstr ""
msgid "Select operator"
msgstr ""
#: superset/assets/src/explore/components/controls/FilterControl.jsx:145
#: superset/templates/appbuilder/general/widgets/search.html:6
msgid "Add Filter"
msgstr "Ajouter un filtre"

View File

@ -1598,7 +1598,6 @@ msgstr "Seleziona una colonna"
msgid "Select operator"
msgstr "Seleziona operatore"
#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:145
#: superset/templates/appbuilder/general/widgets/search.html:6
msgid "Add Filter"
msgstr "Aggiungi filtro"

View File

@ -1202,7 +1202,6 @@ msgstr "列を選択"
msgid "Select operator"
msgstr "オペレータを選択"
#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:138
#: superset/templates/appbuilder/general/widgets/search.html:6
msgid "Add Filter"
msgstr "フィルターを追加"

View File

@ -3814,7 +3814,6 @@ msgstr ""
msgid "Select operator"
msgstr ""
#: superset/assets/src/explore/components/controls/FilterControl.jsx:145
#: superset/templates/appbuilder/general/widgets/search.html:6
msgid "Add Filter"
msgstr ""

View File

@ -1256,7 +1256,6 @@ msgstr "Selecione a coluna"
msgid "Select operator"
msgstr "Selecione o operador"
#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:138
#: superset/templates/appbuilder/general/widgets/search.html:6
msgid "Add Filter"
msgstr "Adicionar filtro"

View File

@ -1599,7 +1599,6 @@ msgstr "Выбрать столбец"
msgid "Select operator"
msgstr "Выбрать оператор"
#: superset/assets/javascripts/explore/components/controls/FilterControl.jsx:145
#: superset/templates/appbuilder/general/widgets/search.html:6
msgid "Add Filter"
msgstr "Добавить фильтр"

View File

@ -3820,7 +3820,6 @@ msgstr "选择列"
msgid "Select operator"
msgstr "选择运算符"
#: superset/assets/src/explore/components/controls/FilterControl.jsx:145
#: superset/templates/appbuilder/general/widgets/search.html:6
msgid "Add Filter"
msgstr "增加过滤条件"

View File

@ -881,4 +881,3 @@ def split_adhoc_filters_into_base_filters(fd):
fd['having'] = ' AND '.join(['({})'.format(sql) for sql in sql_having_filters])
fd['having_filters'] = simple_having_filters
fd['filters'] = simple_where_filters
del fd['adhoc_filters']