diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5ecbc8397df..22642808fa4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -20,7 +20,7 @@ Fixes # **Release note**: ```release-note diff --git a/.prettierignore b/.prettierignore index b7a33870ddd..336d03e2551 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,4 +5,5 @@ pkg/ node_modules public/vendor/ vendor/ +data/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 677a12831dc..d21ef5232d6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,10 +34,10 @@ To setup a local development environment we recommend reading [Building Grafana ### Pull requests with new features Commits should be as small as possible, while ensuring that each commit is correct independently (i.e., each commit should compile and pass tests). -Make sure to include `closes #` or `fixes #` in the pull request description. +Make sure to include `Closes #` or `Fixes #` in the pull request description. ### Pull requests with bug fixes -Please make all changes in one commit if possible. Include `closes #12345` in bottom of the commit message. +Please make all changes in one commit if possible. Include `Closes #` in bottom of the commit message. A commit message for a bug fix should look something like this. ``` @@ -48,7 +48,7 @@ provsioners each provisioner overwrite each other. filling up dashboard_versions quite fast if using default settings. -closes #12864 +Closes #12864 ``` -If the pull request needs changes before its merged the new commits should be rebased into one commit before its merged. \ No newline at end of file +If the pull request needs changes before its merged the new commits should be rebased into one commit before its merged. diff --git a/README.md b/README.md index 550e7facfa8..8c84bfd0e87 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,6 @@ Grafana is an open source, feature rich metrics dashboard and graph editor for Graphite, Elasticsearch, OpenTSDB, Prometheus and InfluxDB. -![](https://www.grafanacon.org/2019/images/grafanacon_la_nav-logo.png) - -Join us Feb 25-26 in Los Angeles, California for GrafanaCon - a two-day event with talks focused on Grafana and the surrounding open source monitoring ecosystem. Get deep dives into Loki, the Explore workflow and all of the new features of Grafana 6, plus participate in hands on workshops to help you get the most out of your data. - -Time is running out - grab your ticket now! http://grafanacon.org - diff --git a/conf/defaults.ini b/conf/defaults.ini index df02e01235b..044d8e59a7a 100644 --- a/conf/defaults.ini +++ b/conf/defaults.ini @@ -231,6 +231,7 @@ verify_email_enabled = false # Background text for the user field on the login page login_hint = email or username +password_hint = password # Default UI theme ("dark" or "light") default_theme = dark diff --git a/conf/sample.ini b/conf/sample.ini index 57ff82181de..dc1e4fbde8e 100644 --- a/conf/sample.ini +++ b/conf/sample.ini @@ -211,6 +211,7 @@ log_queries = # Background text for the user field on the login page ;login_hint = email or username +;password_hint = password # Default UI theme ("dark" or "light") ;default_theme = dark diff --git a/docs/sources/alerting/notifications.md b/docs/sources/alerting/notifications.md index 5327039341f..8aafdb0362c 100644 --- a/docs/sources/alerting/notifications.md +++ b/docs/sources/alerting/notifications.md @@ -83,7 +83,11 @@ or a bot integration via Slack Apps. Follow Slack's guide to set up a bot integr Setting | Description ---------- | ----------- -Recipient | allows you to override the Slack recipient. +Url | Slack incoming webhook url. +Username | Set the username for the bot's message. +Recipient | Allows you to override the Slack recipient. +Icon emoji | Provide an emoji to use as the icon for the bot's message. Ex :smile: +Icon URL | Provide a url to an image to use as the icon for the bot's message. Mention | make it possible to include a mention in the Slack notification sent by Grafana. Ex @here or @channel Token | If provided, Grafana will upload the generated image via Slack's file.upload API method, not the external image destination. diff --git a/docs/sources/auth/generic-oauth.md b/docs/sources/auth/generic-oauth.md index 1a83432b8f7..510776750f3 100644 --- a/docs/sources/auth/generic-oauth.md +++ b/docs/sources/auth/generic-oauth.md @@ -217,10 +217,10 @@ Some OAuth2 providers might not support `client_id` and `client_secret` passed v results in `invalid_client` error. To allow Grafana to authenticate via these type of providers, the client identifiers must be send via POST body, which can be enabled via the following settings: - ```bash - [auth.generic_oauth] - send_client_credentials_via_post = true - ``` +```bash +[auth.generic_oauth] +send_client_credentials_via_post = true +```
diff --git a/docs/sources/installation/configuration.md b/docs/sources/installation/configuration.md index f0418ad31a6..3d1b25979c3 100644 --- a/docs/sources/installation/configuration.md +++ b/docs/sources/installation/configuration.md @@ -162,9 +162,9 @@ executed with working directory set to the installation path. ### enable_gzip -Set this option to `true` to enable HTTP compression, this can improve -transfer speed and bandwidth utilization. It is recommended that most -users set it to `true`. By default it is set to `false` for compatibility +Set this option to `true` to enable HTTP compression, this can improve +transfer speed and bandwidth utilization. It is recommended that most +users set it to `true`. By default it is set to `false` for compatibility reasons. ### cert_file @@ -342,6 +342,14 @@ options are `Admin` and `Editor`. e.g. : Viewers can edit/inspect dashboard settings in the browser. But not save the dashboard. Defaults to `false`. +### login_hint + +Text used as placeholder text on login page for login/username input. + +### password_hint + +Text used as placeholder text on login page for password input. +
## [auth] diff --git a/package.json b/package.json index a937ba6f717..d2760bbad02 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "gui:build": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts gui:build", "gui:releasePrepare": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts gui:release", "gui:publish": "cd packages/grafana-ui/dist && npm publish --access public", - "gui:release": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts gui:release -p", + "gui:release": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts gui:release -p --createVersionCommit", "cli": "ts-node --project ./scripts/cli/tsconfig.json ./scripts/cli/index.ts" }, "husky": { diff --git a/packages/grafana-ui/README.md b/packages/grafana-ui/README.md index fa482003253..935124e99ba 100644 --- a/packages/grafana-ui/README.md +++ b/packages/grafana-ui/README.md @@ -12,6 +12,36 @@ See [package source](https://github.com/grafana/grafana/tree/master/packages/gra `npm install @grafana/ui` +## 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*. The script will prepare the distribution package as well as prompt you to bump library version and publish it to the NPM registry. + +### 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 +To use `package.json` defined version 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 +5. 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. + + diff --git a/packages/grafana-ui/package.json b/packages/grafana-ui/package.json index 6c28c8abbd3..aec32fd9282 100644 --- a/packages/grafana-ui/package.json +++ b/packages/grafana-ui/package.json @@ -24,6 +24,7 @@ "jquery": "^3.2.1", "lodash": "^4.17.10", "moment": "^2.22.2", + "papaparse": "^4.6.3", "react": "^16.6.3", "react-color": "^2.17.0", "react-custom-scrollbars": "^4.2.1", @@ -46,6 +47,7 @@ "@types/jquery": "^1.10.35", "@types/lodash": "^4.14.119", "@types/node": "^10.12.18", + "@types/papaparse": "^4.5.9", "@types/react": "^16.7.6", "@types/react-custom-scrollbars": "^4.0.5", "@types/react-test-renderer": "^16.0.3", diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx index 9b5f5b98432..c67407f2e44 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorInput.tsx @@ -39,7 +39,7 @@ class ColorInput extends React.PureComponent { this.props.onChange(color); }; - handleChange = (event: React.SyntheticEvent) => { + onChange = (event: React.SyntheticEvent) => { const newColor = tinycolor(event.currentTarget.value); this.setState({ @@ -51,7 +51,7 @@ class ColorInput extends React.PureComponent { } }; - handleBlur = () => { + onBlur = () => { const newColor = tinycolor(this.state.value); if (!newColor.isValid()) { @@ -84,7 +84,7 @@ class ColorInput extends React.PureComponent { flexGrow: 1, }} > - + ); diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 183cbfed67f..5a6ddcd01b9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -15,7 +15,7 @@ export const colorPickerFactory = ( static displayName = displayName; pickerTriggerRef = createRef(); - handleColorChange = (color: string) => { + onColorChange = (color: string) => { const { onColorChange, onChange } = this.props; const changeHandler = (onColorChange || onChange) as ColorPickerChangeHandler; @@ -25,7 +25,7 @@ export const colorPickerFactory = ( render() { const popoverElement = React.createElement(popover, { ...this.props, - onChange: this.handleColorChange, + onChange: this.onColorChange, }); const { theme, children } = this.props; diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx index ce9ca5130d4..674a283ddb9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPickerPopover.tsx @@ -60,7 +60,7 @@ export class ColorPickerPopover extends React changeHandler(getColorFromHexRgbOrName(color, theme.type)); }; - handleTabChange = (tab: PickerType | keyof T) => { + onTabChange = (tab: PickerType | keyof T) => { return () => this.setState({ activePicker: tab }); }; @@ -104,7 +104,7 @@ export class ColorPickerPopover extends React <> {Object.keys(customPickers).map(key => { return ( -
+
{customPickers[key].name}
); @@ -119,10 +119,10 @@ export class ColorPickerPopover extends React return (
-
+
Colors
-
+
Custom
{this.renderCustomPickerTabs()} diff --git a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx index f4901b28bfd..3e5639172be 100644 --- a/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/NamedColorsPalette.story.tsx @@ -8,7 +8,7 @@ import { renderComponentWithTheme } from '../../utils/storybook/withTheme'; import { UseState } from '../../utils/storybook/UseState'; const BasicGreen = getColorDefinitionByName('green'); -const BasicBlue = getColorDefinitionByName('blue'); +const BasicRed = getColorDefinitionByName('red'); const LightBlue = getColorDefinitionByName('light-blue'); const NamedColorsPaletteStories = storiesOf('UI/ColorPicker/Palettes/NamedColorsPalette', module); @@ -41,7 +41,7 @@ NamedColorsPaletteStories.add('Named colors swatch - support for named colors', 'Selected color', { Green: BasicGreen.variants.dark, - Red: BasicBlue.variants.dark, + Red: BasicRed.variants.dark, 'Light blue': LightBlue.variants.dark, }, 'red' diff --git a/packages/grafana-ui/src/components/ColorPicker/warnAboutColorPickerPropsDeprecation.ts b/packages/grafana-ui/src/components/ColorPicker/warnAboutColorPickerPropsDeprecation.ts index b8919c682e2..fb4e454d2f9 100644 --- a/packages/grafana-ui/src/components/ColorPicker/warnAboutColorPickerPropsDeprecation.ts +++ b/packages/grafana-ui/src/components/ColorPicker/warnAboutColorPickerPropsDeprecation.ts @@ -1,9 +1,9 @@ -import propDeprecationWarning from '../../utils/propDeprecationWarning'; +import deprecationWarning from '../../utils/deprecationWarning'; import { ColorPickerProps } from './ColorPickerPopover'; export const warnAboutColorPickerPropsDeprecation = (componentName: string, props: ColorPickerProps) => { const { onColorChange } = props; if (onColorChange) { - propDeprecationWarning(componentName, 'onColorChange', 'onChange'); + deprecationWarning(componentName, 'onColorChange', 'onChange'); } }; diff --git a/packages/grafana-ui/src/components/DeleteButton/DeleteButton.story.tsx b/packages/grafana-ui/src/components/DeleteButton/DeleteButton.story.tsx index ccbccea9c5c..0f5e85414eb 100644 --- a/packages/grafana-ui/src/components/DeleteButton/DeleteButton.story.tsx +++ b/packages/grafana-ui/src/components/DeleteButton/DeleteButton.story.tsx @@ -1,24 +1,17 @@ -import React, { FunctionComponent } from 'react'; +import React from 'react'; import { storiesOf } from '@storybook/react'; import { DeleteButton } from './DeleteButton'; - -const CenteredStory: FunctionComponent<{}> = ({ children }) => { - return ( -
- {children} -
- ); -}; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { action } from '@storybook/addon-actions'; storiesOf('UI/DeleteButton', module) - .addDecorator(story => {story()}) + .addDecorator(withCenteredStory) .add('default', () => { - return {}} />; + return ( + { + action('Delete Confirmed')('delete!'); + }} + /> + ); }); diff --git a/packages/grafana-ui/src/components/Table/TableInputCSV.story.tsx b/packages/grafana-ui/src/components/Table/TableInputCSV.story.tsx new file mode 100644 index 00000000000..604eefca7a2 --- /dev/null +++ b/packages/grafana-ui/src/components/Table/TableInputCSV.story.tsx @@ -0,0 +1,25 @@ +import React from 'react'; + +import { storiesOf } from '@storybook/react'; +import TableInputCSV from './TableInputCSV'; +import { action } from '@storybook/addon-actions'; +import { TableData } from '../../types/data'; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; + +const TableInputStories = storiesOf('UI/Table/Input', module); + +TableInputStories.addDecorator(withCenteredStory); + +TableInputStories.add('default', () => { + return ( +
+ { + console.log('Table', table, text); + action('Table')(table, text); + }} + /> +
+ ); +}); diff --git a/packages/grafana-ui/src/components/Table/TableInputCSV.test.tsx b/packages/grafana-ui/src/components/Table/TableInputCSV.test.tsx new file mode 100644 index 00000000000..9e2dfbf80c3 --- /dev/null +++ b/packages/grafana-ui/src/components/Table/TableInputCSV.test.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +import renderer from 'react-test-renderer'; +import TableInputCSV from './TableInputCSV'; +import { TableData } from '../../types/data'; + +describe('TableInputCSV', () => { + it('renders correctly', () => { + const tree = renderer + .create( + { + // console.log('Table:', table, 'from:', text); + }} + /> + ) + .toJSON(); + //expect(tree).toMatchSnapshot(); + expect(tree).toBeDefined(); + }); +}); diff --git a/packages/grafana-ui/src/components/Table/TableInputCSV.tsx b/packages/grafana-ui/src/components/Table/TableInputCSV.tsx new file mode 100644 index 00000000000..14bc68550c3 --- /dev/null +++ b/packages/grafana-ui/src/components/Table/TableInputCSV.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import debounce from 'lodash/debounce'; +import { parseCSV, TableParseOptions, TableParseDetails } from '../../utils/processTableData'; +import { TableData } from '../../types/data'; +import { AutoSizer } from 'react-virtualized'; + +interface Props { + options?: TableParseOptions; + text: string; + onTableParsed: (table: TableData, text: string) => void; +} + +interface State { + text: string; + table: TableData; + details: TableParseDetails; +} + +/** + * Expects the container div to have size set and will fill it 100% + */ +class TableInputCSV extends React.PureComponent { + constructor(props: Props) { + super(props); + + // Shoud this happen in onComponentMounted? + const { text, options, onTableParsed } = props; + const details = {}; + const table = parseCSV(text, options, details); + this.state = { + text, + table, + details, + }; + onTableParsed(table, text); + } + + readCSV = debounce(() => { + const details = {}; + const table = parseCSV(this.state.text, this.props.options, details); + this.setState({ table, details }); + }, 150); + + componentDidUpdate(prevProps: Props, prevState: State) { + const { text } = this.state; + if (text !== prevState.text || this.props.options !== prevProps.options) { + this.readCSV(); + } + // If the props text has changed, replace our local version + if (this.props.text !== prevProps.text && this.props.text !== text) { + this.setState({ text: this.props.text }); + } + + if (this.state.table !== prevState.table) { + this.props.onTableParsed(this.state.table, this.state.text); + } + } + + onFooterClicked = (event: any) => { + console.log('Errors', this.state); + const message = this.state.details + .errors!.map(err => { + return err.message; + }) + .join('\n'); + alert('CSV Parsing Errors:\n' + message); + }; + + onTextChange = (event: any) => { + this.setState({ text: event.target.value }); + }; + + render() { + const { table, details } = this.state; + + const hasErrors = details.errors && details.errors.length > 0; + const footerClassNames = hasErrors ? 'gf-table-input-csv-err' : ''; + + return ( + + {({ height, width }) => ( +
+