Skip to content

Commit 2a7b6b4

Browse files
authored
Merge pull request #86 from flextremedev/app-form-styling
App form styling
2 parents 2ce88fc + 64f8241 commit 2a7b6b4

File tree

20 files changed

+713
-122
lines changed

20 files changed

+713
-122
lines changed

.circleci/config.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ jobs:
1919
- checkout
2020
- run: yarn install
2121
- run: yarn web-test-coverage
22-
- run: yarn core-test-coverage
22+
- run: yarn mobile-test-ci
23+
- run: yarn core-test-ci
2324
- codecov/upload:
2425
file: packages/web/coverage/*.json
2526
workflows:

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
],
88
"scripts": {
99
"core-test": "yarn workspace @interval-timer/core run test",
10-
"core-test-coverage": "yarn workspace @interval-timer/core run test --runInBand --coverage",
10+
"core-test-ci": "yarn workspace @interval-timer/core run test --runInBand --coverage",
1111
"mobile": "yarn workspace @interval-timer/mobile start",
12+
"mobile-test": "yarn workspace @interval-timer/mobile run test",
13+
"mobile-test-ci": "yarn workspace @interval-timer/mobile run test --runInBand --coverage",
1214
"web": "yarn workspace @interval-timer/web run start",
1315
"web-build": "yarn workspace @interval-timer/web run build",
1416
"web-lint": "yarn workspace @interval-timer/web run lint:fix",

packages/core/src/machine/timerMachine.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export const timerMachine = createMachine<TimerContext, TimerEvent, TimerState>(
4545
context: {
4646
prepareTime: new Date(5000),
4747
timeLeft: new Date(5000),
48-
rounds: 2,
48+
rounds: 1,
4949
roundsLeft: 0,
5050
workInterval: new Date(0),
5151
breakInterval: new Date(0),

packages/mobile/App.tsx

Lines changed: 3 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,86 +1,7 @@
1-
import { useTimerMachine } from '@interval-timer/core';
2-
import React from 'react';
3-
import { StyleSheet, Text, View } from 'react-native';
4-
5-
import { Button } from './src/components/Button/Button';
6-
import { DurationInput } from './src/components/DurationInput/DurationInput';
7-
import { Input } from './src/components/Input/Input';
1+
import * as React from 'react';
82

3+
import { App as AppFromSrc } from './src/App';
94
// eslint-disable-next-line import/no-default-export
105
export default function App(): JSX.Element {
11-
const {
12-
service,
13-
setBreakInterval,
14-
setRounds,
15-
setWorkInterval,
16-
state,
17-
toggleTimer,
18-
} = useTimerMachine();
19-
20-
React.useEffect(() => {
21-
let interval: NodeJS.Timeout | null = null;
22-
const subscription = service.subscribe((newState) => {
23-
if (newState.event.type === 'START') {
24-
interval = setInterval(() => {
25-
service.send({ type: 'TICK' });
26-
}, 50);
27-
} else if (newState.event.type === 'STOP') {
28-
if (interval) {
29-
clearInterval(interval);
30-
}
31-
}
32-
});
33-
return (): void => {
34-
subscription.unsubscribe();
35-
if (interval) {
36-
clearInterval(interval);
37-
}
38-
};
39-
}, [service]);
40-
41-
const {
42-
breakInterval,
43-
rounds,
44-
roundsLeft,
45-
timeLeft,
46-
workInterval,
47-
} = state.context;
48-
return (
49-
<View style={styles.container}>
50-
<Input
51-
label="Rounds"
52-
value={String(rounds)}
53-
onBlur={setRounds}
54-
type="number"
55-
/>
56-
<Input
57-
label="Round"
58-
value={`${rounds - roundsLeft}/${rounds}`}
59-
readOnly
60-
/>
61-
<DurationInput
62-
value={workInterval}
63-
onChange={setWorkInterval}
64-
label="Work Interval"
65-
/>
66-
<DurationInput
67-
value={breakInterval}
68-
onChange={setBreakInterval}
69-
label="Break Interval"
70-
/>
71-
<DurationInput value={timeLeft} label="Time left" readOnly />
72-
<Button onPress={toggleTimer}>
73-
<Text>Go</Text>
74-
</Button>
75-
</View>
76-
);
6+
return <AppFromSrc />;
777
}
78-
79-
const styles = StyleSheet.create({
80-
container: {
81-
flex: 1,
82-
backgroundColor: '#fff',
83-
alignItems: 'center',
84-
justifyContent: 'center',
85-
},
86-
});

packages/mobile/jest.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
module.exports = {
2+
coverageThreshold: {
3+
global: {
4+
branches: 95.5,
5+
functions: 93.5,
6+
lines: 96.25,
7+
statements: 96.25,
8+
},
9+
},
210
preset: 'jest-expo',
311
};

packages/mobile/src/App.test.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { fireEvent, render } from '@testing-library/react-native';
2+
import * as React from 'react';
3+
4+
import { App } from './App';
5+
6+
describe('App', () => {
7+
it('should work', () => {
8+
const { getByDisplayValue, getByTestId } = render(<App />);
9+
const workMinutesValue = '01';
10+
const workSecondsValue = '01';
11+
const workMinutes = Number(workMinutesValue);
12+
const workSeconds = Number(workSecondsValue);
13+
14+
const breakMinutesValue = '01';
15+
const breakSecondsValue = '01';
16+
const breakMinutes = Number(breakMinutesValue);
17+
const breakSeconds = Number(breakSecondsValue);
18+
19+
const roundsValue = '2';
20+
fireEvent.changeText(getByDisplayValue('1'), roundsValue);
21+
fireEvent.changeText(
22+
getByTestId('work-interval-duration-input-minutes'),
23+
workMinutesValue
24+
);
25+
fireEvent.changeText(
26+
getByTestId('work-interval-duration-input-seconds'),
27+
workSecondsValue
28+
);
29+
fireEvent.changeText(
30+
getByTestId('break-interval-duration-input-minutes'),
31+
breakMinutesValue
32+
);
33+
fireEvent.changeText(
34+
getByTestId('break-interval-duration-input-seconds'),
35+
breakSecondsValue
36+
);
37+
});
38+
39+
it('should trigger color mode', () => {
40+
const { queryByTestId, getByTestId } = render(<App />);
41+
expect(queryByTestId('color-mode-toggle-light')).toBeFalsy();
42+
43+
fireEvent.press(getByTestId('color-mode-toggle-dark'));
44+
expect(queryByTestId('color-mode-toggle-dark')).toBeFalsy();
45+
expect(getByTestId('color-mode-toggle-light')).toBeTruthy();
46+
});
47+
});

packages/mobile/src/App.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as React from 'react';
2+
3+
import { ThemeContextProvider } from './context/theme-context';
4+
import { Home } from './pages/Home';
5+
6+
// eslint-disable-next-line import/no-default-export
7+
export function App(): JSX.Element {
8+
return (
9+
<ThemeContextProvider>
10+
<Home />
11+
</ThemeContextProvider>
12+
);
13+
}
Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,30 @@
11
import * as React from 'react';
2-
import { StyleSheet, TouchableHighlight } from 'react-native';
2+
import { StyleProp, StyleSheet, Pressable, ViewStyle } from 'react-native';
3+
4+
import { theme } from '../../theme';
35

46
type ButtonProps = {
57
onPress?: () => void;
8+
style?: StyleProp<ViewStyle>;
69
};
7-
const Button: React.FC<ButtonProps> = ({ onPress, children }) => {
10+
const Button: React.FC<ButtonProps> = ({ onPress, children, style }) => {
811
return (
9-
<TouchableHighlight style={styles.button} onPress={onPress}>
12+
<Pressable style={[styles.button, style]} onPress={onPress}>
1013
{children}
11-
</TouchableHighlight>
14+
</Pressable>
1215
);
1316
};
1417

1518
const styles = StyleSheet.create({
1619
button: {
17-
alignSelf: 'stretch',
20+
height: 56,
21+
minWidth: 56,
22+
borderRadius: 56,
1823
textAlign: 'center',
24+
justifyContent: 'center',
25+
alignItems: 'center',
26+
paddingLeft: theme.spaces.l,
27+
paddingRight: theme.spaces.l,
1928
},
2029
});
2130
export { Button };
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { fireEvent, render } from '@testing-library/react-native';
2+
import * as React from 'react';
3+
4+
import { DoubleDigitInput } from './DoubleDigitInput';
5+
6+
describe('DoubleDigitInput', () => {
7+
it('should handle input', () => {
8+
let value = '00';
9+
const handleChange = (text: string): void => {
10+
value = text;
11+
};
12+
const { getByDisplayValue, rerender } = render(
13+
<DoubleDigitInput onChangeText={handleChange} value={value} />
14+
);
15+
16+
expect(getByDisplayValue('00')).toBeTruthy();
17+
18+
fireEvent.changeText(getByDisplayValue('00'), '1');
19+
rerender(<DoubleDigitInput onChangeText={handleChange} value={value} />);
20+
fireEvent.changeText(getByDisplayValue('1'), '12');
21+
rerender(<DoubleDigitInput onChangeText={handleChange} value={value} />);
22+
23+
expect(getByDisplayValue('12')).toBeTruthy();
24+
});
25+
26+
it('should be readonly', () => {
27+
const handleChange = jest.fn();
28+
const { getByDisplayValue } = render(
29+
<DoubleDigitInput
30+
onChangeText={handleChange}
31+
value="00"
32+
editable={false}
33+
/>
34+
);
35+
fireEvent.changeText(getByDisplayValue('00'), '1');
36+
expect(handleChange).toHaveBeenCalledTimes(0);
37+
});
38+
39+
it('should handle invalid input', () => {
40+
let value = '00';
41+
const handleChange = (text: string): void => {
42+
value = text;
43+
};
44+
const { getByDisplayValue, queryByDisplayValue } = render(
45+
<DoubleDigitInput onChangeText={handleChange} value={value} />
46+
);
47+
48+
expect(getByDisplayValue('00')).toBeTruthy();
49+
50+
fireEvent.changeText(getByDisplayValue('00'), 'e');
51+
52+
expect(queryByDisplayValue('e')).toBeFalsy();
53+
});
54+
});

packages/mobile/src/components/DoubleDigitInput/DoubleDigitInput.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,14 @@ const DoubleDigitInput = ({
6464
}
6565
};
6666

67+
/* could't find a way to test focus */
68+
/* istanbul ignore next */
6769
const handleFocus = (): void => {
6870
setInputStatus('initial');
6971
};
7072

73+
/* could't find a way to test blur */
74+
/* istanbul ignore next */
7175
const handleBlur = (): void => {
7276
setInputStatus('done');
7377
};

0 commit comments

Comments
 (0)