Skip to content

Commit a00ce6a

Browse files
author
Matt Shwery
authored
v4 (#64)
* replace innerRef with forwardRef (#61) * enable safe hrefs by default (#63) * use separate sourcemaps and fix yarn size-limit * format file, remove comments from dist * lint --fix a few things * node 10.18+ * upgrade ts deps and use simpler polymorphic typing (#71) * v4.0.0-1 * update types again * export types * v4.0.0-2 * revert propsOf back * v4.0.0-3
1 parent 9cdecc4 commit a00ce6a

File tree

16 files changed

+1462
-1428
lines changed

16 files changed

+1462
-1428
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ version: 2
22
jobs:
33
test:
44
docker:
5-
- image: circleci/node:8.11
5+
- image: circleci/node:10.18
66
steps:
77
- checkout
88

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10.18

README.md

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,6 @@ E.g:
6262
<Box is={Link} to="/login">Login</Box>
6363
```
6464

65-
##### innerRef
66-
67-
Type: `function`
68-
69-
Callback that gets passed a ref to inner DOM node (or component if the `is` prop is set to a React component type).
70-
7165
##### clearfix
7266

7367
Type: `boolean`
@@ -310,14 +304,15 @@ setClassNamePrefix('📦')
310304

311305
### Safe `href`s
312306

313-
By default `ui-box` does not ensure that urls use safe protocols when passed to an element. But we built this functionality into `ui-box` to protect the end users of the products you are building. You can alter this by using `configureSafeHref({enabled?: boolean, origin?: string})`. This will ensure that only safe protocols are used (`http:`, `https:`, `mailto:`, `tel:`, and `data:`) and that the correct `rel` values are added (`noopener`, `noreferrer`(for external links)).
307+
By default `ui-box` ensures that urls use safe protocols when passed to an element. We built this functionality into `ui-box` to protect the end users of the products you are building. You can opt-out of this by using `configureSafeHref({enabled?: boolean, origin?: string})`. This allows you to configure which protocols are acceptable (`http:`, `https:`, `mailto:`, `tel:`, and `data:`) and that the correct `rel` values are added (`noopener`, `noreferrer`(for external links)).
314308

315309
```js
316310
import { configureSafeHref } from 'ui-box'
317311
configureSafeHref({
318-
enabled: true,
312+
enabled: true, // the default behavior
319313
})
320314
```
315+
321316
```js
322317
import { configureSafeHref } from 'ui-box'
323318
configureSafeHref({
@@ -326,10 +321,10 @@ configureSafeHref({
326321
})
327322
```
328323

329-
Additionally you can overwrite the behavoir on an individual component basis using the prop `allowUnsafeHref`
324+
Additionally you can override the behavior on an individual component basis using the prop `allowUnsafeHref`
330325

331326
```jsx
332-
<Box is="a" href="javascript:alert('hi')" allowUnsafeHref={true}>This is unsafe</Box>
327+
<Box is="a" href="javascript:alert('hi')" allowUnsafeHref>This is unsafe</Box>
333328
```
334329

335330
### Server side rendering

package.json

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ui-box",
3-
"version": "3.3.0",
3+
"version": "4.0.0-3",
44
"description": "Blazing Fast React UI Primitive",
55
"contributors": [
66
"Jeroen Ransijn (https://twitter.com/jeroen_ransijn)",
@@ -14,20 +14,23 @@
1414
],
1515
"repository": "segmentio/ui-box",
1616
"license": "MIT",
17-
"main": "dist/index.js",
18-
"typings": "dist/index.d.ts",
17+
"main": "dist/src/index.js",
18+
"typings": "dist/src/index.d.ts",
1919
"files": [
20-
"dist"
20+
"dist/src"
2121
],
2222
"sideEffects": false,
23+
"engines": {
24+
"node": ">=10.18"
25+
},
2326
"scripts": {
2427
"test": "xo && nyc ava",
2528
"prepublishOnly": "rm -rf dist && yarn run build",
2629
"dev": "start-storybook -p 9009",
2730
"build": "tsc",
2831
"build-storybook": "build-storybook -s .storybook/static -o .out",
2932
"release": "np",
30-
"benchmark": "echo ui-box && react-benchmark tools/benchmarks/box.js",
33+
"benchmark": "echo ui-box && react-benchmark dist/tools/benchmarks/box.js",
3134
"size": "size-limit",
3235
"coverage": "nyc report --reporter=html"
3336
},
@@ -41,6 +44,7 @@
4144
},
4245
"devDependencies": {
4346
"@babel/core": "^7.4.4",
47+
"@size-limit/preset-big-lib": "^4.5.4",
4448
"@storybook/react": "^5.0.1",
4549
"@storybook/storybook-deployer": "^2.8.1",
4650
"@types/enzyme": "^3.9.1",
@@ -72,7 +76,7 @@
7276
"react-dom": "^16.8.4",
7377
"react-test-renderer": "^16.8.4",
7478
"sinon": "^7.2.7",
75-
"size-limit": "^1.3.1",
79+
"size-limit": "^4.5.4",
7680
"ts-node": "^8.1.0",
7781
"typescript": "^3.4.5",
7882
"webpack": "^4.30.0",
@@ -149,9 +153,8 @@
149153
},
150154
"size-limit": [
151155
{
152-
"webpack": false,
153-
"path": "dist/index.js",
154-
"limit": "5 KB",
156+
"path": "dist/src/index.js",
157+
"limit": "50 KB",
155158
"running": false,
156159
"gzip": false
157160
}

src/box.tsx

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
import React from 'react'
1+
import React, { forwardRef } from 'react'
22
import PropTypes from 'prop-types'
3-
import {BoxComponent} from './types/box-types'
4-
import {propTypes} from './enhancers'
3+
import { BoxProps } from './types/box-types'
4+
import { propTypes } from './enhancers'
55
import enhanceProps from './enhance-props'
6-
import {extractAnchorProps, getUseSafeHref} from './utils/safeHref'
6+
import { extractAnchorProps, getUseSafeHref } from './utils/safeHref'
77

8-
const Box: BoxComponent = ({ is = 'div', innerRef, children, allowUnsafeHref, ...props }) => {
8+
const Box = forwardRef(<E extends React.ElementType>({ is, children, allowUnsafeHref, ...props }: BoxProps<E>, ref: React.Ref<Element>) => {
99
// Convert the CSS props to class names (and inject the styles)
1010
const {className, enhancedProps: parsedProps} = enhanceProps(props)
1111

1212
parsedProps.className = className
1313

14-
if (innerRef) {
15-
parsedProps.ref = innerRef
14+
if (ref) {
15+
parsedProps.ref = ref
1616
}
1717

1818
/**
@@ -27,22 +27,21 @@ const Box: BoxComponent = ({ is = 'div', innerRef, children, allowUnsafeHref, ..
2727
parsedProps.rel = safeRel
2828
}
2929

30-
return React.createElement(is, parsedProps, children)
31-
}
30+
return React.createElement(is || 'div', parsedProps, children)
31+
}) as <E extends React.ElementType = 'div'>(props: BoxProps<E>) => JSX.Element
3232

33+
// @ts-ignore
3334
Box.displayName = 'Box'
3435

36+
// @ts-ignore
3537
Box.propTypes = {
3638
...propTypes,
37-
innerRef: PropTypes.oneOfType([
38-
PropTypes.func,
39-
PropTypes.shape({ current: PropTypes.element })
40-
]),
41-
is: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.elementType])
39+
is: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.elementType]),
40+
allowUnsafeHref: PropTypes.bool
4241
}
4342

43+
// @ts-ignore
4444
Box.defaultProps = {
45-
innerRef: null,
4645
is: 'div',
4746
boxSizing: 'border-box'
4847
}

src/index.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import * as cache from './cache'
22
import * as styles from './styles'
33

4-
export {default} from './box'
5-
export {default as splitProps} from './utils/split-props'
6-
export {default as splitBoxProps} from './utils/split-box-props'
4+
export { default } from './box'
5+
export { default as splitProps } from './utils/split-props'
6+
export { default as splitBoxProps } from './utils/split-box-props'
77
export { setClassNamePrefix } from './get-class-name'
88
export { configureSafeHref } from './utils/safeHref'
9+
export { BoxProps, BoxOwnProps, EnhancerProps, PropsOf, PolymorphicBoxProps, BoxComponent } from './types/box-types'
910

1011
export {
1112
background,

src/types/box-types.ts

Lines changed: 29 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React from 'react'
22
import { EnhancerProps } from './enhancers'
3-
import { DomNodes } from './dom-nodes'
43

54
export { EnhancerProps }
65

@@ -10,60 +9,42 @@ export { EnhancerProps }
109
*/
1110
export type Without<T, K> = Pick<T, Exclude<keyof T, K>>
1211

13-
/**
14-
* "is" prop
15-
* @template P Props
16-
*/
17-
export type Is<P = any> = React.ElementType<P>
12+
export type PropsOf<
13+
E extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any>
14+
> = JSX.LibraryManagedAttributes<E, React.ComponentPropsWithRef<E>>
1815

1916
/**
20-
* Custom Ref to handle `is` prop
17+
* Generic component props with "is" prop
18+
* @template P Additional props
19+
* @template T React component or string element
2120
*/
22-
export type RefType<T> = T extends keyof DomNodes
23-
? DomNodes[T] // Get the DOM node type
24-
: T extends typeof React.Component
25-
? T['prototype'] // Convert component class type back to a class instance
26-
: never // Functional components can't have refs
21+
export type BoxOwnProps<E extends React.ElementType = React.ElementType, P = {}> = Without<EnhancerProps, keyof P> & {
22+
/**
23+
* Replaces the underlying element
24+
*/
25+
is?: E
26+
27+
/**
28+
* Allows the high level value of safeHref to be overwritten on an individual component basis
29+
*/
30+
allowUnsafeHref?: boolean
31+
}
2732

28-
/**
29-
* Remove box props from object `T` if they're present
30-
* @template T Object
31-
*/
32-
type WithoutBoxProps<T> = Without<T, "is" | "innerRef">
33+
export type BoxProps<E extends React.ElementType> = BoxOwnProps<E> & Without<PropsOf<E>, keyof BoxOwnProps>
3334

3435
/**
35-
* Grab components passed to the `is` prop and return their props
36-
* @template T Component type
36+
* Convenience type for defining your own component props that extend Box and pass-through props
3737
*/
38-
type InheritedProps<T extends Is> = WithoutBoxProps<React.ComponentPropsWithoutRef<T>>
38+
export type PolymorphicBoxProps<
39+
E extends React.ElementType,
40+
// optional additional props (which we get removed from BoxOwnProps and PropsOf)
41+
// this is useful for defining some pass-through props on a wrapper for Box
42+
P = {}
43+
> = BoxOwnProps<E, P> & Without<PropsOf<E>, keyof (BoxOwnProps & P)> & P
3944

4045
/**
41-
* Generic component props with "is" prop
42-
* @template P Additional props
43-
* @template T React component or string element
46+
* Convenience type for defining your own components that extend Box and pass-through props
4447
*/
45-
export type BoxProps<T extends Is> = InheritedProps<T> &
46-
EnhancerProps & {
47-
/**
48-
* Replaces the underlying element
49-
*/
50-
is?: T
51-
52-
/**
53-
* Callback that gets passed a ref to inner DOM node (or component if the
54-
* `is` prop is set to a React component type).
55-
*/
56-
innerRef?: React.Ref<RefType<T>>
57-
58-
/**
59-
* Allows the high level value of safeHref to be overwritten on an individual component basis
60-
*/
61-
allowUnsafeHref?: boolean
62-
}
63-
64-
export interface BoxComponent {
65-
<T extends Is>(props: BoxProps<T>): React.ReactElement | null
66-
propTypes?: React.FunctionComponent['propTypes']
67-
defaultProps?: React.FunctionComponent['defaultProps']
68-
displayName?: React.FunctionComponent['displayName']
69-
}
48+
export type BoxComponent<P = {}, D extends React.ElementType = React.ElementType> = <
49+
E extends React.ElementType = D
50+
>(props: PolymorphicBoxProps<E, P>) => JSX.Element

0 commit comments

Comments
 (0)