From 31966cc66c33123ecbf203a8754c1aa7d1b0bc5b Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Wed, 19 Jul 2017 10:16:53 -0700 Subject: [PATCH] [K7] Add KUI Yeoman generator. * Support creation of components. * Add documentation generator for main page, demo, and sandbox. Add additional documentation snippets to KUI generator. (#13076) Fix incorrect use of double quotes in KUI generator snippet. (#13086) Remove infrequently used imports from the KUI generator test template. (#13110) --- .gitignore | 1 + package.json | 8 +- ui_framework/README.md | 107 +++++++-- ui_framework/generator-kui/app/component.js | 28 +++ .../generator-kui/app/documentation.js | 31 +++ ui_framework/generator-kui/component/index.js | 141 ++++++++++++ .../component/templates/_component.scss | 3 + .../component/templates/_index.scss | 1 + .../component/templates/component.js | 33 +++ .../component/templates/component.test.js | 16 ++ .../component/templates/index.js | 1 + .../component/templates/stateless_function.js | 20 ++ .../generator-kui/documentation/index.js | 216 ++++++++++++++++++ .../templates/documentation_page.js | 38 +++ .../templates/documentation_page_demo.js | 11 + .../templates/documentation_sandbox.html | 1 + .../templates/documentation_sandbox.js | 27 +++ ui_framework/generator-kui/utils.js | 35 +++ 18 files changed, 691 insertions(+), 27 deletions(-) create mode 100644 ui_framework/generator-kui/app/component.js create mode 100644 ui_framework/generator-kui/app/documentation.js create mode 100644 ui_framework/generator-kui/component/index.js create mode 100644 ui_framework/generator-kui/component/templates/_component.scss create mode 100644 ui_framework/generator-kui/component/templates/_index.scss create mode 100644 ui_framework/generator-kui/component/templates/component.js create mode 100644 ui_framework/generator-kui/component/templates/component.test.js create mode 100644 ui_framework/generator-kui/component/templates/index.js create mode 100644 ui_framework/generator-kui/component/templates/stateless_function.js create mode 100644 ui_framework/generator-kui/documentation/index.js create mode 100644 ui_framework/generator-kui/documentation/templates/documentation_page.js create mode 100644 ui_framework/generator-kui/documentation/templates/documentation_page_demo.js create mode 100644 ui_framework/generator-kui/documentation/templates/documentation_sandbox.html create mode 100644 ui_framework/generator-kui/documentation/templates/documentation_sandbox.js create mode 100644 ui_framework/generator-kui/utils.js diff --git a/.gitignore b/.gitignore index b04c40f74d13a58..fbd0bc3f495b5c2 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ selenium ui_framework/doc_site/build !ui_framework/doc_site/build/index.html yarn.lock +.yo-rc.json diff --git a/package.json b/package.json index d78858fc487776a..5986103f5370fae 100644 --- a/package.json +++ b/package.json @@ -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", @@ -209,6 +211,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", @@ -277,7 +280,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", diff --git a/ui_framework/README.md b/ui_framework/README.md index 67396c5dd312b9e..929d721b2dd865b 100644 --- a/ui_framework/README.md +++ b/ui_framework/README.md @@ -17,28 +17,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`. @@ -50,7 +89,7 @@ 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. @@ -58,7 +97,17 @@ This makes your styles available to Kibana and the UI Framework documentation. 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. @@ -76,15 +125,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 @@ -97,7 +153,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 diff --git a/ui_framework/generator-kui/app/component.js b/ui_framework/generator-kui/app/component.js new file mode 100644 index 000000000000000..420d99558530809 --- /dev/null +++ b/ui_framework/generator-kui/app/component.js @@ -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, + }); + } +} diff --git a/ui_framework/generator-kui/app/documentation.js b/ui_framework/generator-kui/app/documentation.js new file mode 100644 index 000000000000000..9be00373a0ff244 --- /dev/null +++ b/ui_framework/generator-kui/app/documentation.js @@ -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, + }); + } +} diff --git a/ui_framework/generator-kui/component/index.js b/ui_framework/generator-kui/component/index.js new file mode 100644 index 000000000000000..8f825e334e21a1d --- /dev/null +++ b/ui_framework/generator-kui/component/index.js @@ -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('------------------------------------------------'); + } +} diff --git a/ui_framework/generator-kui/component/templates/_component.scss b/ui_framework/generator-kui/component/templates/_component.scss new file mode 100644 index 000000000000000..17bd718d0a94cfd --- /dev/null +++ b/ui_framework/generator-kui/component/templates/_component.scss @@ -0,0 +1,3 @@ +@include component('<%= cssClassName %>') { + +} diff --git a/ui_framework/generator-kui/component/templates/_index.scss b/ui_framework/generator-kui/component/templates/_index.scss new file mode 100644 index 000000000000000..088dee987494683 --- /dev/null +++ b/ui_framework/generator-kui/component/templates/_index.scss @@ -0,0 +1 @@ +@import '<%= fileName %>'; diff --git a/ui_framework/generator-kui/component/templates/component.js b/ui_framework/generator-kui/component/templates/component.js new file mode 100644 index 000000000000000..8daa74bece5c76c --- /dev/null +++ b/ui_framework/generator-kui/component/templates/component.js @@ -0,0 +1,33 @@ +import React, { + Component, + PropTypes, +} from 'react'; +import classNames from 'classnames'; + +export class <%= componentName %> extends Component { + constructor(props) { + super(props); + } + + render() { + const { + children, + className, + ...rest, + } = this.props; + + const classes = classNames('<%= cssClassName %>', className); + + return ( +
+ {children} +
+ ); + } +} + +<%= componentName %>.propTypes = { +}; diff --git a/ui_framework/generator-kui/component/templates/component.test.js b/ui_framework/generator-kui/component/templates/component.test.js new file mode 100644 index 000000000000000..aecd6497a73cb2c --- /dev/null +++ b/ui_framework/generator-kui/component/templates/component.test.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { render } from 'enzyme'; +import { requiredProps } from '../../test/required_props'; + +import { <%= componentName %> } from './<%= fileName %>'; + +describe('<%= componentName %>', () => { + test('is rendered', () => { + const component = render( + <<%= componentName %> { ...requiredProps } /> + ); + + expect(component) + .toMatchSnapshot(); + }); +}); diff --git a/ui_framework/generator-kui/component/templates/index.js b/ui_framework/generator-kui/component/templates/index.js new file mode 100644 index 000000000000000..0cd8d70f3e13d8c --- /dev/null +++ b/ui_framework/generator-kui/component/templates/index.js @@ -0,0 +1 @@ +export { <%= componentName %> } from './<%= fileName %>'; diff --git a/ui_framework/generator-kui/component/templates/stateless_function.js b/ui_framework/generator-kui/component/templates/stateless_function.js new file mode 100644 index 000000000000000..80f370cc27d2607 --- /dev/null +++ b/ui_framework/generator-kui/component/templates/stateless_function.js @@ -0,0 +1,20 @@ +import React, { + PropTypes, +} from 'react'; +import classNames from 'classnames'; + +export const <%= componentName %> = ({ children, className, ...rest }) => { + const classes = classNames('<%= cssClassName %>', className); + + return ( +
+ {children} +
+ ); +}; + +<%= componentName %>.propTypes = { +}; diff --git a/ui_framework/generator-kui/documentation/index.js b/ui_framework/generator-kui/documentation/index.js new file mode 100644 index 000000000000000..9cbf993b3ea293a --- /dev/null +++ b/ui_framework/generator-kui/documentation/index.js @@ -0,0 +1,216 @@ +const chalk = require('chalk'); +const Generator = require('yeoman-generator'); +const utils = require('../utils'); + +const DOCUMENTATION_PAGE_PATH = 'ui_framework/doc_site/src/views'; + +module.exports = class extends Generator { + constructor(args, options) { + super(args, options); + + this.fileType = options.fileType; + } + + prompting() { + let prompts = [{ + message: 'What\'s the name of the component you\'re documenting? Use snake_case, please.', + name: 'name', + type: 'input', + store: true, + }]; + + if (this.fileType === 'demo') { + prompts.push({ + message: `What's the name of the directory this demo should go in? (Within ui_framework/doc_site/src/views). Use snake_case, please.`, + name: 'folderName', + type: 'input', + store: true, + }); + + prompts.push({ + message: 'What would you like to name this demo? Use snake_case, please.', + name: 'demoName', + type: 'input', + store: true, + }); + } + + return this.prompt(prompts).then(answers => { + this.config = answers; + }); + } + + writing() { + const config = this.config; + + const writeDocumentationPage = () => { + const componentExampleName = utils.makeComponentName(config.name, false); + const componentExamplePrefix = utils.lowerCaseFirstLetter(componentExampleName); + const fileName = config.name; + + const path = DOCUMENTATION_PAGE_PATH; + + const vars = config.documentationVars = { + componentExampleName, + componentExamplePrefix, + fileName, + }; + + const documentationPagePath + = config.documentationPagePath + = `${path}/${config.name}/${config.name}_example.js`; + + this.fs.copyTpl( + this.templatePath('documentation_page.js'), + this.destinationPath(documentationPagePath), + vars + ); + }; + + const writeDocumentationPageDemo = (fileName, folderName) => { + const componentExampleName = utils.makeComponentName(fileName, false); + const componentExamplePrefix = utils.lowerCaseFirstLetter(componentExampleName); + const componentName = utils.makeComponentName(config.name); + + const path = DOCUMENTATION_PAGE_PATH; + + const vars = config.documentationVars = { + componentExampleName, + componentExamplePrefix, + componentName, + fileName, + }; + + const documentationPageDemoPath + = config.documentationPageDemoPath + = `${path}/${folderName}/${fileName}.js`; + + this.fs.copyTpl( + this.templatePath('documentation_page_demo.js'), + this.destinationPath(documentationPageDemoPath), + vars + ); + }; + + const writeSandbox = () => { + const fileName = config.name; + const componentExampleName = utils.makeComponentName(fileName, false); + + const path = DOCUMENTATION_PAGE_PATH; + + const vars = config.documentationVars = { + componentExampleName, + fileName, + }; + + const sandboxPath + = config.documentationPageDemoPath + = `${path}/${config.name}/${fileName}`; + + this.fs.copyTpl( + this.templatePath('documentation_sandbox.html'), + this.destinationPath(`${sandboxPath}_sandbox.html`) + ); + + this.fs.copyTpl( + this.templatePath('documentation_sandbox.js'), + this.destinationPath(`${sandboxPath}_sandbox.js`), + vars + ); + }; + + switch (this.fileType) { + case 'documentation': + writeDocumentationPage(); + writeDocumentationPageDemo(config.name, config.name); + break; + + case 'demo': + writeDocumentationPageDemo(config.demoName, config.folderName); + break; + + case 'sandbox': + writeSandbox(); + break; + } + } + + end() { + const showImportDemoSnippet = () => { + const { + componentExampleName, + componentExamplePrefix, + fileName, + } = this.config.documentationVars; + + this.log(chalk.white('\n// Import demo into example.')); + this.log( + `${chalk.magenta('import')} ${componentExampleName} from ${chalk.cyan(`'./${fileName}'`)};\n` + + `${chalk.magenta('const')} ${componentExamplePrefix}Source = require(${chalk.cyan(`'!!raw!./${fileName}'`)});\n` + + `${chalk.magenta('const')} ${componentExamplePrefix}Html = renderToHtml(${componentExampleName});` + ); + + this.log(chalk.white('\n// Render demo.')); + this.log( + `\n` + + ` \n` + + ` Description needed: how to use the ${componentExampleName} component.\n` + + ` \n` + + `\n` + + ` \n` + + ` <${componentExampleName} />\n` + + ` \n` + + `\n` + ); + }; + + const showImportRouteSnippet = suffix => { + const { + componentExampleName, + fileName, + } = this.config.documentationVars; + + this.log(chalk.white('\n// Import example into routes.js.')); + this.log( + `${chalk.magenta('import')} ${componentExampleName}${suffix}\n` + + ` ${chalk.magenta('from')} ${chalk.cyan(`'../../views/${fileName}/${fileName}_${suffix.toLowerCase()}'`)};` + ); + + this.log(chalk.white('\n// Import route definition into routes.js.')); + this.log( + `{\n` + + ` name: ${chalk.cyan(`'${componentExampleName}'`)},\n` + + ` component: ${componentExampleName}${suffix},\n` + + ` hasReact: ${chalk.magenta('true')},\n` + + `}` + ); + } + + this.log('------------------------------------------------'); + this.log(chalk.bold('Import snippets:')); + + switch (this.fileType) { + case 'documentation': + showImportRouteSnippet('Example'); + break; + + case 'demo': + showImportDemoSnippet(); + break; + + case 'sandbox': + showImportRouteSnippet('Sandbox'); + break; + } + this.log('------------------------------------------------'); + } +} diff --git a/ui_framework/generator-kui/documentation/templates/documentation_page.js b/ui_framework/generator-kui/documentation/templates/documentation_page.js new file mode 100644 index 000000000000000..6782b10b23a064a --- /dev/null +++ b/ui_framework/generator-kui/documentation/templates/documentation_page.js @@ -0,0 +1,38 @@ +import React from 'react'; + +import { renderToHtml } from '../../services'; + +import { + GuideDemo, + GuidePage, + GuideSection, + GuideSectionTypes, + GuideText, +} from '../../components'; + +import <%= componentExampleName %> from './<%= fileName %>'; +const <%= componentExamplePrefix %>Source = require('!!raw!./<%= fileName %>'); +const <%= componentExamplePrefix %>Html = renderToHtml(<%= componentExampleName %>); + +export default props => ( + + Source, + }, { + type: GuideSectionTypes.HTML, + code: <%= componentExamplePrefix %>Html, + }]} + > + + Description needed: how to use the <%= componentExampleName %> component. + + + + <<%= componentExampleName %> /> + + + +); diff --git a/ui_framework/generator-kui/documentation/templates/documentation_page_demo.js b/ui_framework/generator-kui/documentation/templates/documentation_page_demo.js new file mode 100644 index 000000000000000..645f194bb3c7b42 --- /dev/null +++ b/ui_framework/generator-kui/documentation/templates/documentation_page_demo.js @@ -0,0 +1,11 @@ +import React from 'react'; + +import { + <%= componentName %>, +} from '../../../../components'; + +export default () => ( + <<%= componentName %>> + + > +); diff --git a/ui_framework/generator-kui/documentation/templates/documentation_sandbox.html b/ui_framework/generator-kui/documentation/templates/documentation_sandbox.html new file mode 100644 index 000000000000000..2515d47beb72f93 --- /dev/null +++ b/ui_framework/generator-kui/documentation/templates/documentation_sandbox.html @@ -0,0 +1 @@ +

Do whatever you want here!

diff --git a/ui_framework/generator-kui/documentation/templates/documentation_sandbox.js b/ui_framework/generator-kui/documentation/templates/documentation_sandbox.js new file mode 100644 index 000000000000000..3edfa10665c6588 --- /dev/null +++ b/ui_framework/generator-kui/documentation/templates/documentation_sandbox.js @@ -0,0 +1,27 @@ +import React from 'react'; + +import { + GuideDemo, + GuideSandbox, + GuideSandboxCodeToggle, + GuideSectionTypes, +} from '../../components'; + +const html = require('./<%= fileName %>_sandbox.html'); + +export default props => ( + + + + + +); diff --git a/ui_framework/generator-kui/utils.js b/ui_framework/generator-kui/utils.js new file mode 100644 index 000000000000000..8c563f8fd4a0d7c --- /dev/null +++ b/ui_framework/generator-kui/utils.js @@ -0,0 +1,35 @@ +function makeComponentName(str, usePrefix = true) { + const words = str.split('_'); + + const componentName = words.map(function(word) { + return upperCaseFirstLetter(word); + }).join(''); + + return `${usePrefix ? 'Kui' : ''}${componentName}`; +} + +function lowerCaseFirstLetter(str) { + return str.replace(/\w\S*/g, function(txt) { + return txt.charAt(0).toLowerCase() + txt.substr(1); + }); +} + +function upperCaseFirstLetter(str) { + return str.replace(/\w\S*/g, function(txt) { + return txt.charAt(0).toUpperCase() + txt.substr(1); + }); +} + +function addDirectoryToPath(path, dirName, shouldMakeDirectory) { + if (shouldMakeDirectory) { + return path + '/' + dirName; + } + return path; +} + +module.exports = { + makeComponentName: makeComponentName, + lowerCaseFirstLetter: lowerCaseFirstLetter, + upperCaseFirstLetter: upperCaseFirstLetter, + addDirectoryToPath: addDirectoryToPath, +};