Skip to content

Commit

Permalink
Components: Add hooks based Scrollable component wrapper (#28979)
Browse files Browse the repository at this point in the history
* Components: Add hooks based Scrollable component wrapper

* Add README for Scrollable

* Update example doc for scrollable/component.js and README

Co-authored-by: Jon Q <hello@jonquach.com>
  • Loading branch information
sarayourfriend and Jon Q committed Feb 16, 2021
1 parent 22a5dd1 commit eccb3c1
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 0 deletions.
31 changes: 31 additions & 0 deletions packages/components/src/ui/scrollable/README.md
@@ -0,0 +1,31 @@
# Scrollable

`Scrollable` is a layout component that content in a scrollable container.

## Usage

```jsx
import { Scrollable, View } from '@wordpress/components/ui';

function Example() {
return (
<Scrollable style={ { maxHeight: 200 } }>
<View style={ { height: 500 } }>...</View>
</Scrollable>
);
}
```

## Props

##### scrollDirection

**Type**: `x` | `y` | `auto`

Renders a scrollbar for a specific axis when content overflows.

##### smoothScroll

**Type**: `boolean`

Enables (CSS) smooth scrolling.
30 changes: 30 additions & 0 deletions packages/components/src/ui/scrollable/component.js
@@ -0,0 +1,30 @@
/**
* Internal dependencies
*/
import { createComponent } from '../utils';
import { useScrollable } from './hook';

/**
* `Scrollable` is a layout component that content in a scrollable container.
*
* @example
* ```jsx
* import { Scrollable, View } from `@wordpress/components/ui`;
* function Example() {
* return (
* <Scrollable style={ { maxHeight: 200 } }>
* <View style={ { height: 500 } }>...</View>
* </Scrollable>
* );
* }
* ```
*/

const Scrollable = createComponent( {
as: 'div',
useHook: useScrollable,
name: 'Scrollable',
} );

export default Scrollable;
45 changes: 45 additions & 0 deletions packages/components/src/ui/scrollable/hook.js
@@ -0,0 +1,45 @@
/**
* External dependencies
*/
import { useContextSystem } from '@wp-g2/context';
import { cx } from '@wp-g2/styles';

/**
* WordPress dependencies
*/
import { useMemo } from '@wordpress/element';

/**
* Internal dependencies
*/
import * as styles from './styles';

/* eslint-disable jsdoc/valid-types */
/**
* @param {import('@wp-g2/create-styles').ViewOwnProps<import('./types').Props, 'div'>} props
*/
/* eslint-enable jsdoc/valid-types */
export function useScrollable( props ) {
const {
className,
scrollDirection = 'y',
smoothScroll = false,
...otherProps
} = useContextSystem( props, 'Scrollable' );

const classes = useMemo(
() =>
cx(
styles.Scrollable,
styles.scrollableScrollbar,
smoothScroll && styles.smoothScroll,
scrollDirection === 'x' && styles.scrollX,
scrollDirection === 'y' && styles.scrollY,
scrollDirection === 'auto' && styles.scrollAuto,
className
),
[ className, scrollDirection, smoothScroll ]
);

return { ...otherProps, className: classes };
}
3 changes: 3 additions & 0 deletions packages/components/src/ui/scrollable/index.js
@@ -0,0 +1,3 @@
export { default as Scrollable } from './component';

export * from './hook';
18 changes: 18 additions & 0 deletions packages/components/src/ui/scrollable/stories/index.js
@@ -0,0 +1,18 @@
/**
* Internal dependencies
*/
import { View } from '../../index';
import { Scrollable } from '../index';

export default {
component: Scrollable,
title: 'G2 Components (Experimental)/Scrollable',
};

export const _default = () => {
return (
<Scrollable css={ { height: 400, width: 300 } }>
<View css={ { backgroundColor: '#eee', height: 1000 } } />
</Scrollable>
);
};
59 changes: 59 additions & 0 deletions packages/components/src/ui/scrollable/styles.js
@@ -0,0 +1,59 @@
/**
* External dependencies
*/
import { css, ui } from '@wp-g2/styles';

export const scrollableScrollbar = css`
@media only screen and ( min-device-width: 40em ) {
&::-webkit-scrollbar {
height: 12px;
width: 12px;
}
&::-webkit-scrollbar-track {
background-color: transparent;
}
&::-webkit-scrollbar-track {
background: ${ ui.get( 'colorScrollbarTrack' ) };
border-radius: 8px;
}
&::-webkit-scrollbar-thumb {
background-clip: padding-box;
background-color: ${ ui.get( 'colorScrollbarThumb' ) };
border: 2px solid rgba( 0, 0, 0, 0 );
border-radius: 7px;
}
&:hover::-webkit-scrollbar-thumb {
background-color: ${ ui.get( 'colorScrollbarThumbHover' ) };
}
}
`;

export const Scrollable = css`
height: 100%;
`;

export const Content = css`
position: relative;
`;

export const smoothScroll = css`
scroll-behavior: smooth;
`;

export const scrollX = css`
overflow-x: auto;
overflow-y: hidden;
`;

export const scrollY = css`
overflow-x: hidden;
overflow-y: auto;
`;

export const scrollAuto = css`
overflow-y: auto;
`;
@@ -0,0 +1,91 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`props should render correctly 1`] = `
.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0 {
box-sizing: border-box;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
font-family: Inter,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",sans-serif;
font-family: var(--wp-g2-font-family);
font-size: 13px;
font-size: var(--wp-g2-font-size);
font-weight: normal;
font-weight: var(--wp-g2-font-weight);
margin: 0;
height: 100%;
overflow-x: hidden;
overflow-y: auto;
}
@media (prefers-reduced-motion) {
.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0 {
-webkit-transition: none !important;
transition: none !important;
}
}
[data-system-ui-reduced-motion-mode="true"] .emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0 {
-webkit-transition: none !important;
transition: none !important;
}
@media only screen and ( min-device-width:40em ) {
.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0::-webkit-scrollbar {
height: 12px;
width: 12px;
}
.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0::-webkit-scrollbar-track {
background-color: transparent;
}
.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.04);
background: var(--wp-g2-color-scrollbar-track);
border-radius: 8px;
}
.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0::-webkit-scrollbar-thumb {
background-clip: padding-box;
background-color: rgba(0, 0, 0, 0.2);
background-color: var(--wp-g2-color-scrollbar-thumb);
border: 2px solid rgba( 0,0,0,0 );
border-radius: 7px;
}
.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0.emotion-0:hover::-webkit-scrollbar-thumb {
background-color: rgba(0, 0, 0, 0.5);
background-color: var(--wp-g2-color-scrollbar-thumb-hover);
}
}
<div
class="components-scrollable wp-components-scrollable emotion-0"
data-g2-c16t="true"
data-g2-component="Scrollable"
>
WordPress.org - Code is Poetry
</div>
`;

exports[`props should render smoothScroll 1`] = `
"Snapshot Diff:
- First value
+ Second value
- Snapshot Diff:
- - First value
- + Second value
-
- @@ -7,10 +7,9 @@
- \\"margin-top\\": \\"0px\\",
- \\"margin-right\\": \\"0px\\",
- \\"margin-bottom\\": \\"0px\\",
- \\"margin-left\\": \\"0px\\",
- \\"height\\": \\"100%\\",
- - \\"scroll-behavior\\": \\"smooth\\",
- \\"overflow-x\\": \\"hidden\\",
- \\"overflow-y\\": \\"auto\\",
- \\"visibility\\": \\"visible\\"
- }"
`;
31 changes: 31 additions & 0 deletions packages/components/src/ui/scrollable/test/index.js
@@ -0,0 +1,31 @@
/**
* External dependencies
*/
import { render } from '@testing-library/react';

/**
* Internal dependencies
*/
import { Scrollable } from '../index';

describe( 'props', () => {
let base;

beforeEach( () => {
( { container: base } = render(
<Scrollable>WordPress.org - Code is Poetry</Scrollable>
) );
} );
test( 'should render correctly', () => {
expect( base.firstChild ).toMatchSnapshot();
} );

test( 'should render smoothScroll', () => {
const { container } = render(
<Scrollable smoothScroll>WordPress.org - Code is Poetry</Scrollable>
);
expect( container.firstChild ).toMatchStyleDiffSnapshot(
base.firstChild
);
} );
} );
16 changes: 16 additions & 0 deletions packages/components/src/ui/scrollable/types.ts
@@ -0,0 +1,16 @@
export type ScrollableDirection = 'x' | 'y' | 'auto';

export type Props = {
/**
* Renders a scrollbar for a specific axis when content overflows.
*
* @default 'y'
*/
scrollDirection?: ScrollableDirection;
/**
* Enables (CSS) smooth scrolling.
*
* @default false
*/
smoothScroll?: boolean;
};

0 comments on commit eccb3c1

Please sign in to comment.