Compare commits

...

136 Commits

Author SHA1 Message Date
Tobias Åström
d3c39bea75 Merge pull request #32 from qlik-oss/tsm-color-picker-check
Update metric-semaphores.js
2019-04-05 13:46:58 +02:00
Albert Backenhof
433a725f33 Merge pull request #33 from qlik-oss/snapshotFix
New preview image and fixed snapshot
2019-03-20 10:28:52 +01:00
Albert Backenhof
769d5cfa3f New preview image and fixed snapshot
-SnapshotApi doesn't have a getProperties
 so fetching the source properties from
 qlik-app instead.

-The snapshot still cannot be rendered in the
 "printing-snapshot" test page because of
 limitations on that page.
2019-03-19 10:38:29 +01:00
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
Tobias Åström
34527c3d6d Update metric-semaphores.js
We recently had a bug on the color picker when used in extensions (QLIK-94131) so I went through all extensions to check their status, this was the only one with a potential problem.
The property notation should be as I changed it now (but after fixing the bug it will work anyway). However, there might be code elsewhere in the extension that mitigates the bug, so make sure this gets tested.
2019-03-12 09:04:43 +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
Tobias Åström
b65d1c51fc Update qlik-smart-pivot.qext 2019-02-16 10:49:46 +01:00
Tobias Åström
9111ec762b Update config.yml 2019-02-16 10:48:49 +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
50 changed files with 1980 additions and 1077 deletions

View File

@@ -18,6 +18,14 @@ jobs:
- run:
name: Install dependencies
command: npm install
- run:
name: BlackDuck scan
command: curl -s https://blackducksoftware.github.io/hub-detect/hub-detect.sh | bash -s -- \
--blackduck.url="https://qliktech.blackducksoftware.com" \
--blackduck.trust.cert=true \
--blackduck.username="svc-blackduck" \
--blackduck.password=${svc_blackduck} \
--detect.project.name="viz-bundle-qlik-smart-pivot"
- run:
name: Run tests
command: npm run test-once

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",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 B

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "Smart pivot",
"description": "Formatted table for P&L reports.",
"name": "P&L pivot",
"description": "Profit & Loss reporting with color and font customizations.",
"type": "visualization",
"version": "X.Y.Z",
"icon": "table",

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,132 @@ 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: function (props, propName) {
if (props.isSnapshot || typeof props[propName] === 'function') {
return null;
}
return new Error('Missing implementation of qlik.backendApi.selectValues.');
}
}).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 +175,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>
);
}
@@ -38,7 +46,12 @@ RowHeader.propTypes = {
}).isRequired,
qlik: PropTypes.shape({
backendApi: PropTypes.shape({
selectValues: PropTypes.func.isRequired
selectValues: function (props, propName) {
if (props.isSnapshot || typeof props[propName] === 'function') {
return null;
}
return new Error('Missing implementation of qlik.backendApi.selectValues.');
}
}).isRequired
}).isRequired,
rowStyle: PropTypes.shape({}).isRequired,

91
src/dataset.js Normal file
View File

@@ -0,0 +1,91 @@
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 app = qlik.currApp(component);
const designDimensionIndex = findDesignDimension(layout.qHyperCube.qDataPages[0].qMatrix);
const dimensionsInformation = layout.qHyperCube.qDimensionInfo;
const dimensionIndexes = getDimensionIndexes(dimensionsInformation, designDimensionIndex);
let properties;
if (component.backendApi.isSnapshot) {
// Fetch properties of source
properties = (await app.getObjectProperties(layout.sourceObjectId)).properties;
} else {
properties = await component.backendApi.getProperties();
}
const originCubeDefinition = properties.qHyperCubeDef;
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: {
@@ -39,8 +39,9 @@ const metricSemaphores = {
label: 'Critic Color Fill',
type: 'object',
component: 'color-picker',
dualOutput: true,
defaultValue: {
index: 7,
index: 8,
color: '#f93f17'
}
},
@@ -49,8 +50,9 @@ const metricSemaphores = {
label: 'Critic Color Text',
type: 'object',
component: 'color-picker',
dualOutput: true,
defaultValue: {
index: 10,
index: 11,
color: '#ffffff'
}
},
@@ -65,8 +67,9 @@ const metricSemaphores = {
label: 'Medium Color Fill',
type: 'object',
component: 'color-picker',
dualOutput: true,
defaultValue: {
index: 8,
index: 9,
color: '#ffcf02'
}
},
@@ -75,8 +78,9 @@ const metricSemaphores = {
label: 'Medium Color Text',
type: 'object',
component: 'color-picker',
dualOutput: true,
defaultValue: {
index: 11,
index: 12,
color: '#000000'
}
},
@@ -85,8 +89,9 @@ const metricSemaphores = {
label: 'Success Color Fill',
type: 'object',
component: 'color-picker',
dualOutput: true,
defaultValue: {
index: 9,
index: 10,
color: '#276e27'
}
},
@@ -95,8 +100,9 @@ const metricSemaphores = {
label: 'Success Color Text',
type: 'object',
component: 'color-picker',
dualOutput: true,
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>
);
}
@@ -50,7 +57,12 @@ ColumnHeader.propTypes = {
}).isRequired,
qlik: PropTypes.shape({
backendApi: PropTypes.shape({
selectValues: PropTypes.func.isRequired
selectValues: function (props, propName) {
if (props.isSnapshot || typeof props[propName] === 'function') {
return null;
}
return new Error('Missing implementation of qlik.backendApi.selectValues.');
}
}).isRequired
}).isRequired,
styling: PropTypes.shape({

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}
@@ -125,7 +126,12 @@ HeadersTable.propTypes = {
general: PropTypes.shape({}).isRequired,
qlik: PropTypes.shape({
backendApi: PropTypes.shape({
selectValues: PropTypes.func.isRequired
selectValues: function (props, propName) {
if (props.isSnapshot || typeof props[propName] === 'function') {
return null;
}
return new Error('Missing implementation of qlik.backendApi.selectValues.');
}
}).isRequired
}).isRequired,
styling: PropTypes.shape({

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'
})
]
};