mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Merge branch 'master' into courté
This commit is contained in:
commit
c4b01f6e1a
26 changed files with 723 additions and 339 deletions
278
CONTRIBUTING.md
278
CONTRIBUTING.md
|
@ -1,67 +1,117 @@
|
|||
# Contributing to Kibana
|
||||
|
||||
## How issues work
|
||||
At any given time the Kibana team at Elastic is working on dozens of features and enhancements to Kibana and other projects at Elastic. When you file an issue we'll take the time to digest it, consider solutions, and weigh its applicability to both the broad Kibana user base and our own goals for the project. Once we've completed that process we will assign the issue a priority.
|
||||
We understand that you may not have days at a time to work on Kibana. We ask that you read our contributing guidelines carefully so that you spend less time, overall, struggling to push your PR through our code review processes.
|
||||
|
||||
- **P1**: A high priority issue that affects almost all Kibana users. Bugs that would cause incorrect results, security issues and features that would vastly improve the user experience for everyone. Work arounds for P1s generally don't exist without a code change.
|
||||
At the same time, reading the contributing guidelines will give you a better idea of how to post meaningful issues that will be more easily be parsed, considered, and resolved. A big win for everyone involved! :tada:
|
||||
|
||||
## Table of Contents
|
||||
|
||||
A high level overview of our contributing guidelines.
|
||||
|
||||
- [Effective issue reporting in Kibana](#effective-issue-reporting-in-kibana)
|
||||
- [Voicing the importance of an issue](#voicing-the-importance-of-an-issue)
|
||||
- ["My issue isn't getting enough attention"](#my-issue-isnt-getting-enough-attention)
|
||||
- ["I want to help!"](#i-want-to-help)
|
||||
- [Contributing Code](#contributing-code)
|
||||
- [Setting Up Your Development Environment](#setting-up-your-development-environment)
|
||||
- [Customizing `config/kibana.dev.yml`](#customizing-configkibanadevyml)
|
||||
- [Setting Up SSL](#setting-up-ssl)
|
||||
- [Linting](#linting)
|
||||
- [Testing and Building](#testing-and-building)
|
||||
- [Debugging Unit Tests](#debugging-unit-tests)
|
||||
- [Unit Testing Plugins](#unit-testing-plugins)
|
||||
- [Running Browser Automation Tests](#running-browser-automation-tests)
|
||||
- [Browser Automation Notes](#browser-automation-notes)
|
||||
- [Building OS packages](#building-os-packages)
|
||||
- [Signing the contributor license agreement](#signing-the-contributor-license-agreement)
|
||||
- [Submitting a Pull Request](#submitting-a-pull-request)
|
||||
- [Code Reviewing](#code-reviewing)
|
||||
- [Getting to the Code Review Stage](#getting-to-the-code-review-stage)
|
||||
- [Reviewing Pull Requests](#reviewing-pull-requests)
|
||||
|
||||
Don't fret, it's not as daunting as the table of contents makes it out to be!
|
||||
|
||||
## Effective issue reporting in Kibana
|
||||
|
||||
At any given time the Kibana team at Elastic is working on dozens of features and enhancements, both for Kibana itself and for a few other projects at Elastic. When you file an issue, we'll take the time to digest it, consider solutions, and weigh its applicability to both the Kibana user base at large and the long-term vision for the project. Once we've completed that process we will assign the issue a priority.
|
||||
|
||||
- **P1**: A high-priority issue that affects virtually all Kibana users. Bugs that would cause incorrect results, security issues and features that would vastly improve the user experience for everyone. Work arounds for P1s generally don't exist without a code change.
|
||||
- **P2**: A broadly applicable, high visibility, issue that enhances the usability of Kibana for a majority users.
|
||||
- **P3**: Nice-to-have bug fixes or functionality. Work arounds for P3 items generally exist.
|
||||
- **P4**: Niche and special interest issues that may not fit our core goals. We would take a high quality pull for this if implemented in such a way that it does not meaningfully impact other functionality or existing code. Issues may also be labeled P4 if they would be better implemented in Elasticsearch.
|
||||
- **P5**: Highly niche or in opposition to our core goals. Should usually be closed. This doesn't mean we wouldn't take a pull for it, but if someone really wanted this they would be better off working on a plugin. The Kibana team will usually not work on P5 issues but may be willing to assist plugin developers on IRC.
|
||||
|
||||
#### How to express the importance of an issue
|
||||
Let's just get this out there: **Feel free to +1 an issue**. That said, a +1 isn't a vote. We keep up on highly commented issues, but comments are but one of many reasons we might, or might not, work on an issue. A solid write up of your use case is more likely to make your case than a comment that says *+10000*.
|
||||
### Voicing the importance of an issue
|
||||
|
||||
#### My issue isn't getting enough attention
|
||||
First of all, sorry about that, we want you to have a great time with Kibana! You should join us on IRC ([#kibana](https://kiwiirc.com/client/irc.freenode.net/?#kibana) on freenode) and chat about it. Github is terrible for conversations. With that out of the way, there are a number of variables that go into deciding what to work on. These include priority, impact, difficulty, applicability to use cases, and last, and importantly: What we feel like working on.
|
||||
We seriously appreciate thoughtful comments. If an issue is important to you, add a comment with a solid write up of your use case and explain why it's so important. Please avoid posting comments comprised solely of a thumbs up emoji 👍.
|
||||
|
||||
### I want to help!
|
||||
**Now we're talking**. If you have a bugfix or new feature that you would like to contribute to Kibana, please **find or open an issue about it before you start working on it.** Talk about what you would like to do. It may be that somebody is already working on it, or that there are particular issues that you should know about before implementing the change.
|
||||
Granted that you share your thoughts, we might even be able to come up with creative solutions to your specific problem. If everything you'd like to say has already been brought up but you'd still like to add a token of support, feel free to add a [👍 thumbs up reaction](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments) on the issue itself and on the comment which best summarizes your thoughts.
|
||||
|
||||
### "My issue isn't getting enough attention"
|
||||
|
||||
First of all, **sorry about that!** We want you to have a great time with Kibana.
|
||||
|
||||
Hosting meaningful discussions on GitHub can be challenging. For that reason, we'll sometimes ask that you join us on IRC _([#kibana](https://kiwiirc.com/client/irc.freenode.net/?#kibana) on freenode)_ to chat about your issues. You may also experience **faster response times** when engaging us via IRC.
|
||||
|
||||
There's hundreds of open issues and prioritizing what to work on is an important aspect of our daily jobs. We prioritize issues according to impact and difficulty, so some issues can be neglected while we work on more pressing issues.
|
||||
|
||||
Feel free to bump your issues if you think they've been neglected for a prolonged period, or just jump on IRC and let us have it!
|
||||
|
||||
### "I want to help!"
|
||||
|
||||
**Now we're talking**. If you have a bug fix or new feature that you would like to contribute to Kibana, please **find or open an issue about it before you start working on it.** Talk about what you would like to do. It may be that somebody is already working on it, or that there are particular issues that you should know about before implementing the change.
|
||||
|
||||
We enjoy working with contributors to get their code accepted. There are many approaches to fixing a problem and it is important to find the best approach before writing too much code.
|
||||
|
||||
## How to contribute code
|
||||
## Contributing Code
|
||||
|
||||
### Sign the contributor license agreement
|
||||
These guidelines will help you get your Pull Request into shape so that a code review can start as soon as possible.
|
||||
|
||||
Please make sure you have signed the [Contributor License Agreement](http://www.elastic.co/contributor-agreement/). We are not asking you to assign copyright to us, but to give us the right to distribute your code without restriction. We ask this of all contributors in order to assure our users of the origin and continuing existence of the code. You only need to sign the CLA once.
|
||||
### Setting Up Your Development Environment
|
||||
|
||||
### Development Environment Setup
|
||||
Clone the `kibana` repo and change directory into it
|
||||
|
||||
- Clone the kibana repo and move into it
|
||||
```bash
|
||||
git clone https://github.com/elastic/kibana.git kibana
|
||||
cd kibana
|
||||
```
|
||||
|
||||
```sh
|
||||
git clone https://github.com/elastic/kibana.git kibana
|
||||
cd kibana
|
||||
```
|
||||
Install the version of node.js listed in the `.node-version` file _(this can be easily automated with tools such as [nvm](https://github.com/creationix/nvm) and [avn](https://github.com/wbyoung/avn))_
|
||||
|
||||
- Install the version of node.js listed in the `.node-version` file (this is made easy with tools like [nvm](https://github.com/creationix/nvm) and [avn](https://github.com/wbyoung/avn))
|
||||
```bash
|
||||
nvm install "$(cat .node-version)"
|
||||
```
|
||||
|
||||
```sh
|
||||
nvm install "$(cat .node-version)"
|
||||
```
|
||||
Install `npm` dependencies
|
||||
|
||||
- Install dependencies
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
```sh
|
||||
npm install
|
||||
```
|
||||
Start elasticsearch.
|
||||
|
||||
- Start elasticsearch
|
||||
```bash
|
||||
npm run elasticsearch
|
||||
```
|
||||
|
||||
Note: you need to have a java binary in `PATH` or set `JAVA_HOME`.
|
||||
> You'll need to have a `java` binary in `PATH` or set `JAVA_HOME`.
|
||||
|
||||
```sh
|
||||
npm run elasticsearch
|
||||
```
|
||||
If you're just getting started with `elasticsearch`, you could use the following command to populate your instance with a few fake logs to hit the ground running.
|
||||
|
||||
- Start the development server. _On Windows, you'll need you use Git Bash, Cygwin, or a similar shell that exposes the `sh` command. And to successfully build you'll need Cygwin optional packages zip, tar, and shasum._
|
||||
```bash
|
||||
npm run makelogs
|
||||
```
|
||||
|
||||
```sh
|
||||
> Make sure to execute `npm run makelogs` *after* elasticsearch is up and running!
|
||||
|
||||
Start the development server.
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
#### `config/kibana.dev.yml`
|
||||
> On Windows, you'll need you use Git Bash, Cygwin, or a similar shell that exposes the `sh` command. And to successfully build you'll need Cygwin optional packages zip, tar, and shasum.
|
||||
|
||||
#### Customizing `config/kibana.dev.yml`
|
||||
|
||||
The `config/kibana.yml` file stores user configuration directives. Since this file is checked into source control, however, developer preferences can't be saved without the risk of accidentally committing the modified version. To make customizing configuration easier during development, the Kibana CLI will look for a `config/kibana.dev.yml` file if run with the `--dev` flag. This file behaves just like the non-dev version and accepts any of the [standard settings](https://www.elastic.co/guide/en/kibana/master/kibana-server-properties.html).
|
||||
|
||||
|
@ -74,91 +124,125 @@ optimize:
|
|||
lazyPrebuild: false
|
||||
```
|
||||
|
||||
#### SSL
|
||||
#### Setting Up SSL
|
||||
|
||||
When Kibana runs in development mode it will automatically use bundled SSL certificates. These certificates won't be trusted by your OS by default which will likely cause your browser to complain about the cert.
|
||||
When Kibana runs in development mode it will automatically use bundled SSL certificates. These certificates won't be trusted by your OS by default which will likely cause your browser to complain about the certificate.
|
||||
|
||||
You can fix this issue in one of the following ways:
|
||||
If you run into this issue, visit the development server and configure your OS to trust the certificate.
|
||||
|
||||
- Supply your own cert using the `config/kibana.dev.yml` file.
|
||||
- Configure your OS to trust the cert:
|
||||
- OSX: https://www.accuweaver.com/2014/09/19/make-chrome-accept-a-self-signed-certificate-on-osx/
|
||||
- Window: http://stackoverflow.com/a/1412118
|
||||
- Linux: http://unix.stackexchange.com/a/90607
|
||||
- Click through the warning and accept future warnings.
|
||||
- Disable SSL with the `--no-ssl` flag:
|
||||
- `npm start -- --no-ssl`
|
||||
- OSX: https://www.accuweaver.com/2014/09/19/make-chrome-accept-a-self-signed-certificate-on-osx/
|
||||
- Windows: http://stackoverflow.com/a/1412118
|
||||
- Linux: http://unix.stackexchange.com/a/90607
|
||||
|
||||
#### Linting
|
||||
There are a handful of other options, although we enthusiastically recommend that you trust our development certificate.
|
||||
|
||||
- Click through the warning and accept future warnings
|
||||
- Supply your own certificate using the `config/kibana.dev.yml` file
|
||||
- Disable SSL in Kibana by starting the application with `npm start -- --no-ssl`
|
||||
|
||||
### Linting
|
||||
|
||||
A note about linting: We use [eslint](http://eslint.org) to check that the [styleguide](STYLEGUIDE.md) is being followed. It runs in a pre-commit hook and as a part of the tests, but most contributors integrate it with their code editors for real-time feedback.
|
||||
|
||||
Here are some hints for getting eslint setup in your favorite editor:
|
||||
|
||||
| Editor | Plugin |
|
||||
| --- | --- | --- |
|
||||
| Sublime | [SublimeLinter-eslint](https://github.com/roadhump/SublimeLinter-eslint#installation) |
|
||||
| Atom | [linter-eslint](https://github.com/AtomLinter/linter-eslint#installation) |
|
||||
| IntelliJ | Settings » Languages & Frameworks » JavaScript » Code Quality Tools » ESLint |
|
||||
| vi | [scrooloose/syntastic](https://github.com/scrooloose/syntastic) |
|
||||
Editor | Plugin
|
||||
-----------|-------------------------------------------------------------------------------
|
||||
Sublime | [SublimeLinter-eslint](https://github.com/roadhump/SublimeLinter-eslint#installation)
|
||||
Atom | [linter-eslint](https://github.com/AtomLinter/linter-eslint#installation)
|
||||
IntelliJ | Settings » Languages & Frameworks » JavaScript » Code Quality Tools » ESLint
|
||||
`vi` | [scrooloose/syntastic](https://github.com/scrooloose/syntastic)
|
||||
|
||||
Another tool we use for enforcing consistent coding style is Editorconfig, which can be set up by installing a plugin in your editor that dynamically updates its configuration. Take a look at the [Editorconfig](http://editorconfig.org/#download) site to find a plugin for your editor, and browse our [`.editorconfig`](https://github.com/elastic/kibana/blob/master/.editorconfig) file to see what config rules we set up.
|
||||
Another tool we use for enforcing consistent coding style is EditorConfig, which can be set up by installing a plugin in your editor that dynamically updates its configuration. Take a look at the [EditorConfig](http://editorconfig.org/#download) site to find a plugin for your editor, and browse our [`.editorconfig`](https://github.com/elastic/kibana/blob/master/.editorconfig) file to see what config rules we set up.
|
||||
|
||||
### Testing and building
|
||||
### Testing and Building
|
||||
|
||||
To ensure that your changes will not break other functionality, please run the test suite and build process before submitting your pull request.
|
||||
To ensure that your changes will not break other functionality, please run the test suite and build process before submitting your Pull Request.
|
||||
|
||||
Before running the tests you will need to install the projects dependencies as described above.
|
||||
|
||||
Once that is complete just run:
|
||||
Once that's done, just run:
|
||||
|
||||
```
|
||||
sh
|
||||
```bash
|
||||
npm run test && npm run build -- --skip-os-packages
|
||||
```
|
||||
|
||||
#### Debugging unit tests
|
||||
### Debugging Unit Tests
|
||||
|
||||
The standard `npm run test` task runs several sub tasks and can take several minutes to complete, making debugging failures pretty painful. In order to ease the pain specialized tasks provide alternate methods for running the tests.
|
||||
|
||||
To execute both server and browser tests, but skip linting, use `npm run test:quick`.
|
||||
|
||||
`npm run test:quick`
|
||||
Runs both server and browser tests, but skips linting
|
||||
```bash
|
||||
npm run test:quick
|
||||
```
|
||||
|
||||
`npm run test:server`
|
||||
Run only the server tests
|
||||
Use `npm run test:server` when you want to run only the server tests.
|
||||
|
||||
`npm run test:browser`
|
||||
Run only the browser tests. Coverage reports are available for browser tests by running `npm run test:coverage`. You can find the results under the `coverage/` directory that will be created upon completion.
|
||||
```bash
|
||||
npm run test:server
|
||||
```
|
||||
|
||||
When you'd like to execute individual server-side test files, you can use the command below. Note that this command takes care of configuring Mocha with Babel compilation for you, and you'll be better off avoiding a globally installed `mocha` package. This command is great for development and for quickly identifying bugs.
|
||||
|
||||
```bash
|
||||
npm run mocha <file>
|
||||
```
|
||||
|
||||
You could also add the `:debug` target so that `node` is run using the `--debug-brk` flag. You'll need to connect a remote debugger such as [`node-inspector`](https://github.com/node-inspector/node-inspector) to proceed in this mode.
|
||||
|
||||
```bash
|
||||
npm run mocha:debug <file>
|
||||
```
|
||||
|
||||
With `npm run test:browser`, you can run only the browser tests. Coverage reports are available for browser tests by running `npm run test:coverage`. You can find the results under the `coverage/` directory that will be created upon completion.
|
||||
|
||||
```bash
|
||||
npm run test:browser
|
||||
```
|
||||
|
||||
Using `npm run test:dev` initializes an environment for debugging the browser tests. Includes an dedicated instance of the kibana server for building the test bundle, and a karma server. When running this task the build is optimized for the first time and then a karma-owned instance of the browser is opened. Click the "debug" button to open a new tab that executes the unit tests.
|
||||
|
||||
```bash
|
||||
npm run test:dev
|
||||
```
|
||||
|
||||
`npm run test:dev`
|
||||
Initializes an environment for debugging the browser tests. Includes an dedicated instance of the kibana server for building the test bundle, and a karma server. When running this task the build is optimized for the first time and then a karma-owned instance of the browser is opened. Click the "debug" button to open a new tab that executes the unit tests.
|
||||

|
||||
|
||||
`npm run mocha [test file or dir]` or `npm run mocha:debug [test file or dir]`
|
||||
Run a one off test with the local project version of mocha, babel compilation, and optional debugging. Great
|
||||
for development and fixing individual tests.
|
||||
### Unit Testing Plugins
|
||||
|
||||
#### Unit testing plugins
|
||||
This should work super if you're using the [Kibana plugin generator](https://github.com/elastic/generator-kibana-plugin). If you're not using the generator, well, you're on your own. We suggest you look at how the generator works.
|
||||
|
||||
`npm run test:dev -- --kbnServer.testsBundle.pluginId=some_special_plugin --kbnServer.plugin-path=../some_special_plugin`
|
||||
Run the tests for just your particular plugin. Assuming you plugin lives outside of the `plugins directory`, which it should.
|
||||
To run the tests for just your particular plugin, assuming you plugin lives outside of the `plugins directory`, use the following command.
|
||||
|
||||
#### Running browser automation tests:
|
||||
```bash
|
||||
npm run test:dev -- --kbnServer.testsBundle.pluginId=some_special_plugin --kbnServer.plugin-path=../some_special_plugin
|
||||
```
|
||||
|
||||
### Running Browser Automation Tests
|
||||
|
||||
The following will start Kibana, Elasticsearch and the chromedriver for you. To run the functional UI tests use the following commands
|
||||
|
||||
`npm run test:ui`
|
||||
Run the functional UI tests one time and exit. This is used by the CI systems and is great for quickly checking that things pass. It is essentially a combination of the next two tasks. This supports options `--grep=foo` for only running tests that match a regular expression, and `--appSuites=management` for running tests for a specific application.
|
||||
If you want to run the functional UI tests one time and exit, use the following command. This is used by the CI systems and is great for quickly checking that things pass. It is essentially a combination of the next two tasks. This supports options `--grep=foo` for only running tests that match a regular expression, and `--appSuites=management` for running tests for a specific application.
|
||||
|
||||
`npm run test:ui:server`
|
||||
Start the server required for the `test:ui:runner` tasks. Once the server is started `test:ui:runner` can be run multiple times without waiting for the server to start.
|
||||
```bash
|
||||
npm run test:ui
|
||||
```
|
||||
|
||||
`npm run test:ui:runner`
|
||||
Execute the front-end browser tests. This requires the server started by the `test:ui:server` task.
|
||||
|
||||
#### Browser automation notes:
|
||||
In order to start the server required for the `test:ui:runner` tasks, use the following command. Once the server is started `test:ui:runner` can be run multiple times without waiting for the server to start.
|
||||
|
||||
```bash
|
||||
npm run test:ui:server
|
||||
```
|
||||
|
||||
To execute the front-end browser tests, enter the following. This requires the server started by the `test:ui:server` task.
|
||||
|
||||
```bash
|
||||
npm run test:ui:runner
|
||||
```
|
||||
|
||||
#### Browser Automation Notes
|
||||
|
||||
- Using Page Objects pattern (https://theintern.github.io/intern/#writing-functional-test)
|
||||
- At least the initial tests for the Settings, Discover, and Visualize tabs all depend on a very specific set of logstash-type data (generated with makelogs). Since that is a static set of data, all the Discover and Visualize tests use a specific Absolute time range. This guarantees the same results each run.
|
||||
|
@ -167,10 +251,11 @@ Execute the front-end browser tests. This requires the server started by the `te
|
|||
- https://theintern.github.io/
|
||||
- https://theintern.github.io/leadfoot/module-leadfoot_Element.html
|
||||
|
||||
#### Building OS packages
|
||||
### Building OS packages
|
||||
|
||||
Packages are built using fpm, pleaserun, dpkg, and rpm. fpm and pleaserun can be installed using gem. Package building has only been tested on Linux and is not supported on any other platform.
|
||||
```sh
|
||||
|
||||
```bash
|
||||
apt-get install ruby-dev rpm
|
||||
gem install fpm -v 1.5.0
|
||||
gem install pleaserun -v 0.0.24
|
||||
|
@ -178,29 +263,36 @@ npm run build -- --skip-archives
|
|||
```
|
||||
|
||||
To specify a package to build you can add `rpm` or `deb` as an argument.
|
||||
```sh
|
||||
|
||||
```bash
|
||||
npm run build -- --rpm
|
||||
```
|
||||
|
||||
Distributable packages can be found in `target/` after the build completes.
|
||||
|
||||
## Submitting a pull request
|
||||
## Signing the contributor license agreement
|
||||
|
||||
Push your local changes to your forked copy of the repository and submit a pull request. In the pull request, describe what your changes do and mention the number of the issue where discussion has taken place, eg “Closes #123″.
|
||||
Please make sure you have signed the [Contributor License Agreement](http://www.elastic.co/contributor-agreement/). We are not asking you to assign copyright to us, but to give us the right to distribute your code without restriction. We ask this of all contributors in order to assure our users of the origin and continuing existence of the code. You only need to sign the CLA once.
|
||||
|
||||
## Submitting a Pull Request
|
||||
|
||||
Push your local changes to your forked copy of the repository and submit a Pull Request. In the Pull Request, describe what your changes do and mention the number of the issue where discussion has taken place, eg “Closes #123″.
|
||||
|
||||
Always submit your pull against `master` unless the bug is only present in an older version. If the bug effects both `master` and another branch say so in your pull.
|
||||
|
||||
Then sit back and wait. There will probably be discussion about the pull request and, if any changes are needed, we'll work with you to get your pull request merged into Kibana.
|
||||
Then sit back and wait. There will probably be discussion about the Pull Request and, if any changes are needed, we'll work with you to get your Pull Request merged into Kibana.
|
||||
|
||||
### The road to review
|
||||
## Code Reviewing
|
||||
|
||||
After a pull is submitted, it needs to get to review. If you have commit permission on the Kibana repo you will probably perform these steps while submitting your pull request. If not, a member of the elastic organization will do them for you, though you can help by suggesting a reviewer for your changes if you've interacted with someone while working on the issue.
|
||||
After a pull is submitted, it needs to get to review. If you have commit permission on the Kibana repo you will probably perform these steps while submitting your Pull Request. If not, a member of the Elastic organization will do them for you, though you can help by suggesting a reviewer for your changes if you've interacted with someone while working on the issue.
|
||||
|
||||
### Getting to the Code Review Stage
|
||||
|
||||
1. Assign the `review` label. This signals to the team that someone needs to give this attention.
|
||||
1. Do **not** assign a version label. Someone from Elastic staff will assign a version label, if necessary, when your pull request is ready to be merged.
|
||||
1. Do **not** assign a version label. Someone from Elastic staff will assign a version label, if necessary, when your Pull Request is ready to be merged.
|
||||
1. Find someone to review your pull. Don't just pick any yahoo, pick the right person. The right person might be the original reporter of the issue, but it might also be the person most familiar with the code you've changed. If neither of those things apply, or your change is small in scope, try to find someone on the Kibana team without a ton of existing reviews on their plate. As a rule, most pulls will require 2 reviewers, but the first reviewer will pick the 2nd.
|
||||
|
||||
### Review engaged
|
||||
### Reviewing Pull Requests
|
||||
|
||||
So, you've been assigned a pull to review. What's that look like?
|
||||
|
||||
|
@ -216,4 +308,6 @@ Remember, someone is blocked by a pull awaiting review, make it count. Be thorou
|
|||
1. **Suggest improvements** If there are changes needed, be explicit, comment on the lines in the code that you'd like changed. You might consider suggesting fixes. If you can't identify the problem, animated screenshots can help the review understand what's going on.
|
||||
1. **Hand it back** If you found issues, re-assign the submitter to the pull to address them. Repeat until mergable.
|
||||
1. **Hand it off** If you're the first reviewer and everything looks good but the changes are more than a few lines, hand the pull to someone else to take a second look. Again, try to find the right person to assign it to.
|
||||
1. **Merge the code** When everything looks good, put in a `LGTM` (looks good to me) or `Courté` comment. Merge into the target branch. Check the labels on the pull to see if backporting is required, and perform the backport if so.
|
||||
1. **Merge the code** When everything looks good, put in a `LGTM` (looks good to me) or `Courté` (looks sophisticated to me) comment. Merge into the target branch. Check the labels on the pull to see if backporting is required, and perform the backport if so.
|
||||
|
||||
Thank you so much for reading our guidelines! :tada:
|
||||
|
|
|
@ -17,7 +17,7 @@ dashboards. Kibana creates a new index if the index doesn’t already exist.
|
|||
[[tilemap-settings]]`tilemap.url:`:: *Default: `"https://tiles.elastic.co/v1/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana"`* The URL to the tile
|
||||
service that Kibana uses to display map tiles in tilemap visualizations.
|
||||
`tilemap.options.minZoom:`:: *Default: 1* The minimum zoom level.
|
||||
`tilemap.options.maxZoom:`:: *Default: 7* The maximum zoom level.
|
||||
`tilemap.options.maxZoom:`:: *Default: 9* The maximum zoom level.
|
||||
`tilemap.options.attribution:`:: *Default: `"© [Elastic Tile Service](https://www.elastic.co/elastic-tile-service)"`* The map attribution string.
|
||||
`tilemap.options.subdomains:`:: An array of subdomains used by the tile service.
|
||||
Specify the position of the subdomain the URL with the token `{s}`.
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
"angular-bootstrap-colorpicker": "3.0.19",
|
||||
"angular-elastic": "2.5.0",
|
||||
"angular-route": "1.4.7",
|
||||
"angular-sanitize": "1.5.7",
|
||||
"ansicolors": "0.3.2",
|
||||
"autoprefixer": "5.1.1",
|
||||
"autoprefixer-loader": "2.0.0",
|
||||
|
@ -127,7 +128,7 @@
|
|||
"less": "2.5.1",
|
||||
"less-loader": "2.2.0",
|
||||
"lodash": "3.10.1",
|
||||
"marked": "0.3.3",
|
||||
"marked": "0.3.5",
|
||||
"minimatch": "2.0.10",
|
||||
"mkdirp": "0.5.1",
|
||||
"moment": "2.13.0",
|
||||
|
@ -178,7 +179,6 @@
|
|||
"grunt-simple-mocha": "0.4.0",
|
||||
"gruntify-eslint": "1.0.1",
|
||||
"handlebars": "4.0.5",
|
||||
"html-entities": "1.1.3",
|
||||
"husky": "0.8.1",
|
||||
"image-diff": "1.6.0",
|
||||
"intern": "3.2.3",
|
||||
|
@ -193,7 +193,6 @@
|
|||
"license-checker": "3.1.0",
|
||||
"load-grunt-config": "0.19.1",
|
||||
"makelogs": "3.0.2",
|
||||
"marked-text-renderer": "0.1.0",
|
||||
"mocha": "2.3.0",
|
||||
"ncp": "2.0.0",
|
||||
"nock": "2.10.0",
|
||||
|
|
|
@ -5,6 +5,7 @@ import { safeDump } from 'js-yaml';
|
|||
import es from 'event-stream';
|
||||
import readYamlConfig from '../read_yaml_config';
|
||||
import expect from 'expect.js';
|
||||
|
||||
const testConfigFile = follow(`fixtures/reload_logging_config/kibana.test.yml`);
|
||||
const cli = follow(`../../../../bin/kibana`);
|
||||
|
||||
|
@ -22,69 +23,73 @@ function setLoggingJson(enabled) {
|
|||
}
|
||||
|
||||
describe(`Server logging configuration`, function () {
|
||||
it(`should be reloadable via SIGHUP process signaling`, function (done) {
|
||||
this.timeout(60000);
|
||||
|
||||
let asserted = false;
|
||||
let json = Infinity;
|
||||
const conf = setLoggingJson(true);
|
||||
const child = spawn(cli, [`--config`, testConfigFile]);
|
||||
const isWindows = /^win/.test(process.platform);
|
||||
if (isWindows) {
|
||||
it('SIGHUP is not a feature of Windows.');
|
||||
} else {
|
||||
it(`should be reloadable via SIGHUP process signaling`, function (done) {
|
||||
this.timeout(60000);
|
||||
|
||||
child.on('error', err => {
|
||||
done(new Error(`error in child process while attempting to reload config.
|
||||
${err.stack || err.message || err}`));
|
||||
});
|
||||
let asserted = false;
|
||||
let json = Infinity;
|
||||
const conf = setLoggingJson(true);
|
||||
const child = spawn(cli, [`--config`, testConfigFile]);
|
||||
|
||||
child.on('exit', code => {
|
||||
expect(asserted).to.eql(true);
|
||||
expect(code === null || code === 0).to.eql(true);
|
||||
done();
|
||||
});
|
||||
child.on('error', err => {
|
||||
done(new Error(`error in child process while attempting to reload config. ${err.stack || err.message || err}`));
|
||||
});
|
||||
|
||||
child.stdout
|
||||
.pipe(es.split())
|
||||
.pipe(es.mapSync(function (line) {
|
||||
if (!line) {
|
||||
return line; // ignore empty lines
|
||||
child.on('exit', code => {
|
||||
expect(asserted).to.eql(true);
|
||||
expect(code === null || code === 0).to.eql(true);
|
||||
done();
|
||||
});
|
||||
|
||||
child.stdout
|
||||
.pipe(es.split())
|
||||
.pipe(es.mapSync(function (line) {
|
||||
if (!line) {
|
||||
return line; // ignore empty lines
|
||||
}
|
||||
if (json--) {
|
||||
expect(parseJsonLogLine).withArgs(line).to.not.throwError();
|
||||
} else {
|
||||
expectPlainTextLogLine(line);
|
||||
}
|
||||
}));
|
||||
|
||||
function parseJsonLogLine(line) {
|
||||
try {
|
||||
const data = JSON.parse(line);
|
||||
const listening = data.tags.indexOf(`listening`) !== -1;
|
||||
if (listening) {
|
||||
switchToPlainTextLog();
|
||||
}
|
||||
} catch (err) {
|
||||
expect(`Error parsing log line as JSON\n ${err.stack || err.message || err}`).to.eql(true);
|
||||
}
|
||||
if (json--) {
|
||||
expect(parseJsonLogLine).withArgs(line).to.not.throwError();
|
||||
} else {
|
||||
expectPlainTextLogLine(line);
|
||||
}
|
||||
}));
|
||||
|
||||
function parseJsonLogLine(line) {
|
||||
try {
|
||||
const data = JSON.parse(line);
|
||||
const listening = data.tags.indexOf(`listening`) !== -1;
|
||||
if (listening) {
|
||||
switchToPlainTextLog();
|
||||
}
|
||||
} catch (err) {
|
||||
expect(`Error parsing log line as JSON\n
|
||||
${err.stack || err.message || err}`).to.eql(true);
|
||||
}
|
||||
}
|
||||
|
||||
function switchToPlainTextLog() {
|
||||
json = 3; // ignore both "reloading" messages + ui settings status message
|
||||
setLoggingJson(false);
|
||||
child.kill(`SIGHUP`); // reload logging config
|
||||
}
|
||||
function switchToPlainTextLog() {
|
||||
json = 3; // ignore both "reloading" messages + ui settings status message
|
||||
setLoggingJson(false);
|
||||
child.kill(`SIGHUP`); // reload logging config
|
||||
}
|
||||
|
||||
function expectPlainTextLogLine(line) {
|
||||
// assert
|
||||
const tags = `[\u001b[32minfo\u001b[39m][\u001b[36mconfig\u001b[39m]`;
|
||||
const status = `Reloaded logging configuration due to SIGHUP.`;
|
||||
const expected = `${tags} ${status}`;
|
||||
const actual = line.slice(-expected.length);
|
||||
expect(actual).to.eql(expected);
|
||||
function expectPlainTextLogLine(line) {
|
||||
// assert
|
||||
const tags = `[\u001b[32minfo\u001b[39m][\u001b[36mconfig\u001b[39m]`;
|
||||
const status = `Reloaded logging configuration due to SIGHUP.`;
|
||||
const expected = `${tags} ${status}`;
|
||||
const actual = line.slice(-expected.length);
|
||||
expect(actual).to.eql(expected);
|
||||
|
||||
// cleanup
|
||||
asserted = true;
|
||||
setLoggingJson(true);
|
||||
child.kill();
|
||||
}
|
||||
});
|
||||
// cleanup
|
||||
asserted = true;
|
||||
setLoggingJson(true);
|
||||
child.kill();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -81,7 +81,7 @@ module.exports = function (api) {
|
|||
'properties': {
|
||||
'*': {
|
||||
type: {
|
||||
__one_of: ['text', 'keyword', 'float', 'double', 'byte', 'short', 'integer', 'long', 'date', 'boolean',
|
||||
__one_of: ['text', 'keyword', 'float', 'half_float', 'scaled_float', 'double', 'byte', 'short', 'integer', 'long', 'date', 'boolean',
|
||||
'binary', 'object', 'nested', "geo_point", "geo_shape"
|
||||
]
|
||||
},
|
||||
|
@ -111,6 +111,7 @@ module.exports = function (api) {
|
|||
// numeric
|
||||
precision_step: 4,
|
||||
ignore_malformed: BOOLEAN,
|
||||
scaling_factor: 100,
|
||||
|
||||
// geo_point
|
||||
lat_lon: {
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
<div class="sidebar-container">
|
||||
<form class="sidebar-list"
|
||||
ng-submit="visualizeEditor.$invalid ? dontApply() : stageEditableVis()"
|
||||
name="visualizeEditor"
|
||||
novalidate><!-- see http://goo.gl/9kgz5w -->
|
||||
<form
|
||||
class="sidebar-list"
|
||||
ng-submit="visualizeEditor.$invalid ? dontApply() : stageEditableVis()"
|
||||
name="visualizeEditor"
|
||||
novalidate
|
||||
><!-- see http://goo.gl/9kgz5w -->
|
||||
|
||||
<div css-truncate title="{{indexPattern.id}}" ng-if="vis.type.requiresSearch" class="index-pattern">
|
||||
{{ indexPattern.id }}
|
||||
|
@ -14,23 +16,44 @@
|
|||
<!-- tabs -->
|
||||
<ul class="nav navbar-nav">
|
||||
<li ng-class="{active: sidebar.section == 'data'}" ng-show="sidebar.showData">
|
||||
<a class="navbar-link active" ng-click="sidebar.section='data'">Data</a>
|
||||
<a
|
||||
class="vis-editor-subnav-link"
|
||||
ng-class="{'is-vis-editor-sub-nav-link-selected': sidebar.section == 'data'}"
|
||||
ng-click="sidebar.section='data'"
|
||||
>
|
||||
Data
|
||||
</a>
|
||||
</li>
|
||||
<li ng-class="{active: sidebar.section == 'options'}">
|
||||
<a class="navbar-link" ng-click="sidebar.section='options'">Options</a>
|
||||
<a
|
||||
class="vis-editor-subnav-link"
|
||||
ng-class="{'is-vis-editor-sub-nav-link-selected': sidebar.section == 'options'}"
|
||||
ng-click="sidebar.section='options'"
|
||||
>
|
||||
Options
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!-- controls -->
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li ng-if="visualizeEditor.softErrorCount() > 0"
|
||||
<li
|
||||
ng-if="visualizeEditor.softErrorCount() > 0"
|
||||
disabled
|
||||
tooltip="{{ visualizeEditor.describeErrors() }}" tooltip-placement="bottom" tooltip-popup-delay="400" tooltip-append-to-body="1">
|
||||
<a class="danger navbar-link">
|
||||
tooltip="{{ visualizeEditor.describeErrors() }}"
|
||||
tooltip-placement="bottom"
|
||||
tooltip-popup-delay="400"
|
||||
tooltip-append-to-body="1"
|
||||
>
|
||||
<a class="vis-editor-subnav-link vis-editor-subnav-link--danger">
|
||||
<i class="fa fa-warning"></i>
|
||||
</a>
|
||||
</li>
|
||||
<li tooltip="Apply changes" tooltip-placement="bottom" tooltip-popup-delay="400" tooltip-append-to-body="1">
|
||||
<li
|
||||
tooltip="Apply changes"
|
||||
tooltip-placement="bottom"
|
||||
tooltip-popup-delay="400" tooltip-append-to-body="1"
|
||||
>
|
||||
<button class="btn-success navbar-btn-link"
|
||||
type="submit"
|
||||
ng-disabled="!vis.dirty">
|
||||
|
@ -38,7 +61,12 @@
|
|||
<i class="fa fa-play"></i>
|
||||
</button>
|
||||
</li>
|
||||
<li tooltip="Discard changes" tooltip-placement="bottom" tooltip-popup-delay="400" tooltip-append-to-body="1">
|
||||
<li
|
||||
tooltip="Discard changes"
|
||||
tooltip-placement="bottom"
|
||||
tooltip-popup-delay="400"
|
||||
tooltip-append-to-body="1"
|
||||
>
|
||||
<button class="btn-default navbar-btn-link"
|
||||
ng-disabled="!vis.dirty"
|
||||
ng-click="resetEditableVis()">
|
||||
|
|
|
@ -7,36 +7,6 @@
|
|||
.vis-editor {
|
||||
.flex-parent();
|
||||
|
||||
// For the vis-editor sidebar nav
|
||||
.navbar-default .navbar-nav {
|
||||
&> .active > a {
|
||||
border-bottom: 2px solid @kibanaGray2;
|
||||
color: @kibanaGray1;
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
> li {
|
||||
> a {
|
||||
padding: 4px 0;
|
||||
margin: 0 10px;
|
||||
color: @kibanaGray2;
|
||||
}
|
||||
> a:hover {
|
||||
border-bottom: 2px solid @kibanaGray2;
|
||||
}
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: @vis-editor-navbar-error-state-color;
|
||||
background-color: @vis-editor-navbar-error-state-bg;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-xs {
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
@ -382,6 +352,40 @@ vis-editor-vis-options > * {
|
|||
.flex-parent();
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. TODO: Override bootstrap styles. Remove !important once we're rid of bootstrap.
|
||||
*/
|
||||
.vis-editor-subnav-link {
|
||||
padding: 4px 10px 5px 10px !important; /* 1 */
|
||||
color: @kibanaGray2 !important; /* 1 */
|
||||
|
||||
&.is-vis-editor-sub-nav-link-selected {
|
||||
border-bottom: 2px solid @kibanaGray2;
|
||||
color: @kibanaGray1;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
&:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. TODO: Override bootstrap styles. Remove !important once we're rid of bootstrap.
|
||||
*/
|
||||
.vis-editor-subnav-link--danger {
|
||||
color: @vis-editor-navbar-error-state-color !important; /* 1 */
|
||||
background-color: @vis-editor-navbar-error-state-bg;
|
||||
|
||||
&:hover {
|
||||
background-color: darken(@vis-editor-navbar-error-state-bg, 12%) !important; /* 1 */
|
||||
}
|
||||
}
|
||||
|
||||
form.vis-share {
|
||||
div.form-control {
|
||||
height: inherit;
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import marked from 'marked';
|
||||
import uiModules from 'ui/modules';
|
||||
import 'angular-sanitize';
|
||||
|
||||
marked.setOptions({
|
||||
gfm: true, // Github-flavored markdown
|
||||
sanitize: true // Sanitize HTML tags
|
||||
});
|
||||
|
||||
const module = uiModules.get('kibana/markdown_vis', ['kibana']);
|
||||
module.controller('KbnMarkdownVisController', function ($scope, $sce) {
|
||||
|
||||
const module = uiModules.get('kibana/markdown_vis', ['kibana', 'ngSanitize']);
|
||||
module.controller('KbnMarkdownVisController', function ($scope) {
|
||||
$scope.$watch('vis.params.markdown', function (html) {
|
||||
if (!html) return;
|
||||
$scope.html = $sce.trustAsHtml(marked(html));
|
||||
$scope.html = marked(html);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -32,7 +32,7 @@ window.__KBN__ = {
|
|||
url: 'https://tiles.elastic.co/v1/default/{z}/{x}/{y}.png?my_app_name=kibana&my_app_version=1.2.3&elastic_tile_service_tos=agree',
|
||||
options: {
|
||||
minZoom: 1,
|
||||
maxZoom: 7,
|
||||
maxZoom: 9,
|
||||
attribution: '© [Elastic Tile Service](https://www.elastic.co/elastic_tile_service)'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ module.exports = () => Joi.object({
|
|||
options: Joi.object({
|
||||
attribution: Joi.string().default('© [Elastic Tile Service](https://www.elastic.co/elastic-tile-service)'),
|
||||
minZoom: Joi.number().min(1, 'Must not be less than 1').default(1),
|
||||
maxZoom: Joi.number().default(7),
|
||||
maxZoom: Joi.number().default(9),
|
||||
tileSize: Joi.number(),
|
||||
subdomains: Joi.array().items(Joi.string()).single(),
|
||||
errorTileUrl: Joi.string().uri(),
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
</app-switcher>
|
||||
|
||||
<div class="bottom-apps">
|
||||
<div class="chrome-actions app-links" kbn-chrome-append-nav-controls></div>
|
||||
<div class="chrome-actions" kbn-chrome-append-nav-controls></div>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
|
|
|
@ -1,26 +1,11 @@
|
|||
<div class="app-links">
|
||||
<div
|
||||
class="app-link"
|
||||
ng-repeat="link in switcher.shownNavLinks"
|
||||
ng-class="{ active: link.active, 'is-app-switcher-app-link-disabled': !switcher.isNavLinkEnabled(link) }"
|
||||
tooltip="{{link.tooltip}}"
|
||||
tooltip-placement="right"
|
||||
tooltip-popup-delay="400"
|
||||
tooltip-append-to-body="1"
|
||||
>
|
||||
|
||||
<a
|
||||
ng-click="switcher.ensureNavigation($event, link)"
|
||||
ng-href="{{ link.active ? link.url : (link.lastSubUrl || link.url) }}"
|
||||
data-test-subj="appLink"
|
||||
ng-class="{ 'app-link__anchor': !switcher.isNavLinkEnabled(link) }"
|
||||
title="{{ link.title }}"
|
||||
>
|
||||
|
||||
<div ng-if="link.icon" class="app-icon"><img kbn-src="{{'/' + link.icon}}"></div>
|
||||
<div ng-if="!link.icon" class="app-icon-missing">{{ link.title[0] }}</div>
|
||||
|
||||
<div class="app-title">{{ link.title }}</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<app-switcher-link
|
||||
ng-repeat="link in switcher.shownNavLinks"
|
||||
app-switcher-link-is-active="link.active"
|
||||
app-switcher-link-is-disabled="!switcher.isNavLinkEnabled(link)"
|
||||
app-switcher-link-tooltip="link.tooltip"
|
||||
app-switcher-link-on-click="switcher.ensureNavigation($event, link)"
|
||||
app-switcher-link-href="link.active ? link.url : (link.lastSubUrl || link.url)"
|
||||
app-switcher-link-icon="link.icon"
|
||||
app-switcher-link-title="link.title"
|
||||
></app-switcher-link>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import DomLocationProvider from 'ui/dom_location';
|
||||
import { parse } from 'url';
|
||||
import { bindKey } from 'lodash';
|
||||
import '../app_switcher/app_switcher.less';
|
||||
import './app_switcher.less';
|
||||
import uiModules from 'ui/modules';
|
||||
import appSwitcherTemplate from './app_switcher.html';
|
||||
|
||||
|
|
|
@ -70,81 +70,3 @@ body { overflow-x: hidden; }
|
|||
.flex-parent(@shrink: 0);
|
||||
box-shadow: -4px 0px 3px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.app-links {
|
||||
text-align: justify;
|
||||
|
||||
.app-link {
|
||||
width: @as-open-width;
|
||||
height: @app-icon-height;
|
||||
line-height: @app-line-height;
|
||||
|
||||
> a {
|
||||
display: block;
|
||||
height: 100%;
|
||||
color: #ebf7fa;
|
||||
}
|
||||
|
||||
&.is-app-switcher-app-link-disabled {
|
||||
opacity: 0.5;
|
||||
|
||||
.app-link__anchor {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
float: left;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
font-size: 1.7em;
|
||||
display: inline-block;
|
||||
height: @app-icon-height;
|
||||
width: @as-closed-width;
|
||||
|
||||
> img {
|
||||
height: 18px;
|
||||
margin-top: 8px;
|
||||
filter: invert(100%);
|
||||
}
|
||||
> i {
|
||||
color: #fff;
|
||||
line-height: @app-icon-height
|
||||
}
|
||||
}
|
||||
|
||||
.app-icon-missing {
|
||||
float: left;
|
||||
text-align: center;
|
||||
font-size: 1.7em;
|
||||
display: inline-block;
|
||||
height: @app-icon-height;
|
||||
line-height: @app-icon-height;
|
||||
width: @as-closed-width;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.app-title {
|
||||
width: calc(@as-open-width - @as-closed-width);
|
||||
display: inline-block;
|
||||
float: right;
|
||||
font-size: 0.9em;
|
||||
text-align: left;
|
||||
padding-left: 3px;
|
||||
line-height: @app-icon-height;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background-color: @app-links-active-background;
|
||||
> a {
|
||||
color: @white;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
import sinon from 'auto-release-sinon';
|
||||
import ngMock from 'ng_mock';
|
||||
import expect from 'expect.js';
|
||||
|
||||
import '../app_switcher_link';
|
||||
|
||||
describe('appSwitcherLink directive', () => {
|
||||
let scope;
|
||||
let $compile;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
|
||||
beforeEach(() => {
|
||||
ngMock.inject(($rootScope, _$compile_) => {
|
||||
scope = $rootScope.$new();
|
||||
$compile = _$compile_;
|
||||
});
|
||||
});
|
||||
|
||||
function create(attrs) {
|
||||
const template = `
|
||||
<app-switcher-link
|
||||
app-switcher-link-is-active="appSwitcherLinkIsActive"
|
||||
app-switcher-link-is-disabled="appSwitcherLinkIsDisabled"
|
||||
app-switcher-link-tooltip="appSwitcherLinkTooltip"
|
||||
app-switcher-link-on-click="appSwitcherLinkOnClick()"
|
||||
app-switcher-link-href="appSwitcherLinkHref"
|
||||
app-switcher-link-kbn-route="appSwitcherLinkKbnRoute"
|
||||
app-switcher-link-icon="appSwitcherLinkIcon"
|
||||
app-switcher-link-title="appSwitcherLinkTitle"
|
||||
/>
|
||||
`;
|
||||
|
||||
const element = $compile(template)(scope);
|
||||
|
||||
scope.$apply(() => {
|
||||
Object.assign(scope, attrs);
|
||||
});
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
describe('interface', () => {
|
||||
|
||||
describe('appSwitcherLinkIsActive attribute', () => {
|
||||
it(`doesn't apply the active class when false`, () => {
|
||||
const element = create({
|
||||
appSwitcherLinkIsActive: false,
|
||||
});
|
||||
expect(element.hasClass('active')).to.be(false);
|
||||
});
|
||||
|
||||
it('applies the active class when true', () => {
|
||||
const element = create({
|
||||
appSwitcherLinkIsActive: true,
|
||||
});
|
||||
expect(element.hasClass('active')).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('appSwitcherLinkIsDisabled attribute', () => {
|
||||
it(`doesn't apply the is-app-switcher-link-disabled class when false`, () => {
|
||||
const element = create({
|
||||
appSwitcherLinkIsDisabled: false,
|
||||
});
|
||||
expect(element.hasClass('is-app-switcher-link-disabled')).to.be(false);
|
||||
});
|
||||
|
||||
it('applies the is-app-switcher-link-disabled class when true', () => {
|
||||
const element = create({
|
||||
appSwitcherLinkIsDisabled: true,
|
||||
});
|
||||
expect(element.hasClass('is-app-switcher-link-disabled')).to.be(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('appSwitcherLinkTooltip attribute', () => {
|
||||
it('is applied to the tooltip directive', () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkTooltip: 'hello i am a tooltip',
|
||||
};
|
||||
const element = create(attrs);
|
||||
expect(element.attr('tooltip')).to.be(attrs.appSwitcherLinkTooltip);
|
||||
});
|
||||
});
|
||||
|
||||
describe('appSwitcherLinkOnClick attribute', () => {
|
||||
it('is called when the link is clicked', () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkOnClick: sinon.spy(),
|
||||
};
|
||||
const element = create(attrs);
|
||||
element.find('[data-test-subj=appLink]').click();
|
||||
sinon.assert.called(attrs.appSwitcherLinkOnClick);
|
||||
});
|
||||
});
|
||||
|
||||
describe('appSwitcherLinkHref attribute', () => {
|
||||
it('is applied to the link', () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkHref: 'link to a website',
|
||||
};
|
||||
const element = create(attrs);
|
||||
const link = element.find('[data-test-subj=appLink]');
|
||||
expect(link.attr('href')).to.be(attrs.appSwitcherLinkHref);
|
||||
});
|
||||
});
|
||||
|
||||
describe('appSwitcherLinkKbnRoute attribute', () => {
|
||||
it(`is applied to the link when href isn't defined`, () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkKbnRoute: '#test',
|
||||
};
|
||||
const element = create(attrs);
|
||||
const link = element.find('[data-test-subj=appLink]');
|
||||
expect(link.attr('href')).to.be(attrs.appSwitcherLinkKbnRoute);
|
||||
});
|
||||
|
||||
it(`isn't applied to the link when href is defined`, () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkHref: 'link to a website',
|
||||
appSwitcherLinkKbnRoute: '#test',
|
||||
};
|
||||
const element = create(attrs);
|
||||
const link = element.find('[data-test-subj=appLink]');
|
||||
expect(link.attr('href')).not.to.be(attrs.appSwitcherLinkKbnRoute);
|
||||
});
|
||||
});
|
||||
|
||||
describe('appSwitcherLinkIcon attribute', () => {
|
||||
describe('when present', () => {
|
||||
it('displays the img element', () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkIcon: 'icon url',
|
||||
};
|
||||
const element = create(attrs);
|
||||
const img = element.find('img');
|
||||
expect(img.length).to.be(1);
|
||||
});
|
||||
|
||||
it('hides the placeholder', () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkIcon: 'icon url',
|
||||
};
|
||||
const element = create(attrs);
|
||||
const placeholder = element.find('[data-test-subj=appLinkIconPlaceholder]');
|
||||
expect(placeholder.length).to.be(0);
|
||||
});
|
||||
|
||||
it(`is set as the img src`, () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkIcon: 'icon url',
|
||||
};
|
||||
const element = create(attrs);
|
||||
const img = element.find('img');
|
||||
expect(img.attr('src')).to.contain(encodeURI(attrs.appSwitcherLinkIcon));
|
||||
});
|
||||
});
|
||||
|
||||
describe('when not present', () => {
|
||||
it('hides the img element', () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkIcon: undefined,
|
||||
};
|
||||
const element = create(attrs);
|
||||
const img = element.find('img');
|
||||
expect(img.length).to.be(0);
|
||||
});
|
||||
|
||||
it('displays the placeholder', () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkIcon: undefined,
|
||||
};
|
||||
const element = create(attrs);
|
||||
const placeholder = element.find('[data-test-subj=appLinkIconPlaceholder]');
|
||||
expect(placeholder.length).to.be(1);
|
||||
});
|
||||
|
||||
it(`uses the title's first letter as the placeholder`, () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkIcon: undefined,
|
||||
appSwitcherLinkTitle: 'Xyz',
|
||||
};
|
||||
const element = create(attrs);
|
||||
const placeholder = element.find('[data-test-subj=appLinkIconPlaceholder]');
|
||||
expect(placeholder.text()).to.contain(attrs.appSwitcherLinkTitle[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('appSwitcherLinkTitle attribute', () => {
|
||||
it('is displayed', () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkTitle: 'demo title',
|
||||
};
|
||||
const element = create(attrs);
|
||||
const title = element.find('.app-switcher-link__title');
|
||||
expect(title.text().trim()).to.be(attrs.appSwitcherLinkTitle);
|
||||
});
|
||||
|
||||
it('is set as a title attribute on the anchor tag', () => {
|
||||
const attrs = {
|
||||
appSwitcherLinkTitle: 'demo title',
|
||||
};
|
||||
const element = create(attrs);
|
||||
const link = element.find('[data-test-subj=appLink]');
|
||||
expect(link.attr('title')).to.be(attrs.appSwitcherLinkTitle);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
<div
|
||||
class="app-switcher-link"
|
||||
ng-class="{ active: isActive, 'is-app-switcher-link-disabled': isDisabled }"
|
||||
tooltip="{{ tooltip }}"
|
||||
tooltip-placement="right"
|
||||
tooltip-popup-delay="400"
|
||||
tooltip-append-to-body="1"
|
||||
>
|
||||
<a
|
||||
class="app-switcher-link__anchor"
|
||||
href="{{ getHref() }}"
|
||||
ng-click="onClick({ $event: $event })"
|
||||
data-test-subj="appLink"
|
||||
title="{{ title }}"
|
||||
>
|
||||
<div class="app-switcher-link__icon">
|
||||
<img
|
||||
ng-if="icon"
|
||||
class="app-switcher-link__icon-image"
|
||||
kbn-src="{{ '/' + icon }}"
|
||||
>
|
||||
|
||||
<span
|
||||
ng-if="!icon"
|
||||
class="app-switcher-link__icon-placeholder"
|
||||
data-test-subj="appLinkIconPlaceholder"
|
||||
>
|
||||
{{ title[0] }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="app-switcher-link__title">
|
||||
{{ title }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
|
@ -0,0 +1,35 @@
|
|||
|
||||
import appSwitcherLinkTemplate from './app_switcher_link.html';
|
||||
import './app_switcher_link.less';
|
||||
import uiModules from 'ui/modules';
|
||||
|
||||
const module = uiModules.get('kibana');
|
||||
|
||||
module.directive('appSwitcherLink', chrome => {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: true,
|
||||
scope: {
|
||||
isActive: '=appSwitcherLinkIsActive',
|
||||
isDisabled: '=appSwitcherLinkIsDisabled',
|
||||
tooltip: '=appSwitcherLinkTooltip',
|
||||
onClick: '&appSwitcherLinkOnClick',
|
||||
href: '=appSwitcherLinkHref',
|
||||
kbnRoute: '=appSwitcherLinkKbnRoute',
|
||||
icon: '=appSwitcherLinkIcon',
|
||||
title: '=appSwitcherLinkTitle'
|
||||
},
|
||||
template: appSwitcherLinkTemplate,
|
||||
link: scope => {
|
||||
scope.getHref = () => {
|
||||
if (scope.href) {
|
||||
return scope.href;
|
||||
}
|
||||
|
||||
if (scope.kbnRoute) {
|
||||
return chrome.addBasePath(scope.kbnRoute);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
|
@ -0,0 +1,77 @@
|
|||
@import (reference) "~ui/styles/variables";
|
||||
|
||||
.app-switcher-link {
|
||||
width: @as-open-width;
|
||||
height: @app-icon-height;
|
||||
line-height: @app-line-height;
|
||||
|
||||
&.is-app-switcher-link-disabled {
|
||||
opacity: 0.5;
|
||||
|
||||
.app-switcher-link__anchor {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background-color: @app-links-active-background;
|
||||
|
||||
.app-switcher-link__anchor {
|
||||
color: @white;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.app-switcher-link__anchor {
|
||||
display: block;
|
||||
height: 100%;
|
||||
color: #ebf7fa;
|
||||
|
||||
/**
|
||||
* 1. TODO: Override anchor styles. Fix this by removing a tag styles.
|
||||
*/
|
||||
&:focus {
|
||||
color: #ebf7fa; /* 1 */
|
||||
}
|
||||
}
|
||||
|
||||
.app-switcher-link__icon {
|
||||
display: inline-block;
|
||||
width: @as-closed-width;
|
||||
height: @app-icon-height;
|
||||
float: left;
|
||||
text-align: center;
|
||||
font-size: 1.7em;
|
||||
}
|
||||
|
||||
/**
|
||||
* This imgae is used to display the icon.
|
||||
*/
|
||||
.app-switcher-link__icon-image {
|
||||
height: 18px;
|
||||
margin-top: 8px;
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
/**
|
||||
* This placeholder text gets shown if there is no specified icon.
|
||||
*/
|
||||
.app-switcher-link__icon-placeholder {
|
||||
line-height: @app-icon-height;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.app-switcher-link__title {
|
||||
width: calc(@as-open-width - @as-closed-width);
|
||||
display: inline-block;
|
||||
float: right;
|
||||
font-size: 0.9em;
|
||||
text-align: left;
|
||||
padding-left: 3px;
|
||||
line-height: @app-icon-height;
|
||||
white-space: nowrap;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import './app_switcher';
|
||||
import './app_switcher_link';
|
||||
import kbnChromeProv from './kbn_chrome';
|
||||
import kbnChromeNavControlsProv from './append_nav_controls';
|
||||
import './kbn_loading_indicator';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<span ng-if="!isHtml">{{content}}</span>
|
||||
<span ng-if="isHtml" ng-bind-html="content | trustAsHtml"></span>
|
||||
<span ng-if="isHtml" ng-bind-html="content"></span>
|
||||
<span ng-if="truncated">
|
||||
<a ng-click="toggle()">{{action}}</a>
|
||||
</span>
|
||||
|
|
|
@ -3,8 +3,9 @@ import truncText from 'trunc-text';
|
|||
import truncHTML from 'trunc-html';
|
||||
import uiModules from 'ui/modules';
|
||||
import truncatedTemplate from 'ui/directives/partials/truncated.html';
|
||||
import 'ui/filters/trust_as_html';
|
||||
const module = uiModules.get('kibana');
|
||||
import 'angular-sanitize';
|
||||
|
||||
const module = uiModules.get('kibana', ['ngSanitize']);
|
||||
|
||||
module.directive('kbnTruncated', function ($compile) {
|
||||
return {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import marked from 'marked';
|
||||
import uiModules from 'ui/modules';
|
||||
import 'angular-sanitize';
|
||||
|
||||
marked.setOptions({
|
||||
gfm: true, // GitHub-flavored markdown
|
||||
|
@ -7,7 +8,7 @@ marked.setOptions({
|
|||
});
|
||||
|
||||
uiModules
|
||||
.get('kibana')
|
||||
.filter('markdown', function ($sce) {
|
||||
return md => md ? $sce.trustAsHtml(marked(md)) : '';
|
||||
.get('kibana', ['ngSanitize'])
|
||||
.filter('markdown', function ($sanitize) {
|
||||
return md => md ? $sanitize(marked(md)) : '';
|
||||
});
|
||||
|
|
|
@ -106,7 +106,6 @@
|
|||
|
||||
// AppSwitcher =================================================================
|
||||
@app-switcher-application-bg: @gray-lighter;
|
||||
@app-switcher-app-link-color: @white;
|
||||
@app-switcher-app-title-color: @text-color;
|
||||
@app-switcher-app-description-color: @gray;
|
||||
|
||||
|
|
|
@ -11,14 +11,14 @@ import VislibVisualizationsMarkerTypesScaledCirclesProvider from 'ui/vislib/visu
|
|||
import VislibVisualizationsMarkerTypesShadedCirclesProvider from 'ui/vislib/visualizations/marker_types/shaded_circles';
|
||||
import VislibVisualizationsMarkerTypesGeohashGridProvider from 'ui/vislib/visualizations/marker_types/geohash_grid';
|
||||
import VislibVisualizationsMarkerTypesHeatmapProvider from 'ui/vislib/visualizations/marker_types/heatmap';
|
||||
export default function MapFactory(Private, tilemap) {
|
||||
export default function MapFactory(Private, tilemap, $sanitize) {
|
||||
|
||||
let defaultMapZoom = 2;
|
||||
let defaultMapCenter = [15, 5];
|
||||
let defaultMarkerType = 'Scaled Circle Markers';
|
||||
|
||||
let tilemapOptions = tilemap.options;
|
||||
let attribution = marked(tilemapOptions.attribution);
|
||||
let attribution = $sanitize(marked(tilemapOptions.attribution));
|
||||
|
||||
let mapTiles = {
|
||||
url: tilemap.url,
|
||||
|
|
|
@ -6,8 +6,10 @@ import _ from 'lodash';
|
|||
import RegistryVisTypesProvider from 'ui/registry/vis_types';
|
||||
import uiModules from 'ui/modules';
|
||||
import visualizeTemplate from 'ui/visualize/visualize.html';
|
||||
import 'angular-sanitize';
|
||||
|
||||
uiModules
|
||||
.get('kibana/directive')
|
||||
.get('kibana/directive', ['ngSanitize'])
|
||||
.directive('visualize', function (Notifier, SavedVis, indexPatterns, Private, config, $timeout) {
|
||||
|
||||
|
||||
|
|
|
@ -1,37 +1,17 @@
|
|||
let marked = require('marked');
|
||||
let Promise = require('bluebird');
|
||||
let { join } = require('path');
|
||||
let TextRenderer = require('marked-text-renderer');
|
||||
let _ = require('lodash');
|
||||
let fs = require('fs');
|
||||
let { AllHtmlEntities } = require('html-entities');
|
||||
let entities = new AllHtmlEntities();
|
||||
|
||||
TextRenderer.prototype.heading = function (text, level, raw) {
|
||||
return '\n\n' + text + '\n' + _.map(text, function () { return '='; }).join('') + '\n';
|
||||
};
|
||||
|
||||
module.exports = function (grunt) {
|
||||
|
||||
grunt.registerTask('_build:readme', function () {
|
||||
let transform = function (input) {
|
||||
let output = input.replace(/<\!\-\- [^\-]+ \-\->/g, '\n');
|
||||
output = marked(output);
|
||||
return entities.decode(output);
|
||||
};
|
||||
function transformReadme(readme) {
|
||||
return readme.replace(/\s##\sSnapshot\sBuilds[\s\S]*/, '');
|
||||
}
|
||||
|
||||
marked.setOptions({
|
||||
renderer: new TextRenderer(),
|
||||
tables: true,
|
||||
breaks: false,
|
||||
pedantic: false,
|
||||
sanitize: false,
|
||||
smartLists: true,
|
||||
smartypants: false
|
||||
});
|
||||
|
||||
grunt.file.write('build/kibana/README.txt', transform(grunt.file.read('README.md')));
|
||||
grunt.file.write('build/kibana/LICENSE.txt', transform(grunt.file.read('LICENSE.md')));
|
||||
grunt.file.copy('LICENSE.md', 'build/kibana/LICENSE.txt');
|
||||
grunt.file.write('build/kibana/README.txt', transformReadme(grunt.file.read('README.md')));
|
||||
});
|
||||
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue