From 63f1b74fe742afe45b555a3dbf64c4b4ae5d497c Mon Sep 17 00:00:00 2001 From: Chris Date: Mon, 19 Oct 2015 08:11:22 +0200 Subject: [PATCH] Added ability to create stateless components Cleaned up unittests --- CHANGELOG.md | 7 + README.md | 12 +- generators/component/index.js | 8 +- .../component/templates/components/Base.js | 1 - .../templates/components/Stateless.js | 19 + test/generators/component/indexTest.js | 414 +++++++----------- 6 files changed, 196 insertions(+), 265 deletions(-) create mode 100644 generators/component/templates/components/Stateless.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f6d858a..77ce874 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # generator-react-webpack - Changelog +## 2.2.5 +___Upgrades:___ + +1. Added ability to create stateless components +2. Updated README with new installation instructions (need to install globally) +3. Added new tests for components (should be easier to handle in the future) + ## 2.2.4 ___Upgrades:___ diff --git a/README.md b/README.md index 2083cbf..956095f 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,9 @@ If you have built a generator using generator-react-webpack, tell us and we will ## Installation ```bash +# Make sure both is installed globally npm install -g yo -npm install generator-react-webpack +npm install -g generator-react-webpack ``` ## Setting up projects @@ -51,6 +52,15 @@ yo react-webpack:component my/namespaced/components/name The above command will create a new component, as well as its stylesheet and a basic testcase. +## Generating new stateless functional components +``` +yo react-webpack:component:stateless my/namespaced/components/name +``` + +Stateless functional components where introduced in React v0.14. They have a much shorter syntax than regular ones and no state or lifecycle methods at all. Please read the [React 0.14 release notes](https://facebook.github.io/react/blog/2015/10/07/react-v0.14.html) to get more information about those components. + +___Note___: You will still be able to set properties for stateless components! + ## Usage The following commands are available in your project: ```bash diff --git a/generators/component/index.js b/generators/component/index.js index 93fae40..743c9d7 100644 --- a/generators/component/index.js +++ b/generators/component/index.js @@ -6,11 +6,17 @@ module.exports = generator.NamedBase.extend({ constructor: function() { generator.NamedBase.apply(this, arguments); + + this.option('stateless', { + desc: 'Create a stateless component instead of a full one', + defaults: false + }); }, writing: function() { let settings = utils.yeoman.getAllSettingsFromComponentName(this.name, this.config.get('style')); + let componentType = this.options.stateless ? 'Stateless' : 'Base'; // Create the style template this.fs.copyTpl( @@ -21,7 +27,7 @@ module.exports = generator.NamedBase.extend({ // Create the component this.fs.copyTpl( - this.templatePath('components/Base.js'), + this.templatePath(`components/${componentType}.js`), this.destinationPath(settings.component.path + settings.component.fileName), settings ); diff --git a/generators/component/templates/components/Base.js b/generators/component/templates/components/Base.js index eff2a97..a4e7f08 100644 --- a/generators/component/templates/components/Base.js +++ b/generators/component/templates/components/Base.js @@ -20,5 +20,4 @@ class <%= component.className %> extends React.Component { // <%= component.className %>.propTypes = {}; // <%= component.className %>.defaultProps = {}; - export default <%= component.className %>; diff --git a/generators/component/templates/components/Stateless.js b/generators/component/templates/components/Stateless.js new file mode 100644 index 0000000..a73c647 --- /dev/null +++ b/generators/component/templates/components/Stateless.js @@ -0,0 +1,19 @@ +'use strict'; + +import React from 'react'; + +require('<%= style.webpackPath %>'); + +let <%= component.className %> = (props) => ( +
+ Please edit <%= component.path %>/<%= component.fileName %> to update this component! +
+); + +<%= component.className %>.displayName = '<%= component.displayName %>'; + +// Uncomment properties you need +// <%= component.className %>.propTypes = {}; +// <%= component.className %>.defaultProps = {}; + +export default <%= component.className %>; diff --git a/test/generators/component/indexTest.js b/test/generators/component/indexTest.js index 0e7b65f..2cc0980 100644 --- a/test/generators/component/indexTest.js +++ b/test/generators/component/indexTest.js @@ -7,285 +7,175 @@ describe('react-webpack:component', () => { let generatorComponent = path.join(__dirname, '../../../generators/component'); + // List of available style types. Please add a line that says + // testComponentWithStyle(styleTypes.KEY); to the bottom of the file + // to run all unit tests for this filetype. + const styleTypes = { + css: { + type: 'css', + fileName: 'src/styles/Mycomponent.css', + expandedFileName: 'src/styles/my/littleSpecial/Test.css', + assertions: { + componentImport: 'require(\'styles//Mycomponent.css\');', + styleContent: '.mycomponent-component' + } + }, + sass: { + type: 'sass', + fileName: 'src/styles/Mycomponent.sass', + expandedFileName: 'src/styles/my/littleSpecial/Test.sass', + assertions: { + componentImport: 'require(\'styles//Mycomponent.sass\');', + styleContent: '.mycomponent-component' + } + }, + scss: { + type: 'scss', + fileName: 'src/styles/Mycomponent.scss', + expandedFileName: 'src/styles/my/littleSpecial/Test.scss', + assertions: { + componentImport: 'require(\'styles//Mycomponent.scss\');', + styleContent: '.mycomponent-component' + } + }, + less: { + type: 'less', + fileName: 'src/styles/Mycomponent.less', + expandedFileName: 'src/styles/my/littleSpecial/Test.less', + assertions: { + componentImport: 'require(\'styles//Mycomponent.less\');', + styleContent: '.mycomponent-component' + } + }, + stylus: { + type: 'stylus', + fileName: 'src/styles/Mycomponent.styl', + expandedFileName: 'src/styles/my/littleSpecial/Test.styl', + assertions: { + componentImport: 'require(\'styles//Mycomponent.styl\');', + styleContent: '.mycomponent-component' + } + } + }; + /** * Return a newly generated component with given name and style - * @param {String} name - * @param {String} styleType - * @param {Function} callback + * @param {String} name Name of the component + * @param {String} styleType Styling language to use + * @param {Object} options Options to use for the generator + * @param {Function} callback Test callback to run */ - function createGeneratedComponent(name, styleType, callback) { + function createGeneratedComponent(name, styleType, options, callback) { helpers.run(generatorComponent) .withArguments([name]) + .withOptions(options) .on('ready', (instance) => { instance.config.set('style', styleType); }) .on('end', callback); } - describe('When using style type "css"', () => { + /** + * Test a component with styling applied + * @param {Object} style The style to apply (see styleTypes above) + * @param {Object} options Options to use [optional] + */ + function testComponentWithStyle(style, options) { - describe('Setup', () => { + // Make sure we always have options + if(!options) { + options = {}; + } - it('should create the react component, its stylesheet and test file', (done) => { - createGeneratedComponent('mycomponent', 'css', () => { + describe(`When using style type "${style.type}"`, () => { - assert.file([ - 'src/components/MycomponentComponent.js', - 'src/styles/Mycomponent.css', - 'test/components/MycomponentComponentTest.js' - ]); - done(); + describe('When writing is called', () => { + + it(`should create the react component, its ${style.type}-stylesheet and test file`, (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + + assert.file([ + 'src/components/MycomponentComponent.js', + style.fileName, + 'test/components/MycomponentComponentTest.js' + ]); + done(); + }); + }); + }); + + describe('When creating a component', () => { + + it('should always import REACT', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('src/components/MycomponentComponent.js', `import React from 'react';`); + done(); + }); + }); + + it(`should require the created ${style.type} file`, (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('src/components/MycomponentComponent.js', style.assertions.componentImport); + done(); + }); + }); + + it('should have its displayName set per default', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('src/components/MycomponentComponent.js', `displayName = 'MycomponentComponent';`); + done(); + }); + }); + + it('should export the created component', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('src/components/MycomponentComponent.js', `export default MycomponentComponent`); + done(); + }); + }); + + it(`should be possible to create components in a subfolder`, (done) => { + createGeneratedComponent('my/little !special/test', style.type, options, () => { + + assert.file([ + 'src/components/my/littleSpecial/TestComponent.js', + style.expandedFileName, + 'test/components/my/littleSpecial/TestComponentTest.js' + ]); + done(); + }); + }); + }); + + describe('Style', () => { + + it(`should add the components ${style.type} class to the stylesheet`, (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent(style.fileName, style.assertions.styleContent); + done(); + }); + }); + }); + + describe('Test', () => { + + it('should import the react component', (done) => { + createGeneratedComponent('mycomponent', style.type, options, () => { + assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';'); + done(); + }); }); }); }); + } - describe('Component', () => { - - it('should require the created css file', (done) => { - createGeneratedComponent('mycomponent', 'css', () => { - assert.fileContent('src/components/MycomponentComponent.js', 'require(\'styles//Mycomponent.css\');'); - done(); - }); - }); - - it('should have its displayName set per default', (done) => { - - createGeneratedComponent('mycomponent', 'css', () => { - assert.fileContent('src/components/MycomponentComponent.js', `displayName = 'MycomponentComponent';`); - done(); - }); - }); - }); - - describe('Style', () => { - - it('should add the components css class to the stylesheet', (done) => { - createGeneratedComponent('mycomponent', 'css', () => { - assert.fileContent('src/styles/Mycomponent.css', '.mycomponent-component'); - done(); - }); - }); - }); - - describe('Test', () => { - - it('should import the react component', (done) => { - createGeneratedComponent('mycomponent', 'css', () => { - assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';'); - done(); - }); - }); - }); - }); // End css - - describe('When creating a component in a subfolder', () => { - - describe('Setup', () => { - - it('should create the react component, its stylesheet and test file', (done) => { - createGeneratedComponent('my/little !special/test', 'css', () => { - - assert.file([ - 'src/components/my/littleSpecial/TestComponent.js', - 'src/styles/my/littleSpecial/Test.css', - 'test/components/my/littleSpecial/TestComponentTest.js' - ]); - done(); - }); - }); - }); - }); // End css (subfolder) - - describe('When using style type "sass"', () => { - - describe('Setup', () => { - - it('should create the react component, its stylesheet and test file', (done) => { - createGeneratedComponent('mycomponent', 'sass', () => { - - assert.file([ - 'src/components/MycomponentComponent.js', - 'src/styles/Mycomponent.sass', - 'test/components/MycomponentComponentTest.js' - ]); - done(); - }); - }); - }); - - describe('Component', () => { - - it('should require the created sass file', (done) => { - createGeneratedComponent('mycomponent', 'sass', () => { - assert.fileContent('src/components/MycomponentComponent.js', 'require(\'styles//Mycomponent.sass\');'); - done(); - }); - }); - }); - - describe('Style', () => { - - it('should add the components sass class to the stylesheet', (done) => { - createGeneratedComponent('mycomponent', 'sass', () => { - assert.fileContent('src/styles/Mycomponent.sass', '.mycomponent-component'); - done(); - }); - }); - }); - - describe('Test', () => { - - it('should import the react component', (done) => { - createGeneratedComponent('mycomponent', 'sass', () => { - assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';'); - done(); - }); - }); - }); - }); // End sass - - describe('When using style type "scss"', () => { - - describe('Setup', () => { - - it('should create the react component, its stylesheet and test file', (done) => { - createGeneratedComponent('mycomponent', 'scss', () => { - - assert.file([ - 'src/components/MycomponentComponent.js', - 'src/styles/Mycomponent.scss', - 'test/components/MycomponentComponentTest.js' - ]); - done(); - }); - }); - }); - - describe('Component', () => { - - it('should require the created scss file', (done) => { - createGeneratedComponent('mycomponent', 'scss', () => { - assert.fileContent('src/components/MycomponentComponent.js', 'require(\'styles//Mycomponent.scss\');'); - done(); - }); - }); - }); - - describe('Style', () => { - - it('should add the components scss class to the stylesheet', (done) => { - createGeneratedComponent('mycomponent', 'scss', () => { - assert.fileContent('src/styles/Mycomponent.scss', '.mycomponent-component'); - done(); - }); - }); - }); - - describe('Test', () => { - - it('should import the react component', (done) => { - createGeneratedComponent('mycomponent', 'scss', () => { - assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';'); - done(); - }); - }); - }); - }); // End scss - - describe('When using style type "less"', () => { - - describe('Setup', () => { - - it('should create the react component, its stylesheet and test file', (done) => { - createGeneratedComponent('mycomponent', 'less', () => { - - assert.file([ - 'src/components/MycomponentComponent.js', - 'src/styles/Mycomponent.less', - 'test/components/MycomponentComponentTest.js' - ]); - done(); - }); - }); - }); - - describe('Component', () => { - - it('should require the created less file', (done) => { - createGeneratedComponent('mycomponent', 'less', () => { - assert.fileContent('src/components/MycomponentComponent.js', 'require(\'styles//Mycomponent.less\');'); - done(); - }); - }); - }); - - describe('Style', () => { - - it('should add the components less class to the stylesheet', (done) => { - createGeneratedComponent('mycomponent', 'less', () => { - assert.fileContent('src/styles/Mycomponent.less', '.mycomponent-component'); - done(); - }); - }); - }); - - describe('Test', () => { - - it('should import the react component', (done) => { - createGeneratedComponent('mycomponent', 'less', () => { - assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';'); - done(); - }); - }); - }); - }); // End less - - describe('When using style type "stylus"', () => { - - describe('Setup', () => { - - it('should create the react component, its stylesheet and test file', (done) => { - createGeneratedComponent('mycomponent', 'stylus', () => { - - assert.file([ - 'src/components/MycomponentComponent.js', - 'src/styles/Mycomponent.styl', - 'test/components/MycomponentComponentTest.js' - ]); - done(); - }); - }); - }); - - describe('Component', () => { - - it('should require the created stylus file', (done) => { - createGeneratedComponent('mycomponent', 'stylus', () => { - assert.fileContent('src/components/MycomponentComponent.js', 'require(\'styles//Mycomponent.styl\');'); - done(); - }); - }); - }); - - describe('Style', () => { - - it('should add the components stylus class to the stylesheet', (done) => { - createGeneratedComponent('mycomponent', 'stylus', () => { - assert.fileContent('src/styles/Mycomponent.styl', '.mycomponent-component'); - done(); - }); - }); - }); - - describe('Test', () => { - - it('should import the react component', (done) => { - createGeneratedComponent('mycomponent', 'less', () => { - assert.fileContent('test/components/MycomponentComponentTest.js', 'import MycomponentComponent from \'components//MycomponentComponent.js\';'); - done(); - }); - }); - }); - }); // End stylus + // Run all tests for all available style types. + testComponentWithStyle(styleTypes.css); + testComponentWithStyle(styleTypes.sass); + testComponentWithStyle(styleTypes.scss); + testComponentWithStyle(styleTypes.less); + testComponentWithStyle(styleTypes.stylus); + // Test stateless components (should be enough when testing with defaults) + testComponentWithStyle(styleTypes.css, { stateless: true }); });