mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[UI Framework] Create Button React components in UI Framework. (#10646)
* Create KuiButton, KuiLinkButton, KuiSubmitButton, and KuiButtonIcon React components in UI Framework. * Add Jest test coverage for UI Framework, generate report in ui_framework/jest/report. * Add UI Framework to linting task. * Update UI Framework README with instructions on creating and testing React components. * Add both React and HTML examples. * Add UI Framework Jest tests to npm test script. Create separate scripts for watching and generating coverage reports. * Fix appearance of kuiButtons with icons throughout Kibana, by adding a flexbox wrapper. * Improve accessibility of kuiButtonIcon. * Remove disabled attribute from KuiLinkButton. * Remove kuiButton-isDisabled class from KuiButton and KuiSubmitButton.
This commit is contained in:
parent
250fbda30b
commit
b604911720
60 changed files with 2126 additions and 318 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -36,4 +36,5 @@ selenium
|
|||
*.swo
|
||||
*.out
|
||||
ui_framework/doc_site/build/*.js*
|
||||
ui_framework/jest/report
|
||||
yarn.lock
|
||||
|
|
11
package.json
11
package.json
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -209,8 +213,10 @@
|
|||
"chromedriver": "2.24.1",
|
||||
"classnames": "2.2.5",
|
||||
"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",
|
||||
|
@ -230,11 +236,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",
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
class="kuiButton kuiButton--basic kuiButton--iconText"
|
||||
ng-disabled="isDisabled"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__icon kuiIcon fa-spinner fa-spin"
|
||||
ng-class="isDisabled ? ['fa-spinner', 'fa-spin'] : [icon]"
|
||||
/>
|
||||
<span ng-transclude />
|
||||
<span class="kuiButton__inner">
|
||||
<span
|
||||
class="kuiButton__icon kuiIcon fa-spinner fa-spin"
|
||||
ng-class="isDisabled ? ['fa-spinner', 'fa-spin'] : [icon]"
|
||||
/>
|
||||
<span ng-transclude />
|
||||
</span>
|
||||
</button>
|
||||
|
|
|
@ -99,8 +99,10 @@
|
|||
class="kuiButton kuiButton--primary kuiButton--iconText"
|
||||
href="#/dashboard/create"
|
||||
>
|
||||
<span class="kuiButton__icon kuiIcon fa-plus"></span>
|
||||
Create a dashboard
|
||||
<span class="kuiButton__inner">
|
||||
<span class="kuiButton__icon kuiIcon fa-plus"></span>
|
||||
<span>Create a dashboard</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -13,8 +13,10 @@
|
|||
class="kuiButton kuiButton--basic kuiButton--iconText"
|
||||
ng-click="exportAll()"
|
||||
>
|
||||
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-download"></span>
|
||||
Export Everything
|
||||
<span class="kuiButton__inner">
|
||||
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-download"></span>
|
||||
<span>Export Everything</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<file-upload
|
||||
|
@ -25,8 +27,10 @@
|
|||
class="kuiButton kuiButton--basic kuiButton--iconText"
|
||||
data-import-saved-objects-button
|
||||
>
|
||||
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-upload"></span>
|
||||
Import
|
||||
<span class="kuiButton__inner">
|
||||
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-upload"></span>
|
||||
<span>Import</span>
|
||||
</span>
|
||||
</button>
|
||||
</file-upload>
|
||||
</div>
|
||||
|
@ -84,8 +88,10 @@
|
|||
aria-label="Delete selected objects"
|
||||
ng-disabled="selectedItems.length == 0"
|
||||
>
|
||||
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-trash"></span>
|
||||
Delete
|
||||
<span class="kuiButton__inner">
|
||||
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-trash"></span>
|
||||
<span>Delete</span>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<!-- Bulk export button -->
|
||||
|
@ -95,8 +101,10 @@
|
|||
aria-label="Export selected objects"
|
||||
ng-disabled="selectedItems.length == 0"
|
||||
>
|
||||
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-download"></span>
|
||||
Export
|
||||
<span class="kuiButton__inner">
|
||||
<span aria-hidden="true" class="kuiButton__icon kuiIcon fa-download"></span>
|
||||
<span>Export</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -13,16 +13,20 @@
|
|||
class="kuiButton kuiButton--basic kuiButton--iconText"
|
||||
href="{{ link }}"
|
||||
>
|
||||
<span class="kuiButton__icon kuiIcon fa-eye"></span>
|
||||
View {{ title }}
|
||||
<span class="kuiButton__inner">
|
||||
<span class="kuiButton__icon kuiIcon fa-eye"></span>
|
||||
<span>View {{ title }}</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
<button
|
||||
class="kuiButton kuiButton--danger kuiButton--iconText"
|
||||
ng-click="delete()"
|
||||
>
|
||||
<span class="kuiButton__icon kuiIcon fa-trash-o"></span>
|
||||
Delete {{ title }}
|
||||
<span class="kuiButton__inner">
|
||||
<span class="kuiButton__icon kuiIcon fa-trash-o"></span>
|
||||
<span>Delete {{ title }}</span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -97,8 +97,10 @@
|
|||
class="kuiButton kuiButton--primary kuiButton--iconText"
|
||||
href="#/visualize/new"
|
||||
>
|
||||
<span class="kuiButton__icon kuiIcon fa-plus"></span>
|
||||
Create a visualization
|
||||
<span class="kuiButton__inner">
|
||||
<span class="kuiButton__icon kuiIcon fa-plus"></span>
|
||||
<span>Create a visualization</span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,8 @@ export default grunt => ({
|
|||
'scripts',
|
||||
'tasks',
|
||||
'test',
|
||||
'ui_framework/components',
|
||||
'ui_framework/doc_site',
|
||||
'utilities',
|
||||
],
|
||||
},
|
||||
|
|
|
@ -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',
|
||||
|
|
37
tasks/ui_framework_test.js
Normal file
37
tasks/ui_framework_test.js
Normal file
|
@ -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
tasks/utils/ui_framework_test.js
Normal file
8
tasks/utils/ui_framework_test.js
Normal file
|
@ -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);
|
20
tasks/utils/ui_framework_test_config.js
Normal file
20
tasks/utils/ui_framework_test_config.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
const rootDir = 'ui_framework';
|
||||
|
||||
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'],
|
||||
testPathIgnorePatterns: ['<rootDir>/(dist|doc_site|jest)/'],
|
||||
testEnvironment: 'node',
|
||||
testRegex: '.*\.test\.(js|jsx)$',
|
||||
snapshotSerializers: ['<rootDir>/../node_modules/enzyme-to-json/serializer']
|
||||
};
|
3
ui_framework/.babelrc
Normal file
3
ui_framework/.babelrc
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"presets": ["react", "@spalger/babel-presets"]
|
||||
}
|
13
ui_framework/.eslintrc
Normal file
13
ui_framework/.eslintrc
Normal file
|
@ -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
|
||||
}
|
||||
}
|
|
@ -1,53 +1,100 @@
|
|||
# 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
|
||||
|
||||
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.
|
||||
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`.
|
||||
|
||||
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.js` 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 `{component name}_example.js` 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).
|
||||
|
||||
|
@ -55,7 +102,9 @@ Check out our [CSS style guide](https://github.com/elastic/kibana/blob/master/st
|
|||
|
||||
### 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
|
||||
|
||||
|
@ -63,13 +112,18 @@ Engineers can copy and paste sample code into their projects to quickly get reli
|
|||
|
||||
### 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?
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
|
150
ui_framework/components/button/__snapshots__/button.test.js.snap
Normal file
150
ui_framework/components/button/__snapshots__/button.test.js.snap
Normal file
|
@ -0,0 +1,150 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`KuiButton Baseline HTML attributes are rendered 1`] = `
|
||||
<button
|
||||
aria-label="aria label"
|
||||
class="kuiButton testClass1 testClass2"
|
||||
data-test-subj="test subject string"
|
||||
disabled=""
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`KuiButton Baseline is rendered 1`] = `
|
||||
<button
|
||||
class="kuiButton"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`KuiButton Props children is rendered 1`] = `
|
||||
<button
|
||||
class="kuiButton"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
<span>
|
||||
Hello
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`KuiButton Props icon is rendered with children 1`] = `
|
||||
<button
|
||||
class="kuiButton kuiButton--iconText"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
Icon
|
||||
<span>
|
||||
Hello
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`KuiButton Props icon is rendered without children 1`] = `
|
||||
<button
|
||||
class="kuiButton kuiButton--iconText"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
Icon
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`KuiButton Props iconPosition moves the icon to the right 1`] = `
|
||||
<button
|
||||
class="kuiButton kuiButton--iconText"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
<span>
|
||||
Hello
|
||||
</span>
|
||||
Icon
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`KuiButton Props isLoading doesn't render the icon prop 1`] = `
|
||||
<button
|
||||
class="kuiButton kuiButton--iconText"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiButton__icon kuiIcon fa-spinner fa-spin"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`KuiButton Props isLoading renders a spinner 1`] = `
|
||||
<button
|
||||
class="kuiButton kuiButton--iconText"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiButton__icon kuiIcon fa-spinner fa-spin"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`KuiButton Props type basic renders the basic class 1`] = `
|
||||
<button
|
||||
class="kuiButton kuiButton--basic"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`KuiButton Props type danger renders the danger class 1`] = `
|
||||
<button
|
||||
class="kuiButton kuiButton--danger"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`KuiButton Props type hollow renders the hollow class 1`] = `
|
||||
<button
|
||||
class="kuiButton kuiButton--hollow"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`KuiButton Props type primary renders the primary class 1`] = `
|
||||
<button
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</button>
|
||||
`;
|
|
@ -0,0 +1,151 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`KuiLinkButton Baseline HTML attributes are rendered (and disabled renders a class) 1`] = `
|
||||
<a
|
||||
aria-label="aria label"
|
||||
class="kuiButton testClass1 testClass2 kuiButton-isDisabled"
|
||||
data-test-subj="test subject string"
|
||||
href="#"
|
||||
target="_blank"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`KuiLinkButton Baseline is rendered 1`] = `
|
||||
<a
|
||||
class="kuiButton"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`KuiLinkButton Props children is rendered 1`] = `
|
||||
<a
|
||||
class="kuiButton"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
<span>
|
||||
Hello
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`KuiLinkButton Props icon is rendered with children 1`] = `
|
||||
<a
|
||||
class="kuiButton kuiButton--iconText"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
Icon
|
||||
<span>
|
||||
Hello
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`KuiLinkButton Props icon is rendered without children 1`] = `
|
||||
<a
|
||||
class="kuiButton kuiButton--iconText"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
Icon
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`KuiLinkButton Props iconPosition moves the icon to the right 1`] = `
|
||||
<a
|
||||
class="kuiButton kuiButton--iconText"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
<span>
|
||||
Hello
|
||||
</span>
|
||||
Icon
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`KuiLinkButton Props isLoading doesn't render the icon prop 1`] = `
|
||||
<a
|
||||
class="kuiButton kuiButton--iconText"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiButton__icon kuiIcon fa-spinner fa-spin"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`KuiLinkButton Props isLoading renders a spinner 1`] = `
|
||||
<a
|
||||
class="kuiButton kuiButton--iconText"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiButton__icon kuiIcon fa-spinner fa-spin"
|
||||
/>
|
||||
</span>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`KuiLinkButton Props type basic renders the basic class 1`] = `
|
||||
<a
|
||||
class="kuiButton kuiButton--basic"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`KuiLinkButton Props type danger renders the danger class 1`] = `
|
||||
<a
|
||||
class="kuiButton kuiButton--danger"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`KuiLinkButton Props type hollow renders the hollow class 1`] = `
|
||||
<a
|
||||
class="kuiButton kuiButton--hollow"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</a>
|
||||
`;
|
||||
|
||||
exports[`KuiLinkButton Props type primary renders the primary class 1`] = `
|
||||
<a
|
||||
class="kuiButton kuiButton--primary"
|
||||
>
|
||||
<span
|
||||
class="kuiButton__inner"
|
||||
/>
|
||||
</a>
|
||||
`;
|
|
@ -0,0 +1,54 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`KuiSubmitButton Baseline HTML attributes are rendered 1`] = `
|
||||
<input
|
||||
aria-label="aria label"
|
||||
class="kuiButton testClass1 testClass2"
|
||||
data-test-subj="test subject string"
|
||||
disabled=""
|
||||
type="submit"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiSubmitButton Baseline is rendered 1`] = `
|
||||
<input
|
||||
class="kuiButton"
|
||||
type="submit"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiSubmitButton Props children is rendered as value 1`] = `
|
||||
<input
|
||||
class="kuiButton"
|
||||
type="submit"
|
||||
value="Hello"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiSubmitButton Props type basic renders the basic class 1`] = `
|
||||
<input
|
||||
class="kuiButton kuiButton--basic"
|
||||
type="submit"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiSubmitButton Props type danger renders the danger class 1`] = `
|
||||
<input
|
||||
class="kuiButton kuiButton--danger"
|
||||
type="submit"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiSubmitButton Props type hollow renders the hollow class 1`] = `
|
||||
<input
|
||||
class="kuiButton kuiButton--hollow"
|
||||
type="submit"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiSubmitButton Props type primary renders the primary class 1`] = `
|
||||
<input
|
||||
class="kuiButton kuiButton--primary"
|
||||
type="submit"
|
||||
/>
|
||||
`;
|
|
@ -1,8 +1,65 @@
|
|||
/**
|
||||
* 1. Links can't have a disabled attribute, so they can't support :disabled.
|
||||
*/
|
||||
@mixin kuiButtonDisabled {
|
||||
&:disabled {
|
||||
@content;
|
||||
}
|
||||
|
||||
@at-root a#{&}.kuiButton-isDisabled {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Links can't have a disabled attribute, so they can't support :enabled.
|
||||
*/
|
||||
@mixin kuiButtonHover {
|
||||
&:enabled:hover {
|
||||
@content;
|
||||
}
|
||||
|
||||
@at-root a#{&}:not(.kuiButton-isDisabled):hover { /* 1 */
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Links can't have a disabled attribute, so they can't support :enabled.
|
||||
*/
|
||||
@mixin kuiButtonActive {
|
||||
&:enabled:active {
|
||||
@content;
|
||||
}
|
||||
|
||||
@at-root a#{&}:not(.kuiButton-isDisabled):active { /* 1 */
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Links can't have a disabled attribute, so they can't support :enabled.
|
||||
*/
|
||||
@mixin kuiButtonFocus {
|
||||
&:not(a):enabled:focus {
|
||||
@content;
|
||||
}
|
||||
|
||||
@at-root a#{&}:not(.kuiButton-isDisabled):focus { /* 1 */
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin kuiButtonHoverAndActive {
|
||||
@include kuiButtonHover { @content }
|
||||
@include kuiButtonActive { @content }
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Setting to inline-block guarantees the same height when applied to both
|
||||
* button elements and anchor tags.
|
||||
* 2. Disable for Angular.
|
||||
* 3. Safari won't respect :enabled:active on links.
|
||||
* 2. Links can be focused when they're "disabled" (since we're just faking this with a class), but
|
||||
* at least make them look like they're not focused.
|
||||
*/
|
||||
.kuiButton {
|
||||
display: inline-block; /* 1 */
|
||||
|
@ -16,119 +73,123 @@
|
|||
border: none;
|
||||
border-radius: $buttonBorderRadius;
|
||||
|
||||
&:disabled {
|
||||
@include kuiButtonDisabled {
|
||||
cursor: default;
|
||||
pointer-events: none; /* 2 */
|
||||
}
|
||||
|
||||
&:active { /* 3 */
|
||||
@include kuiButtonActive {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none; /* 2 */
|
||||
}
|
||||
|
||||
@include kuiButtonFocus {
|
||||
@include focus;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Solves whitespace problems introduced by inline elements.
|
||||
*/
|
||||
.kuiButton__inner {
|
||||
display: flex; /* 1 */
|
||||
align-items: center; /* 1 */
|
||||
}
|
||||
|
||||
.kuiButton--iconText {
|
||||
.kuiButton__icon {
|
||||
&:first-child {
|
||||
margin-right: 4px;
|
||||
$iconSpacing: 8px;
|
||||
|
||||
&:first-child:not(:only-child) {
|
||||
margin-right: $iconSpacing;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-left: 4px;
|
||||
&:last-child:not(:only-child) {
|
||||
margin-left: $iconSpacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Override Bootstrap.
|
||||
* 2. Safari won't respect :enabled:hover/active on links.
|
||||
*/
|
||||
.kuiButton--basic {
|
||||
color: #5a5a5a;
|
||||
background-color: #F2F2F2;
|
||||
|
||||
// Goes before hover, so that hover can override it.
|
||||
&:focus {
|
||||
color: #5a5a5a !important; /* 1 */
|
||||
@include kuiButtonFocus {
|
||||
color: #5a5a5a;
|
||||
}
|
||||
|
||||
&:hover, /* 2 */
|
||||
&:active { /* 2 */
|
||||
@include kuiButtonHoverAndActive {
|
||||
color: #ffffff !important; /* 1 */
|
||||
background-color: #9B9B9B !important;
|
||||
background-color: #9B9B9B !important; /* 1 */
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@include kuiButtonDisabled {
|
||||
color: #9B9B9B;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Override Bootstrap.
|
||||
* 2. Safari won't respect :enabled:hover/active on links.
|
||||
*/
|
||||
.kuiButton--primary {
|
||||
color: #FFFFFF;
|
||||
background-color: #6EADC1;
|
||||
|
||||
&:hover, /* 2 */
|
||||
&:active { /* 2 */
|
||||
@include kuiButtonFocus {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
@include kuiButtonHoverAndActive {
|
||||
color: #FFFFFF !important; /* 1 */
|
||||
background-color: #006E8A;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@include kuiButtonDisabled {
|
||||
background-color: #B6D6E0;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
color: #FFFFFF !important; /* 1 */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Override Bootstrap.
|
||||
* 2. Safari won't respect :enabled:hover/active on links.
|
||||
*/
|
||||
.kuiButton--danger {
|
||||
color: #FFFFFF;
|
||||
background-color: #D76051;
|
||||
|
||||
&:hover, /* 2 */
|
||||
&:active { /* 2 */
|
||||
@include kuiButtonFocus {
|
||||
@include focus($focusDangerColor);
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
@include kuiButtonHoverAndActive {
|
||||
color: #FFFFFF !important; /* 1 */
|
||||
background-color: #A52E1F;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
@include kuiButtonDisabled {
|
||||
background-color: #efc0ba;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
@include focus($focusDangerColor);
|
||||
color: #FFFFFF !important; /* 1 */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. Override Bootstrap.
|
||||
* 2. Override either Bootstrap or Timelion styles.
|
||||
* 3. Safari won't respect :enabled:hover/active on links.
|
||||
*/
|
||||
.kuiButton--hollow {
|
||||
color: $linkColor !important; /* 2 */
|
||||
background-color: transparent;
|
||||
|
||||
&:hover, /* 3 */
|
||||
&:active { /* 3 */
|
||||
@include kuiButtonHoverAndActive {
|
||||
color: $linkHoverColor !important; /* 1 */
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: #dddddd !important; /* 1 */
|
||||
@include kuiButtonDisabled {
|
||||
color: #dddddd;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
@import "button";
|
||||
@import "button_group";
|
||||
@import "button_group/button_group";
|
||||
|
|
176
ui_framework/components/button/button.js
Normal file
176
ui_framework/components/button/button.js
Normal file
|
@ -0,0 +1,176 @@
|
|||
import React, {
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { KuiButtonIcon } from './button_icon/button_icon';
|
||||
|
||||
const BUTTON_TYPES = [
|
||||
'basic',
|
||||
'hollow',
|
||||
'danger',
|
||||
'primary',
|
||||
];
|
||||
|
||||
const ICON_POSITIONS = [
|
||||
'left',
|
||||
'right',
|
||||
];
|
||||
|
||||
const DEFAULT_ICON_POSITION = 'left';
|
||||
|
||||
const buttonTypeToClassNameMap = {
|
||||
basic: 'kuiButton--basic',
|
||||
hollow: 'kuiButton--hollow',
|
||||
danger: 'kuiButton--danger',
|
||||
primary: 'kuiButton--primary',
|
||||
};
|
||||
|
||||
const getClassName = ({ className, type, hasIcon = false }) =>
|
||||
classNames('kuiButton', className, buttonTypeToClassNameMap[type], {
|
||||
'kuiButton--iconText': hasIcon,
|
||||
});
|
||||
|
||||
const ContentWithIcon = ({ children, icon, iconPosition, isLoading }) => {
|
||||
const iconOrLoading = isLoading
|
||||
? <KuiButtonIcon type="loading" />
|
||||
: icon;
|
||||
|
||||
// We need to wrap the children so that the icon's :first-child etc. pseudo-selectors get applied
|
||||
// correctly.
|
||||
const wrappedChildren = children ? <span>{children}</span> : undefined;
|
||||
|
||||
switch(iconPosition) {
|
||||
case 'left':
|
||||
return (
|
||||
<span className="kuiButton__inner">
|
||||
{iconOrLoading}
|
||||
{wrappedChildren}
|
||||
</span>
|
||||
);
|
||||
|
||||
case 'right':
|
||||
return (
|
||||
<span className="kuiButton__inner">
|
||||
{wrappedChildren}
|
||||
{iconOrLoading}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const KuiButton = ({
|
||||
isLoading,
|
||||
iconPosition = DEFAULT_ICON_POSITION,
|
||||
className,
|
||||
type,
|
||||
icon,
|
||||
children,
|
||||
...rest
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
className={getClassName({
|
||||
className,
|
||||
type,
|
||||
hasIcon: icon || isLoading,
|
||||
})}
|
||||
{...rest}
|
||||
>
|
||||
<ContentWithIcon
|
||||
icon={icon}
|
||||
iconPosition={iconPosition}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
{children}
|
||||
</ContentWithIcon>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
KuiButton.propTypes = {
|
||||
icon: PropTypes.node,
|
||||
iconPosition: PropTypes.oneOf(ICON_POSITIONS),
|
||||
children: PropTypes.node,
|
||||
isLoading: PropTypes.bool,
|
||||
type: PropTypes.oneOf(BUTTON_TYPES),
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
const KuiLinkButton = ({
|
||||
isLoading,
|
||||
icon,
|
||||
iconPosition = DEFAULT_ICON_POSITION,
|
||||
className,
|
||||
disabled,
|
||||
type,
|
||||
children,
|
||||
...rest
|
||||
}) => {
|
||||
const onClick = e => {
|
||||
if (disabled) {
|
||||
e.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
const classes = classNames(getClassName({
|
||||
className,
|
||||
type,
|
||||
hasIcon: icon || isLoading,
|
||||
}), { 'kuiButton-isDisabled': disabled });
|
||||
|
||||
return (
|
||||
<a
|
||||
className={classes}
|
||||
onClick={onClick}
|
||||
{...rest}
|
||||
>
|
||||
<ContentWithIcon
|
||||
icon={icon}
|
||||
iconPosition={iconPosition}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
{children}
|
||||
</ContentWithIcon>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
KuiLinkButton.propTypes = {
|
||||
icon: PropTypes.node,
|
||||
iconPosition: PropTypes.oneOf(ICON_POSITIONS),
|
||||
isLoading: PropTypes.bool,
|
||||
type: PropTypes.oneOf(BUTTON_TYPES),
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
const KuiSubmitButton = ({
|
||||
className,
|
||||
type,
|
||||
children,
|
||||
...rest
|
||||
}) => {
|
||||
// NOTE: The `input` element is a void element and can't contain children.
|
||||
return (
|
||||
<input
|
||||
type="submit"
|
||||
value={children}
|
||||
className={getClassName({ className, type })}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
KuiSubmitButton.propTypes = {
|
||||
children: PropTypes.string,
|
||||
type: PropTypes.oneOf(BUTTON_TYPES),
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export {
|
||||
BUTTON_TYPES,
|
||||
KuiButton,
|
||||
KuiLinkButton,
|
||||
KuiSubmitButton,
|
||||
};
|
143
ui_framework/components/button/button.test.js
Normal file
143
ui_framework/components/button/button.test.js
Normal file
|
@ -0,0 +1,143 @@
|
|||
import React from 'react';
|
||||
import { render, shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import {
|
||||
BUTTON_TYPES,
|
||||
KuiButton,
|
||||
} from './button';
|
||||
|
||||
describe('KuiButton', () => {
|
||||
describe('Baseline', () => {
|
||||
test('is rendered', () => {
|
||||
const $button = render(
|
||||
<KuiButton />
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('HTML attributes are rendered', () => {
|
||||
const $button = render(
|
||||
<KuiButton
|
||||
aria-label="aria label"
|
||||
className="testClass1 testClass2"
|
||||
data-test-subj="test subject string"
|
||||
disabled
|
||||
/>
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Props', () => {
|
||||
describe('type', () => {
|
||||
BUTTON_TYPES.forEach(type => {
|
||||
describe(type, () => {
|
||||
test(`renders the ${type} class`, () => {
|
||||
const $button = render(<KuiButton type={type} />);
|
||||
expect($button).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('icon', () => {
|
||||
test('is rendered with children', () => {
|
||||
const $button = render(
|
||||
<KuiButton icon="Icon">
|
||||
Hello
|
||||
</KuiButton>
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('is rendered without children', () => {
|
||||
const $button = render(
|
||||
<KuiButton icon="Icon" />
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('iconPosition', () => {
|
||||
test('moves the icon to the right', () => {
|
||||
const $button = render(
|
||||
<KuiButton
|
||||
icon="Icon"
|
||||
iconPosition="right"
|
||||
>
|
||||
Hello
|
||||
</KuiButton>
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('children', () => {
|
||||
test('is rendered', () => {
|
||||
const $button = render(
|
||||
<KuiButton>
|
||||
Hello
|
||||
</KuiButton>
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onClick', () => {
|
||||
test(`isn't called upon instantiation`, () => {
|
||||
const onClickHandler = sinon.stub();
|
||||
|
||||
shallow(
|
||||
<KuiButton onClick={onClickHandler} />
|
||||
);
|
||||
|
||||
sinon.assert.notCalled(onClickHandler);
|
||||
});
|
||||
|
||||
test('is called when the button is clicked', () => {
|
||||
const onClickHandler = sinon.stub();
|
||||
|
||||
const $button = shallow(
|
||||
<KuiButton onClick={onClickHandler} />
|
||||
);
|
||||
|
||||
$button.simulate('click');
|
||||
|
||||
sinon.assert.calledOnce(onClickHandler);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isLoading', () => {
|
||||
test('renders a spinner', () => {
|
||||
const $button = render(
|
||||
<KuiButton isLoading />
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
|
||||
test(`doesn't render the icon prop`, () => {
|
||||
const $button = render(
|
||||
<KuiButton isLoading icon="Icon" />
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`KuiButtonGroup Baseline is rendered 1`] = `
|
||||
<div
|
||||
class="kuiButtonGroup"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiButtonGroup Props children is rendered 1`] = `
|
||||
<div
|
||||
class="kuiButtonGroup"
|
||||
>
|
||||
Hello
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`KuiButtonGroup Props isUnited renders the united class 1`] = `
|
||||
<div
|
||||
class="kuiButtonGroup kuiButtonGroup--united"
|
||||
/>
|
||||
`;
|
24
ui_framework/components/button/button_group/button_group.js
Normal file
24
ui_framework/components/button/button_group/button_group.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
import React, {
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
const KuiButtonGroup = props => {
|
||||
const classes = classNames('kuiButtonGroup', {
|
||||
'kuiButtonGroup--united': props.isUnited,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
KuiButtonGroup.propTypes = {
|
||||
children: PropTypes.node,
|
||||
isUnited: PropTypes.bool,
|
||||
};
|
||||
|
||||
export { KuiButtonGroup };
|
|
@ -0,0 +1,43 @@
|
|||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
|
||||
import { KuiButtonGroup } from './button_group';
|
||||
|
||||
describe('KuiButtonGroup', () => {
|
||||
describe('Baseline', () => {
|
||||
test('is rendered', () => {
|
||||
const $buttonGroup = render(
|
||||
<KuiButtonGroup />
|
||||
);
|
||||
|
||||
expect($buttonGroup)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Props', () => {
|
||||
describe('children', () => {
|
||||
test('is rendered', () => {
|
||||
const $buttonGroup = render(
|
||||
<KuiButtonGroup>
|
||||
Hello
|
||||
</KuiButtonGroup>
|
||||
);
|
||||
|
||||
expect($buttonGroup)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isUnited', () => {
|
||||
test('renders the united class', () => {
|
||||
const $buttonGroup = render(
|
||||
<KuiButtonGroup isUnited />
|
||||
);
|
||||
|
||||
expect($buttonGroup)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`KuiButtonIcon Baseline is rendered 1`] = `
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiButton__icon kuiIcon"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiButtonIcon Props className renders the classes 1`] = `
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiButton__icon kuiIcon testClass1 testClass2"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiButtonIcon Props type create renders the create class 1`] = `
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiButton__icon kuiIcon fa-plus"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiButtonIcon Props type delete renders the delete class 1`] = `
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiButton__icon kuiIcon fa-trash"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiButtonIcon Props type loading renders the loading class 1`] = `
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiButton__icon kuiIcon fa-spinner fa-spin"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiButtonIcon Props type next renders the next class 1`] = `
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiButton__icon kuiIcon fa-chevron-right"
|
||||
/>
|
||||
`;
|
||||
|
||||
exports[`KuiButtonIcon Props type previous renders the previous class 1`] = `
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="kuiButton__icon kuiIcon fa-chevron-left"
|
||||
/>
|
||||
`;
|
44
ui_framework/components/button/button_icon/button_icon.js
Normal file
44
ui_framework/components/button/button_icon/button_icon.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
import React, {
|
||||
PropTypes,
|
||||
} from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
const ICON_TYPES = [
|
||||
'create',
|
||||
'delete',
|
||||
'previous',
|
||||
'next',
|
||||
'loading',
|
||||
];
|
||||
|
||||
const KuiButtonIcon = props => {
|
||||
const typeToClassNameMap = {
|
||||
create: 'fa-plus',
|
||||
delete: 'fa-trash',
|
||||
previous: 'fa-chevron-left',
|
||||
next: 'fa-chevron-right',
|
||||
loading: 'fa-spinner fa-spin',
|
||||
};
|
||||
|
||||
const iconClasses = classNames('kuiButton__icon kuiIcon', props.className, {
|
||||
[typeToClassNameMap[props.type]]: props.type,
|
||||
});
|
||||
|
||||
// Purely decorative icons should be hidden from screen readers. Button icons are purely
|
||||
// decorate since assisted users will want to click on the button itself, not the icon within.
|
||||
// (https://www.w3.org/WAI/GL/wiki/Using_aria-hidden%3Dtrue_on_an_icon_font_that_AT_should_ignore)
|
||||
return (
|
||||
<span aria-hidden="true" className={iconClasses} />
|
||||
);
|
||||
};
|
||||
|
||||
KuiButtonIcon.propTypes = {
|
||||
type: PropTypes.oneOf(ICON_TYPES),
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export {
|
||||
ICON_TYPES,
|
||||
KuiButtonIcon,
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
render,
|
||||
} from 'enzyme';
|
||||
|
||||
import {
|
||||
ICON_TYPES,
|
||||
KuiButtonIcon,
|
||||
} from './button_icon';
|
||||
|
||||
describe('KuiButtonIcon', () => {
|
||||
describe('Baseline', () => {
|
||||
test('is rendered', () => {
|
||||
const $buttonIcon = render(
|
||||
<KuiButtonIcon />
|
||||
);
|
||||
|
||||
expect($buttonIcon)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Props', () => {
|
||||
describe('type', () => {
|
||||
ICON_TYPES.forEach(type => {
|
||||
describe(type, () => {
|
||||
test(`renders the ${type} class`, () => {
|
||||
const $buttonIcon = render(<KuiButtonIcon type={ type } />);
|
||||
expect($buttonIcon).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('className', () => {
|
||||
test('renders the classes', () => {
|
||||
const $buttonIcon = render(
|
||||
<KuiButtonIcon className="testClass1 testClass2" />
|
||||
);
|
||||
|
||||
expect($buttonIcon)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
7
ui_framework/components/button/index.js
Normal file
7
ui_framework/components/button/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
export {
|
||||
KuiButton,
|
||||
KuiLinkButton,
|
||||
KuiSubmitButton,
|
||||
} from './button';
|
||||
export { KuiButtonIcon } from './button_icon/button_icon';
|
||||
export { KuiButtonGroup } from './button_group/button_group';
|
120
ui_framework/components/button/link_button.test.js
Normal file
120
ui_framework/components/button/link_button.test.js
Normal file
|
@ -0,0 +1,120 @@
|
|||
import React from 'react';
|
||||
import { render } from 'enzyme';
|
||||
|
||||
import {
|
||||
BUTTON_TYPES,
|
||||
KuiLinkButton,
|
||||
} from './button';
|
||||
|
||||
describe('KuiLinkButton', () => {
|
||||
describe('Baseline', () => {
|
||||
test('is rendered', () => {
|
||||
const $button = render(
|
||||
<KuiLinkButton />
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('HTML attributes are rendered (and disabled renders a class)', () => {
|
||||
const $button = render(
|
||||
<KuiLinkButton
|
||||
aria-label="aria label"
|
||||
className="testClass1 testClass2"
|
||||
data-test-subj="test subject string"
|
||||
disabled
|
||||
href="#"
|
||||
target="_blank"
|
||||
/>
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Props', () => {
|
||||
describe('type', () => {
|
||||
BUTTON_TYPES.forEach(type => {
|
||||
describe(type, () => {
|
||||
test(`renders the ${type} class`, () => {
|
||||
const $button = render(<KuiLinkButton type={type} />);
|
||||
expect($button).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('icon', () => {
|
||||
test('is rendered with children', () => {
|
||||
const $button = render(
|
||||
<KuiLinkButton icon="Icon">
|
||||
Hello
|
||||
</KuiLinkButton>
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('is rendered without children', () => {
|
||||
const $button = render(
|
||||
<KuiLinkButton icon="Icon" />
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('iconPosition', () => {
|
||||
test('moves the icon to the right', () => {
|
||||
const $button = render(
|
||||
<KuiLinkButton
|
||||
icon="Icon"
|
||||
iconPosition="right"
|
||||
>
|
||||
Hello
|
||||
</KuiLinkButton>
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('children', () => {
|
||||
test('is rendered', () => {
|
||||
const $button = render(
|
||||
<KuiLinkButton>
|
||||
Hello
|
||||
</KuiLinkButton>
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isLoading', () => {
|
||||
test('renders a spinner', () => {
|
||||
const $button = render(
|
||||
<KuiLinkButton isLoading />
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
|
||||
test(`doesn't render the icon prop`, () => {
|
||||
const $button = render(
|
||||
<KuiLinkButton isLoading icon="Icon" />
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
85
ui_framework/components/button/submit_button.test.js
Normal file
85
ui_framework/components/button/submit_button.test.js
Normal file
|
@ -0,0 +1,85 @@
|
|||
import React from 'react';
|
||||
import { render, shallow } from 'enzyme';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import {
|
||||
BUTTON_TYPES,
|
||||
KuiSubmitButton,
|
||||
} from './button';
|
||||
|
||||
describe('KuiSubmitButton', () => {
|
||||
describe('Baseline', () => {
|
||||
test('is rendered', () => {
|
||||
const $button = render(
|
||||
<KuiSubmitButton />
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('HTML attributes are rendered', () => {
|
||||
const $button = render(
|
||||
<KuiSubmitButton
|
||||
aria-label="aria label"
|
||||
className="testClass1 testClass2"
|
||||
data-test-subj="test subject string"
|
||||
disabled
|
||||
/>
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Props', () => {
|
||||
describe('type', () => {
|
||||
BUTTON_TYPES.forEach(type => {
|
||||
describe(type, () => {
|
||||
test(`renders the ${type} class`, () => {
|
||||
const $button = render(<KuiSubmitButton type={type} />);
|
||||
expect($button).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('children', () => {
|
||||
test('is rendered as value', () => {
|
||||
const $button = render(
|
||||
<KuiSubmitButton>
|
||||
Hello
|
||||
</KuiSubmitButton>
|
||||
);
|
||||
|
||||
expect($button)
|
||||
.toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('onClick', () => {
|
||||
test(`isn't called upon instantiation`, () => {
|
||||
const onClickHandler = sinon.stub();
|
||||
|
||||
shallow(
|
||||
<KuiSubmitButton onClick={onClickHandler} />
|
||||
);
|
||||
|
||||
sinon.assert.notCalled(onClickHandler);
|
||||
});
|
||||
|
||||
test('is called when the button is clicked', () => {
|
||||
const onClickHandler = sinon.stub();
|
||||
|
||||
const $button = shallow(
|
||||
<KuiSubmitButton onClick={onClickHandler} />
|
||||
);
|
||||
|
||||
$button.simulate('click');
|
||||
|
||||
sinon.assert.calledOnce(onClickHandler);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
7
ui_framework/components/index.js
Normal file
7
ui_framework/components/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
export {
|
||||
KuiButton,
|
||||
KuiButtonGroup,
|
||||
KuiButtonIcon,
|
||||
KuiLinkButton,
|
||||
KuiSubmitButton,
|
||||
} from './button';
|
190
ui_framework/dist/ui_framework.css
vendored
190
ui_framework/dist/ui_framework.css
vendored
|
@ -116,11 +116,23 @@ body {
|
|||
margin-left: 10px;
|
||||
/* 1 */ }
|
||||
|
||||
/**
|
||||
* 1. Links can't have a disabled attribute, so they can't support :disabled.
|
||||
*/
|
||||
/**
|
||||
* 1. Links can't have a disabled attribute, so they can't support :enabled.
|
||||
*/
|
||||
/**
|
||||
* 1. Links can't have a disabled attribute, so they can't support :enabled.
|
||||
*/
|
||||
/**
|
||||
* 1. Links can't have a disabled attribute, so they can't support :enabled.
|
||||
*/
|
||||
/**
|
||||
* 1. Setting to inline-block guarantees the same height when applied to both
|
||||
* button elements and anchor tags.
|
||||
* 2. Disable for Angular.
|
||||
* 3. Safari won't respect :enabled:active on links.
|
||||
* 2. Links can be focused when they're "disabled" (since we're just faking this with a class), but
|
||||
* at least make them look like they're not focused.
|
||||
*/
|
||||
.kuiButton {
|
||||
display: inline-block;
|
||||
|
@ -137,14 +149,28 @@ body {
|
|||
border: none;
|
||||
border-radius: 4px; }
|
||||
.kuiButton:disabled {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
/* 2 */ }
|
||||
.kuiButton:active {
|
||||
/* 3 */
|
||||
cursor: default; }
|
||||
a.kuiButton.kuiButton-isDisabled {
|
||||
cursor: default; }
|
||||
.kuiButton:enabled:active {
|
||||
-webkit-transform: translateY(1px);
|
||||
transform: translateY(1px); }
|
||||
a.kuiButton:not(.kuiButton-isDisabled):active {
|
||||
/* 1 */
|
||||
-webkit-transform: translateY(1px);
|
||||
transform: translateY(1px); }
|
||||
.kuiButton:focus {
|
||||
outline: none;
|
||||
/* 2 */ }
|
||||
.kuiButton:not(a):enabled:focus {
|
||||
z-index: 1;
|
||||
/* 1 */
|
||||
outline: none !important;
|
||||
/* 2 */
|
||||
box-shadow: 0 0 0 1px #ffffff, 0 0 0 2px #6EADC1;
|
||||
/* 3 */ }
|
||||
a.kuiButton:not(.kuiButton-isDisabled):focus {
|
||||
/* 1 */
|
||||
z-index: 1;
|
||||
/* 1 */
|
||||
outline: none !important;
|
||||
|
@ -152,89 +178,175 @@ body {
|
|||
box-shadow: 0 0 0 1px #ffffff, 0 0 0 2px #6EADC1;
|
||||
/* 3 */ }
|
||||
|
||||
.kuiButton--iconText .kuiButton__icon:first-child {
|
||||
margin-right: 4px; }
|
||||
/**
|
||||
* 1. Solves whitespace problems introduced by inline elements.
|
||||
*/
|
||||
.kuiButton__inner {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
/* 1 */
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
/* 1 */ }
|
||||
|
||||
.kuiButton--iconText .kuiButton__icon:last-child {
|
||||
margin-left: 4px; }
|
||||
.kuiButton--iconText .kuiButton__icon:first-child:not(:only-child) {
|
||||
margin-right: 8px; }
|
||||
|
||||
.kuiButton--iconText .kuiButton__icon:last-child:not(:only-child) {
|
||||
margin-left: 8px; }
|
||||
|
||||
/**
|
||||
* 1. Override Bootstrap.
|
||||
* 2. Safari won't respect :enabled:hover/active on links.
|
||||
*/
|
||||
.kuiButton--basic {
|
||||
color: #5a5a5a;
|
||||
background-color: #F2F2F2; }
|
||||
.kuiButton--basic:focus {
|
||||
color: #5a5a5a !important;
|
||||
/* 1 */ }
|
||||
.kuiButton--basic:hover, .kuiButton--basic:active {
|
||||
/* 2 */
|
||||
.kuiButton--basic:not(a):enabled:focus {
|
||||
color: #5a5a5a; }
|
||||
a.kuiButton--basic:not(.kuiButton-isDisabled):focus {
|
||||
/* 1 */
|
||||
color: #5a5a5a; }
|
||||
.kuiButton--basic:enabled:hover {
|
||||
color: #ffffff !important;
|
||||
/* 1 */
|
||||
background-color: #9B9B9B !important; }
|
||||
background-color: #9B9B9B !important;
|
||||
/* 1 */ }
|
||||
a.kuiButton--basic:not(.kuiButton-isDisabled):hover {
|
||||
/* 1 */
|
||||
color: #ffffff !important;
|
||||
/* 1 */
|
||||
background-color: #9B9B9B !important;
|
||||
/* 1 */ }
|
||||
.kuiButton--basic:enabled:active {
|
||||
color: #ffffff !important;
|
||||
/* 1 */
|
||||
background-color: #9B9B9B !important;
|
||||
/* 1 */ }
|
||||
a.kuiButton--basic:not(.kuiButton-isDisabled):active {
|
||||
/* 1 */
|
||||
color: #ffffff !important;
|
||||
/* 1 */
|
||||
background-color: #9B9B9B !important;
|
||||
/* 1 */ }
|
||||
.kuiButton--basic:disabled {
|
||||
color: #9B9B9B; }
|
||||
a.kuiButton--basic.kuiButton-isDisabled {
|
||||
color: #9B9B9B; }
|
||||
|
||||
/**
|
||||
* 1. Override Bootstrap.
|
||||
* 2. Safari won't respect :enabled:hover/active on links.
|
||||
*/
|
||||
.kuiButton--primary {
|
||||
color: #FFFFFF;
|
||||
background-color: #6EADC1; }
|
||||
.kuiButton--primary:hover, .kuiButton--primary:active {
|
||||
/* 2 */
|
||||
.kuiButton--primary:not(a):enabled:focus {
|
||||
color: #FFFFFF; }
|
||||
a.kuiButton--primary:not(.kuiButton-isDisabled):focus {
|
||||
/* 1 */
|
||||
color: #FFFFFF; }
|
||||
.kuiButton--primary:enabled:hover {
|
||||
color: #FFFFFF !important;
|
||||
/* 1 */
|
||||
background-color: #006E8A; }
|
||||
a.kuiButton--primary:not(.kuiButton-isDisabled):hover {
|
||||
/* 1 */
|
||||
color: #FFFFFF !important;
|
||||
/* 1 */
|
||||
background-color: #006E8A; }
|
||||
.kuiButton--primary:enabled:active {
|
||||
color: #FFFFFF !important;
|
||||
/* 1 */
|
||||
background-color: #006E8A; }
|
||||
a.kuiButton--primary:not(.kuiButton-isDisabled):active {
|
||||
/* 1 */
|
||||
color: #FFFFFF !important;
|
||||
/* 1 */
|
||||
background-color: #006E8A; }
|
||||
.kuiButton--primary:disabled {
|
||||
background-color: #B6D6E0; }
|
||||
.kuiButton--primary:focus {
|
||||
color: #FFFFFF !important;
|
||||
/* 1 */ }
|
||||
a.kuiButton--primary.kuiButton-isDisabled {
|
||||
background-color: #B6D6E0; }
|
||||
|
||||
/**
|
||||
* 1. Override Bootstrap.
|
||||
* 2. Safari won't respect :enabled:hover/active on links.
|
||||
*/
|
||||
.kuiButton--danger {
|
||||
color: #FFFFFF;
|
||||
background-color: #D76051; }
|
||||
.kuiButton--danger:hover, .kuiButton--danger:active {
|
||||
/* 2 */
|
||||
color: #FFFFFF !important;
|
||||
/* 1 */
|
||||
background-color: #A52E1F; }
|
||||
.kuiButton--danger:disabled {
|
||||
background-color: #efc0ba; }
|
||||
.kuiButton--danger:focus {
|
||||
.kuiButton--danger:not(a):enabled:focus {
|
||||
z-index: 1;
|
||||
/* 1 */
|
||||
outline: none !important;
|
||||
/* 2 */
|
||||
box-shadow: 0 0 0 1px #ffffff, 0 0 0 2px #ff523c;
|
||||
/* 3 */
|
||||
color: #FFFFFF; }
|
||||
a.kuiButton--danger:not(.kuiButton-isDisabled):focus {
|
||||
/* 1 */
|
||||
z-index: 1;
|
||||
/* 1 */
|
||||
outline: none !important;
|
||||
/* 2 */
|
||||
box-shadow: 0 0 0 1px #ffffff, 0 0 0 2px #ff523c;
|
||||
/* 3 */
|
||||
color: #FFFFFF; }
|
||||
.kuiButton--danger:enabled:hover {
|
||||
color: #FFFFFF !important;
|
||||
/* 1 */ }
|
||||
/* 1 */
|
||||
background-color: #A52E1F; }
|
||||
a.kuiButton--danger:not(.kuiButton-isDisabled):hover {
|
||||
/* 1 */
|
||||
color: #FFFFFF !important;
|
||||
/* 1 */
|
||||
background-color: #A52E1F; }
|
||||
.kuiButton--danger:enabled:active {
|
||||
color: #FFFFFF !important;
|
||||
/* 1 */
|
||||
background-color: #A52E1F; }
|
||||
a.kuiButton--danger:not(.kuiButton-isDisabled):active {
|
||||
/* 1 */
|
||||
color: #FFFFFF !important;
|
||||
/* 1 */
|
||||
background-color: #A52E1F; }
|
||||
.kuiButton--danger:disabled {
|
||||
background-color: #efc0ba; }
|
||||
a.kuiButton--danger.kuiButton-isDisabled {
|
||||
background-color: #efc0ba; }
|
||||
|
||||
/**
|
||||
* 1. Override Bootstrap.
|
||||
* 2. Override either Bootstrap or Timelion styles.
|
||||
* 3. Safari won't respect :enabled:hover/active on links.
|
||||
*/
|
||||
.kuiButton--hollow {
|
||||
color: #3CAED2 !important;
|
||||
/* 2 */
|
||||
background-color: transparent; }
|
||||
.kuiButton--hollow:hover, .kuiButton--hollow:active {
|
||||
/* 3 */
|
||||
.kuiButton--hollow:enabled:hover {
|
||||
color: #006E8A !important;
|
||||
/* 1 */
|
||||
text-decoration: underline; }
|
||||
a.kuiButton--hollow:not(.kuiButton-isDisabled):hover {
|
||||
/* 1 */
|
||||
color: #006E8A !important;
|
||||
/* 1 */
|
||||
text-decoration: underline; }
|
||||
.kuiButton--hollow:enabled:active {
|
||||
color: #006E8A !important;
|
||||
/* 1 */
|
||||
text-decoration: underline; }
|
||||
a.kuiButton--hollow:not(.kuiButton-isDisabled):active {
|
||||
/* 1 */
|
||||
color: #006E8A !important;
|
||||
/* 1 */
|
||||
text-decoration: underline; }
|
||||
.kuiButton--hollow:disabled {
|
||||
color: #dddddd !important;
|
||||
/* 1 */ }
|
||||
color: #dddddd; }
|
||||
a.kuiButton--hollow.kuiButton-isDisabled {
|
||||
color: #dddddd; }
|
||||
|
||||
.kuiButtonGroup {
|
||||
display: -webkit-box;
|
||||
|
|
|
@ -15,16 +15,21 @@ export class GuideDemo extends Component {
|
|||
}
|
||||
|
||||
update() {
|
||||
// Inject HTML
|
||||
// We'll just render the children if we have them.
|
||||
if (this.props.children) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Inject HTML.
|
||||
this.content.innerHTML = this.props.html;
|
||||
|
||||
// Inject JS
|
||||
// Inject JS.
|
||||
const js = document.createElement('script');
|
||||
js.type = 'text/javascript';
|
||||
js.innerHTML = this.props.js;
|
||||
this.content.appendChild(js);
|
||||
|
||||
// Inject CSS
|
||||
// Inject CSS.
|
||||
const css = document.createElement('style');
|
||||
css.innerHTML = this.props.css;
|
||||
this.content.appendChild(css);
|
||||
|
@ -38,12 +43,14 @@ export class GuideDemo extends Component {
|
|||
|
||||
return (
|
||||
<div className={classes} ref={c => (this.content = c)}>
|
||||
{this.props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GuideDemo.propTypes = {
|
||||
children: PropTypes.node,
|
||||
js: PropTypes.string.isRequired,
|
||||
html: PropTypes.string.isRequired,
|
||||
css: PropTypes.string.isRequired,
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
export { renderToHtml } from './string/render_to_html';
|
||||
|
||||
export * from './example/create_example';
|
||||
export { default as createExample } from './example/create_example';
|
||||
|
||||
export * from './js_injector/js_injector';
|
||||
export { default as JsInjector } from './js_injector/js_injector';
|
||||
|
||||
export * from './routes/routes';
|
||||
export { default as Routes } from './routes/routes';
|
||||
|
||||
export * from './string/slugify';
|
||||
export { default as Slugify } from './string/slugify';
|
||||
|
|
17
ui_framework/doc_site/src/services/string/render_to_html.js
Normal file
17
ui_framework/doc_site/src/services/string/render_to_html.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
render,
|
||||
} from 'enzyme';
|
||||
|
||||
import html from 'html';
|
||||
|
||||
export function renderToHtml(componentReference, props = {}) {
|
||||
// Create the React element, render it and get its HTML, then format it prettily.
|
||||
const element = React.createElement(componentReference, props);
|
||||
const htmlString = render(element).html();
|
||||
return html.prettyPrint(htmlString, {
|
||||
indent_size: 2,
|
||||
unformatted: [], // Expand all tags, including spans
|
||||
});
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
export function getIsCodeViewerOpen(state) {
|
||||
return state.codeViewer.isOpen
|
||||
return state.codeViewer.isOpen;
|
||||
}
|
||||
|
||||
export function getSections(state) {
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
} from '../components';
|
||||
|
||||
// Inject version into header.
|
||||
const pkg = require('json!../../../../package.json');
|
||||
const pkg = require('../../../../package.json');
|
||||
|
||||
export class AppView extends Component {
|
||||
constructor(props) {
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<button class="kuiButton kuiButton--basic">
|
||||
Basic button
|
||||
</button>
|
||||
|
||||
<hr class="guideBreak">
|
||||
|
||||
<button disabled class="kuiButton kuiButton--basic">
|
||||
Basic button, disabled
|
||||
</button>
|
26
ui_framework/doc_site/src/views/button/button_basic.js
Normal file
26
ui_framework/doc_site/src/views/button/button_basic.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
KuiButton,
|
||||
} from '../../../../components';
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<KuiButton
|
||||
type="basic"
|
||||
onClick={() => window.alert('Button clicked')}
|
||||
>
|
||||
Basic button
|
||||
</KuiButton>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButton
|
||||
type="basic"
|
||||
onClick={() => window.alert('Button clicked')}
|
||||
disabled
|
||||
>
|
||||
Basic button, disabled
|
||||
</KuiButton>
|
||||
</div>
|
||||
);
|
|
@ -1,9 +0,0 @@
|
|||
<button class="kuiButton kuiButton--danger">
|
||||
Danger button
|
||||
</button>
|
||||
|
||||
<hr class="guideBreak">
|
||||
|
||||
<button disabled class="kuiButton kuiButton--danger">
|
||||
Danger button, disabled
|
||||
</button>
|
23
ui_framework/doc_site/src/views/button/button_danger.js
Normal file
23
ui_framework/doc_site/src/views/button/button_danger.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
KuiButton,
|
||||
} from '../../../../components';
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<KuiButton type="danger">
|
||||
Danger button
|
||||
</KuiButton>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButton
|
||||
type="danger"
|
||||
disabled
|
||||
>
|
||||
Danger button, disabled
|
||||
</KuiButton>
|
||||
</div>
|
||||
|
||||
);
|
|
@ -1,17 +0,0 @@
|
|||
<button class="kuiButton kuiButton--basic">
|
||||
Button element
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
<input
|
||||
type="submit"
|
||||
class="kuiButton kuiButton--basic"
|
||||
value="Submit input element"
|
||||
>
|
||||
|
||||
|
||||
|
||||
<a href="#" class="kuiButton kuiButton--basic">
|
||||
Anchor element
|
||||
</a>
|
57
ui_framework/doc_site/src/views/button/button_elements.js
Normal file
57
ui_framework/doc_site/src/views/button/button_elements.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
KuiButton,
|
||||
KuiLinkButton,
|
||||
KuiSubmitButton,
|
||||
} from '../../../../components';
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<KuiButton type="basic">
|
||||
Button element
|
||||
</KuiButton>
|
||||
|
||||
|
||||
|
||||
<form onSubmit={e => {
|
||||
e.preventDefault();
|
||||
window.alert('Submit');
|
||||
}}>
|
||||
<KuiSubmitButton type="basic">
|
||||
Submit input element
|
||||
</KuiSubmitButton>
|
||||
</form>
|
||||
|
||||
|
||||
<form onSubmit={e => {
|
||||
e.preventDefault();
|
||||
window.alert('Submit');
|
||||
}}>
|
||||
<KuiSubmitButton type="basic" disabled>
|
||||
Submit input element, disabled
|
||||
</KuiSubmitButton>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<KuiLinkButton
|
||||
type="basic"
|
||||
href="http://www.google.com"
|
||||
target="_blank"
|
||||
>
|
||||
Anchor element
|
||||
</KuiLinkButton>
|
||||
|
||||
|
||||
|
||||
<KuiLinkButton
|
||||
type="basic"
|
||||
href="http://www.google.com"
|
||||
target="_blank"
|
||||
disabled
|
||||
>
|
||||
Anchor element, disabled
|
||||
</KuiLinkButton>
|
||||
</div>
|
||||
);
|
|
@ -3,6 +3,8 @@ import React, {
|
|||
PropTypes,
|
||||
} from 'react';
|
||||
|
||||
import { renderToHtml } from '../../services';
|
||||
|
||||
import {
|
||||
GuideDemo,
|
||||
GuideLink,
|
||||
|
@ -12,21 +14,54 @@ import {
|
|||
GuideText,
|
||||
} from '../../components';
|
||||
|
||||
const basicHtml = require('./button_basic.html');
|
||||
const hollowHtml = require('./button_hollow.html');
|
||||
const primaryHtml = require('./button_primary.html');
|
||||
const dangerHtml = require('./button_danger.html');
|
||||
const withIconHtml = require('./button_with_icon.html');
|
||||
const groupHtml = require('./button_group.html');
|
||||
const groupUnitedHtml = require('./button_group_united.html');
|
||||
const inToolBarHtml = require('./buttons_in_tool_bar.html');
|
||||
const elementsHtml = require('./button_elements.html');
|
||||
import Basic from './button_basic';
|
||||
const basicSource = require('!!raw!./button_basic');
|
||||
const basicHtml = renderToHtml(Basic);
|
||||
|
||||
import Hollow from './button_hollow';
|
||||
const hollowSource = require('!!raw!./button_hollow');
|
||||
const hollowHtml = renderToHtml(Hollow);
|
||||
|
||||
import Primary from './button_primary';
|
||||
const primarySource = require('!!raw!./button_primary');
|
||||
const primaryHtml = renderToHtml(Primary);
|
||||
|
||||
import Danger from './button_danger';
|
||||
const dangerSource = require('!!raw!./button_danger');
|
||||
const dangerHtml = renderToHtml(Danger);
|
||||
|
||||
import Loading from './button_loading';
|
||||
const loadingSource = require('!!raw!./button_loading');
|
||||
const loadingHtml = renderToHtml(Loading, { isLoading: true });
|
||||
|
||||
import WithIcon from './button_with_icon';
|
||||
const withIconSource = require('!!raw!./button_with_icon');
|
||||
const withIconHtml = renderToHtml(WithIcon);
|
||||
|
||||
import ButtonGroup from './button_group';
|
||||
const buttonGroupSource = require('!!raw!./button_group');
|
||||
const buttonGroupHtml = renderToHtml(ButtonGroup);
|
||||
|
||||
import ButtonGroupUnited from './button_group_united';
|
||||
const buttonGroupUnitedSource = require('!!raw!./button_group_united');
|
||||
const buttonGroupUnitedHtml = renderToHtml(ButtonGroupUnited);
|
||||
|
||||
import InToolBar from './buttons_in_tool_bar';
|
||||
const inToolBarSource = require('!!raw!./buttons_in_tool_bar');
|
||||
const inToolBarHtml = renderToHtml(InToolBar);
|
||||
|
||||
import Elements from './button_elements';
|
||||
const elementsSource = require('!!raw!./button_elements');
|
||||
const elementsHtml = renderToHtml(Elements);
|
||||
|
||||
export default props => (
|
||||
<GuidePage title={props.route.name}>
|
||||
<GuideSection
|
||||
title="Basic Button"
|
||||
source={[{
|
||||
type: GuideSectionTypes.JS,
|
||||
code: basicSource,
|
||||
}, {
|
||||
type: GuideSectionTypes.HTML,
|
||||
code: basicHtml,
|
||||
}]}
|
||||
|
@ -35,14 +70,17 @@ export default props => (
|
|||
Use the basic Button in most situations.
|
||||
</GuideText>
|
||||
|
||||
<GuideDemo
|
||||
html={basicHtml}
|
||||
/>
|
||||
<GuideDemo>
|
||||
<Basic />
|
||||
</GuideDemo>
|
||||
</GuideSection>
|
||||
|
||||
<GuideSection
|
||||
title="Hollow Button"
|
||||
source={[{
|
||||
type: GuideSectionTypes.JS,
|
||||
code: hollowSource,
|
||||
}, {
|
||||
type: GuideSectionTypes.HTML,
|
||||
code: hollowHtml,
|
||||
}]}
|
||||
|
@ -51,14 +89,17 @@ export default props => (
|
|||
Use the hollow Button when presenting a neutral action, e.g. a "Cancel" button.
|
||||
</GuideText>
|
||||
|
||||
<GuideDemo
|
||||
html={hollowHtml}
|
||||
/>
|
||||
<GuideDemo>
|
||||
<Hollow />
|
||||
</GuideDemo>
|
||||
</GuideSection>
|
||||
|
||||
<GuideSection
|
||||
title="Primary Button"
|
||||
source={[{
|
||||
type: GuideSectionTypes.JS,
|
||||
code: primarySource,
|
||||
}, {
|
||||
type: GuideSectionTypes.HTML,
|
||||
code: primaryHtml,
|
||||
}]}
|
||||
|
@ -68,14 +109,17 @@ export default props => (
|
|||
need to present more than one of these at a time.
|
||||
</GuideText>
|
||||
|
||||
<GuideDemo
|
||||
html={primaryHtml}
|
||||
/>
|
||||
<GuideDemo>
|
||||
<Primary />
|
||||
</GuideDemo>
|
||||
</GuideSection>
|
||||
|
||||
<GuideSection
|
||||
title="Danger Button"
|
||||
source={[{
|
||||
type: GuideSectionTypes.JS,
|
||||
code: dangerSource,
|
||||
}, {
|
||||
type: GuideSectionTypes.HTML,
|
||||
code: dangerHtml,
|
||||
}]}
|
||||
|
@ -84,48 +128,69 @@ export default props => (
|
|||
Danger Buttons represent irreversible, potentially regrettable actions.
|
||||
</GuideText>
|
||||
|
||||
<GuideDemo
|
||||
html={dangerHtml}
|
||||
/>
|
||||
<GuideDemo>
|
||||
<Danger />
|
||||
</GuideDemo>
|
||||
</GuideSection>
|
||||
|
||||
<GuideSection
|
||||
title="Loading Button"
|
||||
source={[{
|
||||
type: GuideSectionTypes.JS,
|
||||
code: loadingSource,
|
||||
}, {
|
||||
type: GuideSectionTypes.HTML,
|
||||
code: loadingHtml,
|
||||
}]}
|
||||
>
|
||||
<GuideDemo>
|
||||
<Loading />
|
||||
</GuideDemo>
|
||||
</GuideSection>
|
||||
|
||||
<GuideSection
|
||||
title="Button with icon"
|
||||
source={[{
|
||||
type: GuideSectionTypes.JS,
|
||||
code: withIconSource,
|
||||
}, {
|
||||
type: GuideSectionTypes.HTML,
|
||||
code: withIconHtml,
|
||||
}]}
|
||||
>
|
||||
<GuideText>
|
||||
You can toss an icon into a Button, with or without text.
|
||||
You can toss an icon into a Button, with or without text. You can also use a predefined icon
|
||||
or specify custom icon classes.
|
||||
</GuideText>
|
||||
|
||||
<GuideDemo
|
||||
html={withIconHtml}
|
||||
/>
|
||||
<GuideDemo>
|
||||
<WithIcon />
|
||||
</GuideDemo>
|
||||
</GuideSection>
|
||||
|
||||
<GuideSection
|
||||
title="ButtonGroup"
|
||||
source={[{
|
||||
type: GuideSectionTypes.JS,
|
||||
code: buttonGroupSource,
|
||||
}, {
|
||||
type: GuideSectionTypes.HTML,
|
||||
code: groupHtml,
|
||||
code: buttonGroupHtml,
|
||||
}]}
|
||||
>
|
||||
<GuideText>
|
||||
|
||||
</GuideText>
|
||||
|
||||
<GuideDemo
|
||||
html={groupHtml}
|
||||
/>
|
||||
<GuideDemo>
|
||||
<ButtonGroup />
|
||||
</GuideDemo>
|
||||
</GuideSection>
|
||||
|
||||
<GuideSection
|
||||
title="United ButtonGroup"
|
||||
source={[{
|
||||
type: GuideSectionTypes.JS,
|
||||
code: buttonGroupUnitedSource,
|
||||
}, {
|
||||
type: GuideSectionTypes.HTML,
|
||||
code: groupUnitedHtml,
|
||||
code: buttonGroupUnitedHtml,
|
||||
}]}
|
||||
>
|
||||
<GuideText>
|
||||
|
@ -138,14 +203,17 @@ export default props => (
|
|||
removed.
|
||||
</GuideText>
|
||||
|
||||
<GuideDemo
|
||||
html={groupUnitedHtml}
|
||||
/>
|
||||
<GuideDemo>
|
||||
<ButtonGroupUnited />
|
||||
</GuideDemo>
|
||||
</GuideSection>
|
||||
|
||||
<GuideSection
|
||||
title="In ToolBar"
|
||||
source={[{
|
||||
type: GuideSectionTypes.JS,
|
||||
code: inToolBarSource,
|
||||
}, {
|
||||
type: GuideSectionTypes.HTML,
|
||||
code: inToolBarHtml,
|
||||
}]}
|
||||
|
@ -154,14 +222,17 @@ export default props => (
|
|||
This example verifies that Buttons are legible against the ToolBar's background.
|
||||
</GuideText>
|
||||
|
||||
<GuideDemo
|
||||
html={inToolBarHtml}
|
||||
/>
|
||||
<GuideDemo>
|
||||
<InToolBar />
|
||||
</GuideDemo>
|
||||
</GuideSection>
|
||||
|
||||
<GuideSection
|
||||
title="Element variations"
|
||||
source={[{
|
||||
type: GuideSectionTypes.JS,
|
||||
code: elementsSource,
|
||||
}, {
|
||||
type: GuideSectionTypes.HTML,
|
||||
code: elementsHtml,
|
||||
}]}
|
||||
|
@ -170,9 +241,9 @@ export default props => (
|
|||
You can create a Button using a button element, link, or input[type="submit"].
|
||||
</GuideText>
|
||||
|
||||
<GuideDemo
|
||||
html={elementsHtml}
|
||||
/>
|
||||
<GuideDemo>
|
||||
<Elements />
|
||||
</GuideDemo>
|
||||
</GuideSection>
|
||||
</GuidePage>
|
||||
);
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
<div class="kuiButtonGroup">
|
||||
<button class="kuiButton kuiButton--basic">
|
||||
Cancel
|
||||
</button>
|
||||
<button class="kuiButton kuiButton--basic">
|
||||
Duplicate
|
||||
</button>
|
||||
<button class="kuiButton kuiButton--primary">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<hr class="guideBreak">
|
||||
|
||||
<div class="kuiButtonGroup">
|
||||
<button class="kuiButton kuiButton--basic">
|
||||
Button group with one button
|
||||
</button>
|
||||
</div>
|
32
ui_framework/doc_site/src/views/button/button_group.js
Normal file
32
ui_framework/doc_site/src/views/button/button_group.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
KuiButton,
|
||||
KuiButtonGroup,
|
||||
} from '../../../../components';
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<KuiButtonGroup>
|
||||
<KuiButton type="basic">
|
||||
Cancel
|
||||
</KuiButton>
|
||||
|
||||
<KuiButton type="basic">
|
||||
Duplicate
|
||||
</KuiButton>
|
||||
|
||||
<KuiButton type="primary">
|
||||
Save
|
||||
</KuiButton>
|
||||
</KuiButtonGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButtonGroup>
|
||||
<KuiButton type="basic">
|
||||
Button group with one button
|
||||
</KuiButton>
|
||||
</KuiButtonGroup>
|
||||
</div>
|
||||
);
|
|
@ -1,24 +0,0 @@
|
|||
<div class="kuiButtonGroup kuiButtonGroup--united">
|
||||
<button class="kuiButton kuiButton--basic">
|
||||
Option A
|
||||
</button>
|
||||
<button class="kuiButton kuiButton--basic">
|
||||
Option B
|
||||
</button>
|
||||
<button class="kuiButton kuiButton--basic">
|
||||
Option C
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<hr class="guideBreak">
|
||||
|
||||
<div class="kuiButtonGroup kuiButtonGroup--united">
|
||||
<button class="kuiButton kuiButton--basic kuiButton--iconText">
|
||||
<span class="kuiButton__icon kuiIcon fa-chevron-left"></span>
|
||||
<span>Back</span>
|
||||
</button>
|
||||
<button class="kuiButton kuiButton--basic kuiButton--iconText">
|
||||
<span>Next</span>
|
||||
<span class="kuiButton__icon kuiIcon fa-chevron-right"></span>
|
||||
</button>
|
||||
</div>
|
|
@ -0,0 +1,39 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
KuiButton,
|
||||
KuiButtonGroup,
|
||||
KuiButtonIcon,
|
||||
} from '../../../../components';
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<KuiButtonGroup isUnited>
|
||||
<KuiButton type="basic">
|
||||
Option A
|
||||
</KuiButton>
|
||||
|
||||
<KuiButton type="basic">
|
||||
Option B
|
||||
</KuiButton>
|
||||
|
||||
<KuiButton type="basic">
|
||||
Option C
|
||||
</KuiButton>
|
||||
</KuiButtonGroup>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButtonGroup isUnited>
|
||||
<KuiButton
|
||||
type="basic"
|
||||
icon={<KuiButtonIcon type="previous" />}
|
||||
/>
|
||||
|
||||
<KuiButton
|
||||
type="basic"
|
||||
icon={<KuiButtonIcon type="next" />}
|
||||
/>
|
||||
</KuiButtonGroup>
|
||||
</div>
|
||||
);
|
|
@ -1,9 +0,0 @@
|
|||
<button class="kuiButton kuiButton--hollow">
|
||||
Hollow button
|
||||
</button>
|
||||
|
||||
<hr class="guideBreak">
|
||||
|
||||
<button disabled class="kuiButton kuiButton--hollow">
|
||||
Hollow button, disabled
|
||||
</button>
|
22
ui_framework/doc_site/src/views/button/button_hollow.js
Normal file
22
ui_framework/doc_site/src/views/button/button_hollow.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
KuiButton,
|
||||
} from '../../../../components';
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<KuiButton type="hollow">
|
||||
Hollow button
|
||||
</KuiButton>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButton
|
||||
type="hollow"
|
||||
disabled
|
||||
>
|
||||
Hollow button, disabled
|
||||
</KuiButton>
|
||||
</div>
|
||||
);
|
59
ui_framework/doc_site/src/views/button/button_loading.js
Normal file
59
ui_framework/doc_site/src/views/button/button_loading.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
import React, {
|
||||
Component,
|
||||
} from 'react';
|
||||
|
||||
import {
|
||||
KuiButtonIcon,
|
||||
KuiButton,
|
||||
} from '../../../../components';
|
||||
|
||||
export default class LoadingButton extends Component {
|
||||
constructor(props) {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
isLoading: props.isLoading || false,
|
||||
};
|
||||
|
||||
this.onClick = this.onClick.bind(this);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
this.setState({
|
||||
isLoading: true,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
});
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<KuiButton
|
||||
type="basic"
|
||||
onClick={this.onClick}
|
||||
isLoading={this.state.isLoading}
|
||||
disabled={this.state.isLoading}
|
||||
>
|
||||
{this.state.isLoading ? 'Loading...' : 'Load more'}
|
||||
</KuiButton>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButton
|
||||
type="primary"
|
||||
onClick={this.onClick}
|
||||
icon={<KuiButtonIcon type="create" />}
|
||||
isLoading={this.state.isLoading}
|
||||
disabled={this.state.isLoading}
|
||||
>
|
||||
{this.state.isLoading ? 'Creating...' : 'Create'}
|
||||
</KuiButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
<button class="kuiButton kuiButton--primary">
|
||||
Primary button
|
||||
</button>
|
||||
|
||||
<hr class="guideBreak">
|
||||
|
||||
<button disabled class="kuiButton kuiButton--primary">
|
||||
Primary button, disabled
|
||||
</button>
|
22
ui_framework/doc_site/src/views/button/button_primary.js
Normal file
22
ui_framework/doc_site/src/views/button/button_primary.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
KuiButton,
|
||||
} from '../../../../components';
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<KuiButton type="primary">
|
||||
Primary button
|
||||
</KuiButton>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButton
|
||||
type="primary"
|
||||
disabled
|
||||
>
|
||||
Primary button, disabled
|
||||
</KuiButton>
|
||||
</div>
|
||||
);
|
|
@ -1,17 +0,0 @@
|
|||
<button class="kuiButton kuiButton--basic kuiButton--iconText">
|
||||
<span class="kuiButton__icon kuiIcon fa-gear"></span>
|
||||
<span>Settings</span>
|
||||
</button>
|
||||
|
||||
<hr class="guideBreak">
|
||||
|
||||
<button class="kuiButton kuiButton--basic kuiButton--iconText">
|
||||
<span>Next</span>
|
||||
<span class="kuiButton__icon kuiIcon fa-chevron-right"></span>
|
||||
</button>
|
||||
|
||||
<hr class="guideBreak">
|
||||
|
||||
<button class="kuiButton kuiButton--basic">
|
||||
<span class="kuiButton__icon kuiIcon fa-gear"></span>
|
||||
</button>
|
61
ui_framework/doc_site/src/views/button/button_with_icon.js
Normal file
61
ui_framework/doc_site/src/views/button/button_with_icon.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
KuiButton,
|
||||
KuiButtonIcon,
|
||||
} from '../../../../components';
|
||||
|
||||
export default () => (
|
||||
<div>
|
||||
<KuiButton
|
||||
type="primary"
|
||||
icon={<KuiButtonIcon type="create" />}
|
||||
>
|
||||
Create
|
||||
</KuiButton>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButton
|
||||
type="danger"
|
||||
icon={<KuiButtonIcon type="delete" />}
|
||||
>
|
||||
Delete
|
||||
</KuiButton>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButton
|
||||
type="basic"
|
||||
icon={<KuiButtonIcon type="previous" />}
|
||||
>
|
||||
Previous
|
||||
</KuiButton>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButton
|
||||
type="basic"
|
||||
icon={<KuiButtonIcon type="next" />}
|
||||
iconPosition='right'
|
||||
>
|
||||
Next
|
||||
</KuiButton>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButton
|
||||
type="basic"
|
||||
icon={<KuiButtonIcon type="loading" />}
|
||||
>
|
||||
Loading
|
||||
</KuiButton>
|
||||
|
||||
<br />
|
||||
|
||||
<KuiButton
|
||||
type="basic"
|
||||
icon={<KuiButtonIcon className="fa-plane" />}
|
||||
/>
|
||||
</div>
|
||||
);
|
|
@ -1,25 +0,0 @@
|
|||
<div class="kuiToolBar">
|
||||
<button class="kuiButton kuiButton--basic">
|
||||
Basic button
|
||||
</button>
|
||||
|
||||
<button disabled class="kuiButton kuiButton--basic">
|
||||
Basic button, disabled
|
||||
</button>
|
||||
|
||||
<button class="kuiButton kuiButton--primary">
|
||||
Primary button
|
||||
</button>
|
||||
|
||||
<button disabled class="kuiButton kuiButton--primary">
|
||||
Primary button, disabled
|
||||
</button>
|
||||
|
||||
<button class="kuiButton kuiButton--danger">
|
||||
Danger button
|
||||
</button>
|
||||
|
||||
<button disabled class="kuiButton kuiButton--danger">
|
||||
Danger button, disabled
|
||||
</button>
|
||||
</div>
|
|
@ -0,0 +1,42 @@
|
|||
import React from 'react';
|
||||
|
||||
import {
|
||||
KuiButton,
|
||||
} from '../../../../components';
|
||||
|
||||
export default () => (
|
||||
<div className="kuiToolBar">
|
||||
<KuiButton type="basic">
|
||||
Basic button
|
||||
</KuiButton>
|
||||
|
||||
<KuiButton
|
||||
type="basic"
|
||||
disabled
|
||||
>
|
||||
Basic button, disabled
|
||||
</KuiButton>
|
||||
|
||||
<KuiButton type="primary">
|
||||
Primary button
|
||||
</KuiButton>
|
||||
|
||||
<KuiButton
|
||||
type="primary"
|
||||
disabled
|
||||
>
|
||||
Primary button, disabled
|
||||
</KuiButton>
|
||||
|
||||
<KuiButton type="danger">
|
||||
Danger button
|
||||
</KuiButton>
|
||||
|
||||
<KuiButton
|
||||
type="danger"
|
||||
disabled
|
||||
>
|
||||
Danger button, disabled
|
||||
</KuiButton>
|
||||
</div>
|
||||
);
|
|
@ -18,8 +18,18 @@ module.exports = {
|
|||
]
|
||||
},
|
||||
|
||||
// These are necessasry for using Enzyme with Webpack (https://github.com/airbnb/enzyme/blob/master/docs/guides/webpack.md).
|
||||
externals: {
|
||||
'react/lib/ExecutionEnvironment': true,
|
||||
'react/lib/ReactContext': true,
|
||||
'react/addons': true,
|
||||
},
|
||||
|
||||
module: {
|
||||
loaders: [{
|
||||
test: /\.json$/,
|
||||
loader: 'json-loader',
|
||||
}, {
|
||||
test: /\.jsx?$/,
|
||||
loader: 'babel',
|
||||
exclude: /node_modules/,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue