Skip to content

Commit

Permalink
chore(TimePicker-compat): add story for custom parsing from time stri…
Browse files Browse the repository at this point in the history
…ng to date (#29707)

* story

* typefix

* rename to parseTimeStringToDate

* Update packages/react-components/react-timepicker-compat-preview/stories/TimePicker/TimePickerCustomParsingAndValidation.stories.tsx

Co-authored-by: ling1726 <[email protected]>

* story update

* chglog

---------

Co-authored-by: ling1726 <[email protected]>
  • Loading branch information
YuanboXue-Amber and ling1726 authored Nov 9, 2023
1 parent 8a1688f commit ff6d3f1
Show file tree
Hide file tree
Showing 10 changed files with 92 additions and 53 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "fix: rename `formatTimeStringToDate` to `parseTimeStringToDate`.",
"packageName": "@fluentui/react-timepicker-compat-preview",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ export type TimePickerProps = Omit<ComponentProps<Partial<ComboboxSlots>, 'input
defaultSelectedTime?: Date;
onTimeChange?: (event: TimeSelectionEvents, data: TimeSelectionData) => void;
formatDateToTimeString?: (date: Date) => string;
formatTimeStringToDate?: (time: string | undefined) => TimeStringValidationResult;
parseTimeStringToDate?: (time: string | undefined) => TimeStringValidationResult;
};

// @public (undocumented)
export type TimePickerSlots = ComboboxSlots;

// @public
export type TimePickerState = ComboboxState & Required<Pick<TimePickerProps, 'freeform' | 'formatTimeStringToDate'>> & {
export type TimePickerState = ComboboxState & Required<Pick<TimePickerProps, 'freeform' | 'parseTimeStringToDate'>> & {
submittedText: string | undefined;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,14 @@ export type TimePickerProps = Omit<
/**
* In the freeform TimePicker, customizes the parsing from the input time string into a Date and provides custom validation.
*/
formatTimeStringToDate?: (time: string | undefined) => TimeStringValidationResult;
parseTimeStringToDate?: (time: string | undefined) => TimeStringValidationResult;
};

/**
* State used in rendering TimePicker
*/
export type TimePickerState = ComboboxState &
Required<Pick<TimePickerProps, 'freeform' | 'formatTimeStringToDate'>> & {
Required<Pick<TimePickerProps, 'freeform' | 'parseTimeStringToDate'>> & {
/**
* Submitted text from the input field. It is used to determine if the input value has changed when user submit a new value on Enter or blur from input.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const useTimePicker_unstable = (props: TimePickerProps, ref: React.Ref<HT
selectedTime: selectedTimeInProps,
showSeconds = false,
startHour = 0,
formatTimeStringToDate: formatTimeStringToDateInProps,
parseTimeStringToDate: parseTimeStringToDateInProps,
...rest
} = props;
const { freeform = false } = rest;
Expand Down Expand Up @@ -110,7 +110,7 @@ export const useTimePicker_unstable = (props: TimePickerProps, ref: React.Ref<HT
ref,
);

const defaultFormatTimeStringToDate = React.useCallback(
const defaultParseTimeStringToDate = React.useCallback(
(time: string | undefined) =>
getDateFromTimeString(time, dateStartAnchor, dateEndAnchor, { hourCycle, showSeconds }),
[dateEndAnchor, dateStartAnchor, hourCycle, showSeconds],
Expand All @@ -119,7 +119,7 @@ export const useTimePicker_unstable = (props: TimePickerProps, ref: React.Ref<HT
const state: TimePickerState = {
...baseState,
freeform,
formatTimeStringToDate: formatTimeStringToDateInProps ?? defaultFormatTimeStringToDate,
parseTimeStringToDate: parseTimeStringToDateInProps ?? defaultParseTimeStringToDate,
submittedText,
};

Expand Down Expand Up @@ -156,7 +156,7 @@ const useStableDateAnchor = (providedDate: Date | undefined, startHour: Hour, en
* - TimePicker loses focus, signifying a possible change.
*/
const useSelectTimeFromValue = (state: TimePickerState, callback: TimePickerProps['onTimeChange']) => {
const { activeOption, freeform, formatTimeStringToDate, submittedText, setActiveOption, value } = state;
const { activeOption, freeform, parseTimeStringToDate, submittedText, setActiveOption, value } = state;

// Base Combobox has activeOption default to first option in dropdown even if it doesn't match input value, and Enter key will select it.
// This effect ensures that the activeOption is cleared when the input doesn't match any option.
Expand All @@ -178,14 +178,14 @@ const useSelectTimeFromValue = (state: TimePickerState, callback: TimePickerProp
return;
}

const { date: selectedTime, errorType } = formatTimeStringToDate(value);
const { date: selectedTime, errorType } = parseTimeStringToDate(value);

// Only triggers callback when the text in input has changed.
if (submittedText !== value) {
callback?.(e, { selectedTime, selectedTimeText: value, errorType });
}
},
[callback, freeform, submittedText, formatTimeStringToDate, value],
[callback, freeform, submittedText, parseTimeStringToDate, value],
);

const handleKeyDown: ComboboxProps['onKeyDown'] = React.useCallback(
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This story sets custom time string in the dropdown options, and performs custom parsing from the input text to Date object on selection.

- The time display format in the dropdown can be customized using `formatDateToTimeString`.
- Freefrom TimePicker can have custom parsing for input text to Date object using `parseTimeStringToDate`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import * as React from 'react';
import { Field, makeStyles } from '@fluentui/react-components';
import { TimePicker, TimePickerProps } from '@fluentui/react-timepicker-compat-preview';
import story from './TimePickerFreeformCustomParsing.md';

const useStyles = makeStyles({
root: {
maxWidth: '300px',
},
});

const formatDateToTimeString = (date: Date) => {
const localeTimeString = date.toLocaleTimeString([], {
hour: 'numeric',
minute: '2-digit',
hourCycle: 'h12',
});
if (date.getHours() < 12) {
return `Morning: ${localeTimeString}`;
}
return `Afternoon: ${localeTimeString}`;
};

export const FreeformCustomParsing = () => {
const styles = useStyles();

const [anchor] = React.useState(() => new Date(2023, 1, 1));

const parseTimeStringToDate: TimePickerProps['parseTimeStringToDate'] = (time: string | undefined) => {
if (!time) {
return { date: null };
}

const [hours, minutes] = (time.split(' ')[1].match(/\d+/g) ?? []).map(Number);
const adjustedHours = time.includes('Afternoon: ') && hours !== 12 ? hours + 12 : hours;
const date = new Date(anchor.getFullYear(), anchor.getMonth(), anchor.getDate(), adjustedHours, minutes);

return { date };
};

const [selectedTimeText, setSelectedTimeText] = React.useState<string | undefined>(undefined);
const onTimeChange: TimePickerProps['onTimeChange'] = (_ev, data) => {
setSelectedTimeText(data.selectedTimeText);
};

return (
<div>
<Field label="Coffee time" className={styles.root}>
<TimePicker
freeform
dateAnchor={anchor}
formatDateToTimeString={formatDateToTimeString}
parseTimeStringToDate={parseTimeStringToDate}
onTimeChange={onTimeChange}
/>
</Field>
{selectedTimeText && <div>Selected time: {selectedTimeText}</div>}
</div>
);
};

FreeformCustomParsing.parameters = {
docs: {
description: {
story,
},
},
};
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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';
import story from './TimePickerFreeformWithErrorHandling.md';

const useStyles = makeStyles({
control: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import bestPracticesMd from './TimePickerBestPractices.md';

export { Default } from './TimePickerDefault.stories';
export { Controlled } from './TimePickerControlled.stories';
export { FreeformWithErrorHandling } from './TimePickerFreeform.stories';
export { CustomTimeString } from './TimePickerCustomTimeString.stories';
export { FreeformWithErrorHandling } from './TimePickerFreeformWithErrorHandling.stories';
export { FreeformCustomParsing } from './TimePickerFreeformCustomParsing.stories';
export { TimePickerWithDatePicker } from './TimePickerWithDatePicker.stories';

export default {
Expand Down

0 comments on commit ff6d3f1

Please sign in to comment.