Skip to content

Commit 36bdb4c

Browse files
author
Gerard Brull
authored
Merge branch 'master' into upgrade_tooling
2 parents 52fda0b + 078dcb5 commit 36bdb4c

File tree

12 files changed

+440
-1
lines changed

12 files changed

+440
-1
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
## [3.2.1](https://github.com/zopaUK/react-components/compare/v3.2.0...v3.2.1) (2020-05-05)
2+
3+
4+
### Bug Fixes
5+
6+
* **alert:** fix font family for Alert component ([329ba85](https://github.com/zopaUK/react-components/commit/329ba8524b83f29ff5566aacdc104f27ec1b46b0))
7+
8+
# [3.2.0](https://github.com/zopaUK/react-components/compare/v3.1.6...v3.2.0) (2020-04-30)
9+
10+
11+
### Features
12+
13+
* **inputrange:** adding Input Range without controls ([933e2da](https://github.com/zopaUK/react-components/commit/933e2daacc04f602588893fea628ed9897c246e1))
14+
115
## [3.1.6](https://github.com/zopaUK/react-components/compare/v3.1.5...v3.1.6) (2020-04-29)
216

317

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@zopauk/react-components",
33
"sideEffects": false,
44
"description": "Shared react styled components for all the Zopa projects.",
5-
"version": "3.1.6",
5+
"version": "3.2.1",
66
"license": "MIT",
77
"author": "Zopa Ltd <[email protected]> (https://zopa.com)",
88
"main": "cjs/src/index.js",

src/components/atoms/Alert/Alert.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { FC } from 'react';
22
import styled from 'styled-components';
3+
import { typography } from '../../../constants/typography';
34

45
type TSeverity = 'info' | 'alert' | 'warning' | 'success';
56

@@ -56,6 +57,7 @@ const Wrapper = styled.div<Required<IAlertProps>>`
5657
color: ${({ severity }) => MAP_BY_SEVERITY[severity].text};
5758
font-size: 15px;
5859
line-height: 20px;
60+
font-family: ${typography.primary};
5961
font-weight: 400;
6062
fill: ${({ severity }) => MAP_BY_SEVERITY[severity].icon};
6163

src/components/atoms/Alert/__snapshots__/Alert.test.tsx.snap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ exports[`Alert renders the component with no props and no a11y violations 1`] =
1212
color: #2C3236;
1313
font-size: 15px;
1414
line-height: 20px;
15+
font-family: "Open Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
1516
font-weight: 400;
1617
fill: #818F9B;
1718
}
@@ -72,6 +73,7 @@ exports[`Alert renders with specific severity: alert 1`] = `
7273
color: #940700;
7374
font-size: 15px;
7475
line-height: 20px;
76+
font-family: "Open Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
7577
font-weight: 400;
7678
fill: #FF453A;
7779
}
@@ -132,6 +134,7 @@ exports[`Alert renders with specific severity: info 1`] = `
132134
color: #2C3236;
133135
font-size: 15px;
134136
line-height: 20px;
137+
font-family: "Open Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
135138
font-weight: 400;
136139
fill: #818F9B;
137140
}
@@ -192,6 +195,7 @@ exports[`Alert renders with specific severity: success 1`] = `
192195
color: #17592B;
193196
font-size: 15px;
194197
line-height: 20px;
198+
font-family: "Open Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
195199
font-weight: 400;
196200
fill: #3EBC64;
197201
}
@@ -252,6 +256,7 @@ exports[`Alert renders with specific severity: warning 1`] = `
252256
color: #704300;
253257
font-size: 15px;
254258
line-height: 20px;
259+
font-family: "Open Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
255260
font-weight: 400;
256261
fill: #FF9F0A;
257262
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
### Summary
2+
3+
Use `<InputRange />` to render a native HTML `<input type="range" />`.
4+
5+
### Examples
6+
7+
With default props:
8+
9+
```tsx
10+
import { InputRange } from '@zopauk/react-components';
11+
12+
<InputRange />;
13+
```
14+
15+
With default value:
16+
17+
```tsx
18+
import { InputRange } from '@zopauk/react-components';
19+
20+
<InputRange defaultValue={75} />;
21+
```
22+
23+
With min and max:
24+
25+
```tsx
26+
import { InputRange } from '@zopauk/react-components';
27+
28+
<InputRange min={100} max={200} />;
29+
```
30+
31+
With step:
32+
33+
```tsx
34+
import { InputRange } from '@zopauk/react-components';
35+
36+
<InputRange step={10} />;
37+
```
38+
39+
With default value, min, max and step:
40+
41+
```tsx
42+
import { InputRange } from '@zopauk/react-components';
43+
44+
<InputRange defaultValue={150} min={100} max={500} step={50} />;
45+
```
46+
47+
### Controlled Examples
48+
49+
Store value of `<InputRange />`:
50+
51+
```tsx
52+
import { Fragment, useState } from 'react';
53+
import { InputRange, Text } from '@zopauk/react-components';
54+
55+
const ControlledInputRange = () => {
56+
const [value, setValue] = useState(75);
57+
const onChangeHandler = event => setValue(Number(event.target.value));
58+
59+
return (
60+
<Fragment>
61+
<Text mb>Value: {value}</Text>
62+
<InputRange value={value} onChange={onChangeHandler} />
63+
</Fragment>
64+
);
65+
};
66+
67+
<ControlledInputRange />;
68+
```
69+
70+
Store value of `<InputRange />` with min and max:
71+
72+
```tsx
73+
import { Fragment, useState } from 'react';
74+
import { InputRange, Text } from '@zopauk/react-components';
75+
76+
const ControlledInputRange = () => {
77+
const [value, setValue] = useState(150);
78+
const onChangeHandler = event => setValue(Number(event.target.value));
79+
80+
return (
81+
<Fragment>
82+
<Text mb>Value: {value}</Text>
83+
<InputRange value={value} min={100} max={200} onChange={onChangeHandler} />
84+
</Fragment>
85+
);
86+
};
87+
88+
<ControlledInputRange />;
89+
```
90+
91+
Store value of `<InputRange />` with step:
92+
93+
```tsx
94+
import { Fragment, useState } from 'react';
95+
import { InputRange, Text } from '@zopauk/react-components';
96+
97+
const ControlledInputRange = () => {
98+
const [value, setValue] = useState(50);
99+
const onChangeHandler = event => setValue(Number(event.target.value));
100+
101+
return (
102+
<Fragment>
103+
<Text mb>Value: {value}</Text>
104+
<InputRange value={value} step={10} onChange={onChangeHandler} />
105+
</Fragment>
106+
);
107+
};
108+
109+
<ControlledInputRange />;
110+
```
111+
112+
Store value of `<InputRange />` with min, max and step:
113+
114+
```tsx
115+
import { Fragment, useState } from 'react';
116+
import { InputRange, Text } from '@zopauk/react-components';
117+
118+
const ControlledInputRange = () => {
119+
const [value, setValue] = useState(150);
120+
const onChangeHandler = event => setValue(Number(event.target.value));
121+
122+
return (
123+
<Fragment>
124+
<Text mb>Value: {value}</Text>
125+
<InputRange value={value} min={100} max={500} step={50} onChange={onChangeHandler} />
126+
</Fragment>
127+
);
128+
};
129+
130+
<ControlledInputRange />;
131+
```
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import React, { ChangeEvent, useState } from 'react';
2+
import { fireEvent, render, screen } from '@testing-library/react';
3+
import InputRange from './InputRange';
4+
import { axe } from 'jest-axe';
5+
6+
describe('<InputRange />', () => {
7+
it('renders and updates as expected', async () => {
8+
const ControlledInputRange = () => {
9+
const [value, setValue] = useState(75);
10+
const onChangeHandler = (event: ChangeEvent<HTMLInputElement>) => setValue(Number(event.target.value));
11+
12+
return <InputRange value={value} onChange={onChangeHandler} />;
13+
};
14+
15+
render(<ControlledInputRange />);
16+
17+
const input = (await screen.findByRole('slider')) as HTMLInputElement;
18+
19+
expect(input.value).toEqual('75');
20+
21+
fireEvent.change(input, { target: { value: '25' } });
22+
23+
expect(input.value).toEqual('25');
24+
});
25+
26+
it('renders the component with no a11y violations', async () => {
27+
const { container } = render(<InputRange aria-label="slider" defaultValue={51} />);
28+
const results = await axe(container.innerHTML);
29+
expect(container.firstChild).toMatchSnapshot();
30+
expect(results).toHaveNoViolations();
31+
});
32+
});
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import React, { InputHTMLAttributes, useState, ChangeEvent, useRef, useEffect } from 'react';
2+
import styled, { css } from 'styled-components';
3+
import { colors } from '../../../constants/colors';
4+
import { calculateTrackPosition } from './helpers';
5+
6+
const trackHeight = 8;
7+
const thumbDiameter = 40;
8+
9+
const TrackStyles = css`
10+
box-sizing: border-box;
11+
border: none;
12+
border-radius: ${trackHeight / 2}px;
13+
height: ${trackHeight}px;
14+
background: ${colors.neutral.light};
15+
`;
16+
17+
const ThumbStyles = css`
18+
box-sizing: border-box;
19+
border: none;
20+
border-radius: 50%;
21+
width: ${thumbDiameter}px;
22+
height: ${thumbDiameter}px;
23+
background: #007468;
24+
`;
25+
26+
const ThumbStylesFocus = css`
27+
background: ${colors.base.secondary};
28+
`;
29+
30+
interface IInput extends InputHTMLAttributes<HTMLInputElement> {
31+
trackPosition: number;
32+
}
33+
34+
const SInputRange = styled.input<IInput>`
35+
-webkit-appearance: none;
36+
width: 100%;
37+
height: ${thumbDiameter}px;
38+
39+
&::-webkit-slider-runnable-track {
40+
${TrackStyles}
41+
}
42+
&::-moz-range-track {
43+
${TrackStyles}
44+
}
45+
46+
&::-webkit-slider-thumb {
47+
-webkit-appearance: none;
48+
margin-top: ${(trackHeight - thumbDiameter) * 0.5}px;
49+
${ThumbStyles}
50+
}
51+
&::-moz-range-thumb {
52+
${ThumbStyles}
53+
}
54+
55+
&::-webkit-slider-runnable-track {
56+
background: -webkit-gradient(
57+
linear,
58+
0% 0%,
59+
100% 0%,
60+
color-stop(${({ trackPosition }) => trackPosition}, ${colors.base.primary}),
61+
color-stop(${({ trackPosition }) => trackPosition}, ${colors.neutral.medium})
62+
);
63+
}
64+
&::-moz-range-progress {
65+
border-radius: ${trackHeight / 2}px;
66+
height: ${trackHeight}px;
67+
background: ${colors.base.primary};
68+
}
69+
70+
&:focus {
71+
outline: none;
72+
73+
&::-webkit-slider-thumb {
74+
${ThumbStylesFocus}
75+
}
76+
&::-moz-range-thumb {
77+
${ThumbStylesFocus}
78+
}
79+
}
80+
81+
::-moz-focus-outer {
82+
border: 0;
83+
}
84+
`;
85+
86+
interface IInputRange extends InputHTMLAttributes<HTMLInputElement> {
87+
defaultValue?: number;
88+
value?: number;
89+
min?: number;
90+
max?: number;
91+
step?: number;
92+
}
93+
94+
const InputRange = ({ min = 0, max = 100, onChange, ...otherProps }: IInputRange) => {
95+
const inputRef = useRef<HTMLInputElement>(null);
96+
const [trackPosition, setTrackPosition] = useState(0.5);
97+
98+
const calculateTrackPositionFromInput = (input: HTMLInputElement) => {
99+
return calculateTrackPosition({ min: Number(input.min), max: Number(input.max), value: Number(input.value) });
100+
};
101+
102+
useEffect(() => {
103+
inputRef.current && setTrackPosition(calculateTrackPositionFromInput(inputRef.current));
104+
}, []);
105+
106+
const onChangeHandler = (e: ChangeEvent<HTMLInputElement>) => {
107+
setTrackPosition(calculateTrackPositionFromInput(e.target));
108+
onChange && onChange(e);
109+
};
110+
111+
return (
112+
<SInputRange
113+
{...otherProps}
114+
role="slider"
115+
trackPosition={trackPosition}
116+
min={min}
117+
max={max}
118+
onChange={onChangeHandler}
119+
type="range"
120+
ref={inputRef}
121+
/>
122+
);
123+
};
124+
125+
export default InputRange;

0 commit comments

Comments
 (0)