-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#25730: TimePicker Default Value Fix, Controllable Usage, Example Upd…
…ates (#26482) * Update prop names, make renamed currentDate prop controlled * Added DatePicker and TimePicker combination example * Rephrase example text * Combine ComboBox imports * Fixed ComboBox import and updated DateTimePicker example * Moved examples into standalone files * Refactors and use new dateAnchor prop * Updated all examples * This should resolve the rest * This should resolve the outstanding issues... * Updated example strings * Updated examples * More changes to the basic example * Removed useComboBoxAsMenuWidth prop * Update formatTimeString function to return 00 if hours is 24 * Addressed comments * Renamed baseDate parameter to dateStartAnchor * Added prop onGetErrorMessage to get validation result * Set selectedTime to invalid or undefined * Clamp value to updated dateAnchor * Make docs look nice and readable * Removed unneeded imports * Updated clampedStartAnchor initialization value * Use internalDateAnchor * Use fallbackDateAnchor and update DateTimePicker example * Revert to ITimePickerProps to extend original omitted IComboBoxProps * Addressed comments * API snapshot update * Updated TimePicker tests and added test for new controlled component variant * Added test for handling changed base date anchor * Verify selected time changes on dateAnchor change * Added yarn change files * Resolve linting errors * Resolved more linting errors * Resolved linting import error by in-lining stack and styles * Addressed comments * Revert onChange prop types to avoid breaking changes and pass React.FormEvent<IComboBox> to setSelectedTime callback * Added explicit undefined pass to setSelectedTime in cases the dateAnchor changes * Updated examples and call onChange outside of useControllableValue to pass in proper event type * Control snapping of TimePicker values on DatePicker anchor change * Added tests for using defaultValue or value as date anchors * Took snapping logic out, fixed invalid key bug, and shared getDateAnchor function * Pass in placeholder since placeholder prop no longer has default value * Reflect optional timeOutOfBoundsErrorMessage string * Updated tests and snapshot * Addressed comments * Removed unnecessary string state variables * Followed comment suggestion and replaced onGetErrorMessage with onValidationError prop * Added onValidationError example * Added onValidationError test case * Export TimePickerErrorData * Renamed onValidationError and TimePickerErrorData to onValidationResult and TimePickerValidationData * Renamed example to reflect new callback prop * Use new example and fix casing * Use onValidationResult and only call when stored error message differs from latest error message * Added test for verifying onValidateResult only gets called on error message changes * Split big test into two smaller tests
- Loading branch information
1 parent
66f1e1d
commit 2d93a25
Showing
18 changed files
with
1,148 additions
and
136 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
change/@fluentui-date-time-utilities-d63c5a5c-4853-4fc8-b329-739b7176136a.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "patch", | ||
"comment": "chore: Refactored getDateFromTimeSelection variable names.", | ||
"packageName": "@fluentui/date-time-utilities", | ||
"email": "[email protected]", | ||
"dependentChangeType": "patch" | ||
} |
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-956c059f-a412-4dce-8d66-05ce162223fc.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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": "[email protected]", | ||
"dependentChangeType": "patch" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
packages/react-examples/src/react/TimePicker/TimePicker.Basic.Example.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
41 changes: 41 additions & 0 deletions
41
packages/react-examples/src/react/TimePicker/TimePicker.Controlled.Example.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
50 changes: 50 additions & 0 deletions
50
packages/react-examples/src/react/TimePicker/TimePicker.CustomTimeStrings.Example.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
62 changes: 62 additions & 0 deletions
62
packages/react-examples/src/react/TimePicker/TimePicker.DateTimePicker.Example.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
60 changes: 0 additions & 60 deletions
60
packages/react-examples/src/react/TimePicker/TimePicker.Example.tsx
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.