Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[_]: feat/Range Slider component #11

Merged
merged 5 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@internxt/internxtui",
"version": "0.0.5",
"version": "0.0.6",
"description": "Library of Internxt components",
"repository": {
"type": "git",
Expand All @@ -19,6 +19,9 @@
"peerDependencies": {
"react": "^18.2.0"
},
"resolutions": {
"jackspeak": "2.1.1"
},
"devDependencies": {
"@chromatic-com/storybook": "^1.2.25",
"@internxt/eslint-config-internxt": "^1.0.9",
Expand Down Expand Up @@ -59,6 +62,7 @@
"tailwindcss": "^3.4.1",
"typescript": "^5.4.3",
"vite": "^5.2.2",
"vite-plugin-css-injected-by-js": "^3.5.2",
"vite-plugin-dts": "^3.7.3",
"vite-plugin-svgr": "^4.2.0",
"vitest": "^1.4.0"
Expand Down
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './switch/Switch';
export * from './radio-button/RadioButton';
export * from './avatar/Avatar';
export * from './spinner/Spinner';
export * from './slider/RangeSlider';
45 changes: 45 additions & 0 deletions src/components/slider/RangeSlider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ChangeEvent } from 'react';
import '../../styles/RangeSlider.css';

interface RangeSliderProps {
value: number;
min?: number;
max: number;
step?: number;
percentageForProgressSliderBar?: number;
className?: string;
onChange: (value: number) => void;
disabled?: boolean;
ariaLabel?: string;
}

export const RangeSlider = ({
value,
min = 0,
max,
step,
className,
disabled = false,
ariaLabel = 'Range slider',
onChange,
}: RangeSliderProps) => {
const percentage = ((value - min) / (max - min)) * 100;
const sliderBackground = `linear-gradient(to right, #3264fe ${percentage}%, #d5d5d5 ${percentage}%)`;

return (
<div className={className}>
<input
id="my-slider"
type="range"
min={min}
max={max}
value={value}
step={step}
onInput={(e: ChangeEvent<HTMLInputElement>) => onChange(Number(e.target.value))}
disabled={disabled}
aria-label={ariaLabel}
style={{ background: sliderBackground }}
/>
</div>
);
};
23 changes: 23 additions & 0 deletions src/components/slider/__test__/RangeSlider.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import { describe, it, expect, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/react';
import { RangeSlider } from '../RangeSlider';

describe('RangeSlider component', () => {
it('RangeSlider onChange should work correctly', () => {
const handleChange = vi.fn();
render(<RangeSlider value={50} max={100} onChange={handleChange} />);

const slider = screen.getByRole('slider');

fireEvent.input(slider, { target: { value: '75' } });

expect(handleChange).toHaveBeenCalledOnce();
expect(handleChange).toHaveBeenCalledWith(75);
});

it('RangeSlider component should render correctly', () => {
const { container } = render(<RangeSlider value={50} max={100} onChange={() => {}} />);
expect(container).toMatchSnapshot();

Check failure on line 21 in src/components/slider/__test__/RangeSlider.test.tsx

View workflow job for this annotation

GitHub Actions / build (18.x)

src/components/slider/__test__/RangeSlider.test.tsx > RangeSlider component > RangeSlider component should render correctly

Error: Snapshot `RangeSlider component > RangeSlider component should render correctly 1` mismatched - Expected + Received @@ -3,11 +3,10 @@ <input aria-label="Range slider" id="my-slider" max="100" min="0" - step="1" type="range" value="50" /> </div> </div> ❯ src/components/slider/__test__/RangeSlider.test.tsx:21:23
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`RangeSlider component > RangeSlider component should render correctly 1`] = `
<div>
<div>
<input
aria-label="Range slider"
id="my-slider"
max="100"
min="0"
step="1"
type="range"
value="50"
/>
</div>
</div>
`;
72 changes: 72 additions & 0 deletions src/stories/components/slider/RangeSlider.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { Decorator, Meta, StoryObj } from '@storybook/react';
import { RangeSlider } from '../../../components/slider/RangeSlider';
import { useArgs } from '@storybook/preview-api';

const onChangeDecorator: Decorator = (Story, context) => {
const [, setArgs] = useArgs();

return Story({
...context,
args: {
...context.args,
onChange: (newValue: number) => setArgs({ ...context.args, value: newValue }),
},
});
};

const meta: Meta<typeof RangeSlider> = {
title: 'Components/RangeSlider',
component: RangeSlider,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
decorators: [onChangeDecorator],
argTypes: {
min: {
description: 'The minimum value of the slider',
control: { type: 'number' },
defaultValue: 0,
},
max: {
description: 'The maximum value of the slider',
control: { type: 'number' },
defaultValue: 100,
},
value: {
description: 'The current value of the slider',
control: { type: 'number' },
},
className: {
description: 'The current width of the slider',
control: { type: 'text' },
},
step: {
description: 'The steps of the slider',
control: { type: 'number' },
},
percentageForProgressSliderBar: {
description: 'The custom percentage of the slider',
control: { type: 'number' },
},
ariaLabel: {
description: 'Label of the component',
control: { type: 'text' },
},
onChange: {
description: 'Function to handle value change',
action: 'onInput',
},
},
};

export default meta;
type Story = StoryObj<typeof RangeSlider>;

export const RangeSliderComponent: Story = {
args: {
min: 0,
max: 100,
value: 50,
},
};
72 changes: 72 additions & 0 deletions src/styles/RangeSlider.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
.custom-range-slider {
width: 100%;
height: 6px;
background-color: #ffffff;
border-radius: 50%;
outline: none;
position: relative;
}

input[type="range"] {
position: relative;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
display: block;
width: '100%';
background: #d5d5d5;
border-radius: 16px;
outline: none;
}

input[type="range"]::-webkit-slider-runnable-track {
-webkit-appearance: none;
height: 6px;
}

input[type="range"]::-moz-track {
appearance: none;
height: 6px;

}

input[type="range"]::-ms-track {
appearance: none;
height: 6px;
}

input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 24px;
width: 24px;
background-color: #3264fe;
border-radius: 50%;
pointer-events: auto;
cursor: pointer;
margin-top: -9px;
border: none;
}

input[type="range"]::-moz-range-thumb {
appearance: none;
height: 24px;
width: 24px;
background-color: #3264fe;
border-radius: 50%;
pointer-events: auto;
cursor: pointer;
margin-top: -9px;
border: none;
}

input[type="range"]::-ms-thumb {
appearance: none;
height: 24px;
width: 24px;
background-color: #3264fe;
border-radius: 50%;
pointer-events: auto;
cursor: pointer;
margin-top: -9px;
border: none;
}
3 changes: 2 additions & 1 deletion vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';
import svgr from 'vite-plugin-svgr';
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
import { configDefaults } from 'vitest/config';
import { peerDependencies } from './package.json';

Expand Down Expand Up @@ -31,5 +32,5 @@ export default defineConfig({
sourcemap: true,
emptyOutDir: true,
},
plugins: [dts(), svgr()],
plugins: [dts(), svgr(), cssInjectedByJsPlugin()],
});
Loading
Loading