Skip to content

feat(select): custom scrollbar #863

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
"intersection-observer": "^0.12.0",
"libphonenumber-js": "^1.8.4",
"lodash.debounce": "^4.0.8",
"perfect-scrollbar": "^1.5.2",
"react-focus-lock": "^2.5.0",
"react-merge-refs": "^1.1.0",
"react-node-resolver": "^2.0.1",
Expand Down
22 changes: 21 additions & 1 deletion packages/picker-button/src/__snapshots__/Component.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ exports[`Snapshots tests should display opened correctly 2`] = `
style="min-width: 0px;"
>
<div
class="optionsList m"
class="optionsList m component ps"
style="height: 0px;"
>
<div
Expand Down Expand Up @@ -347,6 +347,26 @@ exports[`Snapshots tests should display opened correctly 2`] = `
Bohrium
</div>
</div>
<div
class="ps__rail-x"
style="left: 0px; top: 0px;"
>
<div
class="ps__thumb-x"
style="left: 0px; width: 0px;"
tabindex="0"
/>
</div>
<div
class="ps__rail-y"
style="top: 0px; left: 0px;"
>
<div
class="ps__thumb-y"
style="top: 0px; height: 0px;"
tabindex="0"
/>
</div>
</div>
</div>
</div>
Expand Down
Empty file added packages/scrollbar/CHANGELOG.md
Empty file.
26 changes: 26 additions & 0 deletions packages/scrollbar/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@alfalab/core-components-scrollbar",
"version": "1.0.0",
"description": "Scrollbar component",
"keywords": [],
"license": "MIT",
"main": "dist/index.js",
"module": "./dist/esm/index.js",
"files": [
"dist"
],
"scripts": {
"postinstall": "node ./dist/send-stats.js > /dev/null 2>&1 || exit 0"
},
"publishConfig": {
"access": "public"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.1",
"react-dom": "^16.9.0 || ^17.0.1"
},
"dependencies": {
"classnames": "^2.2.6",
"react-merge-refs": "^1.1.0"
}
}
39 changes: 39 additions & 0 deletions packages/scrollbar/src/Component.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';
import { fireEvent, render } from '@testing-library/react';

import { Scrollbar } from './index';
import { ScrollbarProps } from './Component';

const renderScrollbar = (props?: ScrollbarProps) => (
<Scrollbar {...props}>
<div
style={{
height: '200px',
}}
/>
</Scrollbar>
);

describe('Scrollbar', () => {
describe('Display tests', () => {
it('should display correctly', () => {
const dataTestId = 'test-id';

const { container } = render(renderScrollbar({ dataTestId }));

expect(container).toMatchSnapshot();
});
});

it('should call onScroll', async () => {
const onScroll = jest.fn();

const dataTestId = 'test-id';

const { getByTestId } = render(renderScrollbar({ onScroll, dataTestId }));

fireEvent.scroll(getByTestId(dataTestId));

expect(onScroll).toBeCalledTimes(1);
});
});
58 changes: 58 additions & 0 deletions packages/scrollbar/src/Component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React, { useRef, useEffect, forwardRef, MouseEvent, ReactNode } from 'react';
import mergeRefs from 'react-merge-refs';
import cn from 'classnames';
import PerfectScrollbar from 'perfect-scrollbar';

import styles from './index.module.css';

export type ScrollbarProps = {
/**
* Дополнительный класс
*/
className?: string;

/**
* Идентификатор для систем автоматизированного тестирования
*/
dataTestId?: string;

/**
* Контент
*/
children?: ReactNode;

/**
* Обработчик скрола
*/
onScroll?: (event: MouseEvent<HTMLDivElement>) => void;
};

export const Scrollbar = forwardRef<HTMLDivElement, ScrollbarProps>(
({ className, dataTestId, children, onScroll, ...restProps }, ref) => {
const containerRef = useRef<HTMLDivElement>(null);

useEffect(() => {
let ps: PerfectScrollbar;

if (containerRef.current) {
ps = new PerfectScrollbar(containerRef.current, {});
}

return () => {
if (ps) ps.destroy();
};
}, []);

return (
<div
ref={mergeRefs([ref, containerRef])}
className={cn(className, styles.component)}
onScroll={onScroll}
data-test-id={dataTestId}
{...restProps}
>
{children}
</div>
);
},
);
34 changes: 34 additions & 0 deletions packages/scrollbar/src/__snapshots__/Component.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Scrollbar Display tests should display correctly 1`] = `
<div>
<div
class="component ps"
data-test-id="test-id"
>
<div
style="height: 200px;"
/>
<div
class="ps__rail-x"
style="left: 0px; top: 0px;"
>
<div
class="ps__thumb-x"
style="left: 0px; width: 0px;"
tabindex="0"
/>
</div>
<div
class="ps__rail-y"
style="top: 0px; left: 0px;"
>
<div
class="ps__thumb-y"
style="top: 0px; height: 0px;"
tabindex="0"
/>
</div>
</div>
</div>
`;
45 changes: 45 additions & 0 deletions packages/scrollbar/src/docs/Component.stories.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { Meta, Story, Props } from '@storybook/addon-docs/blocks';
import { boolean, select } from '@storybook/addon-knobs';
import { ComponentHeader, Tabs, CssVars } from 'storybook/blocks';

import { Scrollbar } from '../Component';
import { name, version } from '../../package.json';
import Description from './description.mdx';
import Changelog from '../../CHANGELOG.md';
import styles from '!!raw-loader!../index.module.css';


<Meta
title='Компоненты/Scrollbar'
component={Scrollbar}
/>


<!-- Canvas -->

<Story name='Scrollbar'>
<Scrollbar style={{ display: 'inline-block' }}>
<div style={{width: '300px', height: '1200px', background: 'linear-gradient(180deg, black, transparent)'}} />
</Scrollbar>
</Story>


<!-- Docs -->

<ComponentHeader
name='Scrollbar'
version={version}
package='@alfalab/core-components/scrollbar'
stage={1}
/>

```tsx
import { Scrollbar } from '@alfalab/core-components/scrollbar';
```

<Tabs
description={<Description />}
props={<Props of={Scrollbar} />}
cssVars={<CssVars css={styles} />}
changelog={<Changelog />}
/>
9 changes: 9 additions & 0 deletions packages/scrollbar/src/docs/description.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Компонент скроллбара.

Рекомендуется использовать только внутри компонентов, а не для всей страницы целиком.

```tsx live
<Scrollbar>
<div style={{ height: '1200px', background: 'linear-gradient(180deg, black, transparent)' }} />
</Scrollbar>
```
124 changes: 124 additions & 0 deletions packages/scrollbar/src/index.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
@import '../../themes/src/default.css';

:root {
--scrollbar-rail-size: 16px;
--scrollbar-thumb-size: 8px;
--scrollbar-thumb-border-radius: 6px;
--scrollbar-thumb-main-offset: 4px;
--scrollbar-thumb-side-offset: 2px;
--scrollbar-thumb-color: #aaa;
}

.component {
overflow: hidden;
position: relative;
height: 400px;

overflow-anchor: none;
-ms-overflow-style: none;
touch-action: auto;
-ms-touch-action: auto;

& :global(.ps__rail-x) {
display: none;
opacity: 0;
transition: background-color 0.2s ease, opacity 0.2s ease;
height: var(--scrollbar-rail-size);

/* there must be 'bottom' or 'top' for ps__rail-x */
bottom: 0;

/* please don't change 'position' */
position: absolute;
}

& :global(.ps__rail-y) {
display: none;
opacity: 0;
transition: background-color 0.2s ease, opacity 0.2s ease;
width: var(--scrollbar-rail-size);

/* there must be 'right' or 'left' for ps__rail-y */
right: 0;

/* please don't change 'position' */
position: absolute;
}

&:global(.ps--active-x > .ps__rail-x),
&:global(.ps--active-y > .ps__rail-y) {
display: block;
background-color: transparent;
}

&:global(.ps--scrolling-x > .ps__rail-x),
&:global(.ps--scrolling-y > .ps__rail-y) {
opacity: 0.5;
}

& :global(.ps__rail-x:hover),
& :global(.ps__rail-y:hover),
& :global(.ps__rail-x:focus),
& :global(.ps__rail-y:focus),
& :global(.ps__rail-x.ps--clicking),
& :global(.ps__rail-y.ps--clicking) {
/* background-color: #eee; */
opacity: 1;
}

/*
* Scrollbar thumb styles
*/
& :global(.ps__thumb-x) {
background-color: var(--scrollbar-thumb-color);
border-radius: var(--scrollbar-thumb-border-radius);
height: var(--scrollbar-thumb-size);
transition: background-color 0.2s ease, transform 0.2s ease;
transform-origin: bottom;

/* there must be 'bottom' for ps__thumb-x */
bottom: var(--scrollbar-thumb-side-offset);

/* please don't change 'position' */
position: absolute;
}

& :global(.ps__thumb-y) {
background-color: var(--scrollbar-thumb-color);
border-radius: var(--scrollbar-thumb-border-radius);
width: var(--scrollbar-thumb-size);
transition: background-color 0.2s ease, transform 0.2s ease;
transform-origin: right;

/* there must be 'right' for ps__thumb-y */
right: var(--scrollbar-thumb-side-offset);

/* please don't change 'position' */
position: absolute;
}

& :global(.ps__rail-x:hover > .ps__thumb-x),
& :global(.ps__rail-x:focus > .ps__thumb-x),
& :global(.ps__rail-x.ps--clicking .ps__thumb-x) {
transform: scaleY(1.5);
}

& :global(.ps__rail-y:hover > .ps__thumb-y),
& :global(.ps__rail-y:focus > .ps__thumb-y),
& :global(.ps__rail-y.ps--clicking .ps__thumb-y) {
transform: scaleX(1.5);
}
}

/* MS supports */
@supports (-ms-overflow-style: none) {
.component {
overflow: auto !important;
}
}

@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
.component {
overflow: auto !important;
}
}
1 change: 1 addition & 0 deletions packages/scrollbar/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Component';
Loading