Merge branch 'master' into grafana/gtk/verify

This commit is contained in:
Dominik Prokop
2019-07-10 14:39:48 +02:00
79 changed files with 1653 additions and 511 deletions
+15
View File
@@ -0,0 +1,15 @@
## Grafana frontend packages
## Releasing new version
We use [Lerna](https://github.com/lerna/lerna) for packages versioning and releases
### Manual release
1. Run `packages:prepare` script from root directory. This will perform cleanup, run all tests and bump version for all packages. Also, it will create `@packages@[version]` tag and version bump commit with `Packages: publish [version]` message.
2. Run `packages:build` script that will prepare distribution packages.
3. Run `packages:publish` to publish new versions
- add `--dist-tag next` to publish under `next` tag
4. Push version commit
### Building individual packages
To build induvidual packages run `grafana-toolkit package:build --scope=<ui|toolkit|runtime|data>`
+3
View File
@@ -0,0 +1,3 @@
# (2019-07-08)
First public release
+4 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@grafana/data",
"version": "6.3.0-alpha.0",
"version": "6.3.0-alpha.36",
"description": "Grafana Data Library",
"keywords": [
"typescript"
@@ -10,11 +10,12 @@
"tslint": "tslint -c tslint.json --project tsconfig.json",
"typecheck": "tsc --noEmit",
"clean": "rimraf ./dist ./compiled",
"build": "rollup -c rollup.config.ts"
"bundle": "rollup -c rollup.config.ts",
"build": "grafana-toolkit package:build --scope=data",
"postpublish": "npm run clean"
},
"author": "Grafana Labs",
"license": "Apache-2.0",
"dependencies": {},
"devDependencies": {
"@types/jest": "23.3.14",
"@types/jquery": "1.10.35",
+3
View File
@@ -97,6 +97,9 @@ export interface AnnotationEvent {
dashboardId?: number;
panelId?: number;
userId?: number;
login?: string;
email?: string;
avatarUrl?: string;
time?: number;
timeEnd?: number;
isRegion?: boolean;
@@ -91,4 +91,58 @@ describe('Stats Calculators', () => {
expect(stats.step).toEqual(100);
expect(stats.delta).toEqual(300);
});
it('consistent results for first/last value with null', () => {
const info = [
{
rows: [[null], [200], [null]], // first/last value is null
result: 200,
},
{
rows: [[null], [null], [null]], // All null
result: undefined,
},
{
rows: [], // Empty row
result: undefined,
},
];
const fields = [{ name: 'A' }];
const stats = reduceField({
series: { rows: info[0].rows, fields },
fieldIndex: 0,
reducers: [ReducerID.first, ReducerID.last, ReducerID.firstNotNull, ReducerID.lastNotNull], // uses standard path
});
expect(stats[ReducerID.first]).toEqual(null);
expect(stats[ReducerID.last]).toEqual(null);
expect(stats[ReducerID.firstNotNull]).toEqual(200);
expect(stats[ReducerID.lastNotNull]).toEqual(200);
const reducers = [ReducerID.lastNotNull, ReducerID.firstNotNull];
for (const input of info) {
for (const reducer of reducers) {
const v1 = reduceField({
series: { rows: input.rows, fields },
fieldIndex: 0,
reducers: [reducer, ReducerID.mean], // uses standard path
})[reducer];
const v2 = reduceField({
series: { rows: input.rows, fields },
fieldIndex: 0,
reducers: [reducer], // uses optimized path
})[reducer];
if (v1 !== v2 || v1 !== input.result) {
const msg =
`Invalid ${reducer} result for: ` +
input.rows.join(', ') +
` Expected: ${input.result}` + // configured
` Recieved: Multiple: ${v1}, Single: ${v2}`;
expect(msg).toEqual(null);
}
}
}
});
});
+64 -12
View File
@@ -17,6 +17,9 @@ export enum ReducerID {
delta = 'delta',
step = 'step',
firstNotNull = 'firstNotNull',
lastNotNull = 'lastNotNull',
changeCount = 'changeCount',
distinctCount = 'distinctCount',
@@ -132,14 +135,28 @@ function getById(id: string): FieldReducerInfo | undefined {
if (!hasBuiltIndex) {
[
{
id: ReducerID.last,
name: 'Last',
description: 'Last Value (current)',
id: ReducerID.lastNotNull,
name: 'Last (not null)',
description: 'Last non-null value',
standard: true,
alias: 'current',
reduce: calculateLastNotNull,
},
{
id: ReducerID.last,
name: 'Last',
description: 'Last Value',
standard: true,
reduce: calculateLast,
},
{ id: ReducerID.first, name: 'First', description: 'First Value', standard: true, reduce: calculateFirst },
{
id: ReducerID.firstNotNull,
name: 'First (not null)',
description: 'First non-null value',
standard: true,
reduce: calculateFirstNotNull,
},
{ id: ReducerID.min, name: 'Min', description: 'Minimum Value', standard: true },
{ id: ReducerID.max, name: 'Max', description: 'Maximum Value', standard: true },
{ id: ReducerID.mean, name: 'Mean', description: 'Average Value', standard: true, alias: 'avg' },
@@ -231,6 +248,8 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
mean: null,
last: null,
first: null,
lastNotNull: undefined,
firstNotNull: undefined,
count: 0,
nonNullCount: 0,
allIsNull: true,
@@ -246,6 +265,10 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
for (let i = 0; i < data.rows.length; i++) {
let currentValue = data.rows[i][fieldIndex];
if (i === 0) {
calcs.first = currentValue;
}
calcs.last = currentValue;
if (currentValue === null) {
if (ignoreNulls) {
@@ -257,9 +280,9 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
}
if (currentValue !== null) {
const isFirst = calcs.first === null;
const isFirst = calcs.firstNotNull === undefined;
if (isFirst) {
calcs.first = currentValue;
calcs.firstNotNull = currentValue;
}
if (isNumber(currentValue)) {
@@ -268,12 +291,12 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
calcs.nonNullCount++;
if (!isFirst) {
const step = currentValue - calcs.last!;
const step = currentValue - calcs.lastNotNull!;
if (calcs.step > step) {
calcs.step = step; // the minimum interval
}
if (calcs.last! > currentValue) {
if (calcs.lastNotNull! > currentValue) {
// counter reset
calcs.previousDeltaUp = false;
if (i === data.rows.length - 1) {
@@ -307,7 +330,7 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
calcs.allIsZero = false;
}
calcs.last = currentValue;
calcs.lastNotNull = currentValue;
}
}
@@ -331,10 +354,8 @@ function doStandardCalcs(data: DataFrame, fieldIndex: number, ignoreNulls: boole
calcs.range = calcs.max - calcs.min;
}
if (calcs.first !== null && calcs.last !== null) {
if (isNumber(calcs.first) && isNumber(calcs.last)) {
calcs.diff = calcs.last - calcs.first;
}
if (isNumber(calcs.firstNotNull) && isNumber(calcs.lastNotNull)) {
calcs.diff = calcs.lastNotNull - calcs.firstNotNull;
}
return calcs;
@@ -344,10 +365,41 @@ function calculateFirst(data: DataFrame, fieldIndex: number, ignoreNulls: boolea
return { first: data.rows[0][fieldIndex] };
}
function calculateFirstNotNull(
data: DataFrame,
fieldIndex: number,
ignoreNulls: boolean,
nullAsZero: boolean
): FieldCalcs {
for (let idx = 0; idx < data.rows.length; idx++) {
const v = data.rows[idx][fieldIndex];
if (v != null) {
return { firstNotNull: v };
}
}
return { firstNotNull: undefined };
}
function calculateLast(data: DataFrame, fieldIndex: number, ignoreNulls: boolean, nullAsZero: boolean): FieldCalcs {
return { last: data.rows[data.rows.length - 1][fieldIndex] };
}
function calculateLastNotNull(
data: DataFrame,
fieldIndex: number,
ignoreNulls: boolean,
nullAsZero: boolean
): FieldCalcs {
let idx = data.rows.length - 1;
while (idx >= 0) {
const v = data.rows[idx--][fieldIndex];
if (v != null) {
return { lastNotNull: v };
}
}
return { lastNotNull: undefined };
}
function calculateChangeCount(
data: DataFrame,
fieldIndex: number,
+3 -11
View File
@@ -1,19 +1,11 @@
{
"extends": "../../tsconfig.json",
"extends": "../tsconfig.json",
"include": ["src/**/*.ts", "src/**/*.tsx", "../../public/app/types/jquery/*.ts"],
"exclude": ["dist", "node_modules"],
"compilerOptions": {
"rootDirs": ["."],
"module": "esnext",
"outDir": "compiled",
"declaration": true,
"declarationDir": "dist",
"strict": true,
"alwaysStrict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"typeRoots": ["./node_modules/@types", "types"],
"skipLibCheck": true, // Temp workaround for Duplicate identifier tsc errors,
"removeComments": false
"declarationDir": "dist",
"outDir": "compiled"
}
}
+3
View File
@@ -0,0 +1,3 @@
# (2019-07-08)
First public release
+4 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@grafana/runtime",
"version": "6.3.0-alpha.0",
"version": "6.3.0-alpha.36",
"description": "Grafana Runtime Library",
"keywords": [
"grafana"
@@ -10,7 +10,9 @@
"tslint": "tslint -c tslint.json --project tsconfig.json",
"typecheck": "tsc --noEmit",
"clean": "rimraf ./dist ./compiled",
"build": "rollup -c rollup.config.ts"
"bundle": "rollup -c rollup.config.ts",
"build": "grafana-toolkit package:build --scope=runtime",
"postpublish": "npm run clean"
},
"author": "Grafana Labs",
"license": "Apache-2.0",
+1 -1
View File
@@ -20,7 +20,7 @@ const buildCjsPackage = ({ env }) => {
globals: {},
},
],
external: ['lodash'], // Use Lodash from grafana
external: ['lodash', '@grafana/ui', '@grafana/data'], // Use Lodash from grafana
plugins: [
commonjs({
include: /node_modules/,
+3 -11
View File
@@ -1,19 +1,11 @@
{
"extends": "../../tsconfig.json",
"extends": "../tsconfig.json",
"include": ["src/**/*.ts", "src/**/*.tsx", "../../public/app/types/jquery/*.ts"],
"exclude": ["dist", "node_modules"],
"compilerOptions": {
"rootDirs": ["."],
"module": "esnext",
"outDir": "compiled",
"declaration": true,
"declarationDir": "dist",
"strict": true,
"alwaysStrict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"typeRoots": ["./node_modules/@types", "types"],
"skipLibCheck": true, // Temp workaround for Duplicate identifier tsc errors,
"removeComments": false
"declarationDir": "dist",
"outDir": "compiled"
}
}
+3 -1
View File
@@ -84,7 +84,7 @@ Adidtionaly, you can also provide additional Jest config via package.json file.
## Working with CSS & static assets
We support pure css, SASS and CSS in JS approach (via Emotion). All static assets referenced in your code (i.e. images) should be placed under `src/static` directory and referenced using relative paths.
We support pure css, SASS and CSS in JS approach (via Emotion).
1. Single css/sass file
Create your css/sass file and import it in your plugin entry point (typically module.ts):
@@ -101,6 +101,8 @@ If you want to provide different stylesheets for dark/light theme, create `dark.
TODO: add note about loadPluginCss
Note that static files (png, svg, json, html) are all copied to dist directory when the plugin is bundled. Relative paths to those files does not change.
3. Emotion
Starting from Grafana 6.2 our suggested way of styling plugins is by using [Emotion](https://emotion.sh). It's a css-in-js library that we use internaly at Grafana. The biggest advantage of using Emotion is that you will get access to Grafana Theme variables.
+5 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@grafana/toolkit",
"version": "6.3.0-alpha.2",
"version": "6.3.0-alpha.36",
"description": "Grafana Toolkit",
"keywords": [
"grafana",
@@ -14,7 +14,9 @@
"tslint": "tslint -c tslint.json --project tsconfig.json",
"typecheck": "tsc --noEmit",
"precommit": "npm run tslint & npm run typecheck",
"clean": "rimraf ./dist ./compiled"
"clean": "rimraf ./dist ./compiled",
"build": "grafana-toolkit toolkit:build",
"postpublish": "npm run clean"
},
"author": "Grafana Labs",
"license": "Apache-2.0",
@@ -31,6 +33,7 @@
"@types/webpack": "4.4.34",
"axios": "0.19.0",
"babel-loader": "8.0.6",
"babel-plugin-angularjs-annotate": "0.10.0",
"chalk": "^2.4.2",
"commander": "^2.20.0",
"concurrently": "4.1.0",
@@ -46,7 +49,6 @@
"jest-coverage-badges": "^1.1.2",
"lodash": "4.17.11",
"mini-css-extract-plugin": "^0.7.0",
"ng-annotate-webpack-plugin": "^0.3.0",
"node-sass": "^4.12.0",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"ora": "^3.4.0",
+6 -19
View File
@@ -3,8 +3,6 @@ import program from 'commander';
import { execTask } from './utils/execTask';
import chalk from 'chalk';
import { startTask } from './tasks/core.start';
import { buildTask } from './tasks/grafanaui.build';
import { releaseTask } from './tasks/grafanaui.release';
import { changelogTask } from './tasks/changelog';
import { cherryPickTask } from './tasks/cherrypick';
import { precommitTask } from './tasks/precommit';
@@ -16,6 +14,7 @@ import { searchTestDataSetupTask } from './tasks/searchTestDataSetup';
import { closeMilestoneTask } from './tasks/closeMilestone';
import { pluginDevTask } from './tasks/plugin.dev';
import { pluginCITask } from './tasks/plugin.ci';
import { buildPackageTask } from './tasks/package.build';
export const run = (includeInternalScripts = false) => {
if (includeInternalScripts) {
@@ -33,24 +32,12 @@ export const run = (includeInternalScripts = false) => {
});
program
.command('gui:build')
.description('Builds @grafana/ui package to packages/grafana-ui/dist')
.command('package:build')
.option('-s, --scope <packages>', 'packages=[data|runtime|ui|toolkit]')
.description('Builds @grafana/* package to packages/grafana-*/dist')
.action(async cmd => {
// @ts-ignore
await execTask(buildTask)();
});
program
.command('gui:release')
.description('Prepares @grafana/ui release (and publishes to npm on demand)')
.option('-p, --publish', 'Publish @grafana/ui to npm registry')
.option('-u, --usePackageJsonVersion', 'Use version specified in package.json')
.option('--createVersionCommit', 'Create and push version commit')
.action(async cmd => {
await execTask(releaseTask)({
publishToNpm: !!cmd.publish,
usePackageJsonVersion: !!cmd.usePackageJsonVersion,
createVersionCommit: !!cmd.createVersionCommit,
await execTask(buildPackageTask)({
scope: cmd.scope,
});
});
@@ -1,4 +1,3 @@
import axios from 'axios';
// @ts-ignore
import * as _ from 'lodash';
import { Task, TaskRunner } from './task';
@@ -1,190 +0,0 @@
import execa = require('execa');
import { execTask } from '../utils/execTask';
import { changeCwdToGrafanaUiDist, changeCwdToGrafanaUi, restoreCwd } from '../utils/cwd';
import { ReleaseType, inc } from 'semver';
import { prompt } from 'inquirer';
import chalk from 'chalk';
import { useSpinner } from '../utils/useSpinner';
import { savePackage, buildTask, clean } from './grafanaui.build';
import { TaskRunner, Task } from './task';
type VersionBumpType = 'prerelease' | 'patch' | 'minor' | 'major';
interface ReleaseTaskOptions {
publishToNpm: boolean;
usePackageJsonVersion: boolean;
createVersionCommit: boolean;
}
const promptBumpType = async () => {
return prompt<{ type: VersionBumpType }>([
{
type: 'list',
message: 'Select version bump',
name: 'type',
choices: ['prerelease', 'patch', 'minor', 'major'],
},
]);
};
const promptPrereleaseId = async (message = 'Is this a prerelease?', allowNo = true) => {
return prompt<{ id: string }>([
{
type: 'list',
message: message,
name: 'id',
choices: allowNo ? ['no', 'alpha', 'beta'] : ['alpha', 'beta'],
},
]);
};
const promptConfirm = async (message?: string) => {
return prompt<{ confirmed: boolean }>([
{
type: 'confirm',
message: message || 'Is that correct?',
name: 'confirmed',
default: false,
},
]);
};
// Since Grafana core depends on @grafana/ui highly, we run full check before release
const runChecksAndTests = async () =>
// @ts-ignore
useSpinner<void>(`Running checks and tests`, async () => {
try {
await execa('npm', ['run', 'test']);
} catch (e) {
console.log(e);
throw e;
}
})();
const bumpVersion = (version: string) =>
// @ts-ignore
useSpinner<void>(`Saving version ${version} to package.json`, async () => {
changeCwdToGrafanaUi();
await execa('npm', ['version', version]);
changeCwdToGrafanaUiDist();
const pkg = require(`${process.cwd()}/package.json`);
pkg.version = version;
await savePackage({ path: `${process.cwd()}/package.json`, pkg });
})();
const publishPackage = (name: string, version: string) =>
// @ts-ignore
useSpinner<void>(`Publishing ${name} @ ${version} to npm registry...`, async () => {
changeCwdToGrafanaUiDist();
await execa('npm', ['publish', '--access', 'public']);
})();
const ensureMasterBranch = async () => {
const currentBranch = await execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']);
const status = await execa.stdout('git', ['status', '--porcelain']);
if (currentBranch !== 'master' && status !== '') {
console.error(chalk.red.bold('You need to be on clean master branch to release @grafana/ui'));
process.exit(1);
}
};
const prepareVersionCommitAndPush = async (version: string) =>
// @ts-ignore
useSpinner<void>('Commiting and pushing @grafana/ui version update', async () => {
await execa.stdout('git', ['commit', '-a', '-m', `Upgrade @grafana/ui version to v${version}`]);
await execa.stdout('git', ['push']);
})();
const releaseTaskRunner: TaskRunner<ReleaseTaskOptions> = async ({
publishToNpm,
usePackageJsonVersion,
createVersionCommit,
}) => {
changeCwdToGrafanaUi();
// @ts-ignore
await clean(); // Clean previous build if exists
restoreCwd();
if (publishToNpm) {
// TODO: Ensure release branch
// When need to update this when we star keeping @grafana/ui releases in sync with core
await ensureMasterBranch();
}
await runChecksAndTests();
await execTask(buildTask)({} as any);
let releaseConfirmed = false;
let nextVersion;
changeCwdToGrafanaUiDist();
const pkg = require(`${process.cwd()}/package.json`);
console.log(`Current version: ${pkg.version}`);
do {
if (!usePackageJsonVersion) {
const { type } = await promptBumpType();
console.log(type);
if (type === 'prerelease') {
const { id } = await promptPrereleaseId('What kind of prerelease?', false);
nextVersion = inc(pkg.version, type, id as any);
} else {
const { id } = await promptPrereleaseId();
if (id !== 'no') {
nextVersion = inc(pkg.version, `pre${type}` as ReleaseType, id as any);
} else {
nextVersion = inc(pkg.version, type as ReleaseType);
}
}
} else {
nextVersion = pkg.version;
}
console.log(chalk.yellowBright.bold(`You are going to release a new version of ${pkg.name}`));
if (usePackageJsonVersion) {
console.log(chalk.green(`Version based on package.json: `), chalk.bold.yellowBright(`${nextVersion}`));
} else {
console.log(chalk.green(`Version bump: ${pkg.version} ->`), chalk.bold.yellowBright(`${nextVersion}`));
}
const { confirmed } = await promptConfirm();
releaseConfirmed = confirmed;
} while (!releaseConfirmed);
if (!usePackageJsonVersion) {
await bumpVersion(nextVersion);
}
if (createVersionCommit) {
await prepareVersionCommitAndPush(nextVersion);
}
if (publishToNpm) {
console.log(chalk.yellowBright.bold(`\nReview dist package.json before proceeding!\n`));
const { confirmed } = await promptConfirm('Are you ready to publish to npm?');
if (!confirmed) {
process.exit();
}
await publishPackage(pkg.name, nextVersion);
console.log(chalk.green(`\nVersion ${nextVersion} of ${pkg.name} succesfully released!`));
console.log(chalk.yellow(`\nUpdated @grafana/ui/package.json with version bump created.`));
process.exit();
} else {
console.log(
chalk.green(
`\nVersion ${nextVersion} of ${pkg.name} succesfully prepared for release. See packages/grafana-ui/dist`
)
);
console.log(chalk.green(`\nTo publish to npm registry run`), chalk.bold.blue(`npm run gui:publish`));
}
};
export const releaseTask = new Task<ReleaseTaskOptions>('@grafana/ui release', releaseTaskRunner);
@@ -1,7 +1,8 @@
import execa = require('execa');
// @ts-ignore
import * as fs from 'fs';
import { changeCwdToGrafanaUi, restoreCwd } from '../utils/cwd';
// @ts-ignore
import * as path from 'path';
import chalk from 'chalk';
import { useSpinner } from '../utils/useSpinner';
import { Task, TaskRunner } from './task';
@@ -15,7 +16,7 @@ export const clean = useSpinner<void>('Cleaning', async () => await execa('npm',
const compile = useSpinner<void>('Compiling sources', () => execa('tsc', ['-p', './tsconfig.build.json']));
// @ts-ignore
const rollup = useSpinner<void>('Bundling', () => execa('npm', ['run', 'build']));
const rollup = useSpinner<void>('Bundling', () => execa('npm', ['run', 'bundle']));
interface SavePackageOptions {
path: string;
@@ -68,19 +69,34 @@ const moveFiles = () => {
})();
};
const buildTaskRunner: TaskRunner<void> = async () => {
cwd = changeCwdToGrafanaUi();
distDir = `${cwd}/dist`;
const pkg = require(`${cwd}/package.json`);
console.log(chalk.yellow(`Building ${pkg.name} (package.json version: ${pkg.version})`));
interface PackageBuildOptions {
scope: string;
}
await clean();
await compile();
await rollup();
await preparePackage(pkg);
await moveFiles();
const buildTaskRunner: TaskRunner<PackageBuildOptions> = async ({ scope }) => {
if (!scope) {
throw new Error('Provide packages with -s, --scope <packages>');
}
restoreCwd();
const scopes = scope.split(',').map(s => {
return async () => {
cwd = path.resolve(__dirname, `../../../../grafana-${s}`);
// Lerna executes this in package's dir context, but for testing purposes I want to be able to run from root:
// grafana-toolkit package:build --scope=<package>
process.chdir(cwd);
distDir = `${cwd}/dist`;
const pkg = require(`${cwd}/package.json`);
console.log(chalk.yellow(`Building ${pkg.name} (package.json version: ${pkg.version})`));
await clean();
await compile();
await rollup();
await preparePackage(pkg);
await moveFiles();
};
});
await Promise.all(scopes.map(s => s()));
};
export const buildTask = new Task<void>('@grafana/ui build', buildTaskRunner);
export const buildPackageTask = new Task<PackageBuildOptions>('@grafana/ui build', buildTaskRunner);
@@ -4,7 +4,6 @@ import execa = require('execa');
import path = require('path');
import fs = require('fs');
import glob = require('glob');
import util = require('util');
import { Linter, Configuration, RuleFailure } from 'tslint';
import * as prettier from 'prettier';
@@ -17,7 +16,6 @@ interface PluginBuildOptions {
export const bundlePlugin = useSpinner<PluginBundleOptions>('Compiling...', async options => await bundleFn(options));
const readFileAsync = util.promisify(fs.readFile);
// @ts-ignore
export const clean = useSpinner<void>('Cleaning', async () => await execa('rimraf', [`${process.cwd()}/dist`]));
@@ -1,6 +1,5 @@
import { Task, TaskRunner } from './task';
import { pluginBuildRunner } from './plugin.build';
import { useSpinner } from '../utils/useSpinner';
import { restoreCwd } from '../utils/cwd';
import { getPluginJson } from '../../config/utils/pluginValidation';
@@ -1,5 +1,3 @@
import path = require('path');
import fs = require('fs');
import webpack = require('webpack');
import { getWebpackConfig } from '../../../config/webpack.plugin.config';
import formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
@@ -1,4 +1,3 @@
import path = require('path');
import * as jestCLI from 'jest-cli';
import { useSpinner } from '../../utils/useSpinner';
import { jestConfig } from '../../../config/jest.plugin.config';
@@ -46,7 +46,6 @@ export async function getTeam(team: any): Promise<any> {
}
export async function addToTeam(team: any, user: any): Promise<any> {
const members = await client.get(`/teams/${team.id}/members`);
console.log(`Adding user ${user.name} to team ${team.name}`);
await client.post(`/teams/${team.id}/members`, { userId: user.id });
}
@@ -1,6 +1,5 @@
import execa = require('execa');
import * as fs from 'fs';
import { changeCwdToGrafanaUi, restoreCwd, changeCwdToGrafanaToolkit } from '../utils/cwd';
import chalk from 'chalk';
import { useSpinner } from '../utils/useSpinner';
import { Task, TaskRunner } from './task';
@@ -102,7 +101,7 @@ const copySassFiles = () => {
};
const toolkitBuildTaskRunner: TaskRunner<void> = async () => {
cwd = changeCwdToGrafanaToolkit();
cwd = path.resolve(__dirname, '../../../');
distDir = `${cwd}/dist`;
const pkg = require(`${cwd}/package.json`);
console.log(chalk.yellow(`Building ${pkg.name} (package.json version: ${pkg.version})`));
@@ -114,7 +113,6 @@ const toolkitBuildTaskRunner: TaskRunner<void> = async () => {
fs.mkdirSync('./dist/sass');
await moveFiles();
await copySassFiles();
restoreCwd();
};
export const toolkitBuildTask = new Task<void>('@grafana/toolkit build', toolkitBuildTaskRunner);
@@ -17,3 +17,15 @@ export const changeCwdToGrafanaUiDist = () => {
export const restoreCwd = () => {
process.chdir(cwd);
};
type PackageId = 'ui' | 'data' | 'runtime' | 'toolkit';
export const changeCwdToPackage = (scope: PackageId) => {
try {
process.chdir(`${cwd}/packages/grafana-${scope}`);
} catch (e) {
throw e;
}
return process.cwd();
};
@@ -5,9 +5,9 @@ const ReplaceInFileWebpackPlugin = require('replace-in-file-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const ngAnnotatePlugin = require('ng-annotate-webpack-plugin');
import * as webpack from 'webpack';
import { hasThemeStylesheets, getStyleLoaders, getStylesheetEntries, getFileLoaders } from './webpack/loaders';
import { getStyleLoaders, getStylesheetEntries, getFileLoaders } from './webpack/loaders';
interface WebpackConfigurationOptions {
watch?: boolean;
@@ -51,6 +51,7 @@ const getManualChunk = (id: string) => {
};
}
}
return null;
};
const getEntries = () => {
@@ -114,7 +115,6 @@ export const getWebpackConfig: WebpackConfigurationGetter = options => {
const optimization: { [key: string]: any } = {};
if (options.production) {
plugins.push(new ngAnnotatePlugin());
optimization.minimizer = [new TerserPlugin(), new OptimizeCssAssetsPlugin()];
}
@@ -177,8 +177,12 @@ export const getWebpackConfig: WebpackConfigurationGetter = options => {
loaders: [
{
loader: 'babel-loader',
options: { presets: ['@babel/preset-env'] },
options: {
presets: ['@babel/preset-env'],
plugins: ['angularjs-annotate'],
},
},
'ts-loader',
],
exclude: /(node_modules)/,
@@ -3,7 +3,6 @@ import { getStylesheetEntries, hasThemeStylesheets } from './loaders';
describe('Loaders', () => {
describe('stylesheet helpers', () => {
const logSpy = jest.spyOn(console, 'log').mockImplementation();
const errorSpy = jest.spyOn(console, 'error').mockImplementation();
afterAll(() => {
logSpy.mockRestore();
@@ -1,6 +1,3 @@
import { getPluginJson } from '../utils/pluginValidation';
const path = require('path');
const fs = require('fs');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
@@ -122,8 +119,8 @@ export const getFileLoaders = () => {
? {
loader: 'file-loader',
options: {
outputPath: 'static',
name: '[name].[hash:8].[ext]',
outputPath: '/',
name: '[path][name].[ext]',
},
}
: // When using single css import images are inlined as base64 URIs in the result bundle
+2 -6
View File
@@ -1,17 +1,13 @@
{
"extends": "../tsconfig.json",
"include": ["src/**/*.ts"],
"exclude": ["dist", "node_modules"],
"compilerOptions": {
"module": "commonjs",
"rootDirs": ["."],
"outDir": "dist/src",
"strict": true,
"alwaysStrict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"declaration": false,
"typeRoots": ["./node_modules/@types"],
"skipLibCheck": true, // Temp workaround for Duplicate identifier tsc errors,
"removeComments": false,
"esModuleInterop": true,
"lib": ["es2015", "es2017.string"]
}
-34
View File
@@ -15,37 +15,3 @@ See [package source](https://github.com/grafana/grafana/tree/master/packages/gra
## Development
For development purposes we suggest using `yarn link` that will create symlink to @grafana/ui lib. To do so navigate to `packages/grafana-ui` and run `yarn link`. Then, navigate to your project and run `yarn link @grafana/ui` to use the linked version of the lib. To unlink follow the same procedure, but use `yarn unlink` instead.
## Building @grafana/ui
To build @grafana/ui run `npm run gui:build` script _from Grafana repository root_. The build will be created in `packages/grafana-ui/dist` directory. Following steps from [Development](#development) you can test built package.
## Releasing new version
To release new version run `npm run gui:release` script _from Grafana repository root_. This has to be done on the master branch. The script will prepare the distribution package as well as prompt you to bump library version and publish it to the NPM registry. When the new package is published, create a PR with the bumped version in package.json.
### Automatic version bump
When running `npm run gui:release` package.json file will be automatically updated. Also, package.json file will be commited and pushed to upstream branch.
### Manual version bump
Manually update the version in `package.json` and then run `npm run gui:release --usePackageJsonVersion` _from Grafana repository root_.
### Preparing release package without publishing to NPM registry
For testing purposes there is `npm run gui:releasePrepare` task that prepares distribution package without publishing it to the NPM registry.
### V1 release process overview
1. Package is compiled with TSC. Typings are created in `/dist` directory, and the compiled js lands in `/compiled` dir
2. Rollup creates a CommonJS package based on compiled sources, and outputs it to `/dist` directory
3. Readme, changelog and index.js files are moved to `/dist` directory
4. Package version is bumped in both `@grafana/ui` package dir and in dist directory.
5. Version commit is created and pushed to master branch
6. Package is published to npm
## Versioning
To limit the confusion related to @grafana/ui and Grafana versioning we decided to keep the major version in sync between those two.
This means, that first version of @grafana/ui is taged with 6.0.0-alpha.0 to keep version in sync with Grafana 6.0 release.
+4 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@grafana/ui",
"version": "6.3.0-alpha.1",
"version": "6.3.0-alpha.36",
"description": "Grafana Components Library",
"keywords": [
"grafana",
@@ -14,7 +14,9 @@
"storybook": "start-storybook -p 9001 -c .storybook",
"storybook:build": "build-storybook -o ./dist/storybook -c .storybook",
"clean": "rimraf ./dist ./compiled",
"build": "rollup -c rollup.config.ts"
"bundle": "rollup -c rollup.config.ts",
"build": "grafana-toolkit package:build --scope=ui",
"postpublish": "npm run clean"
},
"author": "Grafana Labs",
"license": "Apache-2.0",
@@ -1,7 +1,15 @@
import { TimeRange, TIME_FORMAT, RawTimeRange, TimeZone } from '@grafana/data';
import { describeTimeRange } from '@grafana/data/src/utils/rangeutil';
import { dateMath } from '@grafana/data';
import { isDateTime, dateTime, DateTime, toUtc } from '@grafana/data';
import {
TimeRange,
TIME_FORMAT,
RawTimeRange,
TimeZone,
rangeUtil,
dateMath,
isDateTime,
dateTime,
DateTime,
toUtc,
} from '@grafana/data';
export const rawToTimeRange = (raw: RawTimeRange, timeZone?: TimeZone): TimeRange => {
const from = stringToDateTimeType(raw.from, false, timeZone);
@@ -32,7 +40,7 @@ export const stringToDateTimeType = (value: string | DateTime, roundUp?: boolean
};
export const mapTimeRangeToRangeString = (timeRange: RawTimeRange): string => {
return describeTimeRange(timeRange);
return rangeUtil.describeTimeRange(timeRange);
};
export const isValidTimeString = (text: string) => dateMath.isValid(text);
+41
View File
@@ -0,0 +1,41 @@
// Node.closest() polyfill
if ('Element' in window && !Element.prototype.closest) {
Element.prototype.closest = function(this: any, s: string) {
const matches = (this.document || this.ownerDocument).querySelectorAll(s);
let el = this;
let i;
// eslint-disable-next-line
do {
i = matches.length;
// eslint-disable-next-line
while (--i >= 0 && matches.item(i) !== el) {}
el = el.parentElement;
} while (i < 0 && el);
return el;
};
}
export function getPreviousCousin(node: any, selector: string) {
let sibling = node.parentElement.previousSibling;
let el;
while (sibling) {
el = sibling.querySelector(selector);
if (el) {
return el;
}
sibling = sibling.previousSibling;
}
return undefined;
}
export function getNextCharacter(global = window) {
const selection = global.getSelection();
if (!selection || !selection.anchorNode) {
return null;
}
const range = selection.getRangeAt(0);
const text = selection.anchorNode.textContent;
const offset = range.startOffset;
return text!.substr(offset, 1);
}
+4
View File
@@ -7,3 +7,7 @@ export * from './deprecationWarning';
export * from './validate';
export { getFlotPairs } from './flotPairs';
export * from './slate';
// Export with a namespace
import * as DOMUtil from './dom'; // includes Element.closest polyfil
export { DOMUtil };
+3 -11
View File
@@ -1,19 +1,11 @@
{
"extends": "../../tsconfig.json",
"extends": "../tsconfig.json",
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": ["dist", "node_modules"],
"compilerOptions": {
"rootDirs": [".", "stories"],
"module": "esnext",
"outDir": "compiled",
"declaration": true,
"declarationDir": "dist",
"strict": true,
"alwaysStrict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"typeRoots": ["./node_modules/@types", "types"],
"skipLibCheck": true, // Temp workaround for Duplicate identifier tsc errors,
"removeComments": false
"declarationDir": "dist",
"outDir": "compiled"
}
}
+13
View File
@@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"module": "esnext",
"declaration": true,
"strict": true,
"alwaysStrict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"skipLibCheck": true, // Temp workaround for Duplicate identifier tsc errors,
"removeComments": false
}
}