Skip to content

Commit

Permalink
Merge ee0926f into f4629c8
Browse files Browse the repository at this point in the history
  • Loading branch information
paolostyle committed Jan 8, 2024
2 parents f4629c8 + ee0926f commit e391c4f
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 66 deletions.
71 changes: 47 additions & 24 deletions packages/visx-responsive/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,31 @@ The `@visx/responsive` package is here to help you make responsive graphs.

If you would like your graph to adapt to the screen size, you can use `withScreenSize()`. The
resulting component will pass `screenWidth` and `screenHeight` props to the wrapped component
containing the respective screen dimensions.
containing the respective screen dimensions. You can also optionally pass two config props to the
wrapped component, although in 99% of the cases this is not necessary:

- `windowResizeDebounceTime` - determines how often the size is updated in miliseconds, defaults to
`300`,
- `enableDebounceLeadingCall` - determines whether the size is updated immediately on first render,
defaults to `true`. This is essentially the value of
[`options.leading` in Lodash's `debounce`](https://lodash.com/docs/4.17.15#debounce).

### Example:

```js
import { withScreenSize } from '@visx/responsive';
// or
// import * as Responsive from '@visx/responsive';
// Responsive.withScreenSize(...);
```tsx
import { withScreenSize, WithScreenSizeProvidedProps } from '@visx/responsive';

interface Props extends WithScreenSizeProvidedProps {
myProp: string;
}

let chartToRender = withScreenSize(MySuperCoolVisxChart);
const MySuperCoolVisxChart = ({ myProp, screenWidth, screenHeight }: Props) => {
// ...
};

const ChartToRender = withScreenSize(MySuperCoolVisxChart);

const chartToRender = <ChartToRender />;

// ... Render the chartToRender somewhere
```
Expand All @@ -41,17 +55,32 @@ let chartToRender = withScreenSize(MySuperCoolVisxChart);

If you would like your graph to adapt to it's parent component's size, you can use
`withParentSize()`. The resulting component will pass `parentWidth` and `parentHeight` props to the
wrapped component containing the respective parent's dimensions.
wrapped component containing the respective parent's dimensions. You can also optionally pass config
props to the wrapped component:

- `initialWidth` - initial chart width used before the parent size is determined,
- `initialHeight` - initial chart height used before the parent size is determined,
- `debounceTime` - determines how often the size is updated in miliseconds, defaults to `300`,
- `enableDebounceLeadingCall` - determines whether the size is updated immediately on first render,
defaults to `true`. This is essentially the value of
[`options.leading` in Lodash's `debounce`](https://lodash.com/docs/4.17.15#debounce).

### Example:

```js
import { withParentSize } from '@visx/responsive';
// or
// import * as Responsive from '@visx/responsive';
// Responsive.withParentSize(...);
```tsx
import { withParentSize, WithParentSizeProvidedProps } from '@visx/responsive';

interface Props extends WithParentSizeProvidedProps {
myProp: string;
}

const MySuperCoolVisxChart = ({ myProp, parentWidth, parentHeight }: Props) => {
// ...
};

let chartToRender = withParentSize(MySuperCoolVisxChart);
const ChartWithParentSize = withParentSize(MySuperCoolVisxChart);

const chartToRender = <ChartWithParentSize initialWidth={400} />;

// ... Render the chartToRender somewhere
```
Expand All @@ -62,13 +91,10 @@ You might do the same thing using the `ParentSize` component.

### Example:

```js
```tsx
import { ParentSize } from '@visx/responsive';
// or
// import * as Responsive from '@visx/responsive';
// <Responsive.ParentSize />;

let chartToRender = (
const chartToRender = (
<ParentSize>
{(parent) => (
<MySuperCoolVisxChart
Expand All @@ -94,13 +120,10 @@ You can also create a responsive chart with a specific viewBox with the `ScaleSV

### Example:

```js
```tsx
import { ScaleSVG } from '@visx/responsive';
// or
// import * as Responsive from '@visx/responsive';
// <Responsive.ScaleSVG />

let chartToRender = (
const chartToRender = (
<ScaleSVG width={400} height={400}>
<MySuperCoolVXChart />
</ScaleSVG>
Expand Down
39 changes: 23 additions & 16 deletions packages/visx-responsive/src/enhancers/withParentSize.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import debounce from 'lodash/debounce';
import { ResizeObserver, ResizeObserverPolyfill } from '../types';
import React from 'react';
import { ResizeObserver, ResizeObserverPolyfill, Simplify } from '../types';

const CONTAINER_STYLES = { width: '100%', height: '100%' };

Expand All @@ -9,33 +9,36 @@ interface PrivateWindow {
ResizeObserver: ResizeObserverPolyfill;
}

export type WithParentSizeProps = {
type WithParentSizeConfig = {
debounceTime?: number;
enableDebounceLeadingCall?: boolean;
initialWidth?: number;
initialHeight?: number;
};

type WithParentSizeState = {
parentWidth?: number;
parentHeight?: number;
initialWidth?: number;
initialHeight?: number;
};

export type WithParentSizeProvidedProps = WithParentSizeState;

export default function withParentSize<BaseComponentProps extends WithParentSizeProps = {}>(
BaseComponent: React.ComponentType<BaseComponentProps & WithParentSizeProvidedProps>,
type WithParentSizeComponentProps<P extends WithParentSizeProvidedProps> = Simplify<
Omit<P, keyof WithParentSizeProvidedProps> & WithParentSizeConfig
>;

export default function withParentSize<P extends WithParentSizeProvidedProps>(
BaseComponent: React.ComponentType<P>,
/** Optionally inject a ResizeObserver polyfill, else this *must* be globally available. */
resizeObserverPolyfill?: ResizeObserverPolyfill,
) {
): React.ComponentType<WithParentSizeComponentProps<P>> {
return class WrappedComponent extends React.Component<
BaseComponentProps & WithParentSizeProvidedProps,
WithParentSizeComponentProps<P>,
WithParentSizeState
> {
static defaultProps = {
debounceTime: 300,
enableDebounceLeadingCall: true,
};
displayName = `withParentSize(${
BaseComponent.displayName ?? BaseComponent.name ?? 'Component'
})`;
state = {
parentWidth: undefined,
parentHeight: undefined,
Expand Down Expand Up @@ -80,8 +83,8 @@ export default function withParentSize<BaseComponentProps extends WithParentSize
parentHeight: height,
});
},
this.props.debounceTime,
{ leading: this.props.enableDebounceLeadingCall },
this.props.debounceTime ?? 300,
{ leading: this.props.enableDebounceLeadingCall ?? true },
);

render() {
Expand All @@ -90,7 +93,11 @@ export default function withParentSize<BaseComponentProps extends WithParentSize
return (
<div style={CONTAINER_STYLES} ref={this.setRef}>
{parentWidth != null && parentHeight != null && (
<BaseComponent parentWidth={parentWidth} parentHeight={parentHeight} {...this.props} />
<BaseComponent
parentWidth={parentWidth}
parentHeight={parentHeight}
{...(this.props as P)}
/>
)}
</div>
);
Expand Down
33 changes: 20 additions & 13 deletions packages/visx-responsive/src/enhancers/withScreenSize.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import debounce from 'lodash/debounce';
import React from 'react';
import { Simplify } from '../types';

export type WithScreenSizeProps = {
type WithScreenSizeConfig = {
windowResizeDebounceTime?: number;
enableDebounceLeadingCall?: boolean;
};
Expand All @@ -13,18 +14,20 @@ type WithScreenSizeState = {

export type WithScreenSizeProvidedProps = WithScreenSizeState;

export default function withScreenSize<BaseComponentProps extends WithScreenSizeProps = {}>(
BaseComponent: React.ComponentType<BaseComponentProps>,
) {
type WithScreenSizeComponentProps<P extends WithScreenSizeProvidedProps> = Simplify<
Omit<P, keyof WithScreenSizeProvidedProps> & WithScreenSizeConfig
>;

export default function withScreenSize<P extends WithScreenSizeProvidedProps>(
BaseComponent: React.ComponentType<P>,
): React.ComponentType<WithScreenSizeComponentProps<P>> {
return class WrappedComponent extends React.Component<
BaseComponentProps & WithScreenSizeProvidedProps,
WithScreenSizeComponentProps<P>,
WithScreenSizeState
> {
static defaultProps = {
windowResizeDebounceTime: 300,
enableDebounceLeadingCall: true,
};

displayName = `withScreenSize(${
BaseComponent.displayName ?? BaseComponent.name ?? 'Component'
})`;
state = {
screenWidth: undefined,
screenHeight: undefined,
Expand All @@ -48,14 +51,18 @@ export default function withScreenSize<BaseComponentProps extends WithScreenSize
screenHeight: window.innerHeight,
}));
},
this.props.windowResizeDebounceTime,
{ leading: this.props.enableDebounceLeadingCall },
this.props.windowResizeDebounceTime ?? 300,
{ leading: this.props.enableDebounceLeadingCall ?? true },
);

render() {
const { screenWidth, screenHeight } = this.state;
return screenWidth == null || screenHeight == null ? null : (
<BaseComponent screenWidth={screenWidth} screenHeight={screenHeight} {...this.props} />
<BaseComponent
screenWidth={screenWidth}
screenHeight={screenHeight}
{...(this.props as P)}
/>
);
}
};
Expand Down
6 changes: 3 additions & 3 deletions packages/visx-responsive/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { default as ScaleSVG } from './components/ScaleSVG';
export { default as ParentSize } from './components/ParentSize';
export { default as withParentSize } from './enhancers/withParentSize';
export { default as withScreenSize } from './enhancers/withScreenSize';
export { default as ScaleSVG } from './components/ScaleSVG';
export { default as withParentSize, WithParentSizeProvidedProps } from './enhancers/withParentSize';
export { default as withScreenSize, WithScreenSizeProvidedProps } from './enhancers/withScreenSize';
2 changes: 2 additions & 0 deletions packages/visx-responsive/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ interface ResizeObserverPolyfill {
}

export { ResizeObserver, ResizeObserverCallback, ResizeObserverPolyfill };

export type Simplify<T> = { [Key in keyof T]: T[Key] } & {};
22 changes: 12 additions & 10 deletions packages/visx-responsive/test/withParentSize.test.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import React from 'react';
import { render } from '@testing-library/react';
import { ResizeObserver } from '@juggle/resize-observer';
import '@testing-library/jest-dom';
import { withParentSize } from '../src';
import { render } from '@testing-library/react';
import React from 'react';
import { withParentSize, WithParentSizeProvidedProps } from '../src';

type ComponentProps = {
parentWidth?: number;
parentHeight?: number;
};
interface ComponentProps extends WithParentSizeProvidedProps {
// only there to ensure that TS allows enhanced component to have own props, different than the ones passed by the HOC
role: string;
}

function Component({ parentWidth, parentHeight }: ComponentProps) {
return <div data-testid="Component" style={{ width: parentWidth, height: parentHeight }} />;
function Component({ parentWidth, parentHeight, role }: ComponentProps) {
return (
<div data-testid="Component" role={role} style={{ width: parentWidth, height: parentHeight }} />
);
}

describe('withParentSize', () => {
Expand All @@ -20,7 +22,7 @@ describe('withParentSize', () => {

test('it should pass parentWidth and parentHeight props to its child', () => {
const HOC = withParentSize(Component, ResizeObserver);
const { getByTestId } = render(<HOC initialWidth={200} initialHeight={200} />);
const { getByTestId } = render(<HOC role="img" initialWidth={200} initialHeight={200} />);

const RenderedComponent = getByTestId('Component');
expect(RenderedComponent).toHaveStyle('width: 200px; height: 200px');
Expand Down

0 comments on commit e391c4f

Please sign in to comment.