Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/with react #355

Merged
merged 84 commits into from
May 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
faf3409
starting with-react func
AndyOGo Mar 21, 2018
616a057
added sass to demo build
AndyOGo Mar 21, 2018
2531dc1
watch jsx to
AndyOGo Mar 21, 2018
ec220c6
added dasherize function
AndyOGo Mar 21, 2018
ead4cfa
also resolve jsx
AndyOGo Mar 21, 2018
7236b1e
added replace for ENV
AndyOGo Mar 21, 2018
a11cace
imported dasherize
AndyOGo Mar 21, 2018
27bed76
fixed with react import
AndyOGo Mar 21, 2018
a8209cc
added displayName
AndyOGo Mar 21, 2018
7e08ca0
mostly finalized this func
AndyOGo Mar 21, 2018
43ae22a
added doclet
AndyOGo Mar 21, 2018
7eca5b0
fixed missing braces
AndyOGo Mar 21, 2018
84ffbd6
added click event
AndyOGo Mar 21, 2018
fed0748
added pure option
AndyOGo Mar 26, 2018
a6ac0be
fixed typo
AndyOGo Mar 26, 2018
ae98adc
start adding prop blacklist
AndyOGo Mar 28, 2018
11c3318
fixed env replacement broken for demos
AndyOGo Mar 28, 2018
d95a71e
fixed rollup can't build react exports
AndyOGo Mar 29, 2018
9ca4d1a
start adding todo sample app
AndyOGo Mar 29, 2018
c6663ae
start adding footer
AndyOGo Mar 29, 2018
ed232dc
fixed 0 output
AndyOGo Mar 29, 2018
740303c
start adding header
AndyOGo Mar 29, 2018
110d27f
all-demos.js:8502 Uncaught TypeError: Cannot read property 'id' of un…
AndyOGo Mar 29, 2018
28f70c6
improving autofocus on edit
AndyOGo Mar 29, 2018
aa677cc
just added some margin to make the footer completely visible
AndyOGo Mar 29, 2018
b8ee7d0
quickfixed input losing focus within ce
AndyOGo Mar 29, 2018
9974270
start documentation
AndyOGo Mar 29, 2018
8668f72
improved options
AndyOGo Mar 29, 2018
ada3123
removed log
AndyOGo Mar 29, 2018
68f38b0
removed yet another log
AndyOGo Mar 29, 2018
d770bcb
improved focus hack
AndyOGo Mar 29, 2018
d9074ef
focus hack cleanup
AndyOGo Mar 29, 2018
526e756
start adding styles
AndyOGo Mar 29, 2018
7917322
improving styles
AndyOGo Mar 29, 2018
d5cb7eb
utilised completed state
AndyOGo Mar 29, 2018
0246f4d
display input only on edit
AndyOGo Mar 29, 2018
1b3be9a
improved layout
AndyOGo Mar 29, 2018
deab83b
moar layout
AndyOGo Mar 29, 2018
52944eb
improved inputs
AndyOGo Mar 29, 2018
43c6dd2
improved input styling
AndyOGo Mar 29, 2018
7762d20
improving butons
AndyOGo Mar 29, 2018
f4c728a
fixed little typo
AndyOGo Mar 29, 2018
385f444
return cancelled of fire
AndyOGo Mar 29, 2018
99db53b
fixed custom event check
AndyOGo Mar 29, 2018
8bf977b
start adding custom events to footer links
AndyOGo Mar 29, 2018
54b6c52
try to add custom event to button
AndyOGo Mar 29, 2018
07a28d4
use new custom events
AndyOGo Mar 29, 2018
40309f3
fixed broken event of catching of withReact
AndyOGo Mar 29, 2018
97cb3f8
added support for dashed custom events
AndyOGo Mar 29, 2018
734998a
fixed layout for looong text
AndyOGo Mar 29, 2018
7e3ff8a
fixed misisng gap
AndyOGo Mar 29, 2018
3c06b60
enabled handling of footer links
AndyOGo Mar 29, 2018
f5dec3c
prevent default action
AndyOGo Mar 29, 2018
ef7aa43
updated docs
AndyOGo Mar 29, 2018
9b9568f
added todo
AndyOGo Mar 29, 2018
5e66522
made the logo clickable
AndyOGo Mar 29, 2018
acdd32b
only re-render components, if their props have changed
AndyOGo Mar 30, 2018
2d7ab94
added shouldComponentUpdate to base component's re-render
AndyOGo Mar 30, 2018
d7c71d3
added docs for should component update
AndyOGo Mar 30, 2018
03b5231
removed the input hack
AndyOGo Mar 30, 2018
405b19f
renamed to shouldUpdateCallback
AndyOGo Mar 30, 2018
2397c93
adding array partition reducer
AndyOGo Mar 30, 2018
c770d6a
start adding batch props for integrations
AndyOGo Mar 30, 2018
a9422a3
improved FOUC by batch processing of props
AndyOGo Mar 30, 2018
804617a
added docs for batch props
AndyOGo Mar 30, 2018
373c1f1
fixed initial attribute fetching
AndyOGo Mar 30, 2018
da1db52
added updateProp for batching
AndyOGo Mar 31, 2018
ef3a5d9
add some props changes
AndyOGo Mar 31, 2018
55a307d
added doclet
AndyOGo Mar 31, 2018
058151a
added active item to footer links
AndyOGo Apr 3, 2018
f9abf30
switched ghost not ghost for todo item completion
AndyOGo Apr 3, 2018
6fe9a8e
renamed this private func
AndyOGo Apr 3, 2018
a1b1222
added todo help to footer
AndyOGo Apr 3, 2018
6486549
improved docs of events
AndyOGo Apr 3, 2018
1cc7a08
utilised peer deps for with react
AndyOGo Apr 4, 2018
25f1f44
just a typo
AndyOGo Apr 4, 2018
2b52339
React 14 should also work
AndyOGo Apr 4, 2018
df6c7a1
fixed missing context
AndyOGo Apr 17, 2018
92bf445
start adding event doclets
AndyOGo Apr 18, 2018
7dead93
moar doclets
AndyOGo Apr 18, 2018
45f6370
add mobile menu react entry
Apr 23, 2018
e726f0c
use name prop
AndyOGo Apr 23, 2018
02436e0
added function name utility
AndyOGo Apr 23, 2018
284bc69
fixed IE broken func name prop
AndyOGo Apr 23, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,17 @@ example.exampleMessage = 'hello world';

**Note:** Be careful of choosing your attribute names, never overwrite existing standard attributes without good reason!

#### `batchProps(props)`

A fast and simpler way to update multiple props in one go.
Especially useful for integrations and to prevent multiple or delayed re-renders.

#### `shouldUpdateCallback(newValue, oldValue)`

`shouldUpdateCallback()` is invoked upon `attributeChangedCallback()` or Property `setter()` invocation to determine if rendering is necessary when new props are being received - it returns `true` if re-rendering is desireable, else `false`.

**Important:** This does only a shallow comparison, if you need to deal with more complex data, like objects or arrays either stick to immutable data structures or override this method to implement your own test.

#### `willRenderCallback(initial)`

Invoked before the custom element's [flattened DOM](#flattened-dom) will be rendered.
Expand All @@ -221,6 +232,44 @@ Invoked when the custom element is disconnected from the document's DOM.

The render loop makes sure that upon each [`attributeChangedCallback()`](#attributechangedcallbackname-oldvalue-newvalue) invocation or any observed [property `setter()`](#property-setter) invocation that the flattened DOM is recomputed and that [`willRenderCallback()`](#willrendercallbackinitial) and [`didRenderCallback()`](#didrendercallbackinitial) lifecycle hooks are called respectively.

## Integration

The goal is that custom elements can be shared across frameworks and libraries like Angular, React, Vue, you name it. To ease this process we provide generic wrapper functions.

### `withReact()`

To turn any custom element into a working React Component, you just need to follow these steps:

1. `import` React
2. `import` withReact
3. `import` any web components you need
4. wrap all your needed web components
- and may pass optional options for type of component or event init options
5. use them like regular React components in your app

**Note:** events work similar to React's standard events, but each web components could trigger custom events like `axa-click` - camelcased and `on`-prefixed in React such as `onAxaClick={yourEventHandler}`. Make sure to check them out at the web-components documentation itself!

```js
// import your dependencies - 1, 2, and 3
import React from 'react';
import withReact from '@axa-ch/patterns-library/src/js/with-react';
import AXAButton from '@axa-ch/patterns-library/dist/components/m-button';

// 4. wrap your needed web components
// and optionally pass options
const AXAButtonReact = withReact(AXAButton, {
pure: true,
// event init options are also supported
passive: false,
});

// 5. use them in your app like regular React components
// note the custom event axa-click - camelcased and on-prefixed in React
const MyApp = ({ color, onClick }) => (
<AXAButtonReact color={color} onAxaClick={onClick}>Hello World</AXAButtonReact>
);
```

# How do we release a new version

Please run `npm run release` and follow the steps in the wizard.
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,8 @@
"nanohtml": "^1.2.2",
"react": "^16.2.0",
"react-dom": "^16.2.0"
},
"peerDependencies": {
"react": ">=0.14.0"
}
}
2 changes: 1 addition & 1 deletion src/components/m-button/_template.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default function ({
gpu,
arrow,
}, childrenFragment) {
const buttonClasses = classnames('m-button', classes, {
const buttonClasses = classnames('m-button', 'js-button', classes, {
[`m-button--${color}`]: color,
[`m-button--${size}`]: size,
'm-button--ghost': ghost,
Expand Down
16 changes: 16 additions & 0 deletions src/components/m-button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,29 @@ import styles from './index.scss';
import template from './_template';
import BaseComponentGlobal from '../../js/abstract/base-component-global';
import wcdomready from '../../js/wcdomready';
import Button from './js/button';

class AXAButton extends BaseComponentGlobal {
static get observedAttributes() { return ['arrow', 'classes', 'color', 'ghost', 'motion', 'size', 'tag', 'url']; }

constructor() {
super(styles, template);
}

didRenderCallback() {
if (this.button) {
this.button.destroy();
}

this.button = new Button(this);
}

disconnectedCallback() {
if (this.button) {
this.button.destroy();
delete this.button;
}
}
}

wcdomready(() => {
Expand Down
78 changes: 78 additions & 0 deletions src/components/m-button/js/button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import on from '../../../js/on';
import fire from '../../../js/fire';

/**
* @fires Button#axa-click
*/
class Button {
static DEFAULTS = {
button: '.js-button',
};

constructor(wcNode, options = {}) {
this.wcNode = wcNode;

this.options = {
...Button.DEFAULTS,
...options,
};

this.handleClick = this.handleClick.bind(this);

this.init();
}

init() {
this.button = this.wcNode.querySelector(this.options.button);

this.on();
}

on() {
this.off();

this.unClick = on(this.button, 'click', this.handleClick, {
passive: false,
});
}

off() {
if (this.unClick) {
this.unClick();
}
}

handleClick(event) {
/**
* axa-click event.
*
* @event Button#axa-click
* @type {null}
*/
const cancelled = fire(this.wcNode, 'axa-click', null, { bubbles: true, cancelable: true, composed: true });

if (!cancelled) {
event.preventDefault();
}
}

destroy() {
this.off();

if (this.button) {
delete this.button;
}

if (this.wcNode) {
delete this.wcNode;
}

if (this.options) {
delete this.options;
}

delete this.handleClick;
}
}

export default Button;
6 changes: 3 additions & 3 deletions src/components/m-footer-links/_template.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ export default function ({ title, items }) {
<strong class="m-footer-links__category js-dropdown__toggle">${title}${raw(arrowIcon)}</strong>

<ul class="m-footer-links__list">
${Array.isArray(items) && items.map(({ name, url }) => html`
<li class="m-footer-links__list-item">
<a class="m-footer-links__link" href="${url}">${name}</a>
${Array.isArray(items) && items.map(({ name, url, isActive }, index) => html`
<li class="m-footer-links__list-item ${isActive ? 'is-footer-links__list-item-active' : ''}">
<a class="m-footer-links__link js-footer-links__link" href="${url}" index="${index}">${name}</a>
</li>
`)}
</ul>
Expand Down
1 change: 1 addition & 0 deletions src/components/m-footer-links/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@

color: $footer-links__link-color;

.is-footer-links__list-item-active > &,
&:hover,
&:active,
&:focus {
Expand Down
41 changes: 40 additions & 1 deletion src/components/m-footer-links/js/footer-links.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import on from '../../../js/on';
import fire from '../../../js/fire';
import getAttribute from '../../../js/get-attribute';
import { subscribe } from '../../../js/pubsub';
import DropDown from '../../m-dropdown/js/drop-down';

const hasDropdownBreakpoints = 'xs';

// @TODO: dependency to a-device-state not explicit
/**
* @fires FooterLinks#axa-click
*/
export default class FooterLinks {
constructor(wcNode) {
static DEFAULTS = {
link: 'js-footer-links__link',
};

constructor(wcNode, options) {
this.wcNode = wcNode;
this.options = {
...FooterLinks.DEFAULTS,
...options,
};

this.handleClick = this.handleClick.bind(this);

this.on();
}
Expand All @@ -25,12 +41,35 @@ export default class FooterLinks {
delete this.dropDown;
}
});

this.unClick = on(this.wcNode, 'click', this.options.link, this.handleClick, { passive: false });
}

off() {
if (this.unsubscribe) {
this.unsubscribe();
}

if (this.unClick) {
this.unClick();
}
}

handleClick(event, delegateTarget) {
// @todo: would be cool to be able to use props here, cause now it needs JSON.parse...
const index = getAttribute(delegateTarget, 'index');
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LucaMele
Passing the current item's data down to the custom event is possible 👍

const { wcNode: { items } } = this;
/**
* axa-click event.
*
* @event FooterLinks#axa-click
* @type {object}
*/
const cancelled = fire(this.wcNode, 'axa-click', items[index], { bubbles: true, cancelable: true, composed: true });

if (!cancelled) {
event.preventDefault();
}
}

destroy() {
Expand Down
2 changes: 1 addition & 1 deletion src/components/m-header-logo/_template.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import html from 'nanohtml';
import raw from 'nanohtml/raw';

export default ({ src, alt = 'AXA Logo', href = '#' } = {}) => html`
<a href="${href}" class="m-header-logo__link">
<a href="${href}" class="m-header-logo__link js-header-logo__link">
${src ? html`
<img class="m-header-logo__img" src="${src}" alt="${alt}" />
` : raw('<axa-icon icon="logo-AXA" classes="m-header-logo__icon"></axa-icon>')}
Expand Down
16 changes: 16 additions & 0 deletions src/components/m-header-logo/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import styles from './index.scss';
// import the template used for this component
import template from './_template';
import wcdomready from '../../js/wcdomready';
import HeaderLogo from './js/header-logo';

class AXAHeaderLogo extends BaseComponentGlobal {
static get observedAttributes() { return ['alt', 'href', 'src']; }
Expand All @@ -17,6 +18,21 @@ class AXAHeaderLogo extends BaseComponentGlobal {

this.className = `${this.initialClassName} m-header-logo`;
}

didRenderCallback() {
if (this.logo) {
this.logo.destroy();
}

this.logo = new HeaderLogo(this);
}

disconnectedCallback() {
if (this.logo) {
this.logo.destroy();
delete this.logo;
}
}
}

wcdomready(() => {
Expand Down
66 changes: 66 additions & 0 deletions src/components/m-header-logo/js/header-logo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import on from '../../../js/on';
import fire from '../../../js/fire';

/**
* @fires HeaderLogo#axa-click
*/
class HeaderLogo {
static DEFAULTS = {
link: '.js-header-logo__link',
}

constructor(wcNode, options = {}) {
this.wcNode = wcNode;
this.options = {
...HeaderLogo.DEFAULTS,
...options,
};

this.handleClick = this.handleClick.bind(this);

this.init();
}

init() {
this.link = this.wcNode.querySelector(this.options.link);

this.on();
}

on() {
this.off();

this.unClick = on(this.link, 'click', this.handleClick, { passive: false });
}

handleClick(event) {
/**
* axa-click event.
*
* @event HeaderLogo#axa-click
* @type {null}
*/
const cancelled = fire(this.wcNode, 'axa-click', null, { bubbles: true, cancelable: true, composed: true });

if (!cancelled) {
event.preventDefault();
}
}

off() {
if (this.unClick) {
this.unClick();
}
}

destroy() {
this.off();

delete this.link;
delete this.wcNode;
delete this.options;
delete this.handleClick;
}
}

export default HeaderLogo;
5 changes: 4 additions & 1 deletion src/demos/demo.react.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<article>
<article style="margin-bottom: 100px;">
<h1>Demo on how to pass a callback function:</h1>
<div class="my-event-demo-react"></div>

<h1>Demo TodoMVC:</h1>
<div class="my-todo-demo-react"></div>
</article>
Loading