Skip to content

Commit

Permalink
Push one step further + test
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviertassinari committed Jun 5, 2021
1 parent bffb87f commit fbf529b
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 46 deletions.
35 changes: 27 additions & 8 deletions packages/material-ui-lab/src/ClockPicker/Clock.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import * as React from 'react';
import IconButton from '@material-ui/core/IconButton';
import Typography from '@material-ui/core/Typography';
import { styled } from '@material-ui/core/styles';
import { unstable_useEnhancedEffect as useEnhancedEffect } from '@material-ui/utils';
import ClockPointer from './ClockPointer';
import { useUtils, MuiPickersAdapter } from '../internal/pickers/hooks/useUtils';
import { WrapperVariantContext } from '../internal/pickers/wrappers/WrapperVariantContext';
import { PickerOnChangeFn } from '../internal/pickers/hooks/useViews';
import { PickerSelectionState } from '../internal/pickers/hooks/usePickerState';
import { useMeridiemMode } from '../internal/pickers/hooks/date-helpers-hooks';
import { Meridiem, convertToMeridiem } from '../internal/pickers/time-utils';
import { ClockView, getHours, getMinutes } from './shared';

export interface ClockProps<TDate> extends ReturnType<typeof useMeridiemMode> {
export interface ClockProps<TDate> {
ampm: boolean;
ampmInClock: boolean;
autoFocus?: boolean;
Expand All @@ -23,6 +23,7 @@ export interface ClockProps<TDate> extends ReturnType<typeof useMeridiemMode> {
) => string;
isTimeDisabled: (timeValue: number, type: ClockView) => boolean;
minutesStep?: number;
onValueChange: PickerOnChangeFn<TDate>;
onChange: (value: number, isFinish?: PickerSelectionState) => void;
/**
* DOM id that the selected option should have
Expand All @@ -31,6 +32,7 @@ export interface ClockProps<TDate> extends ReturnType<typeof useMeridiemMode> {
selectedId: string | undefined;
type: ClockView;
value: number;
meridiemMode: Meridiem | null;
}

const ClockRoot = styled('div', { skipSx: true })(({ theme }) => ({
Expand Down Expand Up @@ -82,8 +84,12 @@ const ClockPin = styled('div', { skipSx: true })(({ theme }) => ({
const ClockAmButton = styled(IconButton, { skipSx: true })<{ styleProps: ClockProps<any> }>(
({ theme, styleProps }) => ({
zIndex: 1,
...theme.typography.caption,
position: 'absolute',
bottom: 8,
padding: 4,
width: 42,
height: 42,
left: 8,
...(styleProps.meridiemMode === 'am' && {
backgroundColor: theme.palette.primary.main,
Expand All @@ -98,8 +104,12 @@ const ClockAmButton = styled(IconButton, { skipSx: true })<{ styleProps: ClockPr
const ClockPmButton = styled(IconButton, { skipSx: true })<{ styleProps: ClockProps<any> }>(
({ theme, styleProps }) => ({
zIndex: 1,
...theme.typography.caption,
position: 'absolute',
bottom: 8,
padding: 4,
width: 42,
height: 42,
right: 8,
...(styleProps.meridiemMode === 'pm' && {
backgroundColor: theme.palette.primary.main,
Expand All @@ -122,11 +132,11 @@ function Clock<TDate>(props: ClockProps<TDate>) {
children,
date,
getClockLabelText,
handleMeridiemChange,
isTimeDisabled,
meridiemMode,
minutesStep = 1,
onChange,
onValueChange,
selectedId,
type,
value,
Expand All @@ -142,6 +152,15 @@ function Clock<TDate>(props: ClockProps<TDate>) {
const isSelectedTimeDisabled = isTimeDisabled(value, type);
const isPointerInner = !ampm && type === 'hours' && (value < 1 || value > 12);

const handleMeridiemChange = (mode: Meridiem) => () => {
if (mode === meridiemMode) {
return;
}

const timeWithMeridiem = convertToMeridiem(date, mode, Boolean(ampm), utils);
onValueChange(timeWithMeridiem, 'partial');
};

const handleValueChange = (newValue: number, isFinish: PickerSelectionState) => {
if (isTimeDisabled(newValue, type)) {
return;
Expand Down Expand Up @@ -282,19 +301,19 @@ function Clock<TDate>(props: ClockProps<TDate>) {
<React.Fragment>
<ClockAmButton
data-mui-test="in-clock-am-btn"
onClick={() => handleMeridiemChange('am')}
onClick={handleMeridiemChange('am')}
disabled={meridiemMode === null}
styleProps={styleProps}
>
<Typography variant="caption">AM</Typography>
AM
</ClockAmButton>
<ClockPmButton
disabled={meridiemMode === null}
data-mui-test="in-clock-pm-btn"
onClick={() => handleMeridiemChange('pm')}
onClick={handleMeridiemChange('pm')}
styleProps={styleProps}
>
<Typography variant="caption">PM</Typography>
PM
</ClockPmButton>
</React.Fragment>
)}
Expand Down
6 changes: 3 additions & 3 deletions packages/material-ui-lab/src/ClockPicker/ClockPicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ import { useUtils, useNow, MuiPickersAdapter } from '../internal/pickers/hooks/u
import { getHourNumbers, getMinutesNumbers } from './ClockNumbers';
import PickersArrowSwitcher from '../internal/pickers/PickersArrowSwitcher';
import {
getMeridiem,
convertValueToMeridiem,
createIsAfterIgnoreDatePart,
TimeValidationProps,
} from '../internal/pickers/time-utils';
import { PickerOnChangeFn } from '../internal/pickers/hooks/useViews';
import { PickerSelectionState } from '../internal/pickers/hooks/usePickerState';
import { useMeridiemMode } from '../internal/pickers/hooks/date-helpers-hooks';
import { ClockView } from './shared';

export interface ClockPickerClasses {
Expand Down Expand Up @@ -217,7 +217,7 @@ function ClockPicker<TDate>(inProps: ClockPickerProps<TDate>) {
const utils = useUtils<TDate>();
const dateOrNow = date || now;

const { meridiemMode, handleMeridiemChange } = useMeridiemMode<TDate>(dateOrNow, ampm, onChange);
const meridiemMode = getMeridiem(date, utils);

const isTimeDisabled = React.useCallback(
(rawValue: number, viewType: ClockView) => {
Expand Down Expand Up @@ -387,11 +387,11 @@ function ClockPicker<TDate>(inProps: ClockPickerProps<TDate>) {
ampmInClock={ampmInClock}
type={view}
ampm={ampm}
onValueChange={onChange}
getClockLabelText={getClockLabelText}
minutesStep={minutesStep}
isTimeDisabled={isTimeDisabled}
meridiemMode={meridiemMode}
handleMeridiemChange={handleMeridiemChange}
selectedId={selectedId}
{...viewProps}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import * as React from 'react';
import TextField from '@material-ui/core/TextField';
import { spy } from 'sinon';
import { expect } from 'chai';
import { describeConformance, fireEvent, fireTouchChangedEvent } from 'test/utils';
import { describeConformance, fireEvent, screen, fireTouchChangedEvent } from 'test/utils';
import MobileTimePicker from '@material-ui/lab/MobileTimePicker';
import {
createPickerMount,
createPickerRender,
adapterToUse,
getByMuiTest,
} from '../internal/pickers/test-utils';
import { getMeridiem } from '../internal/pickers/time-utils';

function createMouseEventWithOffsets(
type: 'mousedown' | 'mousemove' | 'mouseup',
Expand Down Expand Up @@ -46,14 +47,14 @@ describe('<MobileTimePicker />', () => {
);

it('accepts time on clock mouse move', () => {
const onChangeMock = spy();
const handleChange = spy();
render(
<MobileTimePicker
ampm
open
value={adapterToUse.date('2018-01-01T00:00:00.000')}
onChange={onChangeMock}
renderInput={(props) => <TextField variant="outlined" {...props} />}
onChange={handleChange}
renderInput={(props) => <TextField {...props} />}
/>,
);

Expand All @@ -67,22 +68,22 @@ describe('<MobileTimePicker />', () => {
fireEvent(getByMuiTest('clock'), createMouseEventWithOffsets('mouseup', fakeEventOptions));

expect(getByMuiTest('hours')).to.have.text('11');
expect(onChangeMock.callCount).to.equal(1);
expect(handleChange.callCount).to.equal(1);
});

it('accepts time on clock touch move', function test() {
if (typeof window.Touch === 'undefined' || typeof window.TouchEvent === 'undefined') {
this.skip();
}

const onChangeMock = spy();
const handleChange = spy();
render(
<MobileTimePicker
ampm
open
openTo="minutes"
value={adapterToUse.date('2018-01-01T00:00:00.000')}
onChange={onChangeMock}
onChange={handleChange}
renderInput={(params) => <TextField {...params} />}
/>,
);
Expand Down Expand Up @@ -128,4 +129,24 @@ describe('<MobileTimePicker />', () => {
expect(getByMuiTest('hours')).not.to.have.text('--');
expect(getByMuiTest('minutes')).not.to.have.text('--');
});

it('should change meridiem', () => {
const handleChange = spy();
const value = adapterToUse.date('2018-01-01T00:00:00.000');
render(
<MobileTimePicker
ampm
open
value={value}
onChange={handleChange}
renderInput={(props) => <TextField {...props} />}
/>,
);

expect(getMeridiem(value, adapterToUse)).to.equal('am');
const pmButton = screen.getByRole('button', { name: 'PM' });
fireEvent.click(pmButton);
expect(handleChange.callCount).to.equal(1);
expect(getMeridiem(handleChange.args[0][0], adapterToUse)).to.equal('pm');
});
});
17 changes: 13 additions & 4 deletions packages/material-ui-lab/src/TimePicker/TimePickerToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import PickersToolbarButton from '../internal/pickers/PickersToolbarButton';
import PickersToolbar from '../internal/pickers/PickersToolbar';
import { arrayIncludes } from '../internal/pickers/utils';
import { useUtils } from '../internal/pickers/hooks/useUtils';
import { useMeridiemMode } from '../internal/pickers/hooks/date-helpers-hooks';
import { getMeridiem, convertToMeridiem } from '../internal/pickers/time-utils';
import { ToolbarComponentProps } from '../internal/pickers/typings/BasePicker';

export interface TimePickerToolbarClasses {
Expand Down Expand Up @@ -134,7 +134,16 @@ const TimePickerToolbar: React.FC<ToolbarComponentProps> = (props) => {
const utils = useUtils();
const theme = useTheme();
const showAmPmControl = Boolean(ampm && !ampmInClock);
const { meridiemMode, handleMeridiemChange } = useMeridiemMode(date, ampm, onChange);

const meridiemMode = getMeridiem(date, utils);
const handleMeridiemChange = (mode: 'am' | 'pm') => () => {
if (mode === meridiemMode) {
return;
}

const timeWithMeridiem = convertToMeridiem(date, mode, Boolean(ampm), utils);
onChange(timeWithMeridiem, 'partial');
};

const formatHours = (time: unknown) =>
ampm ? utils.format(time, 'hours12h') : utils.format(time, 'hours24h');
Expand Down Expand Up @@ -206,7 +215,7 @@ const TimePickerToolbar: React.FC<ToolbarComponentProps> = (props) => {
selected={meridiemMode === 'am'}
typographyClassName={classes.ampmLabel}
value={utils.getMeridiemText('am')}
onClick={() => handleMeridiemChange('am')}
onClick={handleMeridiemChange('am')}
/>
<PickersToolbarButton
disableRipple
Expand All @@ -215,7 +224,7 @@ const TimePickerToolbar: React.FC<ToolbarComponentProps> = (props) => {
selected={meridiemMode === 'pm'}
typographyClassName={classes.ampmLabel}
value={utils.getMeridiemText('pm')}
onClick={() => handleMeridiemChange('pm')}
onClick={handleMeridiemChange('pm')}
/>
</TimePickerToolbarAmPmSelection>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import * as React from 'react';
import { useUtils } from './useUtils';
import { ParseableDate } from '../constants/prop-types';
import { PickerOnChangeFn } from './useViews';
import { getMeridiem, convertToMeridiem } from '../time-utils';

export type OverrideParseableDateProps<TDate, TProps, TKey extends keyof TProps> = Omit<
TProps,
Expand Down Expand Up @@ -56,22 +54,3 @@ export function usePreviousMonthDisabled(
return !utils.isBefore(firstEnabledMonth, month);
}, [disablePast, minDate, month, utils]);
}

export function useMeridiemMode<TDate>(
date: TDate,
ampm: boolean | undefined,
onChange: PickerOnChangeFn<TDate>,
) {
const utils = useUtils<TDate>();
const meridiemMode = getMeridiem(date, utils);

const handleMeridiemChange = React.useCallback(
(mode: 'am' | 'pm') => {
const timeWithMeridiem = convertToMeridiem<TDate>(date, mode, Boolean(ampm), utils);
onChange(timeWithMeridiem, 'partial');
},
[ampm, date, onChange, utils],
);

return { meridiemMode, handleMeridiemChange };
}
6 changes: 3 additions & 3 deletions packages/material-ui-lab/src/internal/pickers/time-utils.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { ParseableDate } from './constants/prop-types';
import { MuiPickersAdapter } from './hooks/useUtils';

type Meridiem = 'am' | 'pm' | null;
export type Meridiem = 'am' | 'pm';

export const getMeridiem = (date: unknown, utils: MuiPickersAdapter): Meridiem => {
export const getMeridiem = (date: unknown, utils: MuiPickersAdapter): Meridiem | null => {
if (!date) {
return null;
}

return utils.getHours(date) >= 12 ? 'pm' : 'am';
};

export const convertValueToMeridiem = (value: number, meridiem: Meridiem, ampm: boolean) => {
export const convertValueToMeridiem = (value: number, meridiem: Meridiem | null, ampm: boolean) => {
if (ampm) {
const currentMeridiem = value >= 12 ? 'pm' : 'am';
if (currentMeridiem !== meridiem) {
Expand Down

0 comments on commit fbf529b

Please sign in to comment.