forked from microsoft/fluentui
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(TimePicker-compat): preview release with added stories and docs (…
…microsoft#29677) * doc and stories * preview release * escape in ts comment * remove CustomValidation story because it makes more sense for later * spec * spec * onTimeSelect => onTimeChange * pick from combobox props instead of omit * validateFreeFormTime => formatTimeStringToDate * api * memo * type * update comment * use freeform timepicker with datepicker * small rename * more docs * fix merge issue * fix merge issue
- Loading branch information
1 parent
a6d0b97
commit d466462
Showing
18 changed files
with
351 additions
and
5 deletions.
There are no files selected for viewing
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
7 changes: 7 additions & 0 deletions
7
change/@fluentui-react-timepicker-compat-preview-fb5e9428-5ce6-4664-b04d-d484656b25a7.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": "minor", | ||
"comment": "feat: release preview package", | ||
"packageName": "@fluentui/react-timepicker-compat-preview", | ||
"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
34 changes: 34 additions & 0 deletions
34
packages/react-components/react-timepicker-compat-preview/docs/Migration.md
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,34 @@ | ||
# @fluentui/react-timepicker-compat-preview Migration Guide | ||
|
||
## Migration from v8 TimePicker | ||
|
||
### Property mapping | ||
|
||
TimePicker specific props: | ||
|
||
| v8 TimePicker | v9 TimePicker | | ||
| --------------------- | ------------------------------------------------------------- | | ||
| `dateAnchor` | `dateAnchor` | | ||
| `defaultValue` | `defaultSelectedTime` | | ||
| `increments` | `increment` | | ||
| `label` | handled by `Field` | | ||
| `onChange` | `onTimeChange` | | ||
| `onFormatDate` | `formatDateToTimeString` | | ||
| `onValidateUserInput` | `formatDateToTimeString` | | ||
| `onValidationResult` | `onTimeChange` contains error type in `data` | | ||
| `showSeconds` | `showSeconds` | | ||
| `strings` | use `Field` to display error. See 'Custom Validation' example | | ||
| `timeRange` | `startHour` and `endHour` | | ||
| `useHour12` | `hourCycle='h11'` or `hourCycle='h12'` | | ||
| `value` | `selectedTime` | | ||
|
||
V8 TimePicker is built on v8 Combobox, and v9 TimePicker compat on v9 Combobox. Please see Combobox migration guide for the rest of the props. | ||
|
||
\*In v9, any native HTML properties supported on an `<input>` element may be set on `<Combobox>`, including the `onChange` handler. Because of this, the v8 `onChange` selection callback has been updated to `onTimeChange`. The v9 TimePicker's `onChange` event behavior is the same as for an `<input>` element, or the v9 Input control. | ||
|
||
### Validate selected time | ||
|
||
V8 TimePicker allows custom validation on freeform input via `onValidateUserInput`. There is no way to validate selected option from dropdown. | ||
V9 TimePicker should be used together with `Field` component, and it provides more flexibility for custom validation. You can perform custom parsing and validation for freeform input using `formatDateToTimeString`. Validation of the selected time option from the dropdown can be achieved by validating the `selectedTime` within `onTimeChange` callback. | ||
|
||
v8 TimePicker has default error messages. v9 TimePicker has no default error message - it returns an error type from `onTimeChange` that can be used to display a custom error message. |
38 changes: 38 additions & 0 deletions
38
packages/react-components/react-timepicker-compat-preview/docs/Spec.md
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,38 @@ | ||
# @fluentui/react-timepicker-compat-preview Spec | ||
|
||
## Background | ||
|
||
Compat component for [V8 TimePicker](https://developer.microsoft.com/en-us/fluentui#/controls/web/timepicker). | ||
|
||
> ⚠️ A compat component is a component taken from v8 and partially updated with the v9 toolset while keeping its original functionality and most of the original API surface. The most noticeable change being the removal of all v8 dependencies and using only v9 dependencies. While this is a good first step, this is not the final v9 component. We are working on a fully fleshed v9 replacement that will follow all v9 patterns and conventions. | ||
TimePicker offers a control that’s optimized for selecting a time from a drop-down list or using free-form input to enter a custom time. | ||
|
||
**TimePicker is built on top of v9 Combobox. Combobox [Spec.md](../../react-combobox/docs/Spec.md) covers the variants, structure and accessibility of TimePicker. This spec highlights the TimePicker specifics.** | ||
|
||
## Prior Art | ||
|
||
- [26642](https://github.com/microsoft/fluentui/issues/26642) | ||
|
||
## Selection Behaviors | ||
|
||
When selecting a time, the time is validated, `onTimeChange` callback is fired with the selected time and the error if the time is invalid. TimePicker has two variants that provides different selection behavior: | ||
|
||
1. **Basic TimePicker**: a v9 Combobox with predefined time options. | ||
- Selecting an option from the dropdown invokes `onTimeChange` callback. | ||
2. **Freeform TimePicker**: a v9 Combobox with predefined time options that allows freeform input. | ||
- Selecting an option from the dropdown invokes `onTimeChange` callback. | ||
- Time is selected from freeform input when its value has changed, and TimePicker loses focus or <kbd>Enter</kbd> key is pressed. `onTimeChange` is triggered with the selected time from `input` value. This behavior aligns with the native `change` event for text input. | ||
> freeform TimePicker's selection behavior is different from freeform Combobox. Combobox lacks the equivalent callback for native change event ([29494](https://github.com/microsoft/fluentui/issues/29494)) | ||
## API | ||
|
||
See API at [TimePicker.types.ts](../src/components/TimePicker/TimePicker.types.ts). | ||
|
||
TimePicker share slots, visual and positioning props with Combobox. Its own specific props are: | ||
|
||
- For selection: `defaultSelectedTime`, `selectedTime` and `onTimeChange`. | ||
- parsing and validation of selected time text: `formatDateToTimeString` | ||
- For generating time options: | ||
- `startHour`, `endHour` and `increment` props are used to generate the predefined time options. | ||
- The options' format can be changed via `hourCycle` and `showSeconds` props. Further customization is available via `formatDateToTimeString`. |
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
1 change: 0 additions & 1 deletion
1
packages/react-components/react-timepicker-compat-preview/package.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
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
9 changes: 8 additions & 1 deletion
9
packages/react-components/react-timepicker-compat-preview/src/index.ts
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 |
---|---|---|
@@ -1,8 +1,15 @@ | ||
export { TimePicker, timePickerClassNames, useTimePickerStyles_unstable, useTimePicker_unstable } from './TimePicker'; | ||
export { | ||
TimePicker, | ||
timePickerClassNames, | ||
useTimePickerStyles_unstable, | ||
useTimePicker_unstable, | ||
formatDateToTimeString, | ||
} from './TimePicker'; | ||
export type { | ||
TimePickerProps, | ||
TimePickerSlots, | ||
TimePickerState, | ||
TimeSelectionData, | ||
TimeSelectionEvents, | ||
TimePickerErrorType, | ||
} from './TimePicker'; |
4 changes: 4 additions & 0 deletions
4
...ents/react-timepicker-compat-preview/stories/TimePicker/TimePickerControlled.md
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,4 @@ | ||
A TimePicker may have controlled selection and value. There are a few things to keep in mind: | ||
|
||
1. **Control `selectedTime` with `value` (or `defaultSelectedTime` with `defaultValue`)**: When the `selectedTime` is controlled or a `defaultSelectedTime` is provided, a controlled `value` or `defaultValue` must also be defined. Otherwise, the TimePicker will not be able to display a value before the Options are rendered. | ||
2. **Clearing input with null**: when controlled, the `selectedTime` prop should use `null` instead of `undefined` to clear the value of the TimePicker. |
71 changes: 71 additions & 0 deletions
71
...nents/react-timepicker-compat-preview/stories/TimePicker/TimePickerControlled.stories.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,71 @@ | ||
import * as React from 'react'; | ||
import { Field, makeStyles } from '@fluentui/react-components'; | ||
import { TimePicker, TimePickerProps, formatDateToTimeString } from '@fluentui/react-timepicker-compat-preview'; | ||
import story from './TimePickerControlled.md'; | ||
|
||
const useStyles = makeStyles({ | ||
root: { | ||
display: 'flex', | ||
flexDirection: 'column', | ||
rowGap: '20px', | ||
maxWidth: '300px', | ||
}, | ||
}); | ||
|
||
const DefaultSelection = () => { | ||
const [defaultSelectedTime] = React.useState(new Date('November 25, 2023 12:30:00')); | ||
return ( | ||
<Field label="Select a time (default Selection)"> | ||
<TimePicker | ||
startHour={8} | ||
endHour={20} | ||
defaultSelectedTime={defaultSelectedTime} | ||
defaultValue={formatDateToTimeString(defaultSelectedTime)} | ||
/> | ||
</Field> | ||
); | ||
}; | ||
|
||
const ControlledSelection = () => { | ||
const [selectedTime, setSelectedTime] = React.useState<Date | null>(new Date('November 25, 2023 12:30:00')); | ||
const [value, setValue] = React.useState<string>(selectedTime ? formatDateToTimeString(selectedTime) : ''); | ||
|
||
const onTimeChange: TimePickerProps['onTimeChange'] = (_ev, data) => { | ||
setSelectedTime(data.selectedTime); | ||
setValue(data.selectedTimeText ?? ''); | ||
}; | ||
const onInput = (ev: React.ChangeEvent<HTMLInputElement>) => { | ||
setValue(ev.target.value); | ||
}; | ||
|
||
return ( | ||
<Field label="Select a time (controlled Selection)"> | ||
<TimePicker | ||
startHour={8} | ||
endHour={20} | ||
selectedTime={selectedTime} | ||
onTimeChange={onTimeChange} | ||
value={value} | ||
onInput={onInput} | ||
/> | ||
</Field> | ||
); | ||
}; | ||
|
||
export const Controlled = () => { | ||
const styles = useStyles(); | ||
return ( | ||
<div className={styles.root}> | ||
<DefaultSelection /> | ||
<ControlledSelection /> | ||
</div> | ||
); | ||
}; | ||
|
||
Controlled.parameters = { | ||
docs: { | ||
description: { | ||
story, | ||
}, | ||
}, | ||
}; |
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,3 @@ | ||
`TimePicker` offers a control that’s optimized for selecting a time from a drop-down list or using free-form input to enter a custom time. | ||
|
||
Note: TimePicker is a compat component - its internal architecture does not follow all the principles regular Fluent UI v9 components follow - it is not composed of atomic hooks and it might be more difficult to tweak its appearance and behavior. It however follows Fluent 2 design and uses design tokens, it is production ready and it is stable. |
9 changes: 9 additions & 0 deletions
9
...onents/react-timepicker-compat-preview/stories/TimePicker/TimePickerFreeform.md
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,9 @@ | ||
TimePicker supports the `freeform` prop, which allows freeform text input. | ||
The selection behavior of freeform TimePicker aligns with the native `change` event behavior for text input: | ||
|
||
- When the value in the TimePicker input changes, and the TimePicker loses focus, the selected time is computed from the `input` value. | ||
- When TimePicker input value has changed and Enter key is pressed on the `input`: | ||
- if the dropdown is expanded and the `input` value is prefix of an option, the selected time is set to the matching option. | ||
- if the dropdown is collapsed or the `input` value does not match any option, the selected time is computed from `input` value. | ||
|
||
The selected time is available in `onTimeChange` callback. Use Field to display the error message based on the error type provided by `onTimeChange`. |
53 changes: 53 additions & 0 deletions
53
...ponents/react-timepicker-compat-preview/stories/TimePicker/TimePickerFreeform.stories.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,53 @@ | ||
import * as React from 'react'; | ||
import { Field, FieldProps, makeStyles } from '@fluentui/react-components'; | ||
import { TimePicker, TimePickerErrorType, TimePickerProps } from '@fluentui/react-timepicker-compat-preview'; | ||
import story from './TimePickerFreeform.md'; | ||
|
||
const useStyles = makeStyles({ | ||
control: { | ||
maxWidth: '300px', | ||
}, | ||
}); | ||
|
||
const getErrorMessage = (error?: TimePickerErrorType): FieldProps['validationMessage'] => { | ||
switch (error) { | ||
case 'invalid-input': | ||
return 'Invalid time format. Please use the 24-hour format HH:MM.'; | ||
case 'out-of-bounds': | ||
return 'Time out of the 10:00 to 19:59 range.'; | ||
case 'required-input': | ||
return 'Time is required.'; | ||
default: | ||
return ''; | ||
} | ||
}; | ||
|
||
export const FreeformWithErrorHandling = () => { | ||
const styles = useStyles(); | ||
|
||
const [errorType, setErrorType] = React.useState<TimePickerErrorType>(); | ||
const handleTimeChange: TimePickerProps['onTimeChange'] = (_ev, data) => { | ||
setErrorType(data.errorType); | ||
}; | ||
|
||
return ( | ||
<Field | ||
required | ||
label={ | ||
`Type a time outside of 10:00 to 19:59,` + | ||
` type an invalid time, or leave the input empty and close the TimePicker.` | ||
} | ||
validationMessage={getErrorMessage(errorType)} | ||
> | ||
<TimePicker className={styles.control} freeform startHour={10} endHour={20} onTimeChange={handleTimeChange} /> | ||
</Field> | ||
); | ||
}; | ||
|
||
FreeformWithErrorHandling.parameters = { | ||
docs: { | ||
description: { | ||
story, | ||
}, | ||
}, | ||
}; |
Oops, something went wrong.