Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
@@ -1,6 +1,10 @@
import * as React from 'react';
import { ITimeRange, TimePicker } from '@fluentui/react/lib/TimePicker';
import { IStackTokens, Stack, IStackStyles, IComboBoxStyles, IComboBox } from '@fluentui/react';
import { IStackTokens, Stack, IStackStyles } from '@fluentui/react/lib/Stack';
import { IComboBoxStyles } from '@fluentui/react/lib/ComboBox';
import { DatePicker } from '@fluentui/react/lib/DatePicker';
import { Label } from '@fluentui/react/lib/Label';
import { Text } from '@fluentui/react/lib/Text';

const stackStyles: Partial<IStackStyles> = { root: { maxWidth: 300 } };
const stackTokens: IStackTokens = { childrenGap: 20 };
Expand All @@ -15,46 +19,72 @@ const timePickerStyles: Partial<IComboBoxStyles> = {
};

const onFormatDate = (date: Date) => `Custom prefix + ${date.toLocaleTimeString()}`;
const onChange = (_: React.FormEvent<IComboBox>, date: Date) => console.log('SELECTED DATE: ', date);
const onTimeChange = (date: Date) => console.log('SELECTED DATE: ', date);

export const TimePickerBasicExample: React.FC = () => {
const [datePickerDate, setDatePickerDate] = React.useState<Date>();
const [currentTimeString, setCurrentTimeString] = React.useState<string>('');

const onSelectDate = React.useCallback((selectedDate: Date) => {
setDatePickerDate(selectedDate);
}, []);

const onDateTimePickerChange = React.useCallback((time: Date) => {
setCurrentTimeString(time.toString());
}, []);

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

return (
<Stack tokens={stackTokens} styles={stackStyles}>
<TimePicker
styles={timePickerStyles}
useHour12
allowFreeform
autoComplete="on"
label={'TimePicker basic example'}
onChange={onChange}
defaultValue={new Date('November 25, 2021 09:15:00')}
useComboBoxAsMenuWidth
/>
<TimePicker
styles={timePickerStyles}
showSeconds
allowFreeform
increments={15}
autoComplete="on"
label={'TimePicker with non default options'}
useComboBoxAsMenuWidth
timeRange={timeRange}
onChange={onChange}
/>
<TimePicker
styles={timePickerStyles}
// eslint-disable-next-line react/jsx-no-bind
onFormatDate={onFormatDate}
useHour12
allowFreeform
autoComplete="on"
label={'TimePicker with custom time string'}
/>
</Stack>
<>
<Stack tokens={stackTokens} styles={stackStyles}>
<TimePicker
styles={timePickerStyles}
useHour12
allowFreeform
autoComplete="on"
label={'TimePicker basic example'}
onTimeChange={onTimeChange}
currentDate={new Date('November 25, 2021 09:15:00')}
useComboBoxAsMenuWidth
/>
<TimePicker
styles={timePickerStyles}
showSeconds
allowFreeform
increments={15}
autoComplete="on"
label={'TimePicker with non default options'}
useComboBoxAsMenuWidth
timeRange={timeRange}
onTimeChange={onTimeChange}
/>
<TimePicker
styles={timePickerStyles}
// eslint-disable-next-line react/jsx-no-bind
onFormatDate={onFormatDate}
useHour12
allowFreeform
autoComplete="on"
label={'TimePicker with custom time string'}
/>
</Stack>
<div style={{ paddingTop: '20px', maxWidth: '50%' }}>
<Label>{'DatePicker and TimePicker combination'}</Label>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gridColumnGap: '3px' }}>
<DatePicker
placeholder="Select a date..."
value={datePickerDate}
onSelectDate={onSelectDate}
minDate={new Date()}
/>
<TimePicker currentDate={datePickerDate} useComboBoxAsMenuWidth onTimeChange={onDateTimePickerChange} />
</div>
<Text>{`TimePicker selected time: ${currentTimeString}`}</Text>
</div>
</>
);
};
38 changes: 23 additions & 15 deletions packages/react/src/components/TimePicker/TimePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react';
import { useConst } from '@fluentui/react-hooks';
import { KeyCodes } from '../../Utilities';
import {
TimeConstants,
Expand All @@ -24,9 +23,11 @@ const getDefaultStrings = (useHour12: boolean, showSeconds: boolean): ITimePicke
const hourUnits = useHour12 ? '12-hour' : '24-hour';
const timeFormat = `hh:mm${showSeconds ? ':ss' : ''}${useHour12 ? ' AP' : ''}`;
const errorMessageToDisplay = `Enter a valid time in the ${hourUnits} format: ${timeFormat}`;
const defaultTimePickerPlaceholder = `Enter or select a time`;

return {
invalidInputErrorMessage: errorMessageToDisplay,
defaultTimePickerPlaceholder,
};
};

Expand All @@ -41,23 +42,31 @@ export const TimePicker: React.FunctionComponent<ITimePickerProps> = ({
useHour12 = false,
timeRange,
strings = getDefaultStrings(useHour12, showSeconds),
defaultValue,
onChange,
currentDate,
onTimeChange,
onFormatDate,
onValidateUserInput,
placeholder = strings.defaultTimePickerPlaceholder,
...rest
}: ITimePickerProps) => {
const [userText, setUserText] = React.useState<string>('');
const [selectedKey, setSelectedKey] = React.useState<string | number | undefined>();
const [errorMessage, setErrorMessage] = React.useState<string>('');

const optionsCount = getDropdownOptionsCount(increments, timeRange);

const initialValue = useConst(defaultValue || new Date());
const baseDate: Date = React.useMemo(() => generateBaseDate(increments, timeRange, initialValue), [
increments,
timeRange,
initialValue,
]);
const baseDate = React.useMemo(() => {
const initialDate = currentDate || new Date();
return generateBaseDate(increments, timeRange, initialDate);
}, [increments, timeRange, currentDate]);

React.useEffect(() => {
if (onTimeChange && !errorMessage && userText) {
const currentChosenTime = userText;
const date = getDateFromTimeSelection(useHour12, currentDate, currentChosenTime);
onTimeChange(date);
}
}, [currentDate]);

const timePickerOptions: IComboBoxOption[] = React.useMemo(() => {
const optionsList = Array(optionsCount);
Expand All @@ -76,10 +85,8 @@ export const TimePicker: React.FunctionComponent<ITimePickerProps> = ({
});
}, [baseDate, increments, optionsCount, showSeconds, onFormatDate, useHour12]);

const [selectedKey, setSelectedKey] = React.useState<string | number | undefined>(timePickerOptions[0].key);

const onInputChange = React.useCallback(
(event: React.FormEvent<IComboBox>, option?: IComboBoxOption, index?: number, value?: string): void => {
(_: React.FormEvent<IComboBox>, option?: IComboBoxOption, _index?: number, value?: string): void => {
const validateUserInput = (userInput: string): string => {
let errorMessageToDisplay = '';
let regex: RegExp;
Expand Down Expand Up @@ -114,10 +121,10 @@ export const TimePicker: React.FunctionComponent<ITimePickerProps> = ({
updatedUserText = option.text;
}

if (onChange && !errorMessageToDisplay) {
if (onTimeChange && !errorMessageToDisplay) {
const selectedTime = value || option?.text || '';
const date = getDateFromTimeSelection(useHour12, baseDate, selectedTime);
onChange(event, date);
onTimeChange(date);
}

setErrorMessage(errorMessageToDisplay);
Expand All @@ -127,7 +134,7 @@ export const TimePicker: React.FunctionComponent<ITimePickerProps> = ({
[
baseDate,
allowFreeform,
onChange,
onTimeChange,
onFormatDate,
onValidateUserInput,
showSeconds,
Expand Down Expand Up @@ -157,6 +164,7 @@ export const TimePicker: React.FunctionComponent<ITimePickerProps> = ({
return (
<ComboBox
{...rest}
placeholder={placeholder}
allowFreeform={allowFreeform}
selectedKey={selectedKey}
label={label}
Expand Down
16 changes: 7 additions & 9 deletions packages/react/src/components/TimePicker/TimePicker.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as React from 'react';
import type { IComboBox, IComboBoxProps } from '../../ComboBox';
import type { IComboBoxProps } from '../../ComboBox';

/**
* {@docCategory TimePicker}
Expand All @@ -20,16 +19,15 @@ export interface ITimeRange {
export interface ITimePickerStrings {
/** Error message to render below the field if input parsing fails. */
invalidInputErrorMessage: string;
/** Default placeholder text to render within ComboBox if no placeholder is provided. */
defaultTimePickerPlaceholder: string;
}

/**
* {@docCategory TimePicker}
*/
export interface ITimePickerProps
extends Omit<
IComboBoxProps,
'options' | 'selectedKey' | 'defaultSelectedKey' | 'multiSelect' | 'text' | 'defaultValue' | 'onChange'
> {
extends Omit<IComboBoxProps, 'options' | 'selectedKey' | 'defaultSelectedKey' | 'multiSelect' | 'text' | 'onChange'> {
/**
* Label of the component
*/
Expand Down Expand Up @@ -70,14 +68,14 @@ export interface ITimePickerProps
strings?: ITimePickerStrings;

/**
* Default value of the TimePicker, if any
* Controlled current date for the TimePicker, if any
*/
defaultValue?: Date;
currentDate?: Date;

/**
* Callback issued when the time is changed
*/
onChange?: (event: React.FormEvent<IComboBox>, time: Date) => void;
onTimeChange?: (time: Date) => void;

/**
* Callback to localize the date strings displayed for dropdown options
Expand Down