Skip to content

Commit

Permalink
Support passing container to component prop helper functions (#382)
Browse files Browse the repository at this point in the history
* Pass container to prop definitions when available (#376)

* Avoid re-decorating the same prop multiple times

* Only use node 14 for github ci
  • Loading branch information
Daniel Brain authored and bluepnume committed Dec 14, 2021
1 parent 6d1f0e6 commit f9c8cad
Show file tree
Hide file tree
Showing 7 changed files with 460 additions and 283 deletions.
357 changes: 203 additions & 154 deletions docs/api/prop-definitions.md
Original file line number Diff line number Diff line change
@@ -1,156 +1,205 @@
# Prop Definitions

## type `string`

The data-type expected for the prop

- `'string'`
- `'number'`
- `'boolean'`
- `'object'`
- `'function'`
- `'array'`

## required `boolean`

Whether or not the prop is mandatory. Defaults to `true`.

```javascript
onLogin: {
type: 'function',
required: false
}
```

## default `({ props }) => value`

A function returning the default value for the prop, if none is passed

```javascript
email: {
type: 'string',
required: false,
default: function() {
return 'a@b.com';
}
}
```

## validate `({ value, props }) => void`

A function to validate the passed value. Should throw an appropriate error if invalid.

```javascript
email: {
type: 'string',
validate: function({ value, props }) {
if (!value.match(/^.+@.+\..+$/)) {
throw new TypeError(`Expected email to be valid format`);
}
}
}
```

## queryParam `boolean | string | (value) => string`

Should a prop be passed in the url.

```javascript
email: {
type: 'string',
queryParam: true // ?email=foo@bar.com
}
```

If a string is set, this specifies the url param name which will be used.

```javascript
email: {
type: 'string',
queryParam: 'user-email' // ?user-email=foo@bar.com
}
```

If a function is set, this is called to determine the url param which should be used.

```javascript
email: {
type: 'string',
queryParam: function({ value }) {
if (value.indexOf('@foo.com') !== -1) {
return 'foo-email'; // ?foo-email=person@foo.com
} else {
return 'generic-email'; // ?generic-email=person@whatever.com
}
}
}
```

## value `({ props }) => value`

The value for the prop, if it should be statically defined at component creation time

```javascript
userAgent: {
type: 'string',
value() {
return window.navigator.userAgent;
}
}
```

## decorate `({ value, props }) => value`

A function used to decorate the prop at render-time. Called with the value of the prop, should return the new value.

```javascript
onLogin: {
type: 'function',
decorate(original) {
return function() {
console.log('User logged in!');
return original.apply(this, arguments);
};
}
}
```

## serialization `string`

If `json`, the prop will be JSON stringified before being inserted into the url

```javascript
user: {
type: 'object',
serialization: 'json' // ?user={"name":"Zippy","age":34}
}
```

If `dotify` the prop will be converted to dot-notation.

```javascript
user: {
type: 'object',
serialization: 'dotify' // ?user.name=Zippy&user.age=34
}
```

If `base64`, the prop will be JSON stringified then base64 encoded before being inserted into the url

```javascript
user: {
type: 'object',
serialization: 'base64' // ?user=eyJuYW1lIjoiWmlwcHkiLCJhZ2UiOjM0fQ==
}
```

## alias `string`

An aliased name for the prop

```javascript
onLogin: {
type: 'function',
alias: 'onUserLogin'
}
```
## type

`string`

The data-type expected for the prop

- `'string'`
- `'number'`
- `'boolean'`
- `'object'`
- `'function'`
- `'array'`

## required

`boolean`

Whether or not the prop is mandatory. Defaults to `true`.

```javascript
onLogin: {
type: 'function',
required: false
}
```

## default

```javascript
({
props : Props,
state : Object,
close : () => Promise<undefined>,
focus : () => Promise<undefined>,
onError : (mixed) => Promise<undefined>,
container : HTMLElement | undefined,
event : Event
}) => value
```

A function returning the default value for the prop, if none is passed

```javascript
email: {
type: 'string',
required: false,
default: function() {
return 'a@b.com';
}
}
```

## validate

`({ value, props }) => void`

A function to validate the passed value. Should throw an appropriate error if invalid.

```javascript
email: {
type: 'string',
validate: function({ value, props }) {
if (!value.match(/^.+@.+\..+$/)) {
throw new TypeError(`Expected email to be valid format`);
}
}
}
```

## queryParam

`boolean | string | (value) => string`

Should a prop be passed in the url.

```javascript
email: {
type: 'string',
queryParam: true // ?email=foo@bar.com
}
```

If a string is set, this specifies the url param name which will be used.

```javascript
email: {
type: 'string',
queryParam: 'user-email' // ?user-email=foo@bar.com
}
```

If a function is set, this is called to determine the url param which should be used.

```javascript
email: {
type: 'string',
queryParam: function({ value }) {
if (value.indexOf('@foo.com') !== -1) {
return 'foo-email'; // ?foo-email=person@foo.com
} else {
return 'generic-email'; // ?generic-email=person@whatever.com
}
}
}
```

## value

```javascript
({
props : Props,
state : Object,
close : () => Promise<undefined>,
focus : () => Promise<undefined>,
onError : (mixed) => Promise<undefined>,
container : HTMLElement | undefined,
event : Event
}) => value
```

The value for the prop, if it should be statically defined at component creation time

```javascript
userAgent: {
type: 'string',
value() {
return window.navigator.userAgent;
}
}
```

## decorate

```javascript
({
props : Props,
state : Object,
close : () => Promise<undefined>,
focus : () => Promise<undefined>,
onError : (mixed) => Promise<undefined>,
container : HTMLElement | undefined,
event : Event,
value : Value
}) => value
```

A function used to decorate the prop at render-time. Called with the value of the prop, should return the new value.

```javascript
onLogin: {
type: 'function',
decorate(original) {
return function() {
console.log('User logged in!');
return original.apply(this, arguments);
};
}
}
```

## serialization

`string`

If `json`, the prop will be JSON stringified before being inserted into the url

```javascript
user: {
type: 'object',
serialization: 'json' // ?user={"name":"Zippy","age":34}
}
```

If `dotify` the prop will be converted to dot-notation.

```javascript
user: {
type: 'object',
serialization: 'dotify' // ?user.name=Zippy&user.age=34
}
```

If `base64`, the prop will be JSON stringified then base64 encoded before being inserted into the url

```javascript
user: {
type: 'object',
serialization: 'base64' // ?user=eyJuYW1lIjoiWmlwcHkiLCJhZ2UiOjM0fQ==
}
```

## alias

`string`

An aliased name for the prop

```javascript
onLogin: {
type: 'function',
alias: 'onUserLogin'
}
```
8 changes: 4 additions & 4 deletions src/component/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { type RenderOptionsType, type ParentHelpers, parentComponent } from '../
import { ZOID, CONTEXT, POST_MESSAGE, WILDCARD, METHOD, PROP_TYPE } from '../constants';
import { react, angular, vue, vue3, angular2 } from '../drivers';
import { getGlobal, destroyGlobal, getInitialParentPayload, isChildComponentWindow } from '../lib';
import type { CssDimensionsType, StringMatcherType } from '../types';
import type { CssDimensionsType, StringMatcherType, ContainerReferenceType } from '../types';

import { validateOptions } from './validate';
import { defaultContainerTemplate, defaultPrerenderTemplate } from './templates';
Expand Down Expand Up @@ -135,8 +135,8 @@ export type ZoidComponentInstance<P, X = void, C = void> = {|
...C,
isEligible : () => boolean,
clone : () => ZoidComponentInstance<P, X, C>,
render : (container? : string | HTMLElement, context? : $Values<typeof CONTEXT>) => ZalgoPromise<void>,
renderTo : (target : CrossDomainWindowType, container? : string | HTMLElement, context? : $Values<typeof CONTEXT>) => ZalgoPromise<void>
render : (container? : ContainerReferenceType, context? : $Values<typeof CONTEXT>) => ZalgoPromise<void>,
renderTo : (target : CrossDomainWindowType, container? : ContainerReferenceType, context? : $Values<typeof CONTEXT>) => ZalgoPromise<void>
|};

// eslint-disable-next-line flowtype/require-exact-type
Expand Down Expand Up @@ -335,7 +335,7 @@ export function component<P, X, C>(opts : ComponentOptionsType<P, X, C>) : Compo
});
};

const getDefaultContainer = (context : $Values<typeof CONTEXT>, container? : string | HTMLElement) : string | HTMLElement => {
const getDefaultContainer = (context : $Values<typeof CONTEXT>, container? : ContainerReferenceType) : ContainerReferenceType => {
if (container) {
if (typeof container !== 'string' && !isElement(container)) {
throw new TypeError(`Expected string or element selector to be passed`);
Expand Down
Loading

0 comments on commit f9c8cad

Please sign in to comment.