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 });
});