Skip to content

Commit

Permalink
[K7] Add KUI Yeoman generator.
Browse files Browse the repository at this point in the history
* Support creation of components.
* Add documentation generator for main page, demo, and sandbox.

Add additional documentation snippets to KUI generator. (elastic#13076)

Fix incorrect use of double quotes in KUI generator snippet. (elastic#13086)

Remove infrequently used imports from the KUI generator test template. (elastic#13110)
  • Loading branch information
cjcenizal committed Aug 11, 2017
1 parent d9a48c6 commit 390b371
Show file tree
Hide file tree
Showing 18 changed files with 691 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ selenium
ui_framework/doc_site/build
!ui_framework/doc_site/build/index.html
yarn.lock
.yo-rc.json
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@
"mocha": "echo 'use `node scripts/mocha`' && false",
"sterilize": "grunt sterilize",
"uiFramework:start": "grunt uiFramework:start",
"uiFramework:build": "grunt uiFramework:build"
"uiFramework:build": "grunt uiFramework:build",
"uiFramework:create": "yo ./ui_framework/generator-kui/app/component.js",
"uiFramework:document": "yo ./ui_framework/generator-kui/app/documentation.js"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -208,6 +210,7 @@
"angular-mocks": "1.4.7",
"babel-eslint": "7.2.3",
"chai": "3.5.0",
"chalk": "2.0.1",
"chance": "1.0.6",
"cheerio": "0.22.0",
"chokidar": "1.6.0",
Expand Down Expand Up @@ -275,7 +278,8 @@
"supertest": "3.0.0",
"supertest-as-promised": "2.0.2",
"tree-kill": "1.1.0",
"webpack-dev-server": "1.14.1"
"webpack-dev-server": "1.14.1",
"yeoman-generator": "1.1.1"
},
"engines": {
"node": "6.11.1",
Expand Down
107 changes: 82 additions & 25 deletions ui_framework/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,67 @@ fully-tested the code is.

See the documentation in [`scripts/jest.js`](../scripts/jest.js) for more options.

### React components

Here are the components you can import from the Framework:

```javascript
import {
KuiButton,
KuiButtonGroup,
KuiButtonIcon,
} from '../path/to/ui_framework/components';
```

## Creating components

There are four steps to creating a new component:

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.
3. Write tests.
4. Document it with examples in `ui_framework/doc_site`.

You can do this using Yeoman (the easy way), or you can do it manually (the hard way).

### Using Yeoman

#### Install Yeoman

```bash
npm install -g yo
```

#### Create a new component

From the command line, run `npm run uiFramework:create`.

First, you'll be prompted for what kind of component to create:

| Choice | Description |
|---|---|
| Stateless function | A stateless functional React component |
| Component class | A class-based React component |

Next, you'll enter a series of prompts.

#### "What's the name of this component?"

Yeoman will ask you what to name the file. It expects you to provide the name
in snake case. Yeoman will automatically add file extensions and a "kui" prefix so you should leave those out.

#### "Where do you want to create this component's files?"

This defaults to the last directory you specified for this prompt, or to the UI Framework's
components directory if you haven't specified one. To change this location, type in the path to the
directory where the files should live.

If you want Yeoman to automatically generate a directory to organize the files,
that directory will be created inside of the location you specify (see next prompt).

#### "Does it need its own directory?""

This defaults to `YES`. This will automatically generate a directory with the
same name as the file, but without a "kui" prefix.

#### Done!

### Create component SCSS
Yeoman will generate the files you need in your project's folder system.

For your convenience, it will also output some snippets you can tweak to import
and re-export the generated JS and SCSS files.

### Manually

#### Create component SCSS

1. Create a directory for your component in `ui_framework/components`.
2. In this directory, create `_{component name}.scss`.
Expand All @@ -54,15 +93,25 @@ you created.

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

### Create the React component
#### 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`.

This makes your React component available for import into Kibana.

### Document the component with examples
#### Test the component

1. Start Jest in watch mode by running `node scripts/jest --watch`.
2. Create test files with the name pattern of `{component name}.test.js`.
3. Write your tests and see them fail or succeed.

To see how well the components have been covered by tests, you can run
`node scripts/jest --coverage` and check the generated report in
`target/jest-coverage/index.html`.

#### 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.
Expand All @@ -80,15 +129,22 @@ complex they should be. In general, your examples should demonstrate:
content.
* The various states of the component, e.g. disabled, selected, empty of content, error state.

### Test the component
## Creating documentation

1. Start Jest in watch mode by running `node scripts/jest --watch`.
2. Create test files with the name pattern of `{component name}.test.js`.
3. Write your tests and see them fail or succeed.
You can use the same Yeoman generator referenced above to create documentation.

To see how well the components have been covered by tests, you can run
`node scripts/jest --coverage` and check the generated report in
`target/jest-coverage/index.html`.
From the command line, run `npm run uiFramework:document`.

First, you'll be prompted for what kind of documentation to create:

| Choice | Description |
|---|---|
| Page | A page for documenting a component(s) with multiple demos |
| Page demo | An individual demo of a particular component use case |
| Sandbox | An empty document where you can do pretty much anything |

Just follow the prompts and your documentation files will be created.
You can use the snippets that are printed to the terminal to integrate these files into the UI Framework documentation site.

## Principles

Expand All @@ -101,7 +157,8 @@ 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).
Check out our [CSS style guide](https://github.com/elastic/kibana/blob/master/style_guides/css_style_guide.md)
and [SCSS style guide](https://github.com/elastic/kibana/blob/master/style_guides/scss_style_guide.md).

## Benefits

Expand Down
28 changes: 28 additions & 0 deletions ui_framework/generator-kui/app/component.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const Generator = require('yeoman-generator');

const componentGenerator = require.resolve('../component/index.js');

module.exports = class extends Generator {
prompting() {
return this.prompt([{
message: 'What do you want to create?',
name: 'fileType',
type: 'list',
choices: [{
name: 'Stateless function',
value: 'function',
}, {
name: 'Component class',
value: 'component',
}],
}]).then(answers => {
this.config = answers;
});
}

writing() {
this.composeWith(componentGenerator, {
fileType: this.config.fileType,
});
}
}
31 changes: 31 additions & 0 deletions ui_framework/generator-kui/app/documentation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const Generator = require('yeoman-generator');

const documentationGenerator = require.resolve('../documentation/index.js');

module.exports = class extends Generator {
prompting() {
return this.prompt([{
message: 'What do you want to create?',
name: 'fileType',
type: 'list',
choices: [{
name: 'Page',
value: 'documentation',
}, {
name: 'Page demo',
value: 'demo',
}, {
name: 'Sandbox',
value: 'sandbox',
}],
}]).then(answers => {
this.config = answers;
});
}

writing() {
this.composeWith(documentationGenerator, {
fileType: this.config.fileType,
});
}
}
141 changes: 141 additions & 0 deletions ui_framework/generator-kui/component/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
const chalk = require('chalk');
const Generator = require('yeoman-generator');
const utils = require('../utils');

module.exports = class extends Generator {
constructor(args, options) {
super(args, options);

this.fileType = options.fileType;
}

prompting() {
return this.prompt([{
message: 'What\'s the name of this component? Use snake_case, please.',
name: 'name',
type: 'input',
}, {
message: `Where do you want to create this component's files?`,
type: 'input',
name: 'path',
default: 'ui_framework/src/components',
store: true,
}, {
message: 'Does it need its own directory?',
name: 'shouldMakeDirectory',
type: 'confirm',
default: true,
}]).then(answers => {
this.config = answers;
});
}

writing() {
const config = this.config;

const writeComponent = isStatelessFunction => {
const componentName = utils.makeComponentName(config.name);
const cssClassName = utils.lowerCaseFirstLetter(componentName);
const fileName = config.name;

const path = utils.addDirectoryToPath(
config.path, fileName, config.shouldMakeDirectory);

const vars = config.vars = {
componentName,
cssClassName,
fileName,
};

const componentPath = config.componentPath = `${path}/${fileName}.js`;
const testPath = config.testPath = `${path}/${fileName}.test.js`;
const stylesPath = config.stylesPath = `${path}/_${fileName}.scss`;
config.stylesImportPath = `./_${fileName}.scss`;

// If it needs its own directory then it will need a root index file too.
if (this.config.shouldMakeDirectory) {
this.fs.copyTpl(
this.templatePath('_index.scss'),
this.destinationPath(`${path}/_index.scss`),
vars
);

this.fs.copyTpl(
this.templatePath('index.js'),
this.destinationPath(`${path}/index.js`),
vars
);
}

// Create component file.
this.fs.copyTpl(
isStatelessFunction ?
this.templatePath('stateless_function.js') :
this.templatePath('component.js'),
this.destinationPath(componentPath),
vars
);

// Create component test file.
this.fs.copyTpl(
this.templatePath('component.test.js'),
this.destinationPath(testPath),
vars
);

// Create component styles file.
this.fs.copyTpl(
this.templatePath('_component.scss'),
this.destinationPath(stylesPath),
vars
);
};

switch (this.fileType) {
case 'component':
writeComponent();
break;

case 'function':
writeComponent(true);
break;
}
}

end() {
const showImportComponentSnippet = () => {
const componentName = this.config.vars.componentName;
const componentPath = this.config.componentPath;

this.log(chalk.white(`\n// Export component (e.. from component's index.js).`));
this.log(
`${chalk.magenta('export')} {\n` +
` ${componentName},\n` +
`} ${chalk.magenta('from')} ${chalk.cyan(`'./${this.config.name}'`)};`
);

this.log(chalk.white('\n// Import styles.'));
this.log(
`${chalk.magenta('@import')} ${chalk.cyan(`'${this.config.name}'`)};`
);

this.log(chalk.white('\n// Import component styles into the root index.scss.'));
this.log(
`${chalk.magenta('@import')} ${chalk.cyan(`'./${this.config.name}/index'`)};`
);
};

this.log('------------------------------------------------');
this.log(chalk.bold('Handy snippets:'));
switch (this.fileType) {
case 'component':
showImportComponentSnippet();
break;

case 'function':
showImportComponentSnippet();
break;
}
this.log('------------------------------------------------');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@include component('<%= cssClassName %>') {

}
1 change: 1 addition & 0 deletions ui_framework/generator-kui/component/templates/_index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import '<%= fileName %>';
Loading

0 comments on commit 390b371

Please sign in to comment.