diff --git a/packages/react-components/react-datepicker/etc/react-datepicker.api.md b/packages/react-components/react-datepicker/etc/react-datepicker.api.md index d4c4cfd5bae50e..2be34795c3d5ca 100644 --- a/packages/react-components/react-datepicker/etc/react-datepicker.api.md +++ b/packages/react-components/react-datepicker/etc/react-datepicker.api.md @@ -8,17 +8,17 @@ import type { ComponentProps } from '@fluentui/react-utilities'; import type { ComponentState } from '@fluentui/react-utilities'; +import { Field } from '@fluentui/react-field'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; import type { IBaseProps } from '@fluentui/utilities'; -import type { ICalloutProps } from '@fluentui/react'; import type { IComponentAs } from '@fluentui/utilities'; -import { InputField_unstable } from '@fluentui/react-input'; +import { Input } from '@fluentui/react-input'; +import type { InputProps } from '@fluentui/react-input'; import type { IStyle } from '@fluentui/style-utilities'; import type { IStyleFunctionOrObject } from '@fluentui/utilities'; -import type { ITextFieldProps } from '@fluentui/react'; import type { ITheme } from '@fluentui/style-utilities'; import type { PopoverProps } from '@fluentui/react-popover'; -import type { PopoverSurfaceProps } from '@fluentui/react-popover'; +import { PopoverSurface } from '@fluentui/react-popover'; import * as React_2 from 'react'; import type { Slot } from '@fluentui/react-utilities'; @@ -415,9 +415,8 @@ export type DatePickerProps = ComponentProps> & { componentRef?: React_2.RefObject; styles?: IStyleFunctionOrObject; theme?: ITheme; - calloutProps?: ICalloutProps; calendarProps?: CalendarProps; - textField?: ITextFieldProps; + textField?: InputProps; calendarAs?: IComponentAs; onSelectDate?: (date: Date | null | undefined) => void; label?: string; @@ -465,7 +464,6 @@ export interface DatePickerStrings extends CalendarStrings { // @public (undocumented) export interface DatePickerStyleProps { - className?: string; // (undocumented) disabled?: boolean; // (undocumented) @@ -691,7 +689,7 @@ export const useCalendarYearStyles_unstable: (props: CalendarYearStyleProps) => export const useDatePicker_unstable: (props: DatePickerProps, ref: React_2.Ref) => DatePickerState; // @public -export const useDatePickerStyles_unstable: (props: DatePickerStyleProps) => Record; +export const useDatePickerStyles_unstable: (state: DatePickerState) => DatePickerState; // @public (undocumented) export interface WeekCorners { diff --git a/packages/react-components/react-datepicker/package.json b/packages/react-components/react-datepicker/package.json index bb16d079700453..702758a665f24b 100644 --- a/packages/react-components/react-datepicker/package.json +++ b/packages/react-components/react-datepicker/package.json @@ -36,7 +36,7 @@ "@fluentui/react-utilities": "^9.5.1", "@fluentui/keyboard-keys": "^9.0.1", "@fluentui/utilities": "^8.13.6", - "@fluentui/react": "^8.105.5", + "@fluentui/react-field": "9.0.0-alpha.18", "@fluentui/react-icons": "^2.0.175", "@fluentui/react-shared-contexts": "^9.2.0", "@fluentui/react-tabster": "^9.5.0", diff --git a/packages/react-components/react-datepicker/src/components/DatePicker/DatePicker.tsx b/packages/react-components/react-datepicker/src/components/DatePicker/DatePicker.tsx index 57f8b123c5d72b..8b5a6225523ad6 100644 --- a/packages/react-components/react-datepicker/src/components/DatePicker/DatePicker.tsx +++ b/packages/react-components/react-datepicker/src/components/DatePicker/DatePicker.tsx @@ -1,12 +1,14 @@ import * as React from 'react'; import { renderDatePicker_unstable } from './renderDatePicker'; import { useDatePicker_unstable } from './useDatePicker'; +import { useDatePickerStyles_unstable } from './useDatePickerStyles'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; import type { DatePickerProps } from './DatePicker.types'; export const DatePicker: ForwardRefComponent = React.forwardRef((props, ref) => { const state = useDatePicker_unstable(props, ref); + useDatePickerStyles_unstable(state); return renderDatePicker_unstable(state); }) as ForwardRefComponent; DatePicker.displayName = 'DatePicker'; diff --git a/packages/react-components/react-datepicker/src/components/DatePicker/DatePicker.types.ts b/packages/react-components/react-datepicker/src/components/DatePicker/DatePicker.types.ts index bbf824ac3b8200..bf52d798c50d85 100644 --- a/packages/react-components/react-datepicker/src/components/DatePicker/DatePicker.types.ts +++ b/packages/react-components/react-datepicker/src/components/DatePicker/DatePicker.types.ts @@ -1,8 +1,10 @@ import * as React from 'react'; -import { InputField_unstable as InputField } from '@fluentui/react-input'; +import { Input } from '@fluentui/react-input'; +import { Field } from '@fluentui/react-field'; import { DayOfWeek, FirstWeekOfYear } from '../../utils'; -import type { ICalloutProps, ITextFieldProps } from '@fluentui/react'; -import type { PopoverProps, PopoverSurfaceProps } from '@fluentui/react-popover'; +import { PopoverSurface } from '@fluentui/react-popover'; +import type { InputProps } from '@fluentui/react-input'; +import type { PopoverProps } from '@fluentui/react-popover'; import type { ComponentProps, ComponentState, Slot } from '@fluentui/react-utilities'; import type { IStyle, ITheme } from '@fluentui/style-utilities'; import type { IStyleFunctionOrObject, IComponentAs } from '@fluentui/utilities'; @@ -11,9 +13,11 @@ import type { CalendarStrings, DateFormatting } from '../../utils'; export type DatePickerSlots = { root: NonNullable>; - // eslint-disable-next-line deprecation/deprecation -- https://github.com/microsoft/fluentui/issues/26505 - inputField: NonNullable>; + inputField: NonNullable>; + input: NonNullable>; wrapper: NonNullable>; + popover: NonNullable>>; + popoverSurface: NonNullable>; }; // eslint-disable-next-line @typescript-eslint/naming-convention @@ -45,11 +49,6 @@ export type DatePickerProps = ComponentProps> & { */ theme?: ITheme; - /** - * Pass callout props to callout component - */ - calloutProps?: ICalloutProps; - /** * Pass calendar props to calendar component */ @@ -59,7 +58,7 @@ export type DatePickerProps = ComponentProps> & { * Pass textField props to textField component. * Prop name is "textField" for compatibility with upcoming slots work. */ - textField?: ITextFieldProps; + textField?: InputProps; /** * Custom Calendar to be used for date picking @@ -260,8 +259,8 @@ export type DatePickerProps = ComponentProps> & { export type DatePickerState = ComponentState & Required> & { calendar: CalendarProps; - popover: Partial; - popoverSurface: PopoverSurfaceProps; + disabled: boolean; + isDatePickerShown: boolean; }; /** @@ -295,11 +294,6 @@ export interface DatePickerStrings extends CalendarStrings { * {@docCategory DatePicker} */ export interface DatePickerStyleProps { - /** - * Accept custom classNames - */ - className?: string; - // Insert DatePicker style props below disabled?: boolean; underlined?: boolean; diff --git a/packages/react-components/react-datepicker/src/components/DatePicker/renderDatePicker.tsx b/packages/react-components/react-datepicker/src/components/DatePicker/renderDatePicker.tsx index ea99431092e063..d455f466e2a033 100644 --- a/packages/react-components/react-datepicker/src/components/DatePicker/renderDatePicker.tsx +++ b/packages/react-components/react-datepicker/src/components/DatePicker/renderDatePicker.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; -import { Popover, PopoverSurface, PopoverTrigger } from '@fluentui/react-popover'; +import { PopoverTrigger } from '@fluentui/react-popover'; import { getSlots } from '@fluentui/react-utilities'; +import type { PopoverProps } from '@fluentui/react-popover'; import type { DatePickerSlots, DatePickerState } from './DatePicker.types'; /** @@ -9,23 +10,27 @@ import type { DatePickerSlots, DatePickerState } from './DatePicker.types'; export const renderDatePicker_unstable = (state: DatePickerState) => { const { slots, slotProps } = getSlots(state); - const { calendar, calendarAs: Calendar, popover, popoverSurface } = state; + const { calendar, calendarAs: Calendar } = state; return ( - + {popoverTriggerChildProps => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const rootProps: any = { root: popoverTriggerChildProps }; - return ; + return ( + + + + ); }} - + - - + + ); diff --git a/packages/react-components/react-datepicker/src/components/DatePicker/useDatePicker.tsx b/packages/react-components/react-datepicker/src/components/DatePicker/useDatePicker.tsx index 6be0f2ff0fa6c6..71b3832166d694 100644 --- a/packages/react-components/react-datepicker/src/components/DatePicker/useDatePicker.tsx +++ b/packages/react-components/react-datepicker/src/components/DatePicker/useDatePicker.tsx @@ -1,17 +1,23 @@ import * as React from 'react'; import { ArrowDown, Enter, Escape } from '@fluentui/keyboard-keys'; import { CalendarMonthRegular } from '@fluentui/react-icons'; -import { InputField_unstable as InputField } from '@fluentui/react-input'; -import { getNativeElementProps, resolveShorthand, useControllableState, useId } from '@fluentui/react-utilities'; +import { Input } from '@fluentui/react-input'; +import { Field } from '@fluentui/react-field'; +import { + getNativeElementProps, + mergeCallbacks, + resolveShorthand, + useControllableState, + useId, +} from '@fluentui/react-utilities'; import { Async } from '@fluentui/utilities'; -import { mergeClasses } from '@griffel/react'; import { compareDatePart, getDatePartHashValue, DayOfWeek, FirstWeekOfYear } from '../../utils'; import { Calendar } from '../Calendar/Calendar'; import { defaultDatePickerStrings } from './defaults'; -import { useDatePickerStyles_unstable } from './useDatePickerStyles'; -import type { ITextField } from '@fluentui/react'; -import type { InputOnChangeData, InputFieldProps_unstable as InputFieldProps } from '@fluentui/react-input'; -import type { OnOpenChangeData, OpenPopoverEvents } from '@fluentui/react-popover'; +import { OnOpenChangeData, OpenPopoverEvents, Popover } from '@fluentui/react-popover'; +import { PopoverSurface } from '@fluentui/react-popover'; +import type { PopoverProps } from '@fluentui/react-popover'; +import type { InputProps, InputOnChangeData } from '@fluentui/react-input'; import type { ICalendar } from '../Calendar/Calendar.types'; import type { DatePickerProps, DatePickerState } from './DatePicker.types'; @@ -20,7 +26,7 @@ function isDateOutOfBounds(date: Date, minDate?: Date, maxDate?: Date): boolean } function useFocusLogic() { - const textFieldRef = React.useRef(null); + const textFieldRef = React.useRef<{ focus: () => void }>(null); const preventFocusOpeningPicker = React.useRef(false); const focus = () => { @@ -217,8 +223,6 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref>(props, divProperties, ['value']); // // const iconProps = textFieldProps && textFieldProps.iconProps; const textFieldId = @@ -518,8 +512,7 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref ), disabled, - label, + id: textFieldId, placeholder, readOnly: !allowTextInput, required: isRequired, role: 'combobox', tabIndex, + ...textFieldProps, + }, + }); + + const inputFieldShorthand = resolveShorthand(props.inputField, { + defaultProps: { + label, + required: isRequired, validationMessage: errorMessage ?? statusMessage, validationState: errorMessage ? 'error' : undefined, - // eslint-disable-next-line deprecation/deprecation -- https://github.com/microsoft/fluentui/issues/26505 - ...(textFieldProps as InputFieldProps), - className: mergeClasses(classNames.textField, textFieldProps?.className), - id: textFieldId, }, required: true, }); - inputFieldShorthand.onBlur = onTextFieldBlur; - inputFieldShorthand.onChange = onTextFieldChanged; - inputFieldShorthand.onClick = onTextFieldClick; - inputFieldShorthand.onFocus = onTextFieldFocus; - inputFieldShorthand.onKeyDown = onTextFieldKeyDown; - inputFieldShorthand.value = formattedDate; + inputShorthand.onBlur = onTextFieldBlur; + inputShorthand.onClick = onTextFieldClick; + inputShorthand.onFocus = onTextFieldFocus; + inputShorthand.onKeyDown = onTextFieldKeyDown; + inputShorthand.onChange = mergeCallbacks(onTextFieldChanged, props.textField?.onChange); + inputShorthand.value = formattedDate; const wrapperShorthand = resolveShorthand(props.wrapper, { defaultProps: { 'aria-owns': isCalendarShown ? calloutId : undefined, - className: classNames.wrapper, + }, + required: true, + }); + + const popoverShorthand = resolveShorthand(props.popover, { + defaultProps: { + onOpenChange: onPopoverOpenChange, + open: isCalendarShown, + positioning: 'below-start', + trapFocus: true, + }, + required: true, + }); + + const popoverSurfaceShorthand = resolveShorthand(props.popoverSurface, { + defaultProps: { + 'aria-label': pickerAriaLabel, + id: calloutId, + role: 'dialog', }, required: true, }); return { calendarAs, + disabled: !!disabled, + isDatePickerShown: isCalendarShown, calendar: { ...calendarProps, @@ -609,28 +626,21 @@ export const useDatePicker_unstable = (props: DatePickerProps, ref: React.Ref>, + popoverSurface: PopoverSurface, }, inputField: inputFieldShorthand, + input: inputShorthand, root, wrapper: wrapperShorthand, }; diff --git a/packages/react-components/react-datepicker/src/components/DatePicker/useDatePickerStyles.ts b/packages/react-components/react-datepicker/src/components/DatePicker/useDatePickerStyles.ts index 7d5244ac100007..e7f856a445f619 100644 --- a/packages/react-components/react-datepicker/src/components/DatePicker/useDatePickerStyles.ts +++ b/packages/react-components/react-datepicker/src/components/DatePicker/useDatePickerStyles.ts @@ -1,20 +1,17 @@ import { tokens } from '@fluentui/react-theme'; import { makeStyles, mergeClasses, shorthands } from '@griffel/react'; import type { SlotClassNames } from '@fluentui/react-utilities'; -import type { DatePickerSlots, DatePickerStyles, DatePickerStyleProps } from './DatePicker.types'; +import type { DatePickerSlots, DatePickerState } from './DatePicker.types'; export const datePickerClassNames: SlotClassNames & Record = { root: 'fui-DatePicker', - inputField: 'fui-DatePicker-inputField', - wrapper: 'fui-DatePicker-wrapper', - callout: 'fui-DatePicker-callout', - withLabel: 'fui-DatePicker-event--with-label', - withoutLabel: 'fui-DatePicker-event--without-label', - disabled: 'fui-DatePicker--disabled ', + inputField: 'fui-DatePicker__inputField', + wrapper: 'fui-DatePicker__wrapper', + popover: 'fui-DatePicker__popover', + popoverSurface: 'fui-DatePicker__popoverSurface', + input: 'fui-DatePicker__input', }; -const TEXTFIELD_HEIGHT = 32; - const useRootStyles = makeStyles({ base: { fontFamily: tokens.fontFamilyBase, @@ -49,90 +46,34 @@ const useTextFieldStyles = makeStyles({ }, }); -const useIconStyles = makeStyles({ - base: { - color: tokens.colorNeutralForeground2, - cursor: 'pointer', - fontSize: tokens.fontSizeBase400, - // NOTE: Using 20px as we don't have an 18px line height in the ramp - lineHeight: tokens.lineHeightBase300, - ...shorthands.padding(`calc(${tokens.spacingHorizontalXS} + ${tokens.spacingHorizontalXXS} / 2)`), - paddingTop: '7px', - pointerEvents: 'initial', - position: 'absolute', - right: tokens.spacingHorizontalXS, - }, - disabled: { - color: tokens.colorNeutralForegroundDisabled, - cursor: 'default', - }, -}); - -const useStatusMessageStyles = makeStyles({ - base: { - color: tokens.colorPaletteDarkRedForeground2, - fontFamily: tokens.fontFamilyBase, - fontSize: tokens.fontSizeBase200, - fontWeight: tokens.fontWeightRegular, - marginTop: `calc(${tokens.spacingHorizontalXS} + ${tokens.spacingHorizontalXXS} / 2)`, - }, -}); - -const useReadOnlyTextFieldStyles = makeStyles({ - base: { - cursor: 'pointer', - height: `${TEXTFIELD_HEIGHT}px`, - lineHeight: `${TEXTFIELD_HEIGHT - 2}px`, - ...shorthands.overflow('hidden'), - textOverflow: 'ellipsis', - }, - underlined: { - lineHeight: `${TEXTFIELD_HEIGHT + 2}px`, - }, -}); - -const useReadOnlyPlaceholderStyles = makeStyles({ - base: { - color: tokens.colorNeutralForeground4, - '@media (forced-colors: active)': { - color: 'GrayText', - }, - }, -}); - /** * Apply styling to the DatePicker slots based on the state */ -// export const useDatePickerStyles_unstable = (state: DatePickerState): DatePickerState => { -export const useDatePickerStyles_unstable = (props: DatePickerStyleProps): Record => { +export const useDatePickerStyles_unstable = (state: DatePickerState): DatePickerState => { const rootStyles = useRootStyles(); const textFieldStyles = useTextFieldStyles(); - const iconStyles = useIconStyles(); - const statusMessageStyles = useStatusMessageStyles(); - const readOnlyTextFieldStyles = useReadOnlyTextFieldStyles(); - const readOnlyPlaceholderStyles = useReadOnlyPlaceholderStyles(); + const { disabled, isDatePickerShown } = state; + + state.root.className = mergeClasses( + datePickerClassNames.root, + rootStyles.base, + isDatePickerShown && 'is-open', + rootStyles.normalize, + state.root.className, + ); + + state.wrapper.className = mergeClasses(datePickerClassNames.wrapper, state.wrapper.className); + + state.input.className = mergeClasses( + datePickerClassNames.input, + textFieldStyles.base, + disabled && textFieldStyles.disabled, + state.input.className, + ); + + state.inputField.className = mergeClasses(datePickerClassNames.inputField, state.inputField.className); - const { className, disabled, isDatePickerShown, label, underlined } = props; + state.popoverSurface.className = mergeClasses(datePickerClassNames.popoverSurface, state.popoverSurface.className); - return { - root: mergeClasses( - datePickerClassNames.root, - rootStyles.base, - isDatePickerShown && 'is-open', - rootStyles.normalize, - className, - ), - wrapper: datePickerClassNames.wrapper, - textField: mergeClasses(textFieldStyles.base, disabled && textFieldStyles.disabled), - callout: datePickerClassNames.callout, - icon: mergeClasses( - iconStyles.base, - label ? datePickerClassNames.withLabel : datePickerClassNames.withoutLabel, - !disabled && datePickerClassNames.disabled, - disabled && iconStyles.disabled, - ), - statusMessage: statusMessageStyles.base, - readOnlyTextField: mergeClasses(readOnlyTextFieldStyles.base, underlined && readOnlyTextFieldStyles.underlined), - readOnlyPlaceholder: readOnlyPlaceholderStyles.base, - }; + return state; }; diff --git a/packages/react-components/react-datepicker/stories/DatePicker/index.stories.tsx b/packages/react-components/react-datepicker/stories/DatePicker/index.stories.tsx index eb6306b1bab963..ac01965718aafa 100644 --- a/packages/react-components/react-datepicker/stories/DatePicker/index.stories.tsx +++ b/packages/react-components/react-datepicker/stories/DatePicker/index.stories.tsx @@ -1,5 +1,4 @@ import { DatePicker } from '@fluentui/react-datepicker'; -import { initializeIcons } from '@fluentui/font-icons-mdl2'; import descriptionMd from './DatePickerDescription.md'; import bestPracticesMd from './DatePickerBestPractices.md'; @@ -13,8 +12,6 @@ export { CustomDateFormatting } from './DatePickerCustomDateFormatting.stories'; export { DateBoundaries } from './DatePickerDateBoundaries.stories'; export { ExternalControls } from './DatePickerExternalControls.stories'; -initializeIcons(); - export default { title: 'Preview Components/DatePicker', component: DatePicker,