Compare commits

...

130 Commits

Author SHA1 Message Date
Albert Backenhof
9d925b6205 Merge pull request #29 from qlik-oss/feature/QPE-427
[QPE-427] Replace csv styling with dimension styling
2019-03-18 06:57:13 +01:00
Kristoffer Lind
35d489c2e2 add updated demo app 2019-02-28 15:58:14 +01:00
Kristoffer Lind
9ef9981305 remove demo app 2019-02-28 15:56:44 +01:00
Kristoffer Lind
398192e057 fix indent and hide when design dimension is applied 2019-02-28 15:36:56 +01:00
Kristoffer Lind
4421217cb0 set default for metric semaphores to apply to nothing 2019-02-28 15:36:56 +01:00
Kristoffer Lind
c92be00ca7 update app with corrected colors for sheet 4 2019-02-28 15:36:56 +01:00
Kristoffer Lind
c66dfdc06c only show row colors when it does not have design dimension 2019-02-28 15:36:55 +01:00
Kristoffer Lind
a882b1d6aa update demo app so its no longer saved with faulty colors 2019-02-28 15:36:55 +01:00
Kristoffer Lind
554b029569 remove unused options include external csv file and path 2019-02-28 15:36:55 +01:00
Kristoffer Lind
4a15628325 remove csv files 2019-02-28 15:36:55 +01:00
Kristoffer Lind
fdb2aaaef4 use originHyperCubeDefinition as basis for dynamically created hyperCubes 2019-02-28 15:36:55 +01:00
Kristoffer Lind
a400e9c233 use createDimension for design cube aswell 2019-02-28 15:36:55 +01:00
Kristoffer Lind
091c564a75 apply styling from dimension 2019-02-28 15:36:54 +01:00
Kristoffer Lind
99eba8afcb split qHyperCube into two cubes, one for data and one for design 2019-02-28 15:36:54 +01:00
giovanni hanselius
cb78a2f2f9 Merge pull request #31 from qlik-oss/QPE-637-header-format-fixes
[QPE 637] Header format fixes
2019-02-28 12:48:09 +01:00
Balazs Gobel
f255efbf5d Handle medium header font size better and prevent cutting text off 2019-02-28 12:40:29 +01:00
giovanni hanselius
2f2d08fedd Merge pull request #30 from qlik-oss/QPE-636-table-format-fixes
[QPE 636] Table format fixes
2019-02-28 11:02:46 +01:00
Balazs Gobel
ac8b70bc84 Use correct fallback for all fonts 2019-02-28 10:49:24 +01:00
giovanni hanselius
0f2a4f9805 Merge pull request #27 from qlik-oss/QPE-631-IE-errors
[QPE 631] Prevent console errors in IE on cell hover
2019-02-28 09:33:55 +01:00
Balazs Gobel
85228412cc show correct color in the color picker 2019-02-27 21:43:09 +01:00
Balazs Gobel
03dfc0ce93 prevent infinite loop in angular color picker
- dualOutput must be the last line
2019-02-27 21:39:59 +01:00
Balazs Gobel
48427df559 enable changing font size in headers 2019-02-27 21:39:31 +01:00
Balazs Gobel
7fda7aa2d9 better defaults when missing font family 2019-02-27 20:54:50 +01:00
Balazs Gobel
4ba12b8b2d show user-selected font-family for the cells 2019-02-27 20:51:18 +01:00
Balazs Gobel
07af7b509e Make table format work again
- working color picker
 - working font size
 - working cell text alignment
 - this is all by preventing the infinite loop in angular
2019-02-27 20:45:06 +01:00
Balazs Gobel
b7ff83e1da prevent console errors in IE on cell hover 2019-02-27 20:37:02 +01:00
giovanni hanselius
621359d6f9 Merge pull request #28 from qlik-oss/QPE-634-column-width-fix
[QPE 634] Fix column width
2019-02-27 15:06:04 +01:00
Balazs Gobel
a71f80f8fa minor cleanup 2019-02-27 14:26:27 +01:00
Balazs Gobel
a6cbfcda70 Fix column width issue
- Look at the value format to determine if column is percentage based
2019-02-27 14:25:50 +01:00
giovanni hanselius
de2e9c16ac Merge pull request #25 from qlik-oss/QPE-549-tooltip-design
[QPE 549] Minor adjustments for tooltip
2019-02-27 12:30:42 +01:00
giovanni hanselius
ad0c0dacba Merge pull request #24 from qlik-oss/QPE-622-visible-scrollbar
[QPE 622] show scrollbar for easier scrolling
2019-02-27 11:14:01 +01:00
giovanni hanselius
97564cf8b1 Merge pull request #26 from qlik-oss/QPE-600-same-padding-in-single-object-mode
[QPE 600] Use same layout in single onject mode as in normal mode
2019-02-27 11:03:33 +01:00
Balazs Gobel
7fa44c06b0 Use same layout in single onject mode as in normal mode
- same padding for single object mode
2019-02-26 16:02:40 +01:00
Balazs Gobel
dae192b7af Minor adjustments for tooltip
- Added tooltip for row header
- Better vertical alignment
- Move static styling to css
2019-02-26 15:44:17 +01:00
Balazs Gobel
5abfd5b7e5 show scrollbar for easier scrolling 2019-02-26 15:10:00 +01:00
giovanni hanselius
65f5d70654 Merge pull request #23 from qlik-oss/feature/QPE-615
[QPE-615] fix metric semaphore colors
2019-02-26 14:34:17 +01:00
Kristoffer Lind
da7ba5c3a8 fix default indexes for metric colors 2019-02-26 14:23:42 +01:00
Kristoffer Lind
6e5df323d8 update sample state 2019-02-26 11:37:31 +01:00
Kristoffer Lind
aad92678db fix metric semaphore colors 2019-02-26 11:31:24 +01:00
giovanni hanselius
15226d8598 Merge pull request #17 from qlik-oss/QPE-426
[QPE-426] color pickers
2019-02-25 15:25:29 +01:00
Balazs Gobel
a5fc586859 Merge branch 'master' into QPE-426 2019-02-25 15:14:13 +01:00
giovanni hanselius
980c0387bf Merge pull request #14 from qlik-oss/feature/QPE-549
[QPE-549] tooltip
2019-02-25 14:45:13 +01:00
Balazs Gobel
710d4a8842 Fix tests 2019-02-25 13:41:11 +01:00
Balazs Gobel
ebb5a7cf16 Additional merge conflict fixes 2019-02-25 12:59:02 +01:00
Balazs Gobel
7107f485be resolve additional merge conflict: removed obsolete code 2019-02-25 12:55:48 +01:00
Balazs Gobel
633fd39b80 Merge branch 'master' into feature/QPE-549
# Conflicts:
#	src/main.less
#	src/paint.jsx
2019-02-25 12:50:47 +01:00
Christopher Lebond
9eeaecb994 Merge pull request #22 from qlik-oss/fix-merge-issues
remove some duplicated css from merge conflicts
2019-02-22 16:11:48 +01:00
Kristoffer Lind
7305175049 fix semaphore alignment 2019-02-22 16:08:51 +01:00
Kristoffer Lind
f99281ff47 hide scrollbars in firefox 2019-02-22 16:04:24 +01:00
Kristoffer Lind
557cd1aeb6 remove some duplicated css 2019-02-22 16:04:24 +01:00
Christopher Lebond
ca5f442fe0 Merge pull request #8 from qlik-oss/feature/QPE-585
[QPE 585] Minor design fixes
2019-02-22 15:37:56 +01:00
ahmed-Bazzara
585243bb73 merge confloicts solved 2019-02-22 14:49:36 +01:00
Kristoffer Lind
a25b2c40c0 removed a few more pieces of the colors library 2019-02-22 14:30:18 +01:00
ahmed-Bazzara
46d6520273 update app added
colors set to have a fallback value
2019-02-22 14:29:23 +01:00
ahmed-Bazzara
19286f6c56 color libraries deleted and Qlik's color-pickers introduced 2019-02-22 14:29:23 +01:00
Ahmed Bazzara
8b760646ab Merge pull request #12 from qlik-oss/feature/QPE-564
[QPE-564] test setup for components and some initial tests
2019-02-22 14:21:04 +01:00
ahmed-Bazzara
b5f25e5e18 babel/plugin-proposal-class-properties added to karma.conf 2019-02-22 14:17:08 +01:00
Ahmed Bazzara
b335b4883e Merge pull request #9 from qlik-oss/feature/QPE-586-stylelint
[QPE 586] Added stricter stylelint
2019-02-22 13:51:53 +01:00
ahmed-Bazzara
94e4203a0b merge conflicts solved 2019-02-22 13:51:09 +01:00
ahmed-Bazzara
221e2d365c merge conflicts solved 2019-02-22 13:45:19 +01:00
Christopher Lebond
6797f7d561 Merge pull request #13 from qlik-oss/feature/QPE-569-change-eslint-rules
[QPE-569] Fix most of the eslint warnings
2019-02-22 13:21:45 +01:00
Christopher Lebond
f843779b64 Merge pull request #21 from qlik-oss/feature/QPE-474
[QPE-474] replace jquery scroll linking
2019-02-22 13:13:57 +01:00
ahmed-Bazzara
951d534343 merge conflicts solved 2019-02-22 12:59:57 +01:00
ahmed-Bazzara
c5acb43a7a merge conflicts solved 2019-02-22 12:59:38 +01:00
ahmed-Bazzara
979c036b49 settings file reverted 2019-02-22 12:44:44 +01:00
ahmed-Bazzara
63c877face merge conflicts solved 2019-02-22 12:40:30 +01:00
ahmed-Bazzara
9809587c68 merge conflicts solved 2019-02-22 12:12:00 +01:00
ahmed-Bazzara
808f4df3e3 merge conflicts solved 2019-02-22 12:01:41 +01:00
ahmed-Bazzara
bbadc711dc merge conflicts resolved 2019-02-22 11:56:38 +01:00
Christopher Lebond
47b4d1aa5b Merge pull request #15 from qlik-oss/feature/QPE-484
[QPE-484] Edit mode interactions
2019-02-22 11:28:35 +01:00
ahmed-Bazzara
18e2b2024e fixing alignment between cells and row-headers 2019-02-22 11:26:17 +01:00
Christopher Lebond
614d768eea Merge pull request #10 from qlik-oss/QPE-477
[QPE-477] Definition object is Qlik standard
2019-02-22 10:41:36 +01:00
Kristoffer Lind
807a609a90 fix rebase issue 2019-02-22 10:24:41 +01:00
Kristoffer Lind
82257be3a8 fix review comments 2019-02-21 18:49:07 +01:00
Kristoffer Lind
734fe33537 update sample state 2019-02-21 18:48:42 +01:00
Kristoffer Lind
347e6b7408 test setup for components and some initial tests 2019-02-21 18:38:30 +01:00
ahmed-Bazzara
bdf231f88d code enhancements 2019-02-21 18:17:45 +01:00
ahmed-Bazzara
88ad73bd41 tooltip position now follow the mouse 2019-02-21 18:17:45 +01:00
ahmed-Bazzara
530f0919f1 tooltip positioning tweaked 2019-02-21 18:17:45 +01:00
ahmed-Bazzara
79b89a3b25 all logic for tooltip has been moved to it's component
data-cell & column-header components reseted to pure ones
2019-02-21 18:17:44 +01:00
ahmed-Bazzara
4520d6a48a handling tooltip logic within it 2019-02-21 18:17:44 +01:00
ahmed-Bazzara
98678d4a13 jQuery commented code deleted 2019-02-21 18:17:44 +01:00
ahmed-Bazzara
da57204c41 console log removed 2019-02-21 18:17:44 +01:00
ahmed-Bazzara
906a11c6b4 tooltip component cleaned 2019-02-21 18:17:44 +01:00
ahmed-Bazzara
c66ad78e48 tooltip added to column header
comopnent changed to normal component to hold state in it
2019-02-21 18:17:44 +01:00
ahmed-Bazzara
6994bf51a3 showTooltip boolean changed name
skipping values that are header rows
2019-02-21 18:17:44 +01:00
ahmed-Bazzara
521d508604 commented jqeury from paint.js for tooltip 2019-02-21 18:17:43 +01:00
ahmed-Bazzara
3946f6c894 mind width set to tooltip
text inside it is center aligned
2019-02-21 18:16:38 +01:00
ahmed-Bazzara
aeccbf5d17 tooltip disabled in edit state 2019-02-21 18:16:38 +01:00
ahmed-Bazzara
95e330a50d tooltip added 2019-02-21 18:16:38 +01:00
Kristoffer Lind
555000be54 fix rebase issues 2019-02-21 17:49:05 +01:00
Kristoffer Lind
c367f24dd9 replace jquery scroll linking with components (fixing some bugs and getting rid of jquery) 2019-02-21 17:38:51 +01:00
giovanni hanselius
0b3b7b3f57 Merge pull request #19 from qlik-oss/QPE-554
[QPE-554] text alignemt
2019-02-21 17:37:00 +01:00
giovanni hanselius
44b33b4c92 Merge pull request #16 from qlik-oss/feature/QPE-550
[QPE-550] fix excel export
2019-02-21 16:48:34 +01:00
giovanni hanselius
61b339b146 Merge pull request #18 from qlik-oss/QPE-479
[QPE-479] Qlik defaults
2019-02-21 15:42:31 +01:00
ahmed-Bazzara
27b84c5623 code enhancements 2019-02-21 12:29:16 +01:00
ahmed-Bazzara
24edf1c6f4 values of text alignement property set to have lowercase 2019-02-21 12:02:42 +01:00
giovanni hanselius
fc363d7739 Merge pull request #20 from qlik-oss/export
exporting image and PDF enabled
2019-02-20 16:01:27 +01:00
ahmed-Bazzara
35d4dde118 exporting image and PDF enabled 2019-02-20 15:50:54 +01:00
ahmed-Bazzara
e70e76a401 small font size is set to be default
and its value matched to Qlik defalut font size
2019-02-20 15:44:36 +01:00
ahmed-Bazzara
a804c31658 tooltip position now follow the mouse 2019-02-19 16:58:39 +01:00
ahmed-Bazzara
9efe580d18 tooltip positioning tweaked 2019-02-19 15:41:30 +01:00
ahmed-Bazzara
bcb9d30237 sending the alignment value straight from props instead of numbers 2019-02-19 14:55:50 +01:00
Kristoffer Lind
fa60dd5248 remove unused component 2019-02-19 10:25:14 +01:00
Balazs Gobel
fd653de0e1 Fix most of the eslint warnings 2019-02-19 10:23:24 +01:00
ahmed-Bazzara
ec140efc56 Text alignment property added 2019-02-18 16:42:39 +01:00
ahmed-Bazzara
34477d7ef1 qlik font added to the fonts dropdown
and was made a default value
2019-02-18 16:12:40 +01:00
ahmed-Bazzara
48970cb4f4 all logic for tooltip has been moved to it's component
data-cell & column-header components reseted to pure ones
2019-02-15 11:33:43 +01:00
ahmed-Bazzara
fe4b5a72ec handling tooltip logic within it 2019-02-15 09:56:32 +01:00
ahmed-Bazzara
8b41022ddc jQuery commented code deleted 2019-02-15 09:32:55 +01:00
ahmed-Bazzara
9c66c09899 console log removed 2019-02-15 09:26:16 +01:00
ahmed-Bazzara
09d9055643 tooltip component cleaned 2019-02-15 08:56:26 +01:00
ahmed-Bazzara
b1204e0929 tooltip added to column header
comopnent changed to normal component to hold state in it
2019-02-15 08:56:02 +01:00
ahmed-Bazzara
d130ca266d showTooltip boolean changed name
skipping values that are header rows
2019-02-15 08:55:13 +01:00
ahmed-Bazzara
f730dc2827 commented jqeury from paint.js for tooltip 2019-02-15 08:54:11 +01:00
ahmed-Bazzara
b5f74395f7 mind width set to tooltip
text inside it is center aligned
2019-02-15 08:53:35 +01:00
Kristoffer Lind
b86806d4cd cleanup tooltips (resulted in whatever header was last hovered to be appended to each column header in xls) 2019-02-14 15:14:23 +01:00
ahmed-Bazzara
dfac9ad5e9 tooltip disabled in edit state 2019-02-14 14:04:15 +01:00
ahmed-Bazzara
db67b864ee edit mode interaction prevented 2019-02-14 13:54:17 +01:00
ahmed-Bazzara
377d642fe2 tooltip added 2019-02-14 13:02:59 +01:00
Kristoffer Lind
c3651a37da Merge branch 'master' into feature/QPE-550 2019-02-14 12:36:55 +01:00
Kristoffer Lind
8b843e028a fix excel export 2019-02-14 11:08:34 +01:00
ahmed-Bazzara
c47b401a1d typo in definiton object 2019-02-12 12:05:01 +01:00
ahmed-Bazzara
3c330465dd Definition object is Qlik standard 2019-02-12 12:03:05 +01:00
Balazs Gobel
8984affe87 minor cleanup 2019-02-12 11:25:22 +01:00
Balazs Gobel
ce1e196b78 Follow the same principle as the old application
- Restyle table cells only when metric semaphore is enabled
2019-02-12 11:25:22 +01:00
Balazs Gobel
e3b7a7640e Fixed misalignment when it's only one dimension 2019-02-12 11:25:22 +01:00
Balazs Gobel
68dccf8575 Fix pixel misalignment between table header and body 2019-02-12 11:25:22 +01:00
Balazs Gobel
8e100f286b Fix alternating colours 2019-02-12 11:25:22 +01:00
Balazs Gobel
f7ceb5c2bf Added stricter stylint rules 2019-02-12 11:24:13 +01:00
47 changed files with 1933 additions and 1072 deletions

View File

@@ -15,6 +15,7 @@ module.exports = {
},
globals: {
angular: false,
beforeEach: false,
define: false,
describe: false,
document: false,
@@ -41,12 +42,12 @@ module.exports = {
"no-cond-assign": ["warn"],
"no-fallthrough": ["warn"],
"no-undef": ["error"],
"no-unused-vars": ["warn"],
"no-use-before-define": ["warn", { "functions": false, "classes": false, "variables": false }],
"no-unused-vars": ["error"],
"no-use-before-define": ["error", { "functions": false, "classes": false, "variables": false }],
"no-useless-escape": ["warn"],
"no-useless-return": ["warn"],
"no-underscore-dangle": ["warn", { "allow": ["_id"] }],
"no-redeclare": ["warn"],
"no-redeclare": ["error"],
"no-restricted-syntax": ["warn"],
"operator-linebreak": ["warn", "before"],
"prefer-promise-reject-errors": ["warn"],
@@ -63,11 +64,11 @@ module.exports = {
"complexity": ["warn"],
"camelcase": ["warn"],
"max-statements": ["off"], // marks the entire functions, a bit too noisy
"sort-vars": ["warn"],
"sort-vars": ["off"], // not much value for the work
"init-declarations": ["off"],
"capitalized-comments": ["off"],
"one-var": ["off"],
"no-var": ["warn"],
"no-var": ["error"],
"no-plusplus": ["warn"],
"vars-on-top": ["off"],
"no-magic-numbers": ["off"], // useful, but also complains for reasonable checks with actual numbers
@@ -80,7 +81,7 @@ module.exports = {
"quote-props": ["off"],
"prefer-template": ["warn"],
"no-lonely-if": ["warn"],
"sort-keys": ["warn"],
"sort-keys": ["off"], // not much value for the work
"no-implicit-coercion": ["warn"],
"no-inline-comments": ["off"],
"spaced-comment": ["warn"],
@@ -107,7 +108,7 @@ module.exports = {
"strict": ["warn"],
"no-ternary": ["off"],
"multiline-ternary": ["off"],
"no-param-reassign": ["warn"],
"no-param-reassign": ["error"],
"prefer-destructuring": ["warn"],
"arrow-parens": ["off"],
"no-array-constructor": ["warn"],
@@ -116,7 +117,7 @@ module.exports = {
"max-params": ["warn"],
"brace-style": ["warn", "1tbs", { "allowSingleLine": true }],
"prefer-const": ["warn"],
"class-methods-use-this":["warn"],
// plugin:react
"react/jsx-indent": ["warn", 2],
"react/jsx-indent-props": ["warn", 2],
@@ -129,7 +130,8 @@ module.exports = {
"react/jsx-no-literals": ["off"],
"react/jsx-max-depth": ["off"], // rule throws exception in single-dimension-measure
"react/jsx-filename-extension": ["warn"],
"react/prefer-stateless-function": ["warn"]
"react/prefer-stateless-function": ["warn"],
"react/no-set-state": ["warn"]
},
extends: [
"eslint:all",

View File

@@ -1,3 +1,4 @@
const path = require('path');
const settings = require('./settings');
module.exports = (config) => {
@@ -6,37 +7,62 @@ module.exports = (config) => {
customLaunchers: {
SlimChromeHeadless: {
base: 'ChromeHeadless',
flags: ['--headless', '--disable-gpu', '--disable-translate', '--disable-extensions']
flags: [
'--headless',
'--disable-gpu',
'--disable-translate',
'--disable-extensions'
]
}
},
files: [
{ pattern: 'src/*.spec.js', watched: false }
{
pattern: 'src/**/*.spec.js',
watched: true
}
],
frameworks: ['jasmine'],
preprocessors: {
'src/*.spec.js': ['webpack', 'sourcemap']
'src/**/*.spec.{js, jsx}': [
'webpack',
'sourcemap'
]
},
webpack: {
devtool: 'source-map',
mode: settings.mode,
externals: {
jquery: {
amd: 'jquery',
commonjs: 'jquery',
commonjs2: 'jquery',
root: '_'
},
}
},
mode: settings.mode,
module: {
rules: [
{
test: /\.js$/,
exclude: [/node_modules/],
loaders: ['babel-loader']
loader: 'babel-loader',
options: {
plugins: [
'@babel/plugin-transform-async-to-generator',
'@babel/plugin-proposal-class-properties'],
presets: ['@babel/preset-react']
},
test: /\.(js|jsx)$/
},
{ test: /\.less$/, loader: 'ignore-loader' },
{ test: /\.json$/, loader: 'ignore-loader' }
{
loader: 'ignore-loader',
test: /\.less$/
}
]
},
resolve: {
alias: {
'test-utilities': path.resolve('test/test-utilities')
},
modules: ['node_modules']
}
}
});

524
package-lock.json generated
View File

@@ -101,6 +101,104 @@
"@babel/types": "^7.0.0"
}
},
"@babel/helper-create-class-features-plugin": {
"version": "7.3.2",
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.3.2.tgz",
"integrity": "sha512-tdW8+V8ceh2US4GsYdNVNoohq5uVwOf9k6krjwW4E1lINcHgttnWcNqgdoessn12dAy8QkbezlbQh2nXISNY+A==",
"dev": true,
"requires": {
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-member-expression-to-functions": "^7.0.0",
"@babel/helper-optimise-call-expression": "^7.0.0",
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-replace-supers": "^7.2.3"
},
"dependencies": {
"@babel/generator": {
"version": "7.3.3",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.3.3.tgz",
"integrity": "sha512-aEADYwRRZjJyMnKN7llGIlircxTCofm3dtV5pmY6ob18MSIuipHpA2yZWkPlycwu5HJcx/pADS3zssd8eY7/6A==",
"dev": true,
"requires": {
"@babel/types": "^7.3.3",
"jsesc": "^2.5.1",
"lodash": "^4.17.11",
"source-map": "^0.5.0",
"trim-right": "^1.0.1"
},
"dependencies": {
"@babel/types": {
"version": "7.3.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz",
"integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.11",
"to-fast-properties": "^2.0.0"
}
}
}
},
"@babel/helper-replace-supers": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.2.3.tgz",
"integrity": "sha512-GyieIznGUfPXPWu0yLS6U55Mz67AZD9cUk0BfirOWlPrXlBcan9Gz+vHGz+cPfuoweZSnPzPIm67VtQM0OWZbA==",
"dev": true,
"requires": {
"@babel/helper-member-expression-to-functions": "^7.0.0",
"@babel/helper-optimise-call-expression": "^7.0.0",
"@babel/traverse": "^7.2.3",
"@babel/types": "^7.0.0"
}
},
"@babel/parser": {
"version": "7.3.3",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.3.3.tgz",
"integrity": "sha512-xsH1CJoln2r74hR+y7cg2B5JCPaTh+Hd+EbBRk9nWGSNspuo6krjhX0Om6RnRQuIvFq8wVXCLKH3kwKDYhanSg==",
"dev": true
},
"@babel/traverse": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.2.3.tgz",
"integrity": "sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.0.0",
"@babel/generator": "^7.2.2",
"@babel/helper-function-name": "^7.1.0",
"@babel/helper-split-export-declaration": "^7.0.0",
"@babel/parser": "^7.2.3",
"@babel/types": "^7.2.2",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.10"
},
"dependencies": {
"@babel/types": {
"version": "7.3.3",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.3.3.tgz",
"integrity": "sha512-2tACZ80Wg09UnPg5uGAOUvvInaqLk3l/IAhQzlxLQOIXacr6bMsra5SH6AWw/hIDRCSbCdHP2KzSOD+cT7TzMQ==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.17.11",
"to-fast-properties": "^2.0.0"
}
}
}
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
}
}
},
"@babel/helper-define-map": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.1.0.tgz",
@@ -302,6 +400,16 @@
"@babel/plugin-syntax-async-generators": "^7.2.0"
}
},
"@babel/plugin-proposal-class-properties": {
"version": "7.3.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.3.3.tgz",
"integrity": "sha512-XO9eeU1/UwGPM8L+TjnQCykuVcXqaO5J1bkRPIygqZ/A2L1xVMJ9aZXrY31c0U4H2/LHKL4lbFQLsxktSrc/Ng==",
"dev": true,
"requires": {
"@babel/helper-create-class-features-plugin": "^7.3.0",
"@babel/helper-plugin-utils": "^7.0.0"
}
},
"@babel/plugin-proposal-json-strings": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz",
@@ -1332,6 +1440,17 @@
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true
},
"array.prototype.flat": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz",
"integrity": "sha512-rVqIs330nLJvfC7JqYvEWwqVr5QjYF1ib02i3YJtR/fICO6527Tjpc/e4Mvmxh3GIePPreRXMdaGyC99YphWEw==",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"es-abstract": "^1.10.0",
"function-bind": "^1.1.1"
}
},
"arraybuffer.slice": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
@@ -1826,6 +1945,12 @@
"multicast-dns-service-types": "^1.1.0"
}
},
"boolbase": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2178,6 +2303,20 @@
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
"cheerio": {
"version": "1.0.0-rc.2",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz",
"integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=",
"dev": true,
"requires": {
"css-select": "~1.2.0",
"dom-serializer": "~0.1.0",
"entities": "~1.1.1",
"htmlparser2": "^3.9.1",
"lodash": "^4.15.0",
"parse5": "^3.0.1"
}
},
"chokidar": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
@@ -2230,6 +2369,12 @@
"integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
"dev": true
},
"circular-json-es6": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/circular-json-es6/-/circular-json-es6-2.0.2.tgz",
"integrity": "sha512-ODYONMMNb3p658Zv+Pp+/XPa5s6q7afhz3Tzyvo+VRh9WIrJ64J76ZC4GQxnlye/NesTn09jvOiuE8+xxfpwhQ==",
"dev": true
},
"class-utils": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@@ -2770,6 +2915,30 @@
"source-list-map": "^2.0.0"
}
},
"css-select": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
"integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
"dev": true,
"requires": {
"boolbase": "~1.0.0",
"css-what": "2.1",
"domutils": "1.5.1",
"nth-check": "~1.0.1"
},
"dependencies": {
"domutils": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
"integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
"dev": true,
"requires": {
"dom-serializer": "0",
"domelementtype": "1"
}
}
}
},
"css-selector-tokenizer": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz",
@@ -2815,6 +2984,12 @@
}
}
},
"css-what": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.2.tgz",
"integrity": "sha512-wan8dMWQ0GUeF7DGEPVjhHemVW/vy6xUYmFzRY8RYqgA0JtXC9rJmbScBjqSu6dg9q0lwPQy6ZAmJVr3PPTvqQ==",
"dev": true
},
"cssesc": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz",
@@ -2924,6 +3099,27 @@
"integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=",
"dev": true
},
"deep-equal-ident": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-equal-ident/-/deep-equal-ident-1.1.1.tgz",
"integrity": "sha1-BvS4nlNxDNbOpKd4HHqVZkLejck=",
"dev": true,
"requires": {
"lodash.isequal": "^3.0"
},
"dependencies": {
"lodash.isequal": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-3.0.4.tgz",
"integrity": "sha1-HDXrO27wzR/1F0Pj6jz3/f/ay2Q=",
"dev": true,
"requires": {
"lodash._baseisequal": "^3.0.0",
"lodash._bindcallback": "^3.0.0"
}
}
}
},
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@@ -3142,6 +3338,12 @@
"path-type": "^3.0.0"
}
},
"discontinuous-range": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
"integrity": "sha1-44Mx8IRLukm5qctxx3FYWqsbxlo=",
"dev": true
},
"dns-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
@@ -3479,6 +3681,79 @@
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
"dev": true
},
"enzyme": {
"version": "3.8.0",
"resolved": "https://registry.npmjs.org/enzyme/-/enzyme-3.8.0.tgz",
"integrity": "sha512-bfsWo5nHyZm1O1vnIsbwdfhU989jk+squU9NKvB+Puwo5j6/Wg9pN5CO0YJelm98Dao3NPjkDZk+vvgwpMwYxw==",
"dev": true,
"requires": {
"array.prototype.flat": "^1.2.1",
"cheerio": "^1.0.0-rc.2",
"function.prototype.name": "^1.1.0",
"has": "^1.0.3",
"is-boolean-object": "^1.0.0",
"is-callable": "^1.1.4",
"is-number-object": "^1.0.3",
"is-string": "^1.0.4",
"is-subset": "^0.1.1",
"lodash.escape": "^4.0.1",
"lodash.isequal": "^4.5.0",
"object-inspect": "^1.6.0",
"object-is": "^1.0.1",
"object.assign": "^4.1.0",
"object.entries": "^1.0.4",
"object.values": "^1.0.4",
"raf": "^3.4.0",
"rst-selector-parser": "^2.2.3",
"string.prototype.trim": "^1.1.2"
},
"dependencies": {
"lodash.escape": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz",
"integrity": "sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg=",
"dev": true
}
}
},
"enzyme-adapter-react-16": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.9.1.tgz",
"integrity": "sha512-Egzogv1y77DUxdnq/CyHxLHaNxmSSKDDSDNNB/EiAXCZVFXdFibaNy2uUuRQ1n24T2m6KH/1Rw16XDRq+1yVEg==",
"dev": true,
"requires": {
"enzyme-adapter-utils": "^1.10.0",
"function.prototype.name": "^1.1.0",
"object.assign": "^4.1.0",
"object.values": "^1.1.0",
"prop-types": "^15.6.2",
"react-is": "^16.7.0",
"react-test-renderer": "^16.0.0-0"
}
},
"enzyme-adapter-utils": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.0.tgz",
"integrity": "sha512-VnIXJDYVTzKGbdW+lgK8MQmYHJquTQZiGzu/AseCZ7eHtOMAj4Rtvk8ZRopodkfPves0EXaHkXBDkVhPa3t0jA==",
"dev": true,
"requires": {
"function.prototype.name": "^1.1.0",
"object.assign": "^4.1.0",
"object.fromentries": "^2.0.0",
"prop-types": "^15.6.2",
"semver": "^5.6.0"
}
},
"enzyme-matchers": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/enzyme-matchers/-/enzyme-matchers-7.0.1.tgz",
"integrity": "sha512-1HmUK3frVSt7kim8icdx5LimuQm+DlklBRfy+dSlKd4SRxwlDGEM1LYTxL21/2kUZNl1XVUT5k5mec/D3k5jWw==",
"dev": true,
"requires": {
"circular-json-es6": "^2.0.1",
"deep-equal-ident": "^1.1.1"
}
},
"errno": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
@@ -5007,6 +5282,17 @@
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"function.prototype.name": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.0.tgz",
"integrity": "sha512-Bs0VRrTz4ghD8pTmbJQD1mZ8A/mN0ur/jGz+A6FBxPDUPkm1tNfF6bhTYPA7i7aF4lZJVr+OXTNNrnnIl58Wfg==",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"function-bind": "^1.1.1",
"is-callable": "^1.1.3"
}
},
"functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
@@ -6073,6 +6359,12 @@
"binary-extensions": "^1.0.0"
}
},
"is-boolean-object": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.0.tgz",
"integrity": "sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M=",
"dev": true
},
"is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
@@ -6225,6 +6517,12 @@
}
}
},
"is-number-object": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.3.tgz",
"integrity": "sha1-8mWrian0RQNO9q/xWo8AsA9VF5k=",
"dev": true
},
"is-obj": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
@@ -6324,6 +6622,18 @@
"integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
"dev": true
},
"is-string": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.4.tgz",
"integrity": "sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ=",
"dev": true
},
"is-subset": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/is-subset/-/is-subset-0.1.1.tgz",
"integrity": "sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY=",
"dev": true
},
"is-supported-regexp-flag": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.1.tgz",
@@ -6431,6 +6741,15 @@
"integrity": "sha512-pa9tbBWgU0EE4SWgc85T4sa886ufuQdsgruQANhECYjwqgV4z7Vw/499aCaP8ZH79JDS4vhm8doDG9HO4+e4sA==",
"dev": true
},
"jasmine-enzyme": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/jasmine-enzyme/-/jasmine-enzyme-7.0.1.tgz",
"integrity": "sha512-baCBO74WUy1pA0xow7FYBETvsEHpCekdOCEL8IB/yQzY1ulvxQqTYjRhCX7K4QRfY2jrsdef9DyQ3QmwBbgTnQ==",
"dev": true,
"requires": {
"enzyme-matchers": "^7.0.1"
}
},
"js-base64": {
"version": "2.4.9",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.4.9.tgz",
@@ -6918,6 +7237,17 @@
"integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=",
"dev": true
},
"lodash._baseisequal": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/lodash._baseisequal/-/lodash._baseisequal-3.0.7.tgz",
"integrity": "sha1-2AJfdjOdKTQnZ9zIh85cuVpbUfE=",
"dev": true,
"requires": {
"lodash.isarray": "^3.0.0",
"lodash.istypedarray": "^3.0.0",
"lodash.keys": "^3.0.0"
}
},
"lodash._basetostring": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz",
@@ -6930,6 +7260,12 @@
"integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=",
"dev": true
},
"lodash._bindcallback": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz",
"integrity": "sha1-5THCdkTPi1epnhftlbNcdIeJOS4=",
"dev": true
},
"lodash._getnative": {
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz",
@@ -6987,6 +7323,12 @@
"lodash._root": "^3.0.0"
}
},
"lodash.flattendeep": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz",
"integrity": "sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=",
"dev": true
},
"lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
@@ -6999,6 +7341,18 @@
"integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=",
"dev": true
},
"lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=",
"dev": true
},
"lodash.istypedarray": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/lodash.istypedarray/-/lodash.istypedarray-3.0.6.tgz",
"integrity": "sha1-yaR3SYYHUB2OhJTSg7h8OSgc72I=",
"dev": true
},
"lodash.keys": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz",
@@ -7010,6 +7364,12 @@
"lodash.isarray": "^3.0.0"
}
},
"lodash.merge": {
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz",
"integrity": "sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ==",
"dev": true
},
"lodash.restparam": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
@@ -7483,6 +7843,12 @@
"minimist": "0.0.8"
}
},
"moo": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/moo/-/moo-0.4.3.tgz",
"integrity": "sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw==",
"dev": true
},
"move-concurrently": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
@@ -7572,6 +7938,19 @@
"integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
"dev": true
},
"nearley": {
"version": "2.16.0",
"resolved": "https://registry.npmjs.org/nearley/-/nearley-2.16.0.tgz",
"integrity": "sha512-Tr9XD3Vt/EujXbZBv6UAHYoLUSMQAxSsTnm9K3koXzjzNWY195NqALeyrzLZBKzAkL3gl92BcSogqrHjD8QuUg==",
"dev": true,
"requires": {
"commander": "^2.19.0",
"moo": "^0.4.3",
"railroad-diagrams": "^1.0.0",
"randexp": "0.4.6",
"semver": "^5.4.1"
}
},
"negotiator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
@@ -7711,6 +8090,15 @@
"path-key": "^2.0.0"
}
},
"nth-check": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
"integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
"dev": true,
"requires": {
"boolbase": "~1.0.0"
}
},
"null-check": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/null-check/-/null-check-1.0.0.tgz",
@@ -7784,6 +8172,18 @@
"integrity": "sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA==",
"dev": true
},
"object-inspect": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz",
"integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==",
"dev": true
},
"object-is": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.0.1.tgz",
"integrity": "sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=",
"dev": true
},
"object-keys": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
@@ -7823,6 +8223,30 @@
"isobject": "^3.0.0"
}
},
"object.entries": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.0.tgz",
"integrity": "sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.12.0",
"function-bind": "^1.1.1",
"has": "^1.0.3"
}
},
"object.fromentries": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.0.tgz",
"integrity": "sha512-9iLiI6H083uiqUuvzyY6qrlmc/Gz8hLQFOcb/Ri/0xXFkSNS3ctV+CbE6yM2+AnkYfOB3dGjdzC0wrMLIhQICA==",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"es-abstract": "^1.11.0",
"function-bind": "^1.1.1",
"has": "^1.0.1"
}
},
"object.getownpropertydescriptors": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz",
@@ -7883,6 +8307,18 @@
"make-iterator": "^1.0.0"
}
},
"object.values": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.0.tgz",
"integrity": "sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.12.0",
"function-bind": "^1.1.1",
"has": "^1.0.3"
}
},
"obuf": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
@@ -8171,6 +8607,15 @@
"integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
"dev": true
},
"parse5": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
"integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"parseqs": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
@@ -8290,8 +8735,7 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
"dev": true,
"optional": true
"dev": true
},
"pify": {
"version": "3.0.0",
@@ -8763,12 +9207,37 @@
"integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=",
"dev": true
},
"raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"dev": true,
"requires": {
"performance-now": "^2.1.0"
}
},
"railroad-diagrams": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
"integrity": "sha1-635iZ1SN3t+4mcG5Dlc3RVnN234=",
"dev": true
},
"ramda": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.25.0.tgz",
"integrity": "sha512-GXpfrYVPwx3K7RQ6aYT8KPS8XViSXUVJT1ONhoKPE9VAleW42YE+U+8VEyGWt41EnEQW7gwecYJriTI0pKoecQ==",
"dev": true
},
"randexp": {
"version": "0.4.6",
"resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
"integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
"dev": true,
"requires": {
"discontinuous-range": "1.0.0",
"ret": "~0.1.10"
}
},
"randomatic": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz",
@@ -8858,6 +9327,36 @@
"scheduler": "^0.12.0"
}
},
"react-is": {
"version": "16.8.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.1.tgz",
"integrity": "sha512-ioMCzVDWvCvKD8eeT+iukyWrBGrA3DiFYkXfBsVYIRdaREZuBjENG+KjrikavCLasozqRWTwFUagU/O4vPpRMA==",
"dev": true
},
"react-test-renderer": {
"version": "16.8.1",
"resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.8.1.tgz",
"integrity": "sha512-Bd21TN3+YVl6GZwav6O0T6m5UwGfOj+2+xZH5VH93ToD6M5uclN/c+R1DGX49ueG413KZPUx7Kw3sOYz2aJgfg==",
"dev": true,
"requires": {
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"react-is": "^16.8.1",
"scheduler": "^0.13.1"
},
"dependencies": {
"scheduler": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.1.tgz",
"integrity": "sha512-VJKOkiKIN2/6NOoexuypwSrybx13MY7NSy9RNt8wPvZDMRT1CW6qlpF5jXRToXNHz3uWzbm2elNpZfXfGPqP9A==",
"dev": true,
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
}
}
},
"read-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
@@ -9359,6 +9858,16 @@
"inherits": "^2.0.1"
}
},
"rst-selector-parser": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz",
"integrity": "sha1-gbIw6i/MYGbInjRy3nlChdmwPZE=",
"dev": true,
"requires": {
"lodash.flattendeep": "^4.4.0",
"nearley": "^2.7.10"
}
},
"run-async": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
@@ -10222,6 +10731,17 @@
}
}
},
"string.prototype.trim": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz",
"integrity": "sha1-0E3iyJ4Tf019IG8Ia17S+ua+jOo=",
"dev": true,
"requires": {
"define-properties": "^1.1.2",
"es-abstract": "^1.5.0",
"function-bind": "^1.0.2"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",

View File

@@ -12,10 +12,12 @@
"eslint:fix": "eslint --fix src",
"test": "karma start karma.conf.js",
"test-once": "karma start karma.conf.js --single-run",
"watch": "gulp watch"
"watch": "gulp watch",
"stylelint": "stylelint src/main.less"
},
"devDependencies": {
"@babel/core": "7.1.2",
"@babel/plugin-proposal-class-properties": "7.3.3",
"@babel/plugin-transform-async-to-generator": "7.1.0",
"@babel/polyfill": "7.0.0",
"@babel/preset-env": "7.1.0",
@@ -25,6 +27,8 @@
"copy-webpack-plugin": "4.5.3",
"css-loader": "1.0.0",
"del": "2.0.2",
"enzyme": "3.8.0",
"enzyme-adapter-react-16": "1.9.1",
"eslint": "5.7.0",
"eslint-loader": "2.1.1",
"eslint-plugin-react": "7.11.1",
@@ -32,6 +36,7 @@
"gulp-json-editor": "2.4.3",
"gulp-zip": "3.0.2",
"jasmine-core": "3.2.1",
"jasmine-enzyme": "7.0.1",
"karma": "3.0.0",
"karma-chrome-launcher": "2.2.0",
"karma-jasmine": "1.1.2",
@@ -39,6 +44,7 @@
"karma-webpack": "3.0.5",
"less": "3.8.1",
"less-loader": "4.1.0",
"lodash.merge": "4.6.1",
"style-loader": "0.23.1",
"stylelint": "8.4.0",
"stylelint-webpack-plugin": "0.10.5",

View File

@@ -1,44 +0,0 @@
Accounts;Bold;Background;FontStyle;LetterColor;Align;Size;Comment
Revenues;;rgb(183, 219, 255);<italic>;;<center>;;<comment>
Gross sales revenues;;;;;;;
Less return & allowances;;;;;;;
Net sales revenues;<bold>;<soft>;;;;;
Cost of goods sold;<bold>;;;;;;
Direct materials;;;;;;;
Direct labor;;;;;;;
Manufacturing overhead;;#b7dbff;<italic>;;<center>;;<comment>
Indirect labor;;;;;;;
Depreciation, manufacturing equip;;;;;;;
Other mfr overhead;;;;;;;
Net mfr overhead;<bold>;<violete>;;;;;
Net costs of goods sold;<bold>;<violete>;;;;;
Gross profit;<bold>;<dark>;;;<center>;<large>;
Operating expenses;<bold>;;<italic>;;;;
Selling expenses;;;;;;;
Sales salaries;;;;;;;
Warranty expenses;;;;;;;
Depreciation, store equipment;;;;;;;
Other selling expenses3;;;;;;;
Total selling expenses;<bold>;<violete>;;;;;
General & administrative expenses;;#b7dbff;<italic>;;<center>;;<comment>
Administration salaries;;;;;;;
Rent expenses;;;;;;;
Depreciation, computers;;;;;;;
Other general & admin expenses;;;;;;;
total general & admin expenses;<bold>;<soft>;;;;;
total operating expenses;<bold>;<violete>;;;;;
Operating income before taxes;<bold>;<dark>;;<white>;;<large>;
Financial revenue & expenses;;#b7dbff;<italic>;;<center>;;<comment>
Revenue from investments;;;;;;;
Less interest expenses;;;;;;;
Net financial gain (expense);;;;;;;
Income before tax & extraordinary items;<bold>;<dark>;;<white>;;<large>;
Less income tax on operations;;;;;;;
Income before extraordinary items;;;;;;;
Extraordinary items;;#b7dbff;<italic>;;<center>;;<comment>
Sale of land;;;;;;;
Less initial cost;;;;;;;
Net gain on sale of land;<bold>;<soft>;;;;;
Less income tax on gain;;;;;;;
Extraordinary items after tax;<bold>;<soft>;;;;;
Net Income (Profit);<bold>;<dark>;;<white>;<center>;<large>;
1 Accounts Bold Background FontStyle LetterColor Align Size Comment
2 Revenues rgb(183, 219, 255) <italic> <center> <comment>
3 Gross sales revenues
4 Less return & allowances
5 Net sales revenues <bold> <soft>
6 Cost of goods sold <bold>
7 Direct materials
8 Direct labor
9 Manufacturing overhead #b7dbff <italic> <center> <comment>
10 Indirect labor
11 Depreciation, manufacturing equip
12 Other mfr overhead
13 Net mfr overhead <bold> <violete>
14 Net costs of goods sold <bold> <violete>
15 Gross profit <bold> <dark> <center> <large>
16 Operating expenses <bold> <italic>
17 Selling expenses
18 Sales salaries
19 Warranty expenses
20 Depreciation, store equipment
21 Other selling expenses3
22 Total selling expenses <bold> <violete>
23 General & administrative expenses #b7dbff <italic> <center> <comment>
24 Administration salaries
25 Rent expenses
26 Depreciation, computers
27 Other general & admin expenses
28 total general & admin expenses <bold> <soft>
29 total operating expenses <bold> <violete>
30 Operating income before taxes <bold> <dark> <white> <large>
31 Financial revenue & expenses #b7dbff <italic> <center> <comment>
32 Revenue from investments
33 Less interest expenses
34 Net financial gain (expense)
35 Income before tax & extraordinary items <bold> <dark> <white> <large>
36 Less income tax on operations
37 Income before extraordinary items
38 Extraordinary items #b7dbff <italic> <center> <comment>
39 Sale of land
40 Less initial cost
41 Net gain on sale of land <bold> <soft>
42 Less income tax on gain
43 Extraordinary items after tax <bold> <soft>
44 Net Income (Profit) <bold> <dark> <white> <center> <large>

View File

@@ -1,21 +0,0 @@
Accounts;Bold;Background;FontStyle;LetterColor;Align;Size;Comment
Revenues;<bold>;;<italic>;;<center>;;<comment>
Net sales revenues;<bold>;rgb(128, 191, 255);;;;;
Cost of goods sold;<bold>;rgb(128, 191, 255);;;;;
Manufacturing overhead;<bold>;;<italic>;;<center>;;<comment>
Net mfr overhead;<bold>;rgb(128, 191, 255);;;;;
Net costs of goods sold;<bold>;rgb(128, 191, 255);;;;;
Gross profit;<bold>;rgb(0, 102, 204);<white>;;<center>;<large>;
Operating expenses;<bold>;;<italic>;;;;
Total selling expenses;<bold>;rgb(128, 191, 255);;;;;
General & administrative expenses;<bold>;;<italic>;;<center>;;<comment>
Other general & admin expenses;<bold>;rgb(128, 191, 255);<white>;;<center>;<large>;
total general & admin expenses;<bold>;<soft>;;;;;
total operating expenses;<bold>;rgb(128, 191, 255);;;;;
Operating income before taxes;<bold>;rgb(0, 102, 204);;<white>;;<large>;
Financial revenue & expenses;<bold>;;<italic>;;<center>;;<comment>
Income before tax & extraordinary items;<bold>;rgb(0, 102, 204);;<white>;;<large>;
Extraordinary items;<bold>;;<italic>;;<center>;;<comment>
Net gain on sale of land;<bold>;rgb(0, 102, 204);<white>;;<center>;<large>;
Extraordinary items after tax;<bold>;rgb(0, 102, 204);<white>;;<center>;<large>;
Net Income (Profit);<bold>;<night>;;<white>;<center>;<large>;
1 Accounts Bold Background FontStyle LetterColor Align Size Comment
2 Revenues <bold> <italic> <center> <comment>
3 Net sales revenues <bold> rgb(128, 191, 255)
4 Cost of goods sold <bold> rgb(128, 191, 255)
5 Manufacturing overhead <bold> <italic> <center> <comment>
6 Net mfr overhead <bold> rgb(128, 191, 255)
7 Net costs of goods sold <bold> rgb(128, 191, 255)
8 Gross profit <bold> rgb(0, 102, 204) <white> <center> <large>
9 Operating expenses <bold> <italic>
10 Total selling expenses <bold> rgb(128, 191, 255)
11 General & administrative expenses <bold> <italic> <center> <comment>
12 Other general & admin expenses <bold> rgb(128, 191, 255) <white> <center> <large>
13 total general & admin expenses <bold> <soft>
14 total operating expenses <bold> rgb(128, 191, 255)
15 Operating income before taxes <bold> rgb(0, 102, 204) <white> <large>
16 Financial revenue & expenses <bold> <italic> <center> <comment>
17 Income before tax & extraordinary items <bold> rgb(0, 102, 204) <white> <large>
18 Extraordinary items <bold> <italic> <center> <comment>
19 Net gain on sale of land <bold> rgb(0, 102, 204) <white> <center> <large>
20 Extraordinary items after tax <bold> rgb(0, 102, 204) <white> <center> <large>
21 Net Income (Profit) <bold> <night> <white> <center> <large>

Binary file not shown.

View File

@@ -1,21 +0,0 @@
Accounts;Bold;Background;FontStyle;LetterColor;Align;Size;Comment
Revenues;<bold>;;<italic>;;<center>;;<comment>
Net sales revenues;<bold>;RGB(225,226,226);;;;;
Cost of goods sold;<bold>;RGB(225,226,226);;;;;
Manufacturing overhead;<bold>;;<italic>;;<center>;;<comment>
Net mfr overhead;<bold>;RGB(225,226,226);;;;;
Net costs of goods sold;<bold>;RGB(225,226,226);;;;;
Gross profit;<bold>;RGB(193,216,47);;;<center>;<large>;
Operating expenses;<bold>;;<italic>;;;;
Total selling expenses;<bold>;RGB(225,226,226);;;;;
General & administrative expenses;<bold>;;<italic>;;<center>;;<comment>
Other general & admin expenses;<bold>;rgb(128, 191, 255);<white>;;<center>;<large>;
total general & admin expenses;<bold>;<soft>;;;;;
total operating expenses;<bold>;rgb(128, 191, 255);<white>;;;;
Operating income before taxes;<bold>;RGB(193,216,47);;;;<large>;
Financial revenue & expenses;<bold>;;<italic>;;<center>;;<comment>
Income before tax & extraordinary items;<bold>;RGB(193,216,47);;;;<large>;
Extraordinary items;<bold>;;<italic>;;<center>;;<comment>
Net gain on sale of land;<bold>;RGB(193,216,47);;;<center>;<large>;
Extraordinary items after tax;<bold>;RGB(193,216,47);;;<center>;<large>;
Net Income (Profit);<bold>;<night>;;<white>;<center>;<large>;
1 Accounts Bold Background FontStyle LetterColor Align Size Comment
2 Revenues <bold> <italic> <center> <comment>
3 Net sales revenues <bold> RGB(225,226,226)
4 Cost of goods sold <bold> RGB(225,226,226)
5 Manufacturing overhead <bold> <italic> <center> <comment>
6 Net mfr overhead <bold> RGB(225,226,226)
7 Net costs of goods sold <bold> RGB(225,226,226)
8 Gross profit <bold> RGB(193,216,47) <center> <large>
9 Operating expenses <bold> <italic>
10 Total selling expenses <bold> RGB(225,226,226)
11 General & administrative expenses <bold> <italic> <center> <comment>
12 Other general & admin expenses <bold> rgb(128, 191, 255) <white> <center> <large>
13 total general & admin expenses <bold> <soft>
14 total operating expenses <bold> rgb(128, 191, 255) <white>
15 Operating income before taxes <bold> RGB(193,216,47) <large>
16 Financial revenue & expenses <bold> <italic> <center> <comment>
17 Income before tax & extraordinary items <bold> RGB(193,216,47) <large>
18 Extraordinary items <bold> <italic> <center> <comment>
19 Net gain on sale of land <bold> RGB(193,216,47) <center> <large>
20 Extraordinary items after tax <bold> RGB(193,216,47) <center> <large>
21 Net Income (Profit) <bold> <night> <white> <center> <large>

View File

@@ -2,10 +2,127 @@ import React from 'react';
import PropTypes from 'prop-types';
import { ApplyPreMask } from '../masking';
import { addSeparators } from '../utilities';
import Tooltip from '../tooltip/index.jsx';
class DataCell extends React.PureComponent {
constructor (props) {
super(props);
this.handleSelect = this.handleSelect.bind(this);
}
handleSelect () {
const { data: { meta: { dimensionCount } }, general: { allowFilteringByClick }, measurement, qlik } = this.props;
const hasSecondDimension = dimensionCount > 1;
if (!allowFilteringByClick) {
return;
}
qlik.backendApi.selectValues(0, [measurement.parents.dimension1.elementNumber], true);
if (hasSecondDimension) {
qlik.backendApi.selectValues(1, [measurement.parents.dimension2.elementNumber], true);
}
}
render () {
const {
data,
general,
measurement,
styleBuilder,
styling
} = this.props;
const isColumnPercentageBased = (/%/).test(measurement.format);
let formattedMeasurementValue = formatMeasurementValue(measurement, styling);
if (styleBuilder.hasComments()) {
formattedMeasurementValue = '.';
}
let textAlignment = 'Right';
const textAlignmentProp = styling.options.textAlignment;
if (textAlignmentProp) {
textAlignment = textAlignmentProp;
}
let cellStyle = {
fontFamily: styling.options.fontFamily,
...styleBuilder.getStyle(),
paddingLeft: '5px',
textAlign: textAlignment
};
const { semaphoreColors, semaphoreColors: { fieldsToApplyTo } } = styling;
const isValidSemaphoreValue = !styleBuilder.hasComments() && !isNaN(measurement.value);
const dimension1Row = measurement.parents.dimension1.elementNumber;
const isSpecifiedMetricField = fieldsToApplyTo.metricsSpecificFields.indexOf(dimension1Row) !== -1;
const shouldHaveSemaphoreColors = (fieldsToApplyTo.applyToMetric || isSpecifiedMetricField);
if (isValidSemaphoreValue && shouldHaveSemaphoreColors) {
const { backgroundColor, color } = getSemaphoreColors(measurement, semaphoreColors);
cellStyle = {
...styleBuilder.getStyle(),
backgroundColor,
color,
fontFamily: styling.options.fontFamily,
paddingLeft: '5px',
textAlign: textAlignment
};
}
let cellClass = 'grid-cells';
const hasTwoDimensions = data.headers.dimension2 && data.headers.dimension2.length > 0;
const shouldUseSmallCells = isColumnPercentageBased && data.headers.measurements.length > 1 && hasTwoDimensions;
if (shouldUseSmallCells) {
cellClass = 'grid-cells-small';
}
return (
<td
className={`${cellClass}${general.cellSuffix}`}
onClick={this.handleSelect}
style={cellStyle}
>
<Tooltip
tooltipText={formattedMeasurementValue}
>
{formattedMeasurementValue}
</Tooltip>
</td>
);
}
}
DataCell.propTypes = {
data: PropTypes.shape({
headers: PropTypes.shape({
measurements: PropTypes.array.isRequired
}).isRequired
}).isRequired,
general: PropTypes.shape({
cellSuffix: PropTypes.string.isRequired
}).isRequired,
measurement: PropTypes.shape({
format: PropTypes.string,
name: PropTypes.string,
value: PropTypes.any
}).isRequired,
qlik: PropTypes.shape({
backendApi: PropTypes.shape({
selectValues: PropTypes.func.isRequired
}).isRequired
}).isRequired,
styleBuilder: PropTypes.shape({
hasComments: PropTypes.func.isRequired
}).isRequired,
styling: PropTypes.shape({
symbolForNulls: PropTypes.any.isRequired
}).isRequired
};
export default DataCell;
function formatMeasurementValue (measurement, styling) {
// TODO: measurement.name is a horrible propertyname, it's actually the column header
const isColumnPercentageBased = measurement.parents.measurement.header.substring(0, 1) === '%';
const isColumnPercentageBased = (/%/).test(measurement.format);
let formattedMeasurementValue = '';
if (isColumnPercentageBased) {
if (isNaN(measurement.value)) {
@@ -53,109 +170,11 @@ function formatMeasurementValue (measurement, styling) {
}
function getSemaphoreColors (measurement, semaphoreColors) {
if (measurement < semaphoreColors.status.critical) {
if (measurement.value < semaphoreColors.status.critical) {
return semaphoreColors.statusColors.critical;
}
if (measurement < semaphoreColors.status.medium) {
if (measurement.value < semaphoreColors.status.medium) {
return semaphoreColors.statusColors.medium;
}
return semaphoreColors.statusColors.normal;
}
class DataCell extends React.PureComponent {
constructor (props) {
super(props);
this.handleSelect = this.handleSelect.bind(this);
}
handleSelect () {
const { data: { meta: { dimensionCount } }, general: { allowFilteringByClick }, measurement, qlik } = this.props;
const hasSecondDimension = dimensionCount > 1;
if (!allowFilteringByClick) {
return;
}
qlik.backendApi.selectValues(0, [measurement.parents.dimension1.elementNumber], true);
if (hasSecondDimension) {
qlik.backendApi.selectValues(1, [measurement.parents.dimension2.elementNumber], true);
}
}
render () {
const { data, general, measurement, styleBuilder, styling } = this.props;
const isColumnPercentageBased = measurement.name.substring(0, 1) === '%';
let formattedMeasurementValue = formatMeasurementValue(measurement, styling);
if (styleBuilder.hasComments()) {
formattedMeasurementValue = '.';
}
let cellStyle = {
fontFamily: styling.options.fontFamily,
...styleBuilder.getStyle(),
paddingLeft: '4px',
textAlign: 'right'
};
const { semaphoreColors } = styling;
const isValidSemaphoreValue = !styleBuilder.hasComments() && !isNaN(measurement.value);
const shouldHaveSemaphoreColors = semaphoreColors.fieldsToApplyTo.applyToAll || semaphoreColors.fieldsToApplyTo.specificFields.indexOf(measurement.parents.dimension1.header) !== -1;
if (isValidSemaphoreValue && shouldHaveSemaphoreColors) {
const { backgroundColor, color } = getSemaphoreColors(measurement, semaphoreColors);
cellStyle = {
backgroundColor,
color,
fontFamily: styling.options.fontFamily,
fontSize: styleBuilder.getStyle().fontSize,
paddingLeft: '4px',
textAlign: 'right'
};
}
let cellClass = 'grid-cells';
const shouldUseSmallCells = isColumnPercentageBased && data.headers.measurements.length > 1;
if (shouldUseSmallCells) {
cellClass = 'grid-cells-small';
}
return (
<td
className={`${cellClass}${general.cellSuffix}`}
onClick={this.handleSelect}
style={cellStyle}
>
{formattedMeasurementValue}
</td>
);
}
}
DataCell.propTypes = {
data: PropTypes.shape({
headers: PropTypes.shape({
measurements: PropTypes.array.isRequired
}).isRequired
}).isRequired,
general: PropTypes.shape({
cellSuffix: PropTypes.string.isRequired
}).isRequired,
measurement: PropTypes.shape({
format: PropTypes.string,
name: PropTypes.string,
value: PropTypes.any
}).isRequired,
qlik: PropTypes.shape({
backendApi: PropTypes.shape({
selectValues: PropTypes.func.isRequired
}).isRequired
}).isRequired,
styleBuilder: PropTypes.shape({
hasComments: PropTypes.func.isRequired
}).isRequired,
styling: PropTypes.shape({
symbolForNulls: PropTypes.any.isRequired
}).isRequired
};
export default DataCell;

View File

@@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import StyleBuilder from '../style-builder';
import DataCell from './data-cell.jsx';
import HeaderPadding from './header-padding.jsx';
import RowHeader from './row-header.jsx';
import { injectSeparators } from '../utilities';

View File

@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import HeaderPadding from './header-padding.jsx';
import Tooltip from '../tooltip/index.jsx';
class RowHeader extends React.PureComponent {
constructor (props) {
@@ -15,18 +16,25 @@ class RowHeader extends React.PureComponent {
}
render () {
const { entry, rowStyle, styleBuilder, styling } = this.props;
const { entry, rowStyle, styleBuilder, styling, qlik } = this.props;
const inEditState = qlik.inEditState();
return (
<td
className="fdim-cells"
onClick={this.handleSelect}
style={rowStyle}
>
<HeaderPadding
styleBuilder={styleBuilder}
styling={styling}
/>
{entry.displayValue}
<Tooltip
isTooltipActive={!inEditState}
tooltipText={entry.displayValue}
>
<HeaderPadding
styleBuilder={styleBuilder}
styling={styling}
/>
{entry.displayValue}
</Tooltip>
</td>
);
}

83
src/dataset.js Normal file
View File

@@ -0,0 +1,83 @@
import qlik from 'qlik';
function createCube (definition, app) {
return new Promise(resolve => {
app.createCube(definition, resolve);
});
}
async function buildDataCube (originCubeDefinition, dimensionIndexes, app) {
const cubeDefinition = {
...originCubeDefinition,
qInitialDataFetch: [
{
qHeight: 1000,
qWidth: 10
}
],
qDimensions: [originCubeDefinition.qDimensions[dimensionIndexes.dimension1]],
qMeasures: originCubeDefinition.qMeasures
};
if (dimensionIndexes.dimension2) {
cubeDefinition.qDimensions.push(originCubeDefinition.qDimensions[dimensionIndexes.dimension2]);
}
const cube = await createCube(cubeDefinition, app);
return cube.qHyperCube.qDataPages[0].qMatrix;
}
async function buildDesignCube (originCubeDefinition, dimensionIndexes, app) {
if (!dimensionIndexes.design) {
return null;
}
const cube = await createCube({
qInitialDataFetch: [
{
qHeight: 1000,
qWidth: 1
}
],
qDimensions: [originCubeDefinition.qDimensions[dimensionIndexes.design]]
}, app);
return cube.qHyperCube.qDataPages[0].qMatrix;
}
const STYLE_SEPARATOR_COUNT = 7;
function findDesignDimension (qMatrix) {
return qMatrix[0].map(entry => (entry.qText.match(/;/g) || []).length).indexOf(STYLE_SEPARATOR_COUNT);
}
function getDimensionIndexes (dimensionsInformation, designDimensionIndex) {
const hasDesign = designDimensionIndex !== -1;
const nonDesignDimensionCount = hasDesign ? dimensionsInformation.length - 1 : dimensionsInformation.length;
const dimension1 = designDimensionIndex === 0 ? 1 : 0;
let dimension2 = false;
if (nonDesignDimensionCount === 2) {
dimension2 = hasDesign && designDimensionIndex < 2 ? 2 : 1;
}
const design = hasDesign && designDimensionIndex;
const firstMeasurementIndex = dimensionsInformation.length;
return {
design,
dimension1,
dimension2,
firstMeasurementIndex
};
}
export async function initializeCubes ({ component, layout }) {
const properties = await component.backendApi.getProperties();
const originCubeDefinition = properties.qHyperCubeDef;
const app = qlik.currApp(component);
const designDimensionIndex = findDesignDimension(layout.qHyperCube.qDataPages[0].qMatrix);
const dimensionsInformation = layout.qHyperCube.qDimensionInfo;
const dimensionIndexes = getDimensionIndexes(dimensionsInformation, designDimensionIndex);
const designCube = await buildDesignCube(originCubeDefinition, dimensionIndexes, app);
const dataCube = await buildDataCube(originCubeDefinition, dimensionIndexes, app);
return {
design: designCube,
data: dataCube
};
}

View File

@@ -1,68 +0,0 @@
const colorLibrary = {
type: 'items',
label: 'Primary Colors Library',
items: {
ColLibClean: {
ref: 'collibclean',
translation: 'Clean',
type: 'string',
defaultValue: '#ffffff'
},
ColLibSoft: {
ref: 'collibsoft',
translation: 'Soft',
type: 'string',
defaultValue: '#efefef'
},
ColLibDark: {
ref: 'collibdark',
translation: 'Dark',
type: 'string',
defaultValue: '#c4c4c4'
},
ColLibNight: {
ref: 'collibnight',
translation: 'Night',
type: 'string',
defaultValue: '#808080'
},
ColLibRed: {
ref: 'collibred',
translation: 'Red',
type: 'string',
defaultValue: '#d58b94'
},
ColLibOrange: {
ref: 'colliborange',
translation: 'Orange',
type: 'string',
defaultValue: '#fd6600'
},
ColLibViolete: {
ref: 'collibviolete',
translation: 'Violete',
type: 'string',
defaultValue: '#ccc0ff'
},
ColLibBlue: {
ref: 'collibblue',
translation: 'Blue',
type: 'string',
defaultValue: '#4575b4'
},
ColLibGreen: {
ref: 'collibgreen',
translation: 'Green',
type: 'string',
defaultValue: '#7bb51c'
},
ColLibCustom: {
ref: 'collibcustom',
label: 'Custom',
type: 'string',
defaultValue: '#ffcccc'
}
}
};
export default colorLibrary;

View File

@@ -24,102 +24,26 @@ const header = {
defaultValue: 2
},
headercolors: {
component: 'color-picker',
defaultValue: {
index: 6,
color: '#4477aa'
},
label: 'Background Header Color',
ref: 'HeaderColorSchema',
type: 'string',
component: 'dropdown',
label: 'BackGround Header Color',
options: [
{
value: 'Clean',
label: 'Clean'
},
{
value: 'Soft',
label: 'Soft'
},
{
value: 'Dark',
label: 'Dark'
},
{
value: 'Night',
label: 'Night'
},
{
value: 'Blue',
label: 'Blue'
},
{
value: 'Orange',
label: 'Orange'
},
{
value: 'Red',
label: 'Red'
},
{
value: 'Green',
label: 'Green'
},
{
value: 'Violete',
label: 'Violete'
},
{
value: 'Custom',
label: 'Custom'
}
],
defaultValue: 'Night'
type: 'object',
dualOutput: true
},
HeaderTextColor: {
ref: 'HeaderTextColorSchema',
type: 'string',
component: 'dropdown',
label: 'Text Header Color',
options: [
{
value: 'Black',
label: 'Black'
},
{
value: 'DimGray',
label: 'DimGray'
},
{
value: 'ForestGreen',
label: 'ForestGreen'
},
{
value: 'Gainsboro',
label: 'Gainsboro'
},
{
value: 'Indigo',
label: 'Indigo'
},
{
value: 'Navy',
label: 'Navy'
},
{
value: 'Purple',
label: 'Purple'
},
{
value: 'WhiteSmoke',
label: 'WhiteSmoke'
},
{
value: 'White',
label: 'White'
},
{
value: 'YellowGreen',
label: 'YellowGreen'
}
],
defaultValue: 'WhiteSmoke'
component: 'color-picker',
defaultValue: {
index: 1,
color: '#ffffff'
},
type: 'object',
dualOutput: true
},
HeaderFontSize: {
ref: 'lettersizeheader',
@@ -136,7 +60,7 @@ const header = {
label: 'Medium'
}
],
defaultValue: 2
defaultValue: 1
}
}
};

View File

@@ -1,33 +1,30 @@
import pagination from './pagination';
import header from './header';
import formatted from './formatted';
import tableFormat from './table-format';
import conceptSemaphores from './concept-semaphores';
import metricSemaphores from './metric-semaphores';
import colorLibrary from './color-library';
import pijamaColorLibrary from './pijama-color-library';
const definition = {
component: 'accordion',
items: {
dimensions: {
max: 2,
min: 1,
uses: 'dimensions'
},
measures: {
max: 9,
min: 1,
uses: 'measures'
data: {
items: {
dimensions: {
disabledRef: ''
},
measures: {
disabledRef: ''
}
},
uses: 'data'
},
settings: {
items: {
ColorLibrary: colorLibrary,
ConceptSemaphores: conceptSemaphores,
Formatted: formatted,
Formatted: tableFormat,
Header: header,
MetricSemaphores: metricSemaphores,
Pagination: pagination,
PijamaColorLibrary: pijamaColorLibrary
Pagination: pagination
},
uses: 'settings'
},

View File

@@ -23,9 +23,9 @@ const metricSemaphores = {
ref: 'metricssemaphore',
translation: 'Metrics affected (1,2,4,...)',
type: 'string',
defaultValue: '0',
defaultValue: '-',
show (data) {
return data.allmetrics == false;
return !data.allmetrics;
}
},
MetricStatus1: {
@@ -40,7 +40,7 @@ const metricSemaphores = {
type: 'object',
component: 'color-picker',
defaultValue: {
index: 7,
index: 8,
color: '#f93f17'
}
},
@@ -50,7 +50,7 @@ const metricSemaphores = {
type: 'object',
component: 'color-picker',
defaultValue: {
index: 10,
index: 11,
color: '#ffffff'
}
},
@@ -66,7 +66,7 @@ const metricSemaphores = {
type: 'object',
component: 'color-picker',
defaultValue: {
index: 8,
index: 9,
color: '#ffcf02'
}
},
@@ -76,7 +76,7 @@ const metricSemaphores = {
type: 'object',
component: 'color-picker',
defaultValue: {
index: 11,
index: 12,
color: '#000000'
}
},
@@ -86,7 +86,7 @@ const metricSemaphores = {
type: 'object',
component: 'color-picker',
defaultValue: {
index: 9,
index: 10,
color: '#276e27'
}
},
@@ -96,7 +96,7 @@ const metricSemaphores = {
type: 'object',
component: 'color-picker',
defaultValue: {
index: 10,
index: 11,
color: '#ffffff'
}
}

View File

@@ -1,68 +0,0 @@
const pijamaColorLibrary = {
type: 'items',
label: 'Pijama Colors Library',
items: {
ColLibCleanP: {
ref: 'collibcleanp',
translation: 'Clean',
type: 'string',
defaultValue: '#ffffff'
},
ColLibSoftP: {
ref: 'collibsoftp',
translation: 'Soft',
type: 'string',
defaultValue: '#ffffff'
},
ColLibDarkP: {
ref: 'collibdarkp',
translation: 'Dark',
type: 'string',
defaultValue: '#efefef'
},
ColLibNightP: {
ref: 'collibnightp',
translation: 'Night',
type: 'string',
defaultValue: '#c4c4c4'
},
ColLibRedP: {
ref: 'collibredp',
translation: 'Red',
type: 'string',
defaultValue: '#ffcccc'
},
ColLibOrangeP: {
ref: 'colliborangep',
translation: 'Orange',
type: 'string',
defaultValue: '#ffcc66'
},
ColLibVioleteP: {
ref: 'collibvioletep',
translation: 'Violete',
type: 'string',
defaultValue: '#e6e6ff'
},
ColLibBlueP: {
ref: 'collibbluep',
translation: 'Blue',
type: 'string',
defaultValue: '#b3d9ff'
},
ColLibGreenP: {
ref: 'collibgreenp',
translation: 'Green',
type: 'string',
defaultValue: '#98fb98'
},
ColLibCustomP: {
ref: 'collibcustomp',
label: 'Custom',
type: 'string',
defaultValue: '#ffffff'
}
}
};
export default pijamaColorLibrary;

View File

@@ -1,4 +1,9 @@
const formatted = {
// fixes case for when there are 3 dimensions, missies the case with 1 design dimension and 1 data dimension
function hasDesignDimension (data) {
return data.qHyperCubeDef.qDimensions.length > 2;
}
const tableFormat = {
type: 'items',
label: 'Table Format',
items: {
@@ -6,7 +11,8 @@ const formatted = {
ref: 'indentbool',
type: 'boolean',
label: 'Indent',
defaultValue: true
defaultValue: false,
show: data => !hasDesignDimension(data)
},
SeparatorColumns: {
ref: 'separatorcols',
@@ -14,72 +20,29 @@ const formatted = {
label: 'Separator Columns',
defaultValue: false
},
CustomFileBool: {
ref: 'customfilebool',
type: 'boolean',
label: 'Include External File',
defaultValue: false
rowEvenBGColor: {
component: 'color-picker',
defaultValue: {
color: '#fff',
index: 1
},
label: 'Even row background color',
ref: 'rowEvenBGColor',
type: 'object',
dualOutput: true,
show: data => !hasDesignDimension(data)
},
CustomFile: {
ref: 'customfile',
label: 'Name of CSV file (; separated)',
type: 'string',
defaultValue: '',
show (data) {
return data.customfilebool;
}
},
colors: {
ref: 'ColorSchema',
type: 'string',
component: 'dropdown',
label: 'BackGround Style',
options: [
{
value: 'Clean',
label: 'Clean'
},
{
value: 'Soft',
label: 'Soft'
},
{
value: 'Dark',
label: 'Dark'
},
{
value: 'Night',
label: 'Night'
},
{
value: 'Blue',
label: 'Blue'
},
{
value: 'Orange',
label: 'Orange'
},
{
value: 'Red',
label: 'Red'
},
{
value: 'Green',
label: 'Green'
},
{
value: 'Violete',
label: 'Violete'
},
{
value: 'Custom',
label: 'Custom'
}
],
defaultValue: 'Clean',
show (data) {
return data.customfilebool == false;
}
rowOddBGColor: {
component: 'color-picker',
defaultValue: {
color: '#b6d7ea',
index: 4
},
label: 'Odd row background color',
ref: 'rowOddBGColor',
type: 'object',
dualOutput: true,
show: data => !hasDesignDimension(data)
},
BodyTextColor: {
ref: 'BodyTextColorSchema',
@@ -129,9 +92,7 @@ const formatted = {
}
],
defaultValue: 'Black',
show (data) {
return data.customfilebool == false;
}
show: data => !hasDesignDimension(data)
},
FontFamily: {
ref: 'FontFamily',
@@ -140,31 +101,35 @@ const formatted = {
label: 'FontFamily',
options: [
{
value: 'Arial',
value: 'QlikView Sans, -apple-system, sans-serif',
label: 'QlikView Sans'
},
{
value: 'Arial, -apple-system, sans-serif',
label: 'Arial'
},
{
value: 'Calibri',
value: 'Calibri, -apple-system, sans-serif',
label: 'Calibri'
},
{
value: 'Comic Sans MS',
value: 'Comic Sans MS, -apple-system, sans-serif',
label: 'Comic Sans MS'
},
{
value: 'MS Sans Serif',
value: 'MS Sans Serif, -apple-system, sans-serif',
label: 'MS Sans Serif'
},
{
value: 'Tahoma',
value: 'Tahoma, -apple-system, sans-serif',
label: 'Tahoma'
},
{
value: 'Verdana',
value: 'Verdana, -apple-system, sans-serif',
label: 'Verdana'
}
],
defaultValue: 'Calibri'
defaultValue: 'QlikView Sans, -apple-system, sans-serif'
},
DataFontSize: {
ref: 'lettersize',
@@ -181,7 +146,27 @@ const formatted = {
label: 'Medium'
}
],
defaultValue: 2
defaultValue: 1
},
textAlignment: {
ref: 'cellTextAlignment',
label: 'Cell Text alignment',
component: 'buttongroup',
options: [
{
value: 'left',
label: 'Left'
},
{
value: 'center',
label: 'Center'
},
{
value: 'right',
label: 'Right'
}
],
defaultValue: 'right'
},
ColumnWidthSlider: {
type: 'number',
@@ -236,4 +221,4 @@ const formatted = {
}
};
export default formatted;
export default tableFormat;

View File

@@ -1,73 +1,86 @@
import $ from 'jquery';
const isIE = /* @cc_on!@*/false || Boolean(document.documentMode);
const isChrome = Boolean(window.chrome) && Boolean(window.chrome.webstore);
const isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
const isFirefox = typeof InstallTrigger !== 'undefined';
export function enableExcelExport (layout, f) {
let myTitle = '';
let mySubTitle = '';
let myFootNote = '';
if (layout.title.length > 0) {
myTitle += '<p style="font-size:15pt"><b>';
myTitle += layout.title;
myTitle += '</b></p>';
}
if (layout.subtitle.length > 0) {
mySubTitle += '<p style="font-size:11pt">';
mySubTitle += layout.subtitle;
mySubTitle += '</p>';
}
if (layout.footnote.length > 0) {
myFootNote += '<p style="font-size:11pt"><i>Note:</i>';
myFootNote += layout.footnote;
myFootNote += '</p>';
}
$('.icon-xls').on('click', () => {
$('.header-wrapper th').children('.tooltip')
.remove(); // remove some popup effects when exporting
$('.header-wrapper th').children('.icon-xls')
.remove(); // remove the xls icon when exporting
if (isChrome || isSafari) {
const $clonedDiv = $('.data-table').clone(true); // .kpi-table a secas exporta la 1ªcol
let vEncodeHead = '<html><head><meta charset="UTF-8"></head>';
vEncodeHead += myTitle + mySubTitle + myFootNote;
const vEncode = encodeURIComponent($clonedDiv.html());
let vDecode = `${vEncodeHead + vEncode}</html>`;
$clonedDiv.find('tr.header');
vDecode = vDecode.split('%3E.%3C').join('%3E%3C');
window.open(`data:application/vnd.ms-excel,${vDecode}`);
$.preventDefault();
}
if (isIE) {
let a = '<html><head><meta charset="UTF-8"></head>';
a += myTitle + mySubTitle + myFootNote;
a += f;
a = a.split('>.<').join('><');
a += '</html>';
const w = window.open();
w.document.open();
w.document.write(a);
w.document.close();
w.document.execCommand('SaveAs', true, 'Analysis.xls' || 'c:\TMP');
w.close();
}
if (isFirefox) {
const $clonedDiv = $('.data-table').clone(true);// .kpi-table a secas exporta la 1ªcol
let vEncodeHead = '<html><head><meta charset="UTF-8"></head>';
vEncodeHead += myTitle + mySubTitle + myFootNote;
const vEncode = encodeURIComponent($clonedDiv.html());
let vDecode = `${vEncodeHead + vEncode}</html>`;
$clonedDiv.find('tr.header');
vDecode = vDecode.split('>.<').join('><');
window.open(`data:application/vnd.ms-excel,${vDecode}`);
$.preventDefault();
function removeAllTooltips (node) {
const tooltips = node.querySelectorAll('.tooltip');
[].forEach.call(tooltips, tooltip => {
if (tooltip.parentNode) {
tooltip.parentNode.removeChild(tooltip);
}
});
}
function buildTableHTML (title, subtitle, footnote) {
const titleHTML = `<p style="font-size:15pt"><b>${title}</b></p>`;
const subtitleHTML = `<p style="font-size:11pt">${subtitle}</p>`;
const footnoteHTML = `<p style="font-size:11pt"><i>Note:</i>${footnote}</p>`;
const dataTableClone = document.querySelector('.data-table').cloneNode(true);
removeAllTooltips(dataTableClone);
const tableHTML = `
<html
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns="http://www.w3.org/TR/REC-html40"
>
<head>
<meta charset="UTF-8">
<!--[if gte mso 9]>
<xml>
<x:ExcelWorkbook>
<x:ExcelWorksheets>
<x:ExcelWorksheet>
<x:Name>${title || 'Analyze'}</x:Name>
<x:WorksheetOptions>
<x:DisplayGridlines/>
</x:WorksheetOptions>
</x:ExcelWorksheet>
</x:ExcelWorksheets>
</x:ExcelWorkbook>
</xml>
<![endif]-->
</head>
<body>
${titleHTML.length > 0 ? titleHTML : ''}
${subtitleHTML.length > 0 ? subtitleHTML : ''}
${footnoteHTML.length > 0 ? footnoteHTML : ''}
${dataTableClone.outerHTML}
</body>
</html>
`.split('>.<')
.join('><')
.split('>*<')
.join('><');
return tableHTML;
}
function downloadXLS (html) {
const filename = 'analysis.xls';
// IE/Edge
if (window.navigator.msSaveOrOpenBlob) {
const blobObject = new Blob([html]);
return window.navigator.msSaveOrOpenBlob(blobObject, filename);
}
const dataURI = generateDataURI(html);
const link = window.document.createElement('a');
link.href = dataURI;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
return true;
}
function generateDataURI (html) {
const dataType = 'data:application/vnd.ms-excel;base64,';
const data = window.btoa(unescape(encodeURIComponent(html)));
return `${dataType}${data}`;
}
export function exportXLS (title, subtitle, footnote) {
// original was removing icon when starting export, disable and some spinner instead, shouldn't take enough time to warrant either..?
const table = buildTableHTML(title, subtitle, footnote);
downloadXLS(table);
}

View File

@@ -1,13 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import { exportXLS } from './excel-export';
// TODO: move interaction logic in here from excel-export.js
class ExportButton extends React.PureComponent {
constructor (props) {
super(props);
this.handleExport = this.handleExport.bind(this);
}
handleExport () {
const { excelExport, general } = this.props;
const { title, subtitle, footnote } = general;
if (excelExport) {
exportXLS(title, subtitle, footnote);
}
}
render () {
const { excelExport } = this.props;
return excelExport === true && (
<input
className="icon-xls"
onClick={this.handleExport}
src="/Extensions/qlik-smart-pivot/Excel.png"
type="image"
/>
@@ -20,7 +34,8 @@ ExportButton.defaultProps = {
};
ExportButton.propTypes = {
excelExport: PropTypes.bool
excelExport: PropTypes.bool,
general: PropTypes.shape({}).isRequired
};
export default ExportButton;

View File

@@ -1,9 +0,0 @@
/* https://randomhaiku.com */
describe('behind the money', () => {
describe('Canada and Panda work.', () => {
it('Tiger starts blowing.', () => {
expect(true).toBeTruthy();
});
});
});

View File

@@ -1,10 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import Tooltip from '../tooltip/index.jsx';
class ColumnHeader extends React.PureComponent {
constructor (props) {
super(props);
this.handleSelect = this.handleSelect.bind(this);
}
@@ -14,10 +14,12 @@ class ColumnHeader extends React.PureComponent {
}
render () {
const { baseCSS, cellSuffix, colSpan, entry, styling } = this.props;
const { baseCSS, cellSuffix, colSpan, entry, styling, qlik } = this.props;
const inEditState = qlik.inEditState();
const style = {
...baseCSS,
fontSize: `${14 + styling.headerOptions.fontSizeAdjustment} px`,
fontSize: `${14 + styling.headerOptions.fontSizeAdjustment}px`,
height: '45px',
verticalAlign: 'middle'
};
@@ -29,7 +31,12 @@ class ColumnHeader extends React.PureComponent {
onClick={this.handleSelect}
style={style}
>
{entry.displayValue}
<Tooltip
isTooltipActive={!inEditState}
tooltipText={entry.displayValue}
>
{entry.displayValue}
</Tooltip>
</th>
);
}

View File

@@ -1,14 +1,16 @@
import React from 'react';
import PropTypes from 'prop-types';
import ExportButton from '../export-button.jsx';
import { HEADER_FONT_SIZE } from '../initialize-transformed';
const ExportColumnHeader = ({ baseCSS, title, allowExcelExport, hasSecondDimension, styling }) => {
const ExportColumnHeader = ({ baseCSS, general, title, allowExcelExport, hasSecondDimension, styling }) => {
const rowSpan = hasSecondDimension ? 2 : 1;
const isMediumFontSize = styling.headerOptions.fontSizeAdjustment === HEADER_FONT_SIZE.MEDIUM;
const style = {
...baseCSS,
cursor: 'default',
fontSize: `${16 + styling.headerOptions.fontSizeAdjustment} px`,
height: '80px',
fontSize: `${16 + styling.headerOptions.fontSizeAdjustment}px`,
height: isMediumFontSize ? '100px' : '80px',
verticalAlign: 'middle',
width: '230px'
};
@@ -19,7 +21,10 @@ const ExportColumnHeader = ({ baseCSS, title, allowExcelExport, hasSecondDimensi
rowSpan={rowSpan}
style={style}
>
<ExportButton excelExport={allowExcelExport} />
<ExportButton
excelExport={allowExcelExport}
general={general}
/>
{title}
</th>
);
@@ -28,6 +33,7 @@ const ExportColumnHeader = ({ baseCSS, title, allowExcelExport, hasSecondDimensi
ExportColumnHeader.propTypes = {
allowExcelExport: PropTypes.bool.isRequired,
baseCSS: PropTypes.shape({}).isRequired,
general: PropTypes.shape({}).isRequired,
hasSecondDimension: PropTypes.bool.isRequired,
styling: PropTypes.shape({
headerOptions: PropTypes.shape({

View File

@@ -0,0 +1,5 @@
function Model (component) {
this.component = component;
}
export default Model;

View File

@@ -29,6 +29,7 @@ const HeadersTable = ({ data, general, qlik, styling }) => {
<ExportColumnHeader
allowExcelExport={general.allowExcelExport}
baseCSS={baseCSS}
general={general}
hasSecondDimension={hasSecondDimension}
styling={styling}
title={dimension1[0].name}

View File

@@ -0,0 +1,43 @@
import merge from 'lodash.merge';
import Model from './index.componentModel';
import Component from './index.jsx';
import { mountedComponent } from 'test-utilities';
import sampleState from 'test-utilities/capex-sample-state';
describe('<HeadersTable />', () => {
const { data, general, styling } = sampleState;
const defaultProps = {
data,
general,
qlik: {
backendApi: {
selectValues: () => {}
},
inEditState: () => {}
},
styling
};
function setup (otherProps = {}) {
const props = merge(defaultProps, otherProps);
return mountedComponent(Model, Component, props);
}
it('should render without exploding when 2 dimensions', () => {
const model = setup();
expect(model.component).toBeDefined();
});
it('should render without exploding when 1 dimension', () => {
const noSecondDimensionProps = {
data: {
...defaultProps.data.headers,
dimension2: []
}
};
const model = setup(noSecondDimensionProps);
expect(model.component).toBeDefined();
});
});

View File

@@ -1,9 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import { HEADER_FONT_SIZE } from '../initialize-transformed';
import Tooltip from '../tooltip/index.jsx';
const MeasurementColumnHeader = ({ baseCSS, general, hasSecondDimension, measurement, styling }) => {
const title = `${measurement.name} ${measurement.magnitudeLabelSuffix}`;
const { fontSizeAdjustment } = styling.headerOptions;
const isMediumFontSize = styling.headerOptions.fontSizeAdjustment === HEADER_FONT_SIZE.MEDIUM;
if (hasSecondDimension) {
const isPercentageFormat = measurement.format.substring(measurement.format.length - 1) === '%';
let baseFontSize = 14;
@@ -15,8 +19,8 @@ const MeasurementColumnHeader = ({ baseCSS, general, hasSecondDimension, measure
const cellStyle = {
...baseCSS,
cursor: 'default',
fontSize: `${baseFontSize + fontSizeAdjustment} px`,
height: '25px',
fontSize: `${baseFontSize + fontSizeAdjustment}px`,
height: isMediumFontSize ? '50px' : '25px',
verticalAlign: 'middle'
};
return (
@@ -25,18 +29,23 @@ const MeasurementColumnHeader = ({ baseCSS, general, hasSecondDimension, measure
style={cellStyle}
>
<span className="wrapclass25">
{title}
<Tooltip
tooltipText={title}
>
{title}
</Tooltip>
</span>
</th>
);
}
const isLong = (title.length > 11 && fontSizeAdjustment === 0) || (title.length > 12 && fontSizeAdjustment === -2);
const suffixWrap = isLong ? '70' : 'empty';
const style = {
...baseCSS,
cursor: 'default',
fontSize: `${15 + fontSizeAdjustment} px`,
height: '70px',
fontSize: `${15 + fontSizeAdjustment}px`,
height: isMediumFontSize ? '100px' : '80px',
verticalAlign: 'middle'
};
return (

View File

@@ -10,21 +10,45 @@ export default {
controller: [
'$scope',
'$timeout',
function () { }
function controller () {}
],
design: {
dimensions: {
max: 1,
min: 0
}
},
data: {
dimensions: {
max: 3,
min: 1,
uses: 'dimensions'
},
measures: {
max: 8,
min: 1,
uses: 'measures'
}
},
definition,
initialProperties: {
version: 1.0,
qHyperCubeDef: {
qDimensions: [],
qInitialDataFetch: [
{
qHeight: 1000,
qHeight: 1,
qWidth: 10
}
],
qMeasures: []
}
},
support: {
export: true,
exportData: true,
snapshot: true
},
paint ($element, layout) {
try {
paint($element, layout, this);

View File

@@ -1,31 +1,9 @@
import jQuery from 'jquery';
import { distinctArray } from './utilities';
// TODO: rename colors
function initializeColors ({ layout }) {
return {
vColLibBlue: layout.collibblue,
vColLibBlueP: layout.collibbluep,
vColLibClean: layout.collibclean,
vColLibCleanP: layout.collibcleanp,
vColLibCustom: layout.collibcustom,
vColLibCustomP: layout.collibcustomp,
vColLibDark: layout.collibdark,
vColLibDarkP: layout.collibdarkp,
vColLibGreen: layout.collibgreen,
vColLibGreenP: layout.collibgreenp,
vColLibNight: layout.collibnight,
vColLibNightP: layout.collibnightp,
vColLibOrange: layout.colliborange,
vColLibOrangeP: layout.colliborangep,
vColLibRed: layout.collibred,
vColLibRedP: layout.collibredp,
vColLibSoft: layout.collibsoft,
vColLibSoftP: layout.collibsoftp,
vColLibViolete: layout.collibviolete,
vColLibVioleteP: layout.collibvioletep
};
}
export const HEADER_FONT_SIZE = {
SMALL: -1,
MEDIUM: 1
};
function getAlignment (option) {
const alignmentOptions = {
@@ -39,8 +17,8 @@ function getAlignment (option) {
function getFontSizeAdjustment (option) {
const fontSizeAdjustmentOptions = {
1: -2,
2: 0,
1: HEADER_FONT_SIZE.SMALL,
2: HEADER_FONT_SIZE.MEDIUM,
3: 2
};
@@ -129,15 +107,16 @@ function generateMatrixCell ({ cell, dimension1Information, dimension2Informatio
}
let lastRow = 0;
function generateDataSet (component, dimensionsInformation, measurementsInformation) {
function generateDataSet (component, dimensionsInformation, measurementsInformation, cubes) {
const dimension1 = [];
const dimension2 = [];
const measurements = generateMeasurements(measurementsInformation);
let matrix = [];
let previousDim1Entry;
const hasSecondDimension = dimensionsInformation.length > 1;
component.backendApi.eachDataRow((rowIndex, row) => {
const hasDesignDimension = cubes.design;
const hasSecondDimension = hasDesignDimension ? dimensionsInformation.length > 2 : dimensionsInformation.length > 1;
cubes.data.forEach(row => {
lastRow += 1;
const dimension1Entry = generateDimensionEntry(dimensionsInformation[0], row[0]);
dimension1.push(dimension1Entry);
@@ -193,8 +172,7 @@ function generateDataSet (component, dimensionsInformation, measurementsInformat
};
}
async function initializeTransformed ({ $element, layout, component }) {
const colors = initializeColors({ layout });
function initializeTransformed ({ $element, component, cubes, layout }) {
const dimensionsInformation = component.backendApi.getDimensionInfos();
const measurementsInformation = component.backendApi.getMeasureInfos();
const dimensionCount = layout.qHyperCube.qDimensionInfo.length;
@@ -205,37 +183,29 @@ async function initializeTransformed ({ $element, layout, component }) {
dimension2,
measurements,
matrix
} = generateDataSet(component, dimensionsInformation, measurementsInformation);
} = generateDataSet(component, dimensionsInformation, measurementsInformation, cubes);
const customSchemaBasic = [];
const customSchemaFull = [];
let customHeadersCount = 0;
function readCustomSchema () {
const url = `/Extensions/qlik-smart-pivot/${layout.customfile}`;
if (cubes.design) {
const allTextLines = cubes.design.map(entry => entry[0].qText);
const headers = allTextLines[0].split(';');
customHeadersCount = headers.length;
for (let lineNumber = 0; lineNumber < allTextLines.length; lineNumber += 1) {
customSchemaFull[lineNumber] = new Array(headers.length);
const data = allTextLines[lineNumber].split(';');
return jQuery.get(url).then(response => {
const allTextLines = response.split(/\r\n|\n/);
const headers = allTextLines[0].split(';');
customHeadersCount = headers.length;
for (let lineNumber = 0; lineNumber < allTextLines.length; lineNumber += 1) {
customSchemaFull[lineNumber] = new Array(headers.length);
const data = allTextLines[lineNumber].split(';');
if (data.length === headers.length) {
for (let headerIndex = 0; headerIndex < headers.length; headerIndex += 1) {
customSchemaBasic[lineNumber] = data[0];
customSchemaFull[lineNumber][headerIndex] = data[headerIndex];
}
if (data.length === headers.length) {
for (let headerIndex = 0; headerIndex < headers.length; headerIndex += 1) {
[customSchemaBasic[lineNumber]] = data;
customSchemaFull[lineNumber][headerIndex] = data[headerIndex];
}
}
});
}
}
const hasCustomSchema = (layout.customfilebool && layout.customfile.length > 4);
const schemaPromise = hasCustomSchema ? readCustomSchema() : Promise.resolve();
await schemaPromise;
// top level properties could be reducers and then components connect to grab what they want,
// possibly with reselect for some presentational transforms (moving some of the presentational logic like formatting and such)
const transformedProperties = {
@@ -255,35 +225,39 @@ async function initializeTransformed ({ $element, layout, component }) {
allowFilteringByClick: layout.filteroncellclick,
cellSuffix: getCellSuffix(layout.columnwidthslider), // TOOD: move to matrix cells or is it headers.measurements?
errorMessage: layout.errormessage,
maxLoops
footnote: layout.footnote,
maxLoops,
subtitle: layout.subtitle,
title: layout.title
},
selection: {
dimensionSelectionCounts: dimensionsInformation.map(dimensionInfo => dimensionInfo.qStateCounts.qSelected)
},
styling: {
colors,
customCSV: {
basic: customSchemaBasic,
count: customHeadersCount,
full: customSchemaFull
},
hasCustomFileStyle: layout.customfilebool,
hasCustomFileStyle: Boolean(cubes.design),
headerOptions: {
alignment: getAlignment(layout.HeaderAlign),
colorSchema: colors[`vColLib${layout.HeaderColorSchema}`],
colorSchema: layout.HeaderColorSchema.color,
fontSizeAdjustment: getFontSizeAdjustment(layout.lettersizeheader),
textColor: layout.HeaderTextColorSchema
textColor: layout.HeaderTextColorSchema.color
},
options: {
backgroundColor: colors[`vColLib${layout.ColorSchema}`],
backgroundColorOdd: colors[`vColLib${layout.ColorSchemaP}`],
backgroundColor: layout.rowEvenBGColor,
backgroundColorOdd: layout.rowOddBGColor,
color: layout.BodyTextColorSchema,
fontFamily: layout.FontFamily,
fontSizeAdjustment: getFontSizeAdjustment(layout.lettersize)
fontSizeAdjustment: getFontSizeAdjustment(layout.lettersize),
textAlignment: layout.cellTextAlignment
},
semaphoreColors: {
fieldsToApplyTo: {
applyToAll: layout.allsemaphores,
applyToMetric: layout.allmetrics,
specificFields: [
layout.conceptsemaphore1,
layout.conceptsemaphore2,
@@ -294,11 +268,12 @@ async function initializeTransformed ({ $element, layout, component }) {
layout.conceptsemaphore7,
layout.conceptsemaphore9,
layout.conceptsemaphore10
]
],
metricsSpecificFields: layout.metricssemaphore.split(',').map(entry => Number(entry))
},
status: {
critical: layout.metricstatus1,
medium: layout.metricstatus2
critical: layout.metricsstatus1,
medium: layout.metricsstatus2
},
statusColors: {
critical: {
@@ -335,7 +310,6 @@ async function initializeTransformed ({ $element, layout, component }) {
});
}
return transformedProperties;
}

View File

@@ -0,0 +1,2 @@
export { default as LinkedScrollWrapper } from './linked-scroll-wrapper.jsx';
export { default as LinkedScrollSection } from './linked-scroll-section.jsx';

View File

@@ -0,0 +1,29 @@
import React from 'react';
import PropTypes from 'prop-types';
import { LinkedScrollContext } from './linked-scroll-wrapper.jsx';
class LinkedScrollSection extends React.PureComponent {
static contextType = LinkedScrollContext;
componentDidMount () {
const { link } = this.context;
link(this);
}
componentWillUnmount () {
const { unlink } = this.context;
unlink(this);
}
render () {
const { children } = this.props;
return children;
}
}
LinkedScrollSection.propTypes = {
children: PropTypes.any
};
export default LinkedScrollSection;

View File

@@ -0,0 +1,82 @@
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
export const LinkedScrollContext = React.createContext();
class LinkedScrollWrapper extends React.PureComponent {
constructor (props) {
super(props);
this.linkComponent = this.linkComponent.bind(this);
this.unlinkComponent = this.unlinkComponent.bind(this);
this.handleScroll = this.handleScroll.bind(this);
this.scrollElements = [];
this.linkActions = {
link: this.linkComponent,
unlink: this.unlinkComponent
};
}
linkComponent (component) {
// eslint-disable-next-line react/no-find-dom-node
const node = ReactDOM.findDOMNode(component);
const element = {
component,
node
};
this.scrollElements.push(element);
node.onscroll = this.handleScroll.bind(this, element);
}
unlinkComponent (component) {
const componentIndex = this.scrollElements.map(element => element.component).indexOf(component);
if (componentIndex !== -1) {
this.scrollElements.removeAt(componentIndex);
// eslint-disable-next-line react/no-find-dom-node
const node = ReactDOM.findDOMNode(component);
node.onscroll = null;
}
}
handleScroll (element) {
window.requestAnimationFrame(() => {
this.sync(element);
});
}
sync (scrollElement) {
this.scrollElements.forEach(element => {
if (scrollElement === element) {
return;
}
element.node.onscroll = null;
if (element.component.props.linkHorizontal) {
element.node.scrollLeft = scrollElement.node.scrollLeft;
}
if (element.component.props.linkVertical) {
element.node.scrollTop = scrollElement.node.scrollTop;
}
window.requestAnimationFrame(() => {
element.node.onscroll = this.handleScroll.bind(this, element);
});
});
}
render () {
const { children } = this.props;
return (
<LinkedScrollContext.Provider value={this.linkActions}>
{children}
</LinkedScrollContext.Provider>
);
}
}
LinkedScrollWrapper.propTypes = {
children: PropTypes.any
};
export default LinkedScrollWrapper;

View File

@@ -1,215 +1,313 @@
/* eslint-disable */
.qv-object-qlik-smart-pivot {
@TableBorder: 1px solid #d3d3d3;
@KpiTableWidth: 230px;
._cell(@Width: 50px) {
min-width: @Width!important;
max-width: @Width!important;
cursor: pointer;
line-height: 1em!important;
*,
*::before,
*::after {
box-sizing: border-box;
}
div.qv-object-content-container {
overflow-x: scroll;
overflow-y: hidden;
z-index: 110;
}
.edit-mode {
pointer-events: none;
}
.icon-xls {
text-align: left;
}
._cell(@Width: 50px) {
min-width: @Width !important;
max-width: @Width !important;
cursor: pointer;
line-height: 1em !important;
}
button {
width: 100%;
}
div.qv-object-content-container {
z-index: 110;
}
table {
border-collapse: collapse;
border-spacing: 0;
width: auto;
border-left: @TableBorder;
border-right: @TableBorder;
border-top: @TableBorder;
}
.icon-xls {
text-align: left;
}
td, th {
border: 1px solid #ffffff;
padding: 5px;
border-collapse: collapse;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
cursor: default;
}
button {
width: 100%;
}
.empty {
width: 3%;
background: #ffffff;
min-width: 4px !important;
max-width: 4px !important;
}
table {
border-collapse: collapse;
border-spacing: 0;
width: auto;
border-left: @TableBorder;
border-right: @TableBorder;
border-top: @TableBorder;
}
th.main-kpi {
text-align: center;
vertical-align: middle;
border-bottom: @TableBorder;
}
td,
th {
border: 1px solid #fff;
border-collapse: collapse;
padding: 5px !important; // prevent overwriting from single object
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
cursor: default;
.numeric {
text-align: right;
}
/*This is for wrap text in headers*/
.wrapclass25 {
width: 100%;
height: 25px;
white-space: pre-line;
overflow: hidden;
display: block;
}
> div {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-family: inherit;
}
}
.wrapclass45 {
width: 100%;
height: 45px;
white-space: pre-line;
overflow: hidden;
display: block;
}
.empty {
width: 3%;
background: #fff;
min-width: 4px !important;
max-width: 4px !important;
}
.wrapclass70 {
width: 100%;
height: 70px;
white-space: pre-line;
overflow: hidden;
display: inline-block;
vertical-align: middle;
line-height: 20px;
}
th.main-kpi {
text-align: center;
vertical-align: middle;
border-bottom: @TableBorder;
}
.wrapclassEmpty {
width: 100%;
}
/*******************/
/* Medium column size*/
/*******************/
.grid-cells { ._cell(70px); }
.grid-cells2 { ._cell(70px); }
.grid-cells-small { ._cell(52px); }
.grid-cells2-small { ._cell(52px); }
/*******************/
/* Small column size*/
/*******************/
.grid-cells-s { ._cell(67px); }
.grid-cells2-s { ._cell(67px); }
.grid-cells-small-s { ._cell(52px); }
.grid-cells2-small-s { ._cell(52px); }
/*******************/
/* large column size*/
/*******************/
.grid-cells-l { ._cell(82px); }
.grid-cells2-l { ._cell(82px); }
.grid-cells-small-l { ._cell(66px); }
.grid-cells2-small-l { ._cell(66px); }
.numeric {
text-align: right;
}
/*END OF GRID CELLS*/
/*First Column*/
.fdim-cells {
min-width: 230px !Important;
max-width: 230px !Important;
cursor: pointer;
background-color: white;
}
// This is for wrap text in headers
.wrapclass25 {
width: 100%;
height: inherit;
white-space: pre-line;
overflow: hidden;
display: block;
text-overflow: ellipsis;
}
.fdim-cells:hover {
/*cursor: default;*/
background-color: #808080 !important;
color: #ffffff;
}
.wrapclass45 {
width: 100%;
height: 45px;
white-space: pre-line;
overflow: hidden;
display: block;
}
tbody tr:hover {
cursor: default;
background-color: #808080 !important;
color: #ffffff;
}
.wrapclass70 {
width: 100%;
height: 70px;
white-space: pre-line;
overflow: hidden;
display: inline-block;
vertical-align: middle;
line-height: 20px;
}
.grid-cells-header {
padding: 0px;
}
.wrapclassEmpty {
width: 100%;
}
.grid-cells-title {
min-width: 522px;
}
// *****************
// Medium column size
// *****************
.grid-cells:before {
content: "\00a0";
}
.grid-cells {
position: relative;
._cell(70px);
}
.grid-cells2 {
._cell(70px);
}
.grid-cells-small {
._cell(52px);
}
.grid-cells2-small {
._cell(52px);
}
// *****************
// Small column size
// *****************
.grid-cells-s {
._cell(67px);
}
.grid-cells2-s {
._cell(67px);
}
.grid-cells-small-s {
._cell(52px);
}
.grid-cells2-small-s {
._cell(52px);
}
// *****************
// Large column size
// *****************
.grid-cells-l {
._cell(82px);
}
.grid-cells2-l {
._cell(82px);
}
.grid-cells-small-l {
._cell(66px);
}
.grid-cells2-small-l {
._cell(66px);
}
// END OF GRID CELLS
// First Column
.fdim-cells {
min-width: 230px !Important;
max-width: 230px !Important;
cursor: pointer;
background-color: #fff;
}
.fdim-cells:hover {
background-color: #808080 !important;
color: #fff;
}
tbody tr:hover {
cursor: default;
background-color: #808080 !important;
color: #fff;
}
.grid-cells-header {
padding: 0;
}
.grid-cells-title {
min-width: 522px;
}
.grid {
height: 50px;
width: 350px;
height: 50px;
width: 350px;
}
.header-wrapper {
position: absolute;
top: 0;
z-index: 1;
}
/*popups for headers*/
.tooltip {
position: fixed !important;
color: RGB(70,70,70);
background-color: RGB(245,239,207);
text-align: center;
border: groove;
position: fixed !important;
color: rgb(70, 70, 70);
background-color: rgb(245, 239, 207);
text-align: center;
border: groove;
}
/*end popups*/
.row-wrapper {
position: absolute;
top: 97px;
height: calc(~"100% - 97px");
overflow-x: hidden;
overflow-y: scroll;
padding: 0;
margin-top: 0;
}
.kpi-table .fdim-cells, .data-table td {
line-height: 1em!important;
.kpi-table .fdim-cells,
.data-table td {
line-height: 1em !important;
}
.data-table .fdim-cells {
display: none;
display: none;
}
.kpi-table {
width: @KpiTableWidth !important;
overflow: hidden !important;
display: table;
height: 100%;
margin: 0;
padding: 0;
z-index: 100;
position: absolute;
top: 0;
left: 0;
border-right: 1px solid white;
box-shadow: 4px 2px 8px #e1e1e1;
width: @KpiTableWidth !important;
overflow: hidden !important;
height: 100%;
margin: 0;
padding: 0;
position: absolute;
top: 0;
left: 0;
border-right: 1px solid #fff;
box-shadow: 4px 2px 8px #e1e1e1;
.row-wrapper {
height: calc(~"100% - 97px");
overflow: scroll;
position: absolute;
padding: 0;
margin-top: 0;
}
}
.kpi-table .row-wrapper {
overflow: hidden;
.row-wrapper .fdim-cells {
padding-left: 12px;
}
.data-table {
width: 272px !important;
float: left;
display: table;
height: 100%;
z-index: 90;
position: absolute;
margin-left: @KpiTableWidth + 13px;
-ms-overflow-style: none;
height: 100%;
width: calc(100% - 243px);
position: absolute;
margin-left: @KpiTableWidth + 13px;
.header-wrapper {
overflow: scroll;
width: 100%;
}
.row-wrapper {
height: calc(~"100% - 97px");
width: 100%;
overflow: scroll;
padding: 0;
margin-top: 0;
}
}
// hide scrollbars
.kpi-table .header-wrapper,
.data-table .header-wrapper {
// stylelint-disable-next-line property-no-unknown
scrollbar-width: none;
-ms-overflow-style: none; // IE 10+
-moz-overflow: -moz-scrollbars-none; // Firefox
&::-webkit-scrollbar {
display: none; // Safari and Chrome
}
}
.tooltip-wrapper {
min-width: 25px;
position: fixed;
padding: 5px;
padding-top: 8px;
background-color: #404040;
z-index: 100;
pointer-events: none;
border-radius: 5px;
height: 30px;
width: auto;
opacity: 0.9;
text-align: center;
transform: translate(-50%, -110%);
&::after {
content: "";
position: absolute;
bottom: -10px;
left: 50%;
border-width: 10px 10px 0;
border-style: solid;
border-color: #404040 transparent;
margin-left: -10px;
pointer-events: none;
}
> p {
color: #fff;
}
}
}
/* eslint-enable */

View File

@@ -13,7 +13,7 @@ export function ApplyPreMask (mask, value) { // aqui
case '+#,##0':
return (addSeparators(value, ',', '.', 0));
default:
return (ApplyMask(mask.substring(0, mask.indexOf(';')), value));
return (applyMask(mask.substring(0, mask.indexOf(';')), value));
}
} else {
const vMyValue = value * -1;
@@ -30,46 +30,47 @@ export function ApplyPreMask (mask, value) { // aqui
case '-#,##0':
return (`(${addSeparators(vMyValue, ',', '.', 0)})`);
default:
return (`(${ApplyMask(vMyMask, vMyValue)})`);
return (`(${applyMask(vMyMask, vMyValue)})`);
}
}
} else {
return (ApplyMask(mask, value));
return (applyMask(mask, value));
}
}
function ApplyMask (mask, value) {
if (!mask || isNaN(Number(value))) {
return value; // return as it is.
function applyMask (originalMask, originalValue) {
if (!originalMask || isNaN(Number(originalValue))) {
return originalValue;
}
let isNegative, result, decimal, group, posLeadZero, posTrailZero, posSeparator,
part, szSep, integer,
// find prefix/suffix
len = mask.length,
start = mask.search(/[0-9\-\+#]/),
prefix = start > 0 ? mask.substring(0, start) : '',
// reverse string: not an ideal method if there are surrogate pairs
str = mask.split('').reverse()
.join(''),
end = str.search(/[0-9\-\+#]/),
offset = len - end,
substr = mask.substring(offset, offset + 1),
indx = offset + ((substr === '.' || (substr === ',')) ? 1 : 0),
suffix = end > 0 ? mask.substring(indx, len) : '';
let isNegative;
let result;
let integer;
// find prefix/suffix
let len = originalMask.length;
const start = originalMask.search(/[0-9\-\+#]/);
const prefix = start > 0 ? originalMask.substring(0, start) : '';
// reverse string: not an ideal method if there are surrogate pairs
let str = originalMask.split('')
.reverse()
.join('');
const end = str.search(/[0-9\-\+#]/);
let offset = len - end;
const substr = originalMask.substring(offset, offset + 1);
let index = offset + ((substr === '.' || (substr === ',')) ? 1 : 0);
const suffix = end > 0 ? originalMask.substring(index, len) : '';
// mask with prefix & suffix removed
mask = mask.substring(start, indx);
let mask = originalMask.substring(start, index);
// convert any string to number according to formation sign.
value = mask.charAt(0) === '-' ? -value : Number(value);
let value = mask.charAt(0) === '-' ? -originalValue : Number(originalValue);
isNegative = value < 0 ? value = -value : 0; // process only abs(), and turn on flag.
// search for separator for grp & decimal, anything not digit, not +/- sign, not #.
result = mask.match(/[^\d\-\+#]/g);
decimal = (result && result[result.length - 1]) || '.'; // treat the right most symbol as decimal
group = (result && result[1] && result[0]) || ','; // treat the left most symbol as group separator
const decimal = (result && result[result.length - 1]) || '.'; // treat the right most symbol as decimal
const group = (result && result[1] && result[0]) || ','; // treat the left most symbol as group separator
// split the decimal for the format string if any.
mask = mask.split(decimal);
@@ -78,16 +79,16 @@ function ApplyMask (mask, value) {
value = String(Number(value)); // convert number to string to trim off *all* trailing decimal zero(es)
// fill back any trailing zero according to format
posTrailZero = mask[1] && mask[1].lastIndexOf('0'); // look for last zero in format
part = value.split('.');
const posTrailZero = mask[1] && mask[1].lastIndexOf('0'); // look for last zero in format
const part = value.split('.');
// integer will get !part[1]
if (!part[1] || (part[1] && part[1].length <= posTrailZero)) {
value = (Number(value)).toFixed(posTrailZero + 1);
}
szSep = mask[0].split(group); // look for separator
const szSep = mask[0].split(group); // look for separator
mask[0] = szSep.join(''); // join back without separator for counting the pos of any leading 0.
posLeadZero = mask[0] && mask[0].indexOf('0');
const posLeadZero = mask[0] && mask[0].indexOf('0');
if (posLeadZero > -1) {
while (part[0].length < (mask[0].length - posLeadZero)) {
part[0] = `0${part[0]}`;
@@ -101,17 +102,17 @@ function ApplyMask (mask, value) {
// process the first group separator from decimal (.) only, the rest ignore.
// get the length of the last slice of split result.
posSeparator = (szSep[1] && szSep[szSep.length - 1].length);
const posSeparator = (szSep[1] && szSep[szSep.length - 1].length);
if (posSeparator) {
integer = value[0];
str = '';
offset = integer.length % posSeparator;
len = integer.length;
for (indx = 0; indx < len; indx++) {
str += integer.charAt(indx); // ie6 only support charAt for sz.
for (index = 0; index < len; index++) {
str += integer.charAt(index); // ie6 only support charAt for sz.
// -posSeparator so that won't trail separator on full length
// jshint -W018
if (!((indx - offset + 1) % posSeparator) && indx < len - posSeparator) {
if (!((index - offset + 1) % posSeparator) && index < len - posSeparator) {
str += group;
}
}

View File

@@ -1,89 +1,28 @@
import $ from 'jquery';
import initializeStore from './store';
import React from 'react';
import ReactDOM from 'react-dom';
import HeadersTable from './headers-table/index.jsx';
import DataTable from './data-table/index.jsx';
import Root from './root.jsx';
import { initializeCubes } from './dataset';
export default async function paint ($element, layout, component) {
const state = await initializeStore({
$element,
const cubes = await initializeCubes({
component,
layout
});
const state = await initializeStore({
$element,
component,
cubes,
layout
});
const editmodeClass = component.inAnalysisState() ? '' : 'edit-mode';
const jsx = (
<React.Fragment>
<div className="kpi-table">
<HeadersTable
data={state.data}
general={state.general}
qlik={component}
styling={state.styling}
/>
<DataTable
data={state.data}
general={state.general}
qlik={component}
renderData={false}
styling={state.styling}
/>
</div>
<div className="data-table">
<HeadersTable
data={state.data}
general={state.general}
qlik={component}
styling={state.styling}
/>
<DataTable
data={state.data}
general={state.general}
qlik={component}
styling={state.styling}
/>
</div>
</React.Fragment>
<Root
qlik={component}
state={state}
editmodeClass={editmodeClass}
/>
);
ReactDOM.render(jsx, $element[0]);
// TODO: skipped the following as they weren't blockers for letting react handle rendering,
// they are however the only reason we still depend on jQuery and should be removed as part of unnecessary dependencies issue
$(`[tid="${layout.qInfo.qId}"] .data-table .row-wrapper`).on('scroll', function () {
$(`[tid="${layout.qInfo.qId}"] .kpi-table .row-wrapper`).scrollTop($(this).scrollTop());
});
// freeze first column
$(`[tid="${layout.qInfo.qId}"] .qv-object-content-container`).on('scroll', (t) => {
$(`[tid="${layout.qInfo.qId}"] .kpi-table`).css('left', `${Math.round(t.target.scrollLeft)}px`);
});
// TODO: fixing tooltips has a seperate issue, make sure to remove this as part of that issue
$(`[tid="${layout.qInfo.qId}"] .header-wrapper th`).hover(function () {
$(`[tid="${layout.qInfo.qId}"] .tooltip`).delay(500)
.show(0);
$(`[tid="${layout.qInfo.qId}"] .header-wrapper th`).children(`[tid="${layout.qInfo.qId}"] .tooltip`)
.remove();
const element = $(this);
const offset = element.offset();
const toolTip = $('<div class="tooltip"></div>');
toolTip.css({
left: offset.left,
top: offset.top
});
toolTip.text(element.text());
$(`[tid="${layout.qInfo.qId}"] .header-wrapper th`).append(toolTip);
}, () => {
$(`[tid="${layout.qInfo.qId}"] .tooltip`).delay(0)
.hide(0);
});
// TODO: excel export is broken in most browsers, fixing it has an issue of it's own (leaving it disabled for now)
// import { enableExcelExport } from './excel-export';
// enableExcelExport(layout, html);
}

View File

@@ -0,0 +1,5 @@
function Model (component) {
this.component = component;
}
export default Model;

60
src/root.jsx Normal file
View File

@@ -0,0 +1,60 @@
import React from 'react';
import PropTypes from 'prop-types';
import HeadersTable from './headers-table/index.jsx';
import DataTable from './data-table/index.jsx';
import { LinkedScrollWrapper, LinkedScrollSection } from './linked-scroll';
const Root = ({ state, qlik, editmodeClass }) => (
<LinkedScrollWrapper>
<div className={`kpi-table ${editmodeClass}`}>
<HeadersTable
data={state.data}
general={state.general}
qlik={qlik}
styling={state.styling}
/>
<LinkedScrollSection linkVertical>
<DataTable
data={state.data}
general={state.general}
qlik={qlik}
renderData={false}
styling={state.styling}
/>
</LinkedScrollSection>
</div>
<div className={`data-table ${editmodeClass}`}>
<LinkedScrollSection linkHorizontal>
<HeadersTable
data={state.data}
general={state.general}
qlik={qlik}
styling={state.styling}
/>
</LinkedScrollSection>
<LinkedScrollSection
linkHorizontal
linkVertical
>
<DataTable
data={state.data}
general={state.general}
qlik={qlik}
styling={state.styling}
/>
</LinkedScrollSection>
</div>
</LinkedScrollWrapper>
);
Root.propTypes = {
qlik: PropTypes.shape({}).isRequired,
state: PropTypes.shape({
data: PropTypes.object.isRequired,
general: PropTypes.object.isRequired,
styling: PropTypes.object.isRequired
}).isRequired,
editmodeClass: PropTypes.string.isRequired
};
export default Root;

29
src/root.spec.js Normal file
View File

@@ -0,0 +1,29 @@
import merge from 'lodash.merge';
import Model from './root.componentModel';
import Component from './root.jsx';
import { mountedComponent } from 'test-utilities';
import sampleState from 'test-utilities/capex-sample-state';
describe('<Root />', () => {
const state = sampleState;
const defaultProps = {
qlik: {
backendApi: {
selectValues: () => {}
},
inEditState: () => {}
},
state
};
function setup (otherProps = {}) {
const props = merge(defaultProps, otherProps);
return mountedComponent(Model, Component, props);
}
it('should render without exploding', () => {
const model = setup();
expect(model.component).toBeDefined();
});
});

View File

@@ -1,9 +1,10 @@
import initializeTransformed from './initialize-transformed';
async function initialize ({ $element, layout, component }) {
async function initialize ({ $element, layout, component, cubes }) {
const transformedProperties = await initializeTransformed({
$element,
component,
cubes,
layout
});

View File

@@ -1,6 +1,5 @@
function StyleBuilder (styling) {
const {
colors,
customCSV,
options
} = styling;
@@ -12,9 +11,13 @@ function StyleBuilder (styling) {
let hasCustomFileStyle = false;
function applyStandardAttributes (rowNumber) {
const isEven = rowNumber % 2 === 0;
style.backgroundColor = isEven ? options.backgroundColor : options.backgroundColorOdd;
style.color = options.color;
const hasBackgroundColor = options.backgroundColor && options.backgroundColor.color;
const hasOddBackgroundColor = options.backgroundColorOdd && options.backgroundColorOdd.color;
if (hasBackgroundColor && hasOddBackgroundColor) {
const isEven = rowNumber % 2 === 0;
style.backgroundColor = isEven ? options.backgroundColor.color : options.backgroundColorOdd.color;
style.color = options.color;
}
style.fontSize = `${13 + options.fontSizeAdjustment}px`;
}
@@ -30,16 +33,7 @@ function StyleBuilder (styling) {
'<bold>': () => { style.fontWeight = 'bold'; },
'<italic>': () => { style.fontStyle = 'italic'; },
'<oblique>': () => { style.fontStyle = 'oblique'; },
// background and comment color
'<dark>': () => applyColor(colors.vColLibDark),
'<night>': () => applyColor(colors.vColLibNight),
'<soft>': () => applyColor(colors.vColLibSoft),
'<red>': () => applyColor(colors.vColLibRed),
'<orange>': () => applyColor(colors.vColLibOrange),
'<violete>': () => applyColor(colors.vColLibViolete),
'<blue>': () => applyColor(colors.vColLibBlue),
'<green>': () => applyColor(colors.vColLibGreen),
// font color TODO: this is a color just like the others, but it applies to text instead.. any way to make it less weird?
// font color
'<white>': () => { style.color = 'white'; },
// font size
'<large>': () => { style.fontSize = `${15 + options.fontSizeAdjustment}px`; },

62
src/tooltip/index.jsx Normal file
View File

@@ -0,0 +1,62 @@
import React from 'react';
import PropTypes from 'prop-types';
const handleCalculateTooltipPosition = (event) => {
const tooltip = document.querySelector('.tooltip-wrapper');
if (!tooltip) {
return;
}
tooltip.style.left = `${event.clientX}px`;
tooltip.style.top = `${event.clientY}px`;
};
class Tooltip extends React.PureComponent {
constructor (props) {
super(props);
this.state = {
showTooltip: false
};
this.handleRenderTooltip = this.handleRenderTooltip.bind(this);
}
handleRenderTooltip () {
const { showTooltip } = this.state;
this.setState({ showTooltip: !showTooltip });
}
render () {
const { children, tooltipText } = this.props;
const { showTooltip } = this.state;
return (
<div
onMouseMove={handleCalculateTooltipPosition}
onMouseOut={this.handleRenderTooltip}
onMouseOver={this.handleRenderTooltip}
>
{children}
{showTooltip
? (
<div
className="tooltip-wrapper"
>
<p>
{tooltipText}
</p>
</div>
) : null}
</div>
);
}
}
Tooltip.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node
]).isRequired,
tooltipText: PropTypes.string.isRequired
};
export default Tooltip;

View File

@@ -9,17 +9,19 @@ export function distinctArray (array) {
.map(entry => JSON.parse(entry));
}
export function addSeparators (nStr, thousandsSep, decimalSep, numDecimals) {
let x1;
nStr = nStr.toFixed(numDecimals);
const x = nStr.split('.');
x1 = x[0];
const x2 = x.length > 1 ? decimalSep + x[1] : '';
const rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, `$1${thousandsSep}$2`);
export function addSeparators (number, thousandSeparator, decimalSeparator, numberOfDecimals) {
const numberString = number.toFixed(numberOfDecimals);
const numberStringParts = numberString.split('.');
let [
wholeNumber,
decimal
] = numberStringParts;
decimal = numberStringParts.length > 1 ? decimalSeparator + decimal : '';
const regexCheckForThousand = /(\d+)(\d{3})/;
while (regexCheckForThousand.test(wholeNumber)) {
wholeNumber = wholeNumber.replace(regexCheckForThousand, `$1${thousandSeparator}$2`);
}
return x1 + x2;
return wholeNumber + decimal;
}
export function Deferred () {

View File

@@ -1,35 +1,129 @@
"use strict";
module.exports = {
rules: {
"at-rule-no-unknown": true,
"block-no-empty": true,
"color-no-invalid-hex": true,
"comment-no-empty": true,
"declaration-block-no-duplicate-properties": [
true,
'rules': {
'at-rule-empty-line-before': [
'always',
{
ignore: ["consecutive-duplicates-with-different-values"]
except: [
'blockless-after-same-name-blockless',
'first-nested'
],
ignore: ['after-comment']
}
],
"declaration-block-no-shorthand-property-overrides": true,
"font-family-no-duplicate-names": true,
"font-family-no-missing-generic-family-keyword": true,
"function-calc-no-unspaced-operator": true,
"function-linear-gradient-no-nonstandard-direction": true,
"keyframe-declaration-no-important": true,
"media-feature-name-no-unknown": true,
"no-descending-specificity": true,
"no-duplicate-at-import-rules": true,
"no-duplicate-selectors": true,
"no-empty-source": true,
"no-extra-semicolons": true,
"no-invalid-double-slash-comments": true,
"property-no-unknown": true,
"selector-pseudo-class-no-unknown": true,
"selector-pseudo-element-no-unknown": true,
"selector-type-no-unknown": true,
"string-no-newline": true,
"unit-no-unknown": true
'at-rule-name-case': 'lower',
'at-rule-name-space-after': 'always-single-line',
'at-rule-semicolon-newline-after': 'always',
'block-closing-brace-empty-line-before': 'never',
'block-closing-brace-newline-after': 'always',
'block-closing-brace-newline-before': 'always-multi-line',
'block-closing-brace-space-before': 'always-single-line',
'block-opening-brace-newline-after': 'always-multi-line',
'block-opening-brace-space-after': 'always-single-line',
'block-opening-brace-space-before': 'always',
'color-hex-case': 'lower',
'color-hex-length': 'short',
'color-named': 'never',
'comment-empty-line-before': [
'always',
{
except: ['first-nested'],
ignore: ['stylelint-commands']
}
],
'comment-whitespace-inside': 'always',
'custom-property-empty-line-before': [
'always',
{
except: [
'after-custom-property',
'first-nested'
],
ignore: [
'after-comment',
'inside-single-line-block'
]
}
],
'declaration-bang-space-after': 'never',
'declaration-bang-space-before': 'always',
'declaration-block-semicolon-newline-after': 'always-multi-line',
'declaration-block-semicolon-space-after': 'always-single-line',
'declaration-block-semicolon-space-before': 'never',
'declaration-block-single-line-max-declarations': 1,
'declaration-block-trailing-semicolon': 'always',
'declaration-colon-newline-after': 'always-multi-line',
'declaration-colon-space-after': 'always-single-line',
'declaration-colon-space-before': 'never',
'declaration-empty-line-before': [
'always',
{
except: [
'after-declaration',
'first-nested'
],
ignore: [
'after-comment',
'inside-single-line-block'
]
}
],
'declaration-no-important': [
true,
{
severity: 'warning'
}
],
'function-comma-newline-after': 'always-multi-line',
'function-comma-space-after': 'always-single-line',
'function-comma-space-before': 'never',
'function-max-empty-lines': 0,
'function-name-case': 'lower',
'function-parentheses-newline-inside': 'always-multi-line',
'function-parentheses-space-inside': 'never-single-line',
'function-whitespace-after': 'always',
'indentation': 2,
'length-zero-no-unit': true,
'max-empty-lines': 1,
'max-nesting-depth': 3,
'media-feature-colon-space-after': 'always',
'media-feature-colon-space-before': 'never',
'media-feature-name-case': 'lower',
'media-feature-parentheses-space-inside': 'never',
'media-feature-range-operator-space-after': 'always',
'media-feature-range-operator-space-before': 'always',
'media-query-list-comma-newline-after': 'always-multi-line',
'media-query-list-comma-space-after': 'always-single-line',
'media-query-list-comma-space-before': 'never',
'no-extra-semicolons': true,
'no-missing-end-of-source-newline': true,
'number-leading-zero': 'always',
'number-no-trailing-zeros': true,
'property-case': 'lower',
'rule-empty-line-before': [
'always-multi-line',
{
except: ['first-nested'],
ignore: ['after-comment']
}
],
'selector-attribute-brackets-space-inside': 'never',
'selector-attribute-operator-space-after': 'never',
'selector-attribute-operator-space-before': 'never',
'selector-combinator-space-after': 'always',
'selector-combinator-space-before': 'always',
'selector-descendant-combinator-no-non-space': true,
'selector-list-comma-newline-after': 'always',
'selector-list-comma-space-before': 'never',
'selector-max-empty-lines': 0,
'selector-pseudo-class-case': 'lower',
'selector-pseudo-class-parentheses-space-inside': 'never',
'selector-pseudo-element-case': 'lower',
'selector-pseudo-element-colon-notation': 'double',
'selector-type-case': 'lower',
'unit-case': 'lower',
'value-list-comma-newline-after': 'always-multi-line',
'value-list-comma-space-after': 'always-single-line',
'value-list-comma-space-before': 'never',
'value-list-max-empty-lines': 0
}
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import jasmineEnzyme from 'jasmine-enzyme';
Enzyme.configure({ adapter: new Adapter() });
beforeEach(() => {
jasmineEnzyme();
});
export * from 'enzyme';

View File

@@ -0,0 +1,9 @@
import React from 'react';
import { mount } from 'test-utilities/enzyme-setup';
export function mountedComponent (Model, Component, props = {}) {
const component = mount((
<Component {...props} />
)).find(Component);
return new Model(component);
}

View File

@@ -6,15 +6,7 @@ console.log('Webpack mode:', settings.mode); // eslint-disable-line no-console
const config = {
devtool: 'source-map',
entry: [
'./src/index.js'
],
mode: settings.mode,
output: {
path: settings.buildDestination,
filename: settings.name + '.js',
libraryTarget: 'amd'
},
entry: ['./src/index.js'],
externals: {
jquery: {
amd: 'jquery',
@@ -22,29 +14,35 @@ const config = {
commonjs2: 'jquery',
root: '_'
},
qlik: {
amd: 'qlik',
commonjs: 'qlik',
commonjs2: 'qlik',
root: '_'
}
},
// TODO: breaks core-js for some reason
// resolve: {
// extensions: ['js', 'jsx']
// },
mode: settings.mode,
module: {
rules: [
{
enforce: 'pre',
test: /\.(js|jsx)$/,
exclude: /(node_modules|Library)/,
loader: 'eslint-loader',
options: {
failOnError: true
}
},
test: /\.(js|jsx)$/
},
{
test: /\.(js|jsx)$/,
exclude: /node_modules/,
test: /\.(js|jsx)$/,
use: {
loader: 'babel-loader',
options: {
plugins: ['@babel/plugin-transform-async-to-generator'],
plugins: [
'@babel/plugin-transform-async-to-generator',
'@babel/plugin-proposal-class-properties'
],
presets: [
'@babel/preset-env',
'@babel/preset-react'
@@ -54,23 +52,29 @@ const config = {
},
{
test: /.less$/,
use: ['style-loader', 'css-loader', 'less-loader']
use: [
'style-loader',
'css-loader',
'less-loader'
]
}
]
},
output: {
filename: `${settings.name}.js`,
libraryTarget: 'amd',
path: settings.buildDestination
},
plugins: [
new CopyWebpackPlugin([
'assets/' + settings.name + '.qext',
'assets/' + settings.name + '.png',
`assets/${settings.name}.qext`,
`assets/${settings.name}.png`,
'assets/wbfolder.wbl',
// TODO: remove entries below this line
'resources/Accounts.csv',
'resources/Accounts2.csv',
'resources/QlikLook.csv',
'resources/Excel.png',
'resources/Excel.png'
], {}),
new StyleLintPlugin()
new StyleLintPlugin({
files: '**/*.less'
})
]
};