Skip to content
This repository has been archived by the owner on Oct 23, 2024. It is now read-only.

Commit

Permalink
Dsys 5 slider number input (#339)
Browse files Browse the repository at this point in the history
* Added arrows support for slider input

* Added tests for slider input arrows

* Dark mode for arrows in input

* Set fill as currentColor

Set fill as currentColor
  • Loading branch information
nathanbrud authored Aug 15, 2023
1 parent c4d80cb commit 4538b56
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 5 deletions.
43 changes: 42 additions & 1 deletion src/components/inputs/Slider/Slider.style.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import styled from 'styled-components';

import { blue, white } from '../../../color';
import { rgba } from 'polished';
import { rem, rgba } from 'polished';
import { SliderInputArrow } from './SliderInputArrow';
import { core } from '../../../tokens';

export const SliderContainer = styled.div<{ range: boolean }>`
width: 100%;
Expand All @@ -13,6 +15,7 @@ export const SliderContainer = styled.div<{ range: boolean }>`
`;

export const Label = styled.div<{ focused: boolean }>`
position: relative;
padding: 0.2125rem;
width: 3.125rem;
text-align: center;
Expand Down Expand Up @@ -41,6 +44,7 @@ export const LabelInput = styled.input.attrs({ type: 'number' })<{
appearance: none;
text-align: center;
font-size: inherit;
user-select: none;
&::-webkit-inner-spin-button {
appearance: none;
Expand Down Expand Up @@ -106,3 +110,40 @@ export const Hidden = styled.input.attrs({ type: 'range' })`
width: 0;
opacity: 0;
`;

export const ArrowsContainer = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
position: absolute;
right: ${rem(4)};
top: 0;
width: 10px;
height: 100%;
opacity: 0;
transition: opacity 300ms ease;
padding: ${rem(1)} 0 ${rem(1.5)} 0;
`;

export const LabelInputContainer = styled.div`
transition: padding 300ms ease;
&:hover {
padding-right: ${rem(10)};
}
&:hover ${ArrowsContainer} {
opacity: 1;
}
`;

export const SliderInputArrowStyled = styled(SliderInputArrow)`
border-radius: ${rem(4)};
cursor: pointer;
&:hover {
background: ${core.color.stroke};
}
color: ${core.color.text(0)};
`;
41 changes: 41 additions & 0 deletions src/components/inputs/Slider/Slider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,45 @@ describe('Slider', () => {
expect(onChange).toHaveBeenCalledTimes(0);
expect(onDragEnd).toHaveBeenCalledTimes(0);
});

it('Change value using arrows', async () => {
const onChange = jest.fn();

render(
<ThemeProvider theme={themes['light']}>
<Slider
onChange={onChange}
initialValues={[0, 100]}
editableLabel
/>
</ThemeProvider>
);

const input = screen.getByRole('start-input');
await userEvent.hover(input);

const arrowUp = screen.getByRole('arrow-up');
await userEvent.click(arrowUp);

expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
target: expect.objectContaining({
value: '1',
}),
})
);

const arrowDown = screen.getByRole('arrow-down');
await userEvent.click(arrowDown);

expect(onChange).toHaveBeenCalledTimes(2);

expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
target: expect.objectContaining({
value: '0',
}),
})
);
});
});
45 changes: 41 additions & 4 deletions src/components/inputs/Slider/Slider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
Background,
ActiveRange,
Label,
LabelInput,
SliderContainer,
} from './Slider.style';
import { Handle } from './Handle';
Expand All @@ -29,6 +28,7 @@ import {
end,
home,
} from '../../../utils/events/KeyCodes';
import { SliderEditableInput } from './SliderEditableInput';

interface State {
values: number[];
Expand Down Expand Up @@ -113,6 +113,19 @@ export function Slider({
setValue([values[0], newValue]);
}

function handleArrowClick(
direction: 'up' | 'down',
handle: 'startInput' | 'endInput'
) {
const action =
handle === 'startInput' ? setStartValue : setEndValue;
const currentValue =
handle === 'startInput' ? values[0] : values[1];

if (direction === 'up') action(currentValue + 1);
else action(currentValue - 1);
}

useLayoutEffect(() => {
const { left, width } = geometry(trackRef.current);
dispatch({ type: 'SET_TRACK_RECT', payload: { left, width } });
Expand Down Expand Up @@ -201,14 +214,26 @@ export function Slider({
{range && (
<Label focused={focused === 'startInput'}>
{editableLabel ? (
<LabelInput
<SliderEditableInput
value={values[0]}
disabled={disabled}
onFocus={() => setFocus('startInput')}
onChange={(e) =>
!e.button && setStartValue(parseInt(e.target.value))
}
role="start-input"
onArrowUpClick={() =>
handleArrowClick(
'up',
range ? 'endInput' : 'startInput'
)
}
onArrowDownClick={() =>
handleArrowClick(
'down',
range ? 'endInput' : 'startInput'
)
}
/>
) : (
formatter(values[0])
Expand Down Expand Up @@ -251,9 +276,9 @@ export function Slider({
focused={focused === (range ? 'endInput' : 'startInput')}
>
{editableLabel ? (
<LabelInput
value={range ? values[1] : values[0]}
<SliderEditableInput
disabled={disabled}
value={range ? values[1] : values[0]}
onFocus={() =>
setFocus(range ? 'endInput' : 'startInput')
}
Expand All @@ -263,6 +288,18 @@ export function Slider({
: setStartValue(parseInt(e.target.value));
}}
role={range ? 'end-input' : 'start-input'}
onArrowUpClick={() =>
handleArrowClick(
'up',
range ? 'endInput' : 'startInput'
)
}
onArrowDownClick={() =>
handleArrowClick(
'down',
range ? 'endInput' : 'startInput'
)
}
/>
) : (
formatter(range ? values[1] : values[0])
Expand Down
43 changes: 43 additions & 0 deletions src/components/inputs/Slider/SliderEditableInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';

import {
ArrowsContainer,
LabelInput,
LabelInputContainer,
SliderInputArrowStyled,
} from './Slider.style';

export const SliderEditableInput = ({
disabled,
onArrowUpClick,
onArrowDownClick,
onChange,
onFocus,
role,
value,
}) => {
return (
<LabelInputContainer>
<LabelInput
value={value}
disabled={disabled}
onFocus={onFocus}
onChange={onChange}
role={role}
/>
<ArrowsContainer>
<SliderInputArrowStyled
role="arrow-up"
onClick={onArrowUpClick}
/>
<SliderInputArrowStyled
role="arrow-down"
onClick={onArrowDownClick}
style={{
transform: 'rotate(180deg)',
}}
/>
</ArrowsContainer>
</LabelInputContainer>
);
};
19 changes: 19 additions & 0 deletions src/components/inputs/Slider/SliderInputArrow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';

export const SliderInputArrow = (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="12"
height="10"
viewBox="0 0 12 10"
fill="none"
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M6.00178 4.91424L3.70689 7.2074L2.29321 5.79265L5.29547 2.79265C5.48308 2.60519 5.73747 2.49993 6.00269 2.50002C6.2679 2.50012 6.52222 2.60558 6.70968 2.79318L9.70743 5.79318L8.29268 7.20687L6.00178 4.91424Z"
fill="currentColor"
/>
</svg>
);

0 comments on commit 4538b56

Please sign in to comment.