Skip to content

Commit

Permalink
[UI Framework] Create Button React components in UI Framework.
Browse files Browse the repository at this point in the history
Backports PR #10646

**Commit 1:**
Create Button React components in UI Framework.

* Original sha: 68e14a2
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-02T03:43:14Z

**Commit 2:**
Integrate button icon variations into button_icon.js. Integrate button variations inbot button.js.
- Rename classes prop to className.

* Original sha: efa0a26
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-02T17:26:16Z

**Commit 3:**
Add KuiLoadingButtonIcon and isLoading prop for KuiButton.

* Original sha: c55ff13
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-02T19:11:52Z

**Commit 4:**
Add Jest test coverage for UI Framework.
- Add tests for KuiButton.
- Generate report in ui_framework/jest/report.

* Original sha: b461275
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-21T01:17:29Z

**Commit 5:**
Add tests for KuiButtonIcon and KuiButtonGroup.

* Original sha: 933f8ec
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-21T18:20:57Z

**Commit 6:**
Add both React and HTML examples for KuiButton.

* Original sha: 36e0ea6
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-21T20:52:04Z

**Commit 7:**
Update UI Framework README with instructions on creating and testing React components.

* Original sha: 802f1ea
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-21T23:52:29Z

**Commit 8:**
Move KuiButton isDisabled check from onClick handler to prop assginment.

* Original sha: 0132a55
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-21T23:58:34Z

**Commit 9:**
Refactor kuiButton to not use createElement, and instead exit early and return the correct element.

* Original sha: 31219b3
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-22T00:10:03Z

**Commit 10:**
Redesign KuiButton and KuiButtonIcon to accept a type prop.

* Original sha: a8000c9
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-22T04:13:29Z

**Commit 11:**
Break KuiButton apart into KuiButton, KuiLinkButton, and KuiSubmitButton.

* Original sha: 020d94c
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-22T04:53:36Z

**Commit 12:**
Move KuiButtonIcon and KuiButtonGroup into their own directories.

* Original sha: 119abf5
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-22T05:01:59Z

**Commit 13:**
Remove unused icon var from KuiSubmitButton.

* Original sha: 24a9a4f
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-22T05:05:32Z

**Commit 14:**
Use simpler rest parameter syntax instead of Object.assign for defining KuiButton propTypes.

* Original sha: 4119bfb
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-22T18:42:31Z

**Commit 15:**
Refactor KuiButton and KuiButtonIcon type prop to emphasize passing string literals instead of enums.

* Original sha: d50d081
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-22T19:06:42Z

**Commit 16:**
Add comment to explain role of nonVoidPropTypes in KuiButton.

* Original sha: f9c55fa
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-22T19:15:57Z

**Commit 17:**
Dynamically define KuiButton and KuiButtonIcon tests for type prop.

* Original sha: 0c5d2fd
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-22T20:46:17Z

**Commit 18:**
Fix Jest coverage configuration for deeply-nested dirs.

* Original sha: 6d724fc
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-22T21:27:30Z

**Commit 19:**
Rename prop testSubject to data-test-subj.

* Original sha: a9a4652
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-22T22:22:51Z

**Commit 20:**
button idea

* Original sha: d1eed94
* Authored by Kim Joar Bekkelund <kjbekkelund@gmail.com> on 2017-03-23T12:57:03Z
* Committed by CJ Cenizal <cj@cenizal.com> on 2017-03-28T15:21:03Z

**Commit 21:**
Remove unnecessary onClick mentions

* Original sha: a2d045e
* Authored by Kim Joar Bekkelund <kjbekkelund@gmail.com> on 2017-03-23T15:36:34Z
* Committed by CJ Cenizal <cj@cenizal.com> on 2017-03-28T15:21:03Z

**Commit 22:**
- Update KuiLinkButton to preventDefault on click when disabled.
- Update getClassName helper to accommodate loading icons.
- Update tests.

* Original sha: 4016eba
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-24T00:45:00Z

**Commit 23:**
Update tests with HTML attributes group. Add test for aria-label.

* Original sha: 05d934b
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-24T17:08:29Z

**Commit 24:**
Add UI Framework to linting task.

* Original sha: 3bc5016
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-24T17:24:41Z

**Commit 25:**
Refactor HTML attribute tests to be more succinct.

* Original sha: 322a968
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-24T18:58:45Z

**Commit 26:**
Remove backticks from ui_framework_test task.

* Original sha: 2187260
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-24T19:00:24Z

**Commit 27:**
Add eslintrc file to ui_framework, for Jest-specific rules.

* Original sha: 94daf03
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-24T19:06:42Z

**Commit 28:**
Add UI Framework Jest tests to npm test script. Create separate scripts for watching and generating coverage reports.

* Original sha: 2fa3cf7
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-24T20:58:00Z

**Commit 29:**
Remove redundant kuiSubmitButton tests.

* Original sha: 8d4222a
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-24T21:06:13Z

**Commit 30:**
Document Enzyme-specific Webpack configuration.

* Original sha: ad9616f
* Authored by CJ Cenizal <cj@cenizal.com> on 2017-03-24T21:09:07Z
  • Loading branch information
cjcenizal committed Mar 29, 2017
1 parent aac87ea commit 2502ec4
Show file tree
Hide file tree
Showing 57 changed files with 1,916 additions and 263 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,5 @@ selenium
*.swo
*.out
ui_framework/doc_site/build/*.js*
ui_framework/jest/report
yarn.lock
11 changes: 10 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@
"mocha": "mocha",
"mocha:debug": "mocha --debug-brk",
"sterilize": "grunt sterilize",
"uiFramework:start": "grunt uiFramework:start"
"uiFramework:start": "grunt uiFramework:start",
"uiFramework:dev": "node tasks/utils/ui_framework_test --env=jsdom --watch",
"uiFramework:coverage": "node tasks/utils/ui_framework_test --env=jsdom --coverage"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -95,6 +97,7 @@
"autoprefixer-loader": "2.0.0",
"babel-cli": "6.18.0",
"babel-core": "6.21.0",
"babel-jest": "18.0.0",
"babel-loader": "6.2.10",
"babel-plugin-add-module-exports": "0.2.1",
"babel-polyfill": "6.20.0",
Expand Down Expand Up @@ -199,6 +202,7 @@
},
"devDependencies": {
"@elastic/eslint-config-kibana": "0.4.0",
"@spalger/babel-presets": "0.3.2",
"angular-mocks": "1.4.7",
"auto-release-sinon": "1.0.3",
"babel-eslint": "6.1.2",
Expand All @@ -210,8 +214,10 @@
"del": "1.2.1",
"elasticdump": "2.1.1",
"enzyme": "2.7.0",
"enzyme-to-json": "1.4.5",
"eslint": "3.11.1",
"eslint-plugin-babel": "4.0.0",
"eslint-plugin-jest": "19.0.1",
"eslint-plugin-mocha": "4.7.0",
"eslint-plugin-react": "6.10.3",
"event-stream": "3.3.2",
Expand All @@ -231,11 +237,14 @@
"gulp-sourcemaps": "1.7.3",
"highlight.js": "9.0.0",
"history": "2.1.1",
"html": "1.0.0",
"html-loader": "0.4.3",
"husky": "0.8.1",
"image-diff": "1.6.0",
"intern": "3.2.3",
"istanbul-instrumenter-loader": "0.1.3",
"jest": "19.0.0",
"jest-cli": "19.0.0",
"jsdom": "9.9.1",
"karma": "1.2.0",
"karma-chrome-launcher": "0.2.0",
Expand Down
2 changes: 2 additions & 0 deletions tasks/config/eslint.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export default grunt => ({
'src',
'tasks',
'test',
'ui_framework/components',
'ui_framework/doc_site',
'utilities',
],
},
Expand Down
1 change: 1 addition & 0 deletions tasks/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ module.exports = function (grunt) {
grunt.registerTask('test:coverage', [ 'run:testCoverageServer', 'karma:coverage' ]);

grunt.registerTask('test:quick', [
'uiFramework:test',
'test:server',
'test:ui',
'test:browser',
Expand Down
37 changes: 37 additions & 0 deletions tasks/ui_framework_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const platform = require('os').platform();
const config = require('./utils/ui_framework_test_config');

module.exports = function (grunt) {
grunt.registerTask('uiFramework:test', function () {
const done = this.async();
Promise.all([uiFrameworkTest()]).then(done);
});

function uiFrameworkTest() {
const serverCmd = {
cmd: /^win/.test(platform) ? '.\\node_modules\\.bin\\jest.cmd' : './node_modules/.bin/jest',
args: [
'--env=jsdom',
`--config=${JSON.stringify(config)}`,
],
opts: { stdio: 'inherit' }
};

return new Promise((resolve, reject) => {
grunt.util.spawn(serverCmd, (error, result, code) => {
if (error || code !== 0) {
const message = result.stderr || result.stdout;

grunt.log.error(message);

return reject();
}

grunt.log.writeln(result);

resolve();
});

});
}
};
8 changes: 8 additions & 0 deletions tasks/utils/ui_framework_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const jest = require('jest');
const config = require('./ui_framework_test_config');

const argv = process.argv.slice(2);

argv.push('--config', JSON.stringify(config));

jest.run(argv);
27 changes: 27 additions & 0 deletions tasks/utils/ui_framework_test_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const path = require('path');
const rootDir = 'ui_framework';
const resolve = relativePath => path.resolve(__dirname, '..', '', relativePath);

module.exports = {
rootDir,
collectCoverageFrom: [
'components/**/*.js',
// Seems to be a bug with jest or micromatch, in which the above glob doesn't match subsequent
// levels of directories, making this glob necessary.
'components/**/**/*.js',
'!components/index.js',
'!components/**/*/index.js',
],
coverageDirectory: '<rootDir>/jest/report',
coverageReporters: ['html'],
moduleFileExtensions: ['jsx', 'js', 'json'],
moduleNameMapper: {
'^.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm)$': resolve('config/jest/FileStub.js'),
'^.+\\.css$': resolve('config/jest/CSSStub.js'),
'^.+\\.scss$': resolve('config/jest/CSSStub.js')
},
testPathIgnorePatterns: ['<rootDir>/(dist|doc_site|jest)/'],
testEnvironment: 'node',
testRegex: '.*\.test\.(js|jsx)$',
snapshotSerializers: ['<rootDir>/../node_modules/enzyme-to-json/serializer']
};
3 changes: 3 additions & 0 deletions ui_framework/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["react", "@spalger/babel-presets"]
}
13 changes: 13 additions & 0 deletions ui_framework/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"plugins": [
"jest"
],
"rules": {
"jest/no-disabled-tests": "error",
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error"
},
"env": {
"jest/globals": true
}
}
109 changes: 82 additions & 27 deletions ui_framework/README.md
Original file line number Diff line number Diff line change
@@ -1,75 +1,129 @@
# Kibana UI Framework

## Development
> The Kibana UI Framework is a collection of React UI components for quickly building user interfaces
> for Kibana. Not using React? No problem! You can still use the CSS behind each component.
* Start development server `npm run uiFramework:start`.
* View docs on `http://localhost:8020/`.
## Using the Framework

## What is this?
### Documentation

The Kibana UI Framework provides you with UI components you can quickly use to build user interfaces for Kibana.
You can view interactive documentation by running `npm run uiFramework:start` and then visiting
`http://localhost:8020/`.

The UI Framework comes with interactive examples which document how to use its various UI components. These components are currently only implemented in CSS, but eventually they'll grow to involve JS as well.
### React components

When you build a UI using this framework (e.g. a plugin's UI), you can rest assured it will integrate seamlessly into the overall Kibana UI.
Here are the components you can import from the Framnework:

## How to create a new component
```javascript
import {
KuiButton,
KuiButtonGroup,
KuiButtonIcon,
} from '../path/to/ui_framework/components';
```

There are two steps to creating a new component:
## Creating components

1. Create the CSS for the component in `ui_framework/components`.
2. Document it with examples in `ui_framework/doc_site`.
There are four steps to creating a new component:

### Create the component CSS
1. Create the SCSS for the component in `ui_framework/components`.
2. Create the React portion of the component.
3. Document it with examples in `ui_framework/doc_site`.
4. Write tests.

### Create component SCSS

1. Create a directory for your component in `ui_framework/components`.
2. In this directory, create `_{component name}.scss`.
3. _Optional:_ Create any other components that should be logically-grouped in this directory (see below).
4. Create an `_index.scss` file in this directory that import all of the new component SCSS files you created.
3. _Optional:_ Create any other components that should be [logically-grouped](#logically-grouped-components)
in this directory.
4. Create an `_index.scss` file in this directory that import all of the new component SCSS files
you created.
5. Import the `_index.scss` file into `ui_framework/components/index.scss`.

This makes your styles available to Kibana and the UI Framework documentation.

#### Logically-grouped components
### Create the React component

1. Create the React component(s) in the same directory as the related SCSS file(s).
2. Export these components from an `index.js` file.
3. Re-export these components from `ui_framework/components/index.js`.

If a component has subcomponents (e.g. ToolBar and ToolBarSearch), tightly-coupled components (e.g. Button and ButtonGroup), or you just want to group some related components together (e.g. TextInput, TextArea, and CheckBox), then they belong in the same logicaly grouping. In this case, you can create additional SCSS files for these components in the same component directory.
This makes your React component available for import into Kibana.

### Document the component with examples

1. Create a directory for your example in `ui_framework/doc_site/src/views`. Name it the name of the component.
2. Create a `{component name}_example.jsx` file inside the directory. You'll use this file to define the different examples for your component.
1. Create a directory for your example in `ui_framework/doc_site/src/views`. Name it the name of the
component.
2. Create a `{component name}_example.js` file inside the directory. You'll use this file to define
the different examples for your component.
3. Add the route to this file in `ui_framework/doc_site/src/services/routes/Routes.js`.
4. In the `.jsx` file you created, define examples which demonstrate the component. An example consists of a title, an optional description, an HTML file and an optional JavaScript file. It might help to refer to other examples to see how they're structured.
4. In the `{component name}_example.js` file you created, define examples which demonstrate the component and describe
its role from a UI perspective.

The complexity of the component should determine how many examples you need to create, and how complex they should be. In general, your examples should demonstrate:
The complexity of the component should determine how many examples you need to create, and how
complex they should be. In general, your examples should demonstrate:

* The most common use-cases for the component.
* How the component handles edge cases, e.g. overflowing content, text-based vs. element-based content.
* How the component handles edge cases, e.g. overflowing content, text-based vs. element-based
content.
* The various states of the component, e.g. disabled, selected, empty of content, error state.

## Writing CSS
### Test the component

1. Create test files with the name pattern of `{component name}.test.js`.
2. Create your tests.
3. Run tests with `npm run uiFramework:test`.

You can check how well the components have been covered
by the tests by viewing the generated report at `ui_framework/jest/report/index.html`.

#### React component development tips

You can run `npm run uiFramework:dev` to watch your files and automatically run the tests when you
make changes. Under this command, the tests will run faster than under `uiFramework:test` because
they'll only test the files you've changed -- the code coverage report won't be re-genereated,
however.

## Principles

### Logically-grouped components

If a component has subcomponents (e.g. ToolBar and ToolBarSearch), tightly-coupled components (e.g.
Button and ButtonGroup), or you just want to group some related components together (e.g. TextInput,
TextArea, and CheckBox), then they belong in the same logicaly grouping. In this case, you can create
additional SCSS files for these components in the same component directory.

### Writing CSS

Check out our [CSS style guide](https://github.com/elastic/kibana/blob/master/style_guides/css_style_guide.md).

## Benefits

### Dynamic, interactive documentation

By having a "living style guide", we relieve our designers of the burden of creating and maintaining static style guides. This also makes it easier for our engineers to translate mockups, prototypes, and wireframes into products.
By having a "living style guide", we relieve our designers of the burden of creating and maintaining
static style guides. This also makes it easier for our engineers to translate mockups, prototypes,
and wireframes into products.

### Copy-pasteable UI

Engineers can copy and paste sample code into their projects to quickly get reliable, consistent results.

### Remove CSS from the day-to-day

The CSS portion of this framework means engineers don't need to spend mental cycles translating a design into CSS. These cycles can be spent on the things critical to the identity of the specific project they're working on, like architecture and business logic.
The CSS portion of this framework means engineers don't need to spend mental cycles translating a
design into CSS. These cycles can be spent on the things critical to the identity of the specific
project they're working on, like architecture and business logic.

Once this framework also provides JS components, engineers won't even need to _see_ CSS -- it will be encapsulated behind the JS components' interfaces.
If they use the React components, engineers won't even need to _see_ CSS -- it will be encapsulated
behind the React components' interfaces.

### More UI tests === fewer UI bugs

By covering our UI components with great unit tests and having those tests live within the framework itself, we can rest assured that our UI layer is tested and remove some of that burden from out integration/end-to-end tests.
By covering our UI components with great unit tests and having those tests live within the framework
itself, we can rest assured that our UI layer is tested and remove some of that burden from our
integration/end-to-end tests.

## Why not just use Bootstrap?

Expand All @@ -84,7 +138,8 @@ We also gain the ability to fix some of the common issues with third-party CSS f
* They have non-semantic markup.
* They deeply nest their selectors.

For a more in-depth analysis of the problems with Bootstrap (and similar frameworks), check out this article and the links it has at the bottom: ["Bootstrap Bankruptcy"](http://www.matthewcopeland.me/blog/2013/11/04/bootstrap-bankruptcy/).
For a more in-depth analysis of the problems with Bootstrap (and similar frameworks), check out this
article and the links it has at the bottom: ["Bootstrap Bankruptcy"](http://www.matthewcopeland.me/blog/2013/11/04/bootstrap-bankruptcy/).

## Examples of other in-house UI frameworks

Expand Down
Loading

0 comments on commit 2502ec4

Please sign in to comment.