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

#25730: TimePicker Default Value Fix, Controllable Usage, Example Updates #26482

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
86b9648
Update prop names, make renamed currentDate prop controlled
CheerfulSatchel Jan 24, 2023
4a6766b
Added DatePicker and TimePicker combination example
CheerfulSatchel Jan 24, 2023
2f2e08a
Rephrase example text
CheerfulSatchel Jan 24, 2023
d8f04f6
Combine ComboBox imports
CheerfulSatchel Jan 24, 2023
4729af9
Fixed ComboBox import and updated DateTimePicker example
CheerfulSatchel Jan 24, 2023
a78b720
Moved examples into standalone files
CheerfulSatchel Feb 27, 2023
0337ed1
Refactors and use new dateAnchor prop
CheerfulSatchel Feb 27, 2023
27c2b3c
Updated all examples
CheerfulSatchel Feb 27, 2023
7a2b6b9
This should resolve the rest
CheerfulSatchel Feb 27, 2023
f706133
This should resolve the outstanding issues...
CheerfulSatchel Feb 28, 2023
6114244
Updated example strings
CheerfulSatchel Feb 28, 2023
83704dd
Updated examples
CheerfulSatchel Feb 28, 2023
f649c52
Merge branch 'master' into fix-time-picker-initial-date
CheerfulSatchel Feb 28, 2023
86ee51a
More changes to the basic example
CheerfulSatchel Feb 28, 2023
caa4604
Removed useComboBoxAsMenuWidth prop
CheerfulSatchel Mar 1, 2023
ea37cb2
Update formatTimeString function to return 00 if hours is 24
CheerfulSatchel Mar 2, 2023
712e838
Addressed comments
CheerfulSatchel Mar 2, 2023
49a7a64
Renamed baseDate parameter to dateStartAnchor
CheerfulSatchel Mar 2, 2023
794406e
Added prop onGetErrorMessage to get validation result
CheerfulSatchel Mar 2, 2023
3682ec8
Set selectedTime to invalid or undefined
CheerfulSatchel Mar 2, 2023
01bfdc7
Clamp value to updated dateAnchor
CheerfulSatchel Mar 2, 2023
e79e7b0
Make docs look nice and readable
CheerfulSatchel Mar 2, 2023
1ff24d6
Removed unneeded imports
CheerfulSatchel Mar 3, 2023
c8ca944
Updated clampedStartAnchor initialization value
CheerfulSatchel Mar 3, 2023
00ae9ec
Use internalDateAnchor
CheerfulSatchel Mar 3, 2023
d4f49ff
Use fallbackDateAnchor and update DateTimePicker example
CheerfulSatchel Mar 3, 2023
e8b0b50
Merged with master and resolved merge conflicts
CheerfulSatchel Apr 11, 2023
791eb11
Revert to ITimePickerProps to extend original omitted IComboBoxProps
CheerfulSatchel Apr 11, 2023
c43adaa
Addressed comments
CheerfulSatchel Apr 11, 2023
ae6be38
API snapshot update
CheerfulSatchel Apr 11, 2023
4d58798
Updated TimePicker tests and added test for new controlled component …
CheerfulSatchel Apr 12, 2023
b36ac86
Added test for handling changed base date anchor
CheerfulSatchel Apr 12, 2023
368ebaf
Verify selected time changes on dateAnchor change
CheerfulSatchel Apr 12, 2023
7553c61
Added yarn change files
CheerfulSatchel Apr 12, 2023
f14f5e0
Resolve linting errors
CheerfulSatchel Apr 12, 2023
11d58b9
Resolved more linting errors
CheerfulSatchel Apr 12, 2023
dcf3e7d
Resolved linting import error by in-lining stack and styles
CheerfulSatchel Apr 12, 2023
2d36ed1
Addressed comments
CheerfulSatchel Apr 13, 2023
89e62ff
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 13, 2023
4a22df1
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 13, 2023
2705874
Revert onChange prop types to avoid breaking changes and pass React.F…
CheerfulSatchel Apr 14, 2023
a09fa50
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 14, 2023
12300fe
Added explicit undefined pass to setSelectedTime in cases the dateAnc…
CheerfulSatchel Apr 14, 2023
3576bcf
Updated examples and call onChange outside of useControllableValue to…
CheerfulSatchel Apr 17, 2023
0e30b38
Control snapping of TimePicker values on DatePicker anchor change
CheerfulSatchel Apr 17, 2023
a872cdc
Added tests for using defaultValue or value as date anchors
CheerfulSatchel Apr 17, 2023
bd3d5b6
Took snapping logic out, fixed invalid key bug, and shared getDateAnc…
CheerfulSatchel Apr 17, 2023
19472bc
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 17, 2023
382f0dd
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 18, 2023
314ad0e
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 18, 2023
c78af1e
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 18, 2023
a0ebfe3
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 18, 2023
33409c8
Pass in placeholder since placeholder prop no longer has default value
CheerfulSatchel Apr 19, 2023
9d1d7a4
Reflect optional timeOutOfBoundsErrorMessage string
CheerfulSatchel Apr 19, 2023
b45529c
Updated tests and snapshot
CheerfulSatchel Apr 19, 2023
c6b4468
Addressed comments
CheerfulSatchel Apr 19, 2023
20e9d8d
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 19, 2023
2bef609
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 19, 2023
1831864
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 20, 2023
0959c73
Removed unnecessary string state variables
CheerfulSatchel Apr 20, 2023
8a3b218
Followed comment suggestion and replaced onGetErrorMessage with onVal…
CheerfulSatchel Apr 20, 2023
e5f3953
Added onValidationError example
CheerfulSatchel Apr 20, 2023
b342a02
Added onValidationError test case
CheerfulSatchel Apr 20, 2023
80a6224
Export TimePickerErrorData
CheerfulSatchel Apr 20, 2023
f32c0aa
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 20, 2023
44927f9
Renamed onValidationError and TimePickerErrorData to onValidationResu…
CheerfulSatchel Apr 21, 2023
de5c8a2
Renamed example to reflect new callback prop
CheerfulSatchel Apr 21, 2023
2839b97
Use new example and fix casing
CheerfulSatchel Apr 21, 2023
55cba39
Use onValidationResult and only call when stored error message differ…
CheerfulSatchel Apr 21, 2023
55cc1a5
Added test for verifying onValidateResult only gets called on error m…
CheerfulSatchel Apr 21, 2023
4e32ecb
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 21, 2023
fd1a0ba
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 24, 2023
dc8f33d
Split big test into two smaller tests
CheerfulSatchel Apr 24, 2023
d403881
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 24, 2023
77faac5
Merge branch 'master' of github.com:microsoft/fluentui into fix-time-…
CheerfulSatchel Apr 24, 2023
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
CheerfulSatchel marked this conversation as resolved.
Show resolved Hide resolved
CheerfulSatchel marked this conversation as resolved.
Show resolved Hide resolved
CheerfulSatchel marked this conversation as resolved.
Show resolved Hide resolved
CheerfulSatchel marked this conversation as resolved.
Show resolved Hide resolved
CheerfulSatchel marked this conversation as resolved.
Show resolved Hide resolved
CheerfulSatchel marked this conversation as resolved.
Show resolved Hide resolved
CheerfulSatchel marked this conversation as resolved.
Show resolved Hide resolved
CheerfulSatchel marked this conversation as resolved.
Show resolved Hide resolved
CheerfulSatchel marked this conversation as resolved.
Show resolved Hide resolved
"type": "patch",
"comment": "chore: Refactored getDateFromTimeSelection variable names.",
"packageName": "@fluentui/date-time-utilities",
"email": "jamwu@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "feat(TimePicker): Updated TimePicker controlled and uncontrolled props to work correctly.",
"packageName": "@fluentui/react",
"email": "jamwu@microsoft.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export const formatYear: (date: Date) => string;
export const getBoundedDateRange: (dateRange: Date[], minDate?: Date | undefined, maxDate?: Date | undefined) => Date[];
CheerfulSatchel marked this conversation as resolved.
Show resolved Hide resolved

// @public
export const getDateFromTimeSelection: (useHour12: boolean, baseDate: Date, selectedTime: string) => Date;
export const getDateFromTimeSelection: (useHour12: boolean, dateStartAnchor: Date, selectedTime: string) => Date;

// @public
export function getDatePartHashValue(date: Date): number;
Expand Down
11 changes: 9 additions & 2 deletions packages/date-time-utilities/src/timeFormatting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@
* @param showSeconds - Whether to show seconds in the formatted string
* @param useHour12 - Whether to use 12-hour time
*/
export const formatTimeString = (date: Date, showSeconds?: boolean, useHour12?: boolean): string =>
date.toLocaleTimeString([], {
export const formatTimeString = (date: Date, showSeconds?: boolean, useHour12?: boolean): string => {
let localeTimeString = date.toLocaleTimeString([], {
hour: 'numeric',
minute: '2-digit',
second: showSeconds ? '2-digit' : undefined,
hour12: useHour12,
});

if (!useHour12 && localeTimeString.slice(0, 2) === '24') {
localeTimeString = '00' + localeTimeString.slice(2);
}

return localeTimeString;
};
15 changes: 9 additions & 6 deletions packages/date-time-utilities/src/timeMath/timeMath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ export const ceilMinuteToIncrement = (date: Date, increments: number) => {
/**
* Returns a date object from the selected time.
* @param useHour12 - If the time picker uses 12 or 24 hour formatting
* @param baseDate - The baseline date to calculate the offset of the selected time
* @param dateStartAnchor - The baseline date to calculate the offset of the selected time
* @param selectedTime - A string representing the user selected time
* @returns A new date object offset from the baseDate using the selected time.
*/
export const getDateFromTimeSelection = (useHour12: boolean, baseDate: Date, selectedTime: string): Date => {
export const getDateFromTimeSelection = (useHour12: boolean, dateStartAnchor: Date, selectedTime: string): Date => {
const [, selectedHours, selectedMinutes, selectedSeconds, selectedAp] =
TimeConstants.TimeFormatRegex.exec(selectedTime) || [];

Expand All @@ -61,17 +61,20 @@ export const getDateFromTimeSelection = (useHour12: boolean, baseDate: Date, sel
}

let hoursOffset;
if (baseDate.getHours() > hours || (baseDate.getHours() === hours && baseDate.getMinutes() > minutes)) {
hoursOffset = TimeConstants.HoursInOneDay - baseDate.getHours() + hours;
if (
dateStartAnchor.getHours() > hours ||
(dateStartAnchor.getHours() === hours && dateStartAnchor.getMinutes() > minutes)
) {
hoursOffset = TimeConstants.HoursInOneDay - dateStartAnchor.getHours() + hours;
} else {
hoursOffset = Math.abs(baseDate.getHours() - hours);
hoursOffset = Math.abs(dateStartAnchor.getHours() - hours);
}

const offset =
TimeConstants.MillisecondsIn1Sec * TimeConstants.MinutesInOneHour * hoursOffset * TimeConstants.SecondsInOneMinute +
seconds * TimeConstants.MillisecondsIn1Sec;

const date = new Date(baseDate.getTime() + offset);
const date = new Date(dateStartAnchor.getTime() + offset);
date.setMinutes(minutes);
date.setSeconds(seconds);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import * as React from 'react';
import {
TimePicker,
ITimeRange,
Text,
IStackTokens,
Stack,
IStackStyles,
IComboBoxStyles,
IComboBox,
} from '@fluentui/react';

const stackStyles: Partial<IStackStyles> = { root: { width: 500 } };
const stackTokens: IStackTokens = { childrenGap: 20 };

const timePickerStyles: Partial<IComboBoxStyles> = {
optionsContainerWrapper: {
height: '500px',
},
root: {
width: '500px',
},
};

export const TimePickerBasicExample: React.FC = () => {
const [basicExampleTimeString, setBasicExampleTimeString] = React.useState<string>('');
const [nonDefaultOptionsExampleTimeString, setNonDefaultOptionsExampleTimeString] = React.useState<string>('');
const basicDateAnchor = new Date('November 25, 2021 09:00:00');
const nonDefaultOptionsDateAnchor = new Date('February 27, 2023 08:00:00');

const onBasicExampleChange = React.useCallback((_ev: React.FormEvent<IComboBox>, basicExampleTime: Date) => {
setBasicExampleTimeString(basicExampleTime.toString());
}, []);

const onNonDefaultOptionsExampleChange = React.useCallback((_, nonDefaultOptionsExampleTime: Date) => {
setNonDefaultOptionsExampleTimeString(nonDefaultOptionsExampleTime?.toString());
}, []);

const timeRange: ITimeRange = {
start: 8,
end: 14,
};

return (
<Stack tokens={stackTokens} styles={stackStyles}>
<TimePicker
placeholder="Basic example placeholder"
styles={timePickerStyles}
useHour12
allowFreeform
autoComplete="on"
label="TimePicker basic example"
onChange={onBasicExampleChange}
dateAnchor={basicDateAnchor}
/>
<Text>{`⚓ Date anchor: ${basicDateAnchor.toString()}`}</Text>
<Text>{`⌚ Selected time: ${basicExampleTimeString ? basicExampleTimeString : '<no time selected>'}`}</Text>

<TimePicker
styles={timePickerStyles}
showSeconds
allowFreeform
increments={15}
autoComplete="on"
label="TimePicker with non default options"
placeholder="Non default options placeholder"
timeRange={timeRange}
dateAnchor={nonDefaultOptionsDateAnchor}
onChange={onNonDefaultOptionsExampleChange}
/>
<Text>{`⚓ Date anchor: ${nonDefaultOptionsDateAnchor.toString()}`}</Text>
<Text>{`⌚ Selected time: ${
nonDefaultOptionsExampleTimeString ? nonDefaultOptionsExampleTimeString : '<no time selected>'
}`}</Text>
</Stack>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as React from 'react';
import { TimePicker, Text, IStackTokens, Stack, IStackStyles, IComboBoxStyles } from '@fluentui/react';

const stackStyles: Partial<IStackStyles> = { root: { width: 500 } };
const stackTokens: IStackTokens = { childrenGap: 20 };

const timePickerStyles: Partial<IComboBoxStyles> = {
optionsContainerWrapper: {
height: '500px',
},
root: {
width: '500px',
},
};

export const TimePickerControlledExample: React.FC = () => {
const dateAnchor = new Date('February 27, 2023 08:00:00');
const [time, setTime] = React.useState<Date>(new Date('February 27, 2023 10:00:00'));

const onControlledExampleChange = React.useCallback((_, newTime: Date) => {
setTime(newTime);
}, []);

return (
<Stack tokens={stackTokens} styles={stackStyles}>
<TimePicker
styles={timePickerStyles}
showSeconds
allowFreeform
increments={15}
autoComplete="on"
label="Controlled TimePicker with non default options"
dateAnchor={dateAnchor}
value={time}
onChange={onControlledExampleChange}
/>
<Text>{`⚓ Date anchor: ${dateAnchor.toString()}`}</Text>
<Text>{`⌚ Selected time: ${time ? time.toString() : '<no time selected>'}`}</Text>
</Stack>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as React from 'react';
import { TimePicker, Text, IStackTokens, Stack, IStackStyles, IComboBoxStyles } from '@fluentui/react';

const stackStyles: Partial<IStackStyles> = { root: { width: 500 } };
const stackTokens: IStackTokens = { childrenGap: 20 };

const timePickerStyles: Partial<IComboBoxStyles> = {
optionsContainerWrapper: {
height: '500px',
},
root: {
width: '500px',
},
};

export const TimePickerCustomTimeStringsExample: React.FC = () => {
const [customTimeString, setCustomTimeString] = React.useState<string>('');
const dateAnchor = new Date('February 27, 2023 08:00:00');
const onFormatDate = React.useCallback((date: Date) => `Custom prefix + ${date.toLocaleTimeString()}`, []);
const onValidateUserInput = React.useCallback((userInput: string) => {
if (!userInput.includes('Custom prefix +')) {
return 'Your input is missing "Custom prefix +"';
}
return '';
}, []);

const onChange = React.useCallback((_, time: Date) => {
console.log('Selected time: ', time);
setCustomTimeString(time.toString());
}, []);

return (
<Stack tokens={stackTokens} styles={stackStyles}>
<TimePicker
placeholder="Custom time strings example placeholder"
styles={timePickerStyles}
onFormatDate={onFormatDate}
onValidateUserInput={onValidateUserInput}
onChange={onChange}
useHour12
allowFreeform={false}
dateAnchor={dateAnchor}
autoComplete="on"
label="TimePicker with custom time strings"
/>
<Text>{`⚓ Date anchor: ${dateAnchor.toString()}`}</Text>
<Text>{`⌚ Selected time: ${customTimeString ? customTimeString : '<no time selected>'}`}</Text>
</Stack>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as React from 'react';
import { TimePicker, DatePicker, Label, Text, IStackTokens, Stack, IStackStyles, IComboBox } from '@fluentui/react';

const stackStyles: Partial<IStackStyles> = { root: { width: 500 } };
const stackTokens: IStackTokens = { childrenGap: 20 };

const snapTimeToUpdatedDateAnchor = (datePickerDate: Date, currentTime: Date) => {
let snappedTime = new Date(currentTime);

if (currentTime && !isNaN(currentTime.valueOf())) {
const startAnchor = new Date(datePickerDate);
const endAnchor = new Date(startAnchor);
endAnchor.setDate(startAnchor.getDate() + 1);
if (currentTime < startAnchor || currentTime > endAnchor) {
snappedTime = new Date(startAnchor);
snappedTime.setHours(currentTime.getHours());
snappedTime.setMinutes(currentTime.getMinutes());
snappedTime.setSeconds(currentTime.getSeconds());
snappedTime.setMilliseconds(currentTime.getMilliseconds());
}
}

return snappedTime;
};

export const TimePickerDateTimePickerExample: React.FC = () => {
const currentDate = new Date('2023-02-01 05:00:00');
const [datePickerDate, setDatePickerDate] = React.useState<Date>(currentDate);
const [currentTime, setCurrentTime] = React.useState<Date>();

const onSelectDate = React.useCallback(
(selectedDate: Date) => {
setDatePickerDate(selectedDate);
if (currentTime) {
const snappedTime = snapTimeToUpdatedDateAnchor(selectedDate, currentTime);
setCurrentTime(snappedTime);
}
},
[currentTime],
);

const onTimePickerChange = React.useCallback((_ev: React.FormEvent<IComboBox>, date: Date) => {
setCurrentTime(date);
}, []);

return (
<Stack tokens={stackTokens} styles={stackStyles}>
<Label>{'DatePicker and TimePicker combination'}</Label>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gridColumnGap: '3px' }}>
<DatePicker placeholder="Select a date..." value={datePickerDate} onSelectDate={onSelectDate} />
<TimePicker
placeholder="Select a time"
dateAnchor={datePickerDate}
value={currentTime}
onChange={onTimePickerChange}
/>
</div>
<Text>{`⚓ Date anchor: ${datePickerDate.toString()}`}</Text>
<Text>{`⌚ Selected time: ${currentTime ? currentTime.toString() : '<no time selected>'}`}</Text>
</Stack>
);
};

This file was deleted.

Loading