# EPP Workshop: How to create a new component
## Architecture
When navigating to a content page...
1. Frontend asks backend what the content of that subpage is
2. Backend responds with list of components
3. Frontend fetches data for all of the components (one by one)
4. Backend responds with component data
5. Frontend initializes components with the relevant data

For an example request roundtrip, click [here](https://hackmd.io/moKlM1oeQjaQZpFGrw9PdQ)
## 0. Create a branch
```
git checkout develop
git pull
git checkout -b EPPNC-1234-some-slug-describing-your-ticket
```
## 1. Create component files
``/dgl-gui/src/shop/client/components/molecules/cms/test-component/``
```javascript
// CMSTestComponent.tsx
import React, { memo, FunctionComponent } from 'react';
import { convertComponent } from 'shop/client/utils/helpers/contentHelper';
import { ICMSComponent } from 'shop/client/interfaces/base/cms/CMSComponent';
import 'shop/client/components/molecules/cms/multi-teaser/CMSMultiTeaserComponent.scss';
export interface IMultiTeaserComponent {
headline: string
}
type Props = ICMSComponent<IMultiTeaserComponent>;
const MultiTeaserComponent: FunctionComponent<Props> = (props) => {
const { headline } = convertComponent<Props>(props);
return (
<div className="test-component">
<h1 className="test-component__headline">{headline}</h1>
</div>
);
};
export default memo(MultiTeaserComponent);
```
```sass
// CmsTestComponent.scss
.test-component {
&__headline {
color: #ff0000;
}
}
```
``` javascript
// CMSTestComponent.spec.tsx
// Empty
```
Example commit: https://bitbucket.netconomy.net/projects/DGL/repos/dgl-gui/commits/730b138ed182eb93458074fee4746d9c4905d7ba
## 2. Add the component to patternlab
### 2.1 Add mock data
`dgl-gui/src/shop/client/constants/CMSConstants.ts`
```diff
EMPTY_CART = 'CMSEmptyCartComponent',
EMPTY_WISH_LIST = 'CMSEmptyWishListComponent',
MULTI_TEASER = 'CMSMultiTeaserSplitComponent',
+ TEST_COMPONENT = 'CMSTestComponent',
ORDER_CONFIRMATION = 'OrderConfirmationComponent',
PRODUCT_CAROUSEL = 'ProductCarouselComponent',
SITE_LOGO = 'SiteLogoComponent',
```
`dgl-gui/src/shop/client/utils/__mocks__/TestComponentMockData.ts`
```javascript
import { CMSComponent } from 'shop/client/constants/CMSConstants';
export const TestComponentMockResponse = {
uid: 'test-multi-teaser-split',
uuid: 'eyJpdGVtSWQiOiJ0ZXN0LW11bHRpLXRlYXNlci1zcGxpdCIsImNhdGFsb2dJZCI6ImRnbC1nbG9iYWwtY29udGVudCIsImNhdGFsb2dWZXJzaW9uIjoiT25saW5lIn0=',
typeCode: CMSComponent.TEST_COMPONENT,
modifiedtime: '2019-07-12T14:35:30.000+0000',
name: 'Test Component',
otherProperties: {
headline: "Thats a nice headline!"
}
};
```
### 2.2 Create patternlab template (container) files
`/dgl-gui/patternlab/source/_patterns/Components/test-component/`
test-component.mustache
```html
<div class="test-component-patternlab-container"></div>
```
test-component-again.mustache
```html
<div class="test-component-patternlab-container-again"></div>
```
### 2.3 Add react component connection to patternlab
`dgl-gui/patternlab/source/js/components/testComponent.js`
```javascript
import CMSTestComponent from 'shop/client/components/molecules/cms/test-component/CMSTestComponent';
import { TestComponentMockResponse } from 'shop/client/utils/__mocks__/TestComponentMockData';
export default [
{
Component: CMSTestComponent,
entrypointClass: 'test-component-patternlab-container',
props: TestComponentMockResponse
},
{
Component: CMSTestComponent,
entrypointClass: 'test-component-patternlab-container-again',
props: TestComponentMockResponse
},
]
```
`patternlab/source/js/index.js`
```diff
import squareImage from './components/squareImage'
import specialBrandImage from './components/specialBrandImage'
import teaserSplit from './components/teaserSplit';
+ import testComponent from './components/testComponent';
import platinSplitTeaser from './components/platinSplitTeaser';
import platinOverlayTeaser from './components/platinOverlayTeaser';
import cmseyecatcher from './components/cmseyecatcher';
```
```diff
...squareImage,
...specialBrandImage,
...teaserSplit,
+ ...testComponent,
...platinSplitTeaser,
...platinOverlayTeaser,
...cmseyecatcher,
```
Run it:
`npm run patternlab:serve`
https://bitbucket.netconomy.net/projects/DGL/repos/dgl-gui/commits/42849c06893dfcccbbc0acd18a1e18af1f0c0f6e
## 3. Add unit test
``` javascript
// CMSTestComponent.spec.tsx
import React from 'react';
import { shallow } from 'enzyme';
import { cloneDeep } from 'lodash'
import CMSTestComponent from 'shop/client/components/molecules/cms/test-component/CMSTestComponent';
import { TestComponentMockResponse } from 'shop/client/utils/__mocks__/TestComponentMockData';
describe('<CMSTestComponent /> component:', () => {
describe('Rendering MultiTeaser', () => {
let shallowComponent: any = null;
beforeAll(() => {
shallowComponent = shallow(
<CMSTestComponent {...TestComponentMockResponse} />
);
});
it('should exist ', () => {
expect(shallowComponent.instance()).toBeDefined();
expect(shallowComponent).toHaveLength(1);
});
it('should render a title ', () => {
expect(shallowComponent.find(".test-component__headline")).toHaveText(TestComponentMockResponse.otherProperties.headline);
});
it('should not render a title if its not given in props', () => {
const newProps = cloneDeep(TestComponentMockResponse);
delete newProps.otherProperties.headline;
shallowComponent.setProps(newProps);
expect(shallowComponent.find(".test-component__headline")).toHaveText("");
});
});
});
```
`npm run test -t "TestComponent"`
Example commit: https://bitbucket.netconomy.net/projects/DGL/repos/dgl-gui/commits/20a8c30255ae9d143e4faa097c24000f26f35a30
### 4. Connect it to SmartEdit
`dgl-gui/src/shop/client/components/molecules/cms/base/registry/CMSRegistry.ts`
```diff
loading: Loading
});
+ const CMSTestComponent = Loadable({
+ loader: () => import('shop/client/components/molecules/cms/test-component/CMSTestComponent'),
+ loading: Loading
+ });
const CMSMultiTeaserComponent = Loadable({
loader: () => import('shop/client/components/molecules/cms/multi-teaser/CMSMultiTeaserComponent'),
loading: Loading
```
```diff
registerCMSComponent(CMSConstants.CMSComponent.SPECIAL_BRAND_IMAGE, CMSSpecialBrandImageComponent);
registerCMSComponent(CMSConstants.CMSComponent.EYE_CATCHER, CMSEyeCatcherComponent);
registerCMSComponent(CMSConstants.CMSComponent.TEASER_SPLIT, CMSTeaserSplitComponent);
+ registerCMSComponent(CMSConstants.CMSComponent.TEST_COMPONENT, CMSTestComponent);
registerCMSComponent(CMSConstants.CMSComponent.MULTI_TEASER, CMSMultiTeaserComponent);
registerCMSComponent(CMSConstants.CMSComponent.BUTTON_LINK, CMSButtonLinkComponent);
registerCMSComponent(CMSConstants.CMSComponent.SQUARE_IMAGE, CMSSquareImageComponent);
```
Example commit: https://bitbucket.netconomy.net/projects/DGL/repos/dgl-gui/commits/40013b8ce8db7986f02b43aa68b87311ca7cc33e