Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d95c0f572e | ||
|
|
fd2f9fa277 | ||
|
|
e2aac7a294 | ||
|
|
b6bcbe7f75 | ||
|
|
f4441ef683 | ||
|
|
3a832e7d6a | ||
|
|
22da42de9f | ||
|
|
fddf286a8e | ||
|
|
e6692b8779 | ||
|
|
4341fdb5db | ||
|
|
d02852b2ed | ||
|
|
25f6593f35 | ||
|
|
76a22121a9 | ||
|
|
d4154fde09 | ||
|
|
d65b619546 |
@@ -20,15 +20,12 @@ jobs:
|
||||
command: npm install
|
||||
- run:
|
||||
name: BlackDuck scan
|
||||
command: curl -s https://blackducksoftware.github.io/hub-detect/hub-detect.sh | bash -s -- \
|
||||
command: curl -s https://detect.synopsys.com/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-network-chart"
|
||||
- run:
|
||||
name: Run tests
|
||||
command: npm run test-once
|
||||
|
||||
bump-version:
|
||||
<<: *defaults
|
||||
@@ -56,16 +53,18 @@ jobs:
|
||||
command: |
|
||||
export VERSION=$(scripts/get-bumped-version.sh)
|
||||
echo "Version: ${VERSION}"
|
||||
npm run build
|
||||
npm run build:zip
|
||||
sudo chmod +x scripts/verify-files.sh
|
||||
scripts/verify-files.sh
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
- persist_to_workspace:
|
||||
root: ~/qlik-network-chart
|
||||
paths:
|
||||
- build
|
||||
- dist
|
||||
- store_artifacts:
|
||||
path: build
|
||||
destination: build
|
||||
path: dist
|
||||
destination: dist
|
||||
deploy:
|
||||
<<: *defaults
|
||||
steps:
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
node_modules/
|
||||
build/
|
||||
dist/
|
||||
BUMPED_VERSION
|
||||
|
||||
89
gulpfile.js
89
gulpfile.js
@@ -1,24 +1,61 @@
|
||||
var gulp = require('gulp');
|
||||
var gutil = require('gulp-util');
|
||||
var zip = require('gulp-zip');
|
||||
var del = require('del');
|
||||
var settings = require('./settings');
|
||||
var webpackConfig = require('./webpack.config');
|
||||
var webpack = require('webpack');
|
||||
var WebpackDevServer = require('webpack-dev-server');
|
||||
var jeditor = require("gulp-json-editor");
|
||||
var pkg = require('./package.json');
|
||||
|
||||
gulp.task('remove-build-folder', function(){
|
||||
return del([settings.buildDestination], { force: true });
|
||||
var DIST = './dist';
|
||||
var VERSION = process.env.VERSION || 'local-dev';
|
||||
|
||||
gulp.task('qext', function () {
|
||||
var qext = {
|
||||
name: 'Network chart',
|
||||
type: 'visualization',
|
||||
description: pkg.description + '\nVersion: ' + VERSION,
|
||||
version: VERSION,
|
||||
icon: 'bubbles',
|
||||
preview: 'network.png',
|
||||
keywords: 'qlik-sense, visualization',
|
||||
author: pkg.author,
|
||||
homepage: pkg.homepage,
|
||||
license: pkg.license,
|
||||
repository: pkg.repository,
|
||||
dependencies: {
|
||||
'qlik-sense': '>=5.5.x'
|
||||
}
|
||||
};
|
||||
if (pkg.contributors) {
|
||||
qext.contributors = pkg.contributors;
|
||||
}
|
||||
var src = require('stream').Readable({
|
||||
objectMode: true
|
||||
});
|
||||
src._read = function () {
|
||||
this.push(new gutil.File({
|
||||
cwd: '',
|
||||
base: '',
|
||||
path: pkg.name + '.qext',
|
||||
contents: Buffer.from(JSON.stringify(qext, null, 4))
|
||||
}));
|
||||
this.push(null);
|
||||
};
|
||||
return src.pipe(gulp.dest(DIST));
|
||||
});
|
||||
|
||||
gulp.task('clean', function(){
|
||||
return del([DIST], { force: true });
|
||||
});
|
||||
|
||||
gulp.task('zip-build', function(){
|
||||
return gulp.src(settings.buildDestination + '/**/*')
|
||||
.pipe(zip(`${settings.name}_${settings.version}.zip`))
|
||||
.pipe(gulp.dest(settings.buildDestination));
|
||||
return gulp.src(DIST + '/**/*')
|
||||
.pipe(zip(`${pkg.name}_${VERSION}.zip`))
|
||||
.pipe(gulp.dest(DIST));
|
||||
});
|
||||
|
||||
gulp.task('add-assets', function(){
|
||||
return gulp.src("./assets/**/*").pipe(gulp.dest(settings.buildDestination));
|
||||
return gulp.src('./assets/**/*').pipe(gulp.dest(DIST));
|
||||
});
|
||||
|
||||
gulp.task('webpack-build', done => {
|
||||
@@ -37,39 +74,13 @@ gulp.task('webpack-build', done => {
|
||||
});
|
||||
});
|
||||
|
||||
gulp.task('update-qext-version', function () {
|
||||
return gulp.src(`${settings.buildDestination}/${settings.name}.qext`)
|
||||
.pipe(jeditor({
|
||||
'version': settings.version
|
||||
}))
|
||||
.pipe(gulp.dest(settings.buildDestination));
|
||||
});
|
||||
|
||||
gulp.task('build',
|
||||
gulp.series('remove-build-folder', 'webpack-build', 'update-qext-version', 'add-assets', 'zip-build')
|
||||
gulp.series('clean', 'webpack-build', 'qext', 'add-assets')
|
||||
);
|
||||
|
||||
gulp.task('watch', () => new Promise((resolve, reject) => {
|
||||
webpackConfig.entry.unshift('webpack-dev-server/client?http://localhost:' + settings.port);
|
||||
const compiler = webpack(webpackConfig);
|
||||
const originalOutputFileSystem = compiler.outputFileSystem;
|
||||
const devServer = new WebpackDevServer(compiler, {
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*"
|
||||
},
|
||||
}).listen(settings.port, 'localhost', error => {
|
||||
compiler.outputFileSystem = originalOutputFileSystem;
|
||||
if (error) {
|
||||
console.error(error); // eslint-disable-line no-console
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Listening at localhost:' + settings.port);
|
||||
|
||||
resolve(null, devServer);
|
||||
});
|
||||
}));
|
||||
gulp.task('zip',
|
||||
gulp.series('build', 'zip-build')
|
||||
);
|
||||
|
||||
gulp.task('default',
|
||||
gulp.series('build')
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
const settings = require('./settings');
|
||||
|
||||
module.exports = (config) => {
|
||||
config.set({
|
||||
browsers: ['SlimChromeHeadless'],
|
||||
customLaunchers: {
|
||||
SlimChromeHeadless: {
|
||||
base: 'ChromeHeadless',
|
||||
flags: ['--headless', '--disable-gpu', '--disable-translate', '--disable-extensions']
|
||||
}
|
||||
},
|
||||
files: [
|
||||
{ pattern: 'src/*.spec.js', watched: false }
|
||||
],
|
||||
frameworks: ['jasmine'],
|
||||
preprocessors: {
|
||||
'src/*.spec.js': ['webpack', 'sourcemap']
|
||||
},
|
||||
webpack: {
|
||||
devtool: 'source-map',
|
||||
mode: settings.mode,
|
||||
externals: {
|
||||
qlik: {
|
||||
amd: 'qlik',
|
||||
commonjs: 'qlik',
|
||||
commonjs2: 'qlik',
|
||||
root: '_'
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: [/node_modules/],
|
||||
loaders: ['babel-loader']
|
||||
},
|
||||
{ test: /\.less$/, loader: 'ignore-loader' },
|
||||
{ test: /\.json$/, loader: 'ignore-loader' }
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
24
package.json
24
package.json
@@ -1,18 +1,15 @@
|
||||
{
|
||||
"name": "qlik-network-chart",
|
||||
"version": "0.0.1",
|
||||
"description": "Network chart",
|
||||
"keywords": "network chart qliksense extension",
|
||||
"description": "Displays hierarchical or relational dimensions as nodes and edges´, with measures to show the significance of its links.",
|
||||
"homepage": "",
|
||||
"repository": "https://github.com/qlik-oss/network-vis-chart",
|
||||
"author": "Michael Laenen (miclae76) <m.laenen@contactoffice.net>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "gulp build",
|
||||
"eslint": "eslint src",
|
||||
"eslint:fix": "eslint --fix src",
|
||||
"test": "karma start karma.conf.js",
|
||||
"test-once": "karma start karma.conf.js --single-run",
|
||||
"watch": "gulp watch"
|
||||
"build:zip": "gulp zip",
|
||||
"eslint": "eslint src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.1.5",
|
||||
@@ -20,29 +17,20 @@
|
||||
"@babel/preset-env": "7.1.5",
|
||||
"babel-eslint": "10.0.1",
|
||||
"babel-loader": "8.0.4",
|
||||
"copy-webpack-plugin": "4.6.0",
|
||||
"css-loader": "1.0.1",
|
||||
"del": "3.0.0",
|
||||
"eslint": "5.8.0",
|
||||
"eslint-loader": "2.1.1",
|
||||
"file-loader": "2.0.0",
|
||||
"gulp": "4.0.0",
|
||||
"gulp-json-editor": "2.4.3",
|
||||
"gulp-util": "^3.0.7",
|
||||
"gulp-zip": "4.2.0",
|
||||
"jasmine-core": "3.3.0",
|
||||
"karma": "3.1.1",
|
||||
"karma-chrome-launcher": "2.2.0",
|
||||
"karma-jasmine": "1.1.2",
|
||||
"karma-sourcemap-loader": "0.3.7",
|
||||
"karma-webpack": "3.0.5",
|
||||
"less": "3.8.1",
|
||||
"less-loader": "4.1.0",
|
||||
"style-loader": "0.23.1",
|
||||
"stylelint": "9.7.1",
|
||||
"stylelint-webpack-plugin": "0.10.5",
|
||||
"webpack": "4.25.1",
|
||||
"webpack-cli": "3.1.2",
|
||||
"webpack-dev-server": "3.1.10"
|
||||
"webpack": "4.25.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"vis": "4.21.0"
|
||||
|
||||
63
readme.md
63
readme.md
@@ -1,71 +1,20 @@
|
||||
# Qlik Network Chart
|
||||
This extension is part of the extension bundles for Qlik Sense. The repository is maintained and moderated by Qlik RD.
|
||||
|
||||
[](https://circleci.com/gh/qlik-oss/network-vis-chart)
|
||||
|
||||
Qlik Sense extension to visualize networks data based on library vis.js (http://visjs.org).
|
||||
Tested with Qlik Sense 2.2.3.
|
||||
|
||||
### Dimensions
|
||||
4 dimensions are mandatory :
|
||||
|
||||
1. node identifier (consecutive numbers starting at 0)
|
||||
2. node label (Any text)
|
||||
3. node parent identifier (Refers to node indentifier)
|
||||
4. node group
|
||||
|
||||
### Measures
|
||||
The measures are optional
|
||||
|
||||
1. tooltip : expression that will be push in the tooltip when hover on a node
|
||||
2. node value : used to scale the node size
|
||||
3. edge value : used to scale the edge width
|
||||
|
||||
### Additional network settings
|
||||
* Edge Type : select type of curve between nodes
|
||||
* Node Shape : dot, square, diamond, triangle ...
|
||||
* Display Edge Value : switch to display the measures on edge curves
|
||||
* Position Edge Label : top, bottom, middle, horizontal
|
||||
* Display Shadow : switch to enable shadow effects behind edge and nodes
|
||||
|
||||
### Sample
|
||||
A data sample can be found here: https://github.com/qlik-oss/network-vis-chart/blob/master/resources/Network%20data.xlsx
|
||||
|
||||
QVF based on characters from Victor Hugo's novel , Les Misérables.
|
||||

|
||||
|
||||
### Data Limit
|
||||
Starts having issues stabilizing(transforming into untangled view) at around 100-200 nodes depending on dataset.
|
||||
|
||||
|
||||
# Getting Started
|
||||
|
||||
## Installation
|
||||
|
||||
1. Download the extension zip, `qlik-network-chart_<version>.zip`, from the latest release(https://github.com/qlik-oss/network-vis-chart/releases/latest)
|
||||
2. Install the extension:
|
||||
|
||||
a. **Qlik Sense Desktop**: unzip to a directory under [My Documents]/Qlik/Sense/Extensions.
|
||||
|
||||
b. **Qlik Sense Server**: import the zip file in the QMC.
|
||||
Feel free to fork and suggest pull requests for improvements and bug fixes. Changes will be moderated and reviewed before inclusion in future bundle versions. Please note that emphasis is on backward compatibility, i.e. breaking changes will most likely not be approved.
|
||||
|
||||
Usage documentation for the extension is available at https://help.qlik.com.
|
||||
|
||||
# Developing the extension
|
||||
|
||||
If you want to do code changes to the extension follow these simple steps to get going.
|
||||
|
||||
1. Get Qlik Sense Desktop
|
||||
1. Create a new app and add the extension to a sheet.
|
||||
1. Create a new app and add Network chart to a sheet.
|
||||
2. Clone the repository
|
||||
3. Run `npm install`
|
||||
4. Set the environment variable `BUILD_PATH` to your extensions directory. It will be something like `C:/Users/<user>/Documents/Qlik/Sense/Extensions/<extension_name>`.
|
||||
5. You now have two options. Either run the watch task or the build task. They are explained below. Both of them default to development mode but can be run in production by setting `NODE_ENV=production` before running the npm task.
|
||||
|
||||
a. **Watch**: `npm run watch`. This will start a watcher which will rebuild the extension and output all needed files to the `buildFolder` for each code change you make. See your changes directly in your Qlik Sense app.
|
||||
|
||||
b. **Build**: `npm run build`. If you want to build the extension package. The output zip-file can be found in the `buildFolder`.
|
||||
4. Run `npm run build` - to build a dev-version to the /dist folder.
|
||||
5. Move the content of the /dist folder to the extension directory. Usually in `C:/Users/<user>/Documents/Qlik/Sense/Extensions/qlik-network-chart`.
|
||||
|
||||
# Original Author
|
||||
|
||||
**Michael Laenen**
|
||||
|
||||
* [github.com/miclae76](https://github.com/miclae76)
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
set -o errexit
|
||||
|
||||
echo "Creating release for version: $VERSION"
|
||||
echo "Artifact name: ./build/${3}_${VERSION}.zip"
|
||||
$HOME/bin/ghr -t ${ghoauth} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} ${VERSION} "./build/${3}_${4}.zip"
|
||||
echo "Artifact name: ./dist/${3}_${VERSION}.zip"
|
||||
$HOME/bin/ghr -t ${ghoauth} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} ${VERSION} "./dist/${3}_${4}.zip"
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
25
scripts/verify-files.sh
Normal file
25
scripts/verify-files.sh
Normal file
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
# The build script has a known race-condition that sometimes causes it to not include all files
|
||||
# in the built zip. This script verifies the that the zip contains the correct number of files.
|
||||
|
||||
set -o errexit
|
||||
|
||||
echo "Verifying built file count"
|
||||
|
||||
while read line; do
|
||||
if [[ $line =~ ^\"name\": ]]; then
|
||||
name=${line#*: \"}
|
||||
name=${name%\"*}
|
||||
fi
|
||||
done < package.json
|
||||
|
||||
expected_file_count=$(($(find dist -type f | wc -l)-1))
|
||||
zip_file_count=$(zipinfo dist/${name}_${VERSION}.zip | grep ^- | wc -l)
|
||||
|
||||
if [ "${expected_file_count}" -ne "${zip_file_count}" ]; then
|
||||
# File count is incorrect
|
||||
echo "Expected file count ${expected_file_count}, but was ${zip_file_count}"
|
||||
exit 1
|
||||
fi
|
||||
echo "File count OK"
|
||||
exit 0
|
||||
13
settings.js
13
settings.js
@@ -1,13 +0,0 @@
|
||||
const path = require('path');
|
||||
const packageJSON = require('./package.json');
|
||||
|
||||
const defaultBuildDestination = path.resolve("./build");
|
||||
|
||||
module.exports = {
|
||||
buildDestination: process.env.BUILD_PATH || defaultBuildDestination,
|
||||
mode: process.env.NODE_ENV || 'development',
|
||||
name: packageJSON.name,
|
||||
version: process.env.VERSION || 'local-dev',
|
||||
url: process.env.BUILD_URL || defaultBuildDestination,
|
||||
port: 8082
|
||||
};
|
||||
24
src/index.js
24
src/index.js
@@ -194,15 +194,35 @@ const component = {
|
||||
defaultValue: false
|
||||
}
|
||||
}
|
||||
},
|
||||
about: {
|
||||
component: 'items',
|
||||
label: 'About',
|
||||
items: {
|
||||
header: {
|
||||
label: 'Network chart',
|
||||
style: 'header',
|
||||
component: 'text'
|
||||
},
|
||||
paragraph1: {
|
||||
label: `Network chart is Qlik Sense chart which allows you to draw a network of connected nodes and edges from a data set to a sheet.`,
|
||||
component: 'text'
|
||||
},
|
||||
paragraph2: {
|
||||
label: 'Network chart is based upon an extension created by Michael Laenen.',
|
||||
component: 'text'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
support: {
|
||||
export: true,
|
||||
snapshot: false
|
||||
snapshot: true,
|
||||
exportData: true
|
||||
},
|
||||
snapshot: {
|
||||
canTakeSnapshot: false
|
||||
canTakeSnapshot: true
|
||||
},
|
||||
paint: paint
|
||||
};
|
||||
|
||||
@@ -179,7 +179,7 @@ function paint ( $element, layout, qTheme, component ) {
|
||||
$("#"+containerId).css('cursor','default');
|
||||
|
||||
network.on('select', function (properties) {
|
||||
if (properties.hasOwnProperty("nodes")) {
|
||||
if (properties.hasOwnProperty("nodes") && component.options.noInteraction !== true) {
|
||||
if (properties.nodes.length > 0) {
|
||||
// find connected nodes to selection
|
||||
var connectedNodes = network.getConnectedNodes(properties.nodes[0]);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import paint from './paint';
|
||||
|
||||
describe('paint', () => {
|
||||
it('should be defined', () => {
|
||||
expect(paint).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name" : "Network chart",
|
||||
"description" : "Displays hierarchical or relational dimensions as nodes and edges´, with measures to show the significance of its links.",
|
||||
"icon" : "bubbles",
|
||||
"type" : "visualization",
|
||||
"version": "X.Y.Z",
|
||||
"preview" : "network.png",
|
||||
"keywords": "qlik-sense, visualization",
|
||||
"author": "Michael Laenen <michael.laenen@agilos.com>",
|
||||
"homepage": "",
|
||||
"license": "MIT",
|
||||
"repository": "",
|
||||
"dependencies": {
|
||||
"qlik-sense": ">=5.5.x"
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,25 @@
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const StyleLintPlugin = require('stylelint-webpack-plugin');
|
||||
const settings = require('./settings');
|
||||
const packageJSON = require('./package.json');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const SOURCE_MAP = 'sourec-map';
|
||||
const DEVTOOL = (process.env.NODE_ENV === 'development') ? SOURCE_MAP : false;
|
||||
|
||||
console.log('Webpack mode:', settings.mode); // eslint-disable-line no-console
|
||||
const DIST = path.resolve("./dist");
|
||||
const MODE = process.env.NODE_ENV || 'development';
|
||||
|
||||
console.log('Webpack mode:', MODE); // eslint-disable-line no-console
|
||||
|
||||
const config = {
|
||||
devtool: 'source-map',
|
||||
devtool: DEVTOOL,
|
||||
entry: [
|
||||
'./src/index.js'
|
||||
],
|
||||
mode: settings.mode,
|
||||
mode: MODE,
|
||||
output: {
|
||||
path: settings.buildDestination,
|
||||
filename: settings.name + '.js',
|
||||
libraryTarget: 'umd'
|
||||
filename: `${packageJSON.name}.js`,
|
||||
libraryTarget: 'amd',
|
||||
path: DIST
|
||||
},
|
||||
externals: {
|
||||
qlik: {
|
||||
@@ -71,9 +76,6 @@ const config = {
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new CopyWebpackPlugin([
|
||||
'src/' + settings.name + '.qext'
|
||||
], {}),
|
||||
new StyleLintPlugin(),
|
||||
new webpack.ContextReplacementPlugin(/moment[\\\/]locale$/, /^\.\/en$/),
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user